ה ש י ר ו ת י ם ב א מ צ ע ו ת( 1 Object של clone ו finalize ע ב ו ד ה ע צ מ י ת 8 ח ל ק א - :finalize ה מ ח ל ק ה A מ מ מ ש ת א ת ה ש י ר ו ת finalize ה. finalize נ ק ר א כ א ש ר ה GC) Garbage Collector ט) מ ח ל י ל פ נ ו ת את האובייקט ו ה finalize מאפשר ל א ו ב י י ק ט ל ה ג ד י ר ו ל ב צ ע פ ע ו ל ו ת נקיון אחרונות לפני שהוא מפונה מ ה ז כ ר ו ן. public class A { private int i; public A(int i){ this.i = i; @Override protected void finalize() throws Throwable{ super.finalize() System.out.println(String.format("{%d : Bye Bye", this.i)); public static void main(string[] args) throws InterruptedException{ A[] aarr = new A[] {new A(1), new A(2); aarr = null; System.gc(); //call gc Thread.sleep(2000); //sleep for 2 seconds System.out.println("Finished running main!"); ב ד ו ג מ א ז ו, א נ ח נ ו מ י י צ ר י ם מ ע ר ך ש ל 2 א ו ב י י ק ט י ם מ ט י פ ו ס A ואז מוחקים את כל ההפניות לאובייקטים האלה ע י ה ה ש מ ה ש ל aarr ל null ב ש ו ר ה ה ש נ י ה ש ל ה. main לאחר מכן, אנחנו מבצעים קריאה י ז ו מ ה ל GC ב ש ב י ל לוודא שהאובייקטים מטיפוס A י פ ו נ ו. הריצו את הקוד ו ו ד א ו ש finalize אכן נ ק ר א ת ) נ י ת ן י ה י ה לראות את ההדפסות שלה ב ( console. הערה : אנחנו ק ו ר א י ם ל finalize של ( super.finalize Object ל י ת ר ב י ט ח ו ן, כיוון שהיא מכילה קוד ש א מ ו ר ל ר ו ץ ע ב ו ר כל אובייקט שעובר פ י נ ו י ) ונדירות הפעמים שבהן היא נ ד ר ס ת (.
2 כעת, התבוננו על התוכנית הבאה, המבוססת על תוכנית שניתנה כ ש א ל ה ב ב ח י נ ה ב ק ו ר ס באחד הסמסטרים ה ק ו ד מ י ם. public class A { private A next = null; public static A singlea = new A(); public static long instancescounter; @Override public void finalize() throws Throwable { super.finalize() this.next = singlea; singlea = this; public static void main(string[] args) throws InterruptedException { while (true) { A a = new A(); instancescounter++; if (instancescounter % 10000 == 0){ System.out.println(String.format("instances: {%d", instancescounter)); int singlealength = 0; for (A start = singlea; start!= null; start = start.next){ singlealength++; System.out.println(String.format("singleALength = %d", singlealength)); ב ת ו כ נ י ת ז ו ק ו ר ה מ ש ה ו מ פ ת י ע ה finalize נ ק ר את כשהאובייקט אמור ל ה י מ ח ק, וזה קורה כשאין שום מצביע לאובייקט הזה, אבל ה finalize בעצם מייצרת מצביע ל א ו ב י י ק ט עליו הוא מופעל ע י ק י ש ו ר ו ל ר ש י מ ה המקושרת המוצבעת ע י singlea שהוא שדה סטטי ) ש י מ ו ל ב ש ל A י ש ש ד ה מ ט י פ ו ס A ש נ ק ר א, next ו ל כ ן נוצר מבנה נתונים מקושר (.. האם האובייקט ימחק? התשובה היא שלא! למרות שה finalize נ ק ר את, ההצבעה שהיא מייצרת תמנע מה GC למחוק את האובייקט. א ז מ ה י ק ר ה כ א ן? ז ה ניראה שאנחנו מייצרים אינסוף אובייקטים שלא מתרוקנים. בסופו של ד ב ר נ ג י ע ל מ צ ב ש א נ ח נ ו צ ו ר כ י ם יותר מדי ז כ ר ו ן ע ל ה heap ונקבל שגיאה. א ם נ ר י ץ א ת ה ק ו ד כמו שהוא ע ל ה, eclipse י ק ח לו הרבה ז מ ן ל ה ג י ע ל ש ג י א ה. כ י צ ד נקבל אותה מהר י ו ת ר? נגביל את הזכרון המוקצה ל ת ו כ נ י ת. ה פ ר מ ט ר Xmx מ ק ב ע א ת ג ו ד ל ה heap המקסימלי המוקצה ל ת ו כ נ י ת בעת ההרצה שלה ) קיים אתחול ד י פ ו ל ט י, כ מ ו ב ן (. נקבע את ג ו ד ל ה heap המקסימלי להיות מאוד קטן ) )2 מ ג ה ב י י ט ע י האתחול הבא : ב, eclipse ד ר ך א ו ת ו ה מ ס ך ש ב ו קובעים את הפרמטרים שהתוכנית מקבלת ) חלון תחתון (
3 ב command line מריצים באופן הבא : java -Xmx2M A ת ו ך ז מ ן ק צ ר, נקבל שגיאת.OutOfMemory הכל טוב ויפה, אבל מאיפה אנחנו יודעים שהשגיאה התקבלה ב א ש מ ת ה? finalize י כ ו ל להיות שיצרנו אינסוף אובייקטים, ה GC ל א נקרא אף פ ע ם, ובאמת חרגנו מהזכרון. כלומר, אולי התוכנית הזו לא מוכיחה שניתן להחיות אובייקט אחרי הקריאה ל.finalize כאן אנחנו י כ ו ל י ם ל ה ע ז ר ב ה ד פ ס ה ) מדפיסים את אורכו של singlealength שמתארך ר ק כ ש ה finalize נ ק ר א (. ההסבר התיאורטי הוא שה GC מתוזמן ג ם ל פ י גודלה של המחסנית למול הגודל המקסימלי כ ש מ ג י ע י ם לגבול העליון של הזכרון המוקצה ע ב ו ר ה heap ה, GC נ ק ר א ב ש ב י ל ל נ ס ו ת ולפנות אותו. במקרה שלנו, הוא לא מפנה כ ל ו ם ו ל כ ן בסופו של דבר התוכנית נ ו פ ל ת ע ל.OutOfMemory ח ל ק ב - :clone כ ע ת נ ר צ ה ל מ מ ש clone במספר אופנים. ה מ ח ל ק ה ב ה נעסוק היא Box שעוטפת בתוכה אובייקט מסוג A. public class A { private int i; public A(int i){ this.i = i; public String tostring(){ return String.format("A({%d", this.i);
מכיוון שה. כזכור, השימוש( א נ ח נ ו, 4 public class Box { A a; String str; public Box(A a, String str){ this.a = a; this.str = str; public String tostring(){ return String.format("B(%s, %s)", this.a.tostring(), this.str); public Object clone(){ return new Box(this.a, this.str); public static void main(string[] args) { A a1 = new A(1); Box b1 = new Box(a1, "abc"); Box b2 = (Box)b1.clone(); System.out.println(b2); נ ס י ו ן ר א ש ו ן : נ מ מ ש א ת ה clone בעצמנו, מבלי ל ה ש ת מ ש בשירות שנורש מ Object ש. ה מ י מ ו י ע ש ה באמצעות קריאה לבנאי של.Box המימוש אכן עובד, אבל אנחנו צ ר י כ י ם ל ש י ם ל ב לכך שזהו שכפול ר ד ו ד shallow) ( ה. מ זאת אומרת? אמנם נוצר אובייקט Box חדש, אבל שני האובייקטים, b1 ו b2 מכילים הצבעה לאותו האובייקט מטיפוס ) A ה ש ד ה a ש ל ש נ י ה ם (. ב = השוואת= מבצע כ ת ו ב ו ת ( א ם נדפיס את השורה b1.a == b2.a נ ק ב ל True ב ש ב י ל לקבל שכפול עמוק אמיתי, ע ל י נ ו ל ש כ פ ל ג ם א ת a. נ ו ס י ף ל A ש י ר ו ת :clone ונעדכן את המימוש של clone ב ת ו ך Box כך שישכפל ג ם א ת a. public Object clone(){ return new A(this.i); public Object clone(){ return new Box((A)this.a.clone(), this.str); Object מחזירה clone ש י מ ו ל ב ל ש י מ ו ש ב casting צ ר י כ י ם ל ב צ ע casting לטיפוס המתאים.
אולי הוא? 5 לאחר שני השינויים האלה, ו ו ד א ו ש b1.a == b2.a מ ד פ י ס.false ש י מ ו לב שאנחנו מפעילים clone ע ל this.a ו ל א ק ו ר א י ם לבנאי שלו, כיוון שאנחנו ל א יודעים אם שימוש ב ב נ א י יבצע שכפול ע מ ו ק ) ו א ם ב כ ל ל יש משמעות ל ש כ פ ו ל ע מ ו ק בתוך האובייקט שלנו, ב מ ק ר ה ש ל A א י ן מ ש מ ע ו ת כ י הוא מכיל ר ק int יחיד (. מסיבה זו אנחנו סומכים ע ל ש י ר ו ת ה clone שלו שיבצע שכפול ע מ ו ק. נסיון שני : אז מימשנו clone וראינו את ההבדל בין שכפול ע מ ו ק ל ר ד ו ד. ו מ ה ע ם ה clone שירשנו מ Object ב ע צ ם י ו ד ע כ ב ר לבצע את השכפול הנדרש? בקוד הבא נ ו ר י ד א ת ה מ י מ ו ש ש ל נ ו ש ל clone ו נ ש ת מ ש ב clone שנורשה מ Object ו. ש י מ לב ששימוש ב clone ע ל ו ל לייצר שגיאה ) מ ו פ י ע ה בחתימה של, main נ י ג ע בזה מאוחר י ו ת ר (. א ת ה clone שאנחנו מפעילים ע ל b1 י ר ש נ ו מ Object הוא. הניראות שלה protected ו ל כ ן נ י ת ן ל ה ש ת מ ש ב ה מ ת ו ך ה ק ו ד ש ל protected) B נ ג י ש למחלקה היורשת ו ג ם למחלקות שנמצאות באותה החבילה (. public class Box { A a; String str; public Box(A a, String str){ this.a = a; this.str = str; public String tostring(){ return String.format("B(%s, %s)", this.a.tostring(), this.str); public static void main(string[] args) throws CloneNotSupportedException { A a1 = new A(1); Box b1 = new Box(a1, "abc"); Box b2 = (Box)b1.clone(); System.out.println(b2); הריצו את הקוד ובדקו את הפלט. ע ב ד ו ע ל י נ ו! י ר ש נ ו clone של Object אבל שימוש ב ו מ י י צ ר ש ג י א ה. ה א ם א פ ש ר לפתור את ז ה? כ ן! הטריק הוא ל ש נ ו ת א ת ה ה ג ד ר ה ש ל Box כ ך ש ת מ מ ש א ת ה מ נ ש ק Cloneable public class Box implements Cloneable וזהו. הריצו את הקוד ועכשיו הוא י ע ב ו ד ויבצע את השכפול. אז מהוא בעצם המנשק הזה? כשבוחנים את התיעוד שלו, רואים שזה מנשק שבעצם ל א מ ג ד י ר ש ו ם ש י ר ו ת. א ז ל מ ה ה ו א ט ו ב? כשמחלקה מממשת מנשק כ ל ש ה ו, ז ה א ו מ ר ש ה י א מ ק י י מ ת א י ת ו י ח ס is-a ומממשת את כל השירותים שהוא מ ג ד י ר. כשאין שירותים, ז ה עדין משאיר י ח ד. is-a כלומר, המנשק Cloneable הוא מנשק הצהרתי המחלקה Box מצהירה ע ל כך שהיא בת שכפול. האם השכפול הוא ע מ ו ק? בדקו את תוצאת ההשוואה ב י ן b1.a ל b2.a ו. א נ ח נ רואים שהשכפול הוא ל א ע מ ו ק.
ה ו א. 6 ב ע צ ם, ז ה ד י ה ג י ו נ י. ה clone של Object ל א באמת מכירה את המבנה הפנימי של המחלקה שהיא משכפלת, ולכן היא פ ש ו ט מ ע ת י ק ה א ת ת ו כ ן ה ז כ ר ו ן ש ל b1 לתוך מקום חדש שאליו י צ ב י ע. b2 ו מ ה כ ת ו ב ב ת ו ך b1 י? ש ת כ ת ו ב ו ת א ח ת ש ל str והשניה של. a כלומר, אין שכפול ל א ש ל a ו ל א ש ל.str ו י ש ע ו ד ב ע י ה ה clone שירשנו מ Object ל א נגיש מחוץ לחבילה שבה נ מ צ א ת B ה י ו ר ש ת ) ש ה י א ( Box ו ר ק ל מ ח ל ק ו ת בתוך החבילה שלנו. נ ג י ד ר ק ל מ ח ל ק ה נפתור את שתי הבעיות בשני שלבים. נתחיל מבעיית הנראות : נ ח ז י ר ל Box את השירות clone הוא, אבל הפעם י ש ת מ ש ב clone שנורש מ Object ב א ו פ ן ד ו מ ה, public Object clone() throws CloneNotSupportedException{ return super.clone(); ע ד כ נ ו א ת ה clone ש ל A. ש י מ ו ל ב, ה clone שהוספנו דורסת את clone של Object ו מ ר ח י ב ה א ת ה נ י ר א ו ת ש ל ה מ protected ל. public זה חוקי ב Java ו נ ד ב ר ע ל ז ה בהמשך הקורס. כ ע ת, נ י ת ן י ה י ה ל ה פ ע י ל clone על אובייקט מטיפוס Box מכל מקום. שימוש לב שהוספת השירות clone ל א פותרת אותנו מהצורך ל ה כ ר י ז על מימוש המנשק. Cloneable ז ה נדרש מאיתנו בגלל השימוש ב clone של.Object פתרנו את בעיית הניראות, ו ע ד כ ן נ ש א ר ל ו ו ד א ש a משוכפל ג ם ה ו א. מ כ י ו ו ן ש clone שלנו לא קוראת ל ש ו ם בנאי, הדרך לשכפל את a ה י א ק ו ד ם כ ל לשכפל את Box ו א ז לוודא שהשדות שמצריכים שכפול י ש ו כ פ ל ו. public Object clone() throws CloneNotSupportedException{ Box clone = (Box)super.clone(); clone.a = (A)this.a.clone(); return clone; ל א ח ר ביצוע השורה הראשונה אנחנו מקבלים שכפול רדוד של האובייקט שלנו. השורה השניה ד ו א ג ת ל כ ך ש a י ש ו כ פ ל, ו clone.a י צ ב י ע לשכפול. אם היו ל נ ו עוד שדות שמצריכים שכפול עמוק, היינו צ ר י כ י ם ל ש כ פ ל ג ם א ו ת ם ב נ פ ר ד. א ז א י ז ו מ ש ת י ה ש י ט ו ת ט ו ב ה יותר? האם שימוש בבנאי או שימוש ב clone של?Object השיטה העדיפה היא השיטה השניה כ י ו ו ן ה clone של Object הוא י ע י ל כיוון שהוא מעתיק שטחי ז כ ר ו ן ש ל מ י ם, לעומת קריאה לבנאי שגם כ ו ת ב ת לזכרון אבל מבצעת ע ו ד פ ע ו ל ו ת.