CodeGym /בלוג Java /Random-HE /50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core...
John Squirrels
רָמָה
San Francisco

50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core. חלק 1

פורסם בקבוצה
שלום לכולם, גבירותיי ורבותיי, מהנדסי תוכנה! בואו נדבר על שאלות ראיון. על מה אתה צריך להתכונן ומה אתה צריך לדעת. זה זמן מצוין לסקור או ללמוד את הנקודות הללו בפעם הראשונה. 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 1 - 1 בסופו של דבר קיבלתי אוסף די נרחב של שאלות נפוצות על OOP, תחביר Java, חריגים של Java, אוספים ו-multithreading, שאותם אחלק לכמה חלקים מטעמי נוחות. קשה לכסות הכל בבת אחת, אבל אני מקווה שהחומר הזה יהווה בסיס טוב למי שמתכונן למצוא את העבודה הראשונה שלהם כמתכנת. למען ההבנה והשימור הטובים ביותר, אני ממליץ לסרוק גם מקורות אחרים. אתה יכול לקבל הבנה עמוקה יותר של מושג על ידי גישה אליו מכמה זוויות שונות. חָשׁוּב:על Java נדבר רק לפני גרסה 8. כל החידושים שהגיעו בגרסאות 9, 10, 11, 12 ו-13 לא ייחשבו כאן. כל רעיון/הערה כיצד לשפר את התשובות יתקבלו בברכה . תהנה מהקריאה שלך. בוא נלך!

ראיון Java: שאלות על OOP

1. מהם המאפיינים של Java?

תשובה:
  1. מושגי OOP:

    1. כיוון עצמים
    2. יְרוּשָׁה
    3. כימוס
    4. רב צורתיות
    5. הַפשָׁטָה
  2. חוצה פלטפורמות: ניתן להפעיל תוכנית Java על כל פלטפורמה ללא שינויים. כמובן, זה דורש JVM מותקן (מכונה וירטואלית של Java).

  3. ביצועים גבוהים: המהדר Just-In-Time (JIT) מאפשר ביצועים גבוהים. מהדר JIT ממיר את קוד הבתים לקוד מכונה ואז ה-JVM מתחיל בביצוע.

  4. ריבוי ההליכים: ה-JVM יוצר חוט של ביצוע הנקרא main thread. מתכנת יכול ליצור שרשורים מרובים על ידי גזירה ממחלקת Thread או יישום Runnableהממשק.

2. מהי ירושה?

ירושה פירושה שמחלקה אחת יכולה לרשת מחלקה אחרת (באמצעות מילת המפתח extends ). זה אומר שאתה יכול לעשות שימוש חוזר בקוד מהכיתה שאתה יורש. המחלקה הקיימת ידועה בשם superclassוהמחלקה החדשה שנוצרה היא subclass. אנשים אומרים גם להשתמש במונחים הורה ו child.
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
היכן Animalנמצא parentוהו . Dog_child

3. מהי אנקפסולציה?

שאלה זו נשאלת לעתים קרובות בראיונות למשרות מפתח Java. Encapsulation מסתירה את היישום על ידי שימוש במתאמני גישה, מגברים והגדרות. זה נעשה על מנת למנוע גישה חיצונית בכל מקום שמפתחים חושבים שהיא נחוצה. דוגמה פשוטה מהחיים האמיתיים היא המכונית. אין לנו גישה ישירה לפעולת המנוע. כל מה שאנחנו צריכים לעשות זה להכניס את המפתח לתוך ההצתה ולהפעיל את המנוע. התהליכים שמתרחשים מתחת למכסה המנוע אינם ענייננו. זאת ועוד, אם היינו מתערבים בפעילות המנוע, הדבר עלול להוביל למצב בלתי צפוי, שעלול לגרום נזק לרכב ולגרום לנזק גופני. בדיוק אותו דבר קורה בתכנות. זה מתואר היטב בויקיפדיה . יש גם מאמר על אנקפסולציה ב- CodeGym .

4. מהו פולימורפיזם?

פולימורפיזם הוא היכולת של תוכנית לטפל באובייקטים עם אותו ממשק באותו אופן, ללא מידע על הסוג הספציפי של האובייקט. כמו שנאמר, "ממשק אחד - יישומים רבים". בעזרת פולימורפיזם, ניתן לשלב ולהשתמש בסוגים שונים של אובייקטים על סמך התנהגויות משותפות. לדוגמה, יש לנו כיתת חיות שיש לה שני צאצאים: כלב וחתול. לכיתה הגנרית Animal יש התנהגות משותפת לכולם, היכולת להשמיע קול. אנו משתמשים ביכולות פולימורפיות כאשר אנו צריכים לאסוף את כל מה שיורש את המחלקה Animal ולבצע את שיטת "השמיע קול". כך זה נראה:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
במילים אחרות, פולימורפיזם מועיל. וזה תקף גם לשיטות פולימורפיות (עמוסות יתר). כיצד להשתמש בפולימורפיזם

שאלות ראיון על תחביר Java

5. מהו בנאי ב-Java?

לבנאים יש את המאפיינים הבאים:
  1. כאשר אובייקט חדש נוצר, התוכנית משתמשת בבנאי המתאים כדי ליצור אותו.
  2. בנאי הוא כמו שיטה. המאפיינים הייחודיים שלו נעוצים בעובדה שאין ערך החזרה (כולל void) ושהשם שלו זהה לשם המחלקה.
  3. אם לא נוצר בנאי באופן מפורש, בנאי ריק נוצר באופן אוטומטי.
  4. ניתן לעקוף בנאי.
  5. אם אתה מצהיר על בנאי עם פרמטרים אבל צריך גם אחד בלי פרמטרים, אז אתה חייב ליצור אותו בנפרד, מכיוון שהוא לא ייווצר אוטומטית.

6. אילו שתי מחלקות לא יורשות את Object?

אל תלך שולל על ידי שאלות טריק - אין שיעורים כאלה. כל המחלקות יורשות את המחלקה Object ישירות או דרך אבות!

7. מהו משתנה מקומי?

זוהי שאלת ראיון פופולרית נוספת עבור מפתחי Java. משתנה מקומי הוא משתנה המוגדר בתוך שיטה וקיים כל עוד השיטה מבוצעת. ברגע שהביצוע מסתיים, המשתנה המקומי מפסיק להתקיים. הנה תוכנית שמשתמשת במשתנה מקומי בשם helloMessage בשיטת main():
public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8. מהו משתנה מופע?

משתנה מופע הוא משתנה המוצהר בתוך מחלקה. זה קיים כל עוד אובייקט קיים. לדוגמה, יש לנו מחלקה Bee, שיש לה שני משתני מופע - nectarLoad ו-maxNectarLoad:
public class Bee {

   /**
    * Current nectar load
    */
   private double nectarLoad;

   /**
    * Maximum nectar that can the bee can collect.
    */
   private double maxNectarLoad = 20.0;

  ...
}

9. מהם משנה גישה?

משנה גישה הם מנגנון להתאמה אישית של גישה למחלקות, שיטות ומשתנים. השינויים הבאים קיימים, ברשימה לפי סדר הגדלת הגישה:
  1. private- משנה גישה זה משמש בשיטות, שדות ובנאים. הגישה מוגבלת למחלקה שבה הם מוצהרים.
  2. package-private (default)- זוהי רמת הגישה המוגדרת כברירת מחדל עבור מחלקות. הגישה מוגבלת לחבילה הספציפית שבה מוכרזים מחלקה, שיטה, משתנה או בנאי.
  3. protected- משנה גישה זה מציע את אותה רמת גישה כמו package-privateבתוספת גישה למחלקות שיורשות מחלקה עם protectedהמשנה.
  4. public- רמת גישה זו משמשת גם לשיעורים. רמת גישה זו פירושה שיש גישה מלאה בכל האפליקציה.
50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 1 - 2

10. מהי עקיפת השיטה?

אנו עוקפים שיטות כאשר כיתת ילד רוצה לשנות את ההתנהגות של כיתת ההורים שלה. אם אנחנו צריכים לעשות גם את מה שיש בשיטת האב, נוכל להשתמש ב-super.methodName() ב-child, שיבצע את שיטת האב. אנחנו יכולים להוסיף את ההיגיון הנוסף שלנו לאחר מכן. דרישות שיש להקפיד עליהן:
  • חתימת השיטה חייבת להיות זהה
  • ערך ההחזר חייב להיות זהה

11. מהן חתימות השיטה?

50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 1 - 3חתימת שיטה היא השילוב של שם השיטה והארגומנטים שהשיטה לוקחת. חתימת השיטה היא מזהה ייחודי של שיטה בעת עומס יתר של שיטות.

12. מהו עומס יתר בשיטה?

עומס יתר של שיטות הוא מאפיין של פולימורפיזם שבו אנו משנים את חתימת השיטה כדי ליצור מספר שיטות שמבצעות את אותה פעולה:
  • אותו שם
  • טיעונים שונים
  • יכולים להיות סוגי החזרות שונים
לדוגמה, ניתן להעמיס על המתודה ArrayListשל המחלקה add(), מה שמאפשר לנו להוסיף בדרכים שונות בהתאם לארגומנטים הקלט:
  • add(Object o)- השיטה הזו פשוט מוסיפה אובייקט
  • add(int index, Object o)- שיטה זו מוסיפה אובייקט באינדקס מסוים
  • add(Collection<Object> c)- שיטה זו מוסיפה רשימה של אובייקטים
  • add(int index, Collection<Object> c)- שיטה זו מוסיפה רשימה של אובייקטים החל מאינדקס ספציפי.

13. מהו ממשק?

Java אינה תומכת בהורשה מרובה. כדי להתגבר על מגבלה זו, נוספו ממשקים בצורה שאנו מכירים ואוהבים ;) במשך זמן רב, לממשקים היו רק שיטות ללא כל יישום. בהקשר של תשובה זו, בואו נדבר עליהם. לדוגמה:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
כמה פרטים נובעים מכך:
  • כל השיטות בממשק הן ציבוריות ומופשטות
  • כל המשתנים הם סופיים סטטיים ציבוריים
  • מחלקות לא יורשות ממשקים (כלומר אנחנו לא משתמשים במילת המפתח extends). במקום זאת, מחלקות מיישמות אותם (כלומר אנו משתמשים במילת המפתח implements). יתר על כן, אתה יכול ליישם כמה ממשקים שאתה רוצה.
  • מחלקות המטשמות ממשק חייבות לספק יישום של כל השיטות שנמצאות בממשק.
ככה:
public class Cat implements Animal {
   public void makeSound() {
       // Method implementation
   }

   public void eat() {
       // Implementation
   }

   public void sleep() {
       // Implementation
   }
}

14. מהי שיטת ברירת מחדל בממשק?

עכשיו בואו נדבר על שיטות ברירת מחדל. בשביל מה הם נועדו? למי הם מיועדים? שיטות אלו נוספו כדי לשרת "שתי ידיים". על מה אני מדבר? ובכן, מצד אחד, היה צורך להוסיף פונקציונליות חדשה: lambdas ו- Stream API. מצד שני, היה צורך לשמור על מה ש-Java מפורסמת בו - תאימות לאחור. לשם כך, ממשקים היו זקוקים לכמה פתרונות מוכנים חדשים. כך הגיעו אלינו שיטות ברירת המחדל. שיטת ברירת מחדל היא שיטה מיושמת בממשק, המסומנת במילת defaultהמפתח. למשל השיטה המוכרת stream()בממשק Collection. תאמין לי, הממשק הזה לא פשוט כמו שהוא נראה. או גם forEach()השיטה המפורסמת לא פחות בממשק Iterable. זה גם לא היה קיים עד שנוספו שיטות ברירת המחדל. אגב, תוכלו לקרוא על זה גם ב-CodeGym כאן .

15. כיצד אם כן יורש שתי שיטות ברירת מחדל זהות?

התשובה הקודמת לגבי מהי שיטת ברירת מחדל מעלה שאלה נוספת. אם אתה יכול ליישם שיטות בממשקים, אז תיאורטית אתה יכול ליישם שני ממשקים באותה שיטה. איך אנחנו עושים את זה? להלן שני ממשקים שונים באותה שיטה:
interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
ויש לנו מחלקה שמיישמת את שני הממשקים האלה. אבל איך בדיוק נבחר שיטה ספציפית בממשק A או B? המבנה המיוחד הבא מאפשר זאת: A.super.foo():
public class C implements A, B {
   public void fooA() {
       A.super.foo();
   }

   public void fooB() {
       B.super.foo();
   }
}
לפיכך, fooA()השיטה תשתמש foo()בשיטת ברירת המחדל של Aהממשק, בעוד שהשיטה fooB()תשתמש foo()בשיטת הממשק B.

16. מהן שיטות ושיעורים מופשטים?

בג'אווה abstractהיא מילה שמורה. הוא משמש לציון מחלקות ושיטות מופשטות. ראשית, אנחנו צריכים הגדרות. מתודה אבסטרקטית היא שיטה המוצהרת באמצעות abstractמילת המפתח ללא יישום במחלקה אבסטרקטית. כלומר, זו שיטה כמו בממשק, אבל בתוספת מילת מפתח, למשל:
public abstract void foo();
מחלקה מופשטת היא מחלקה המסומנת גם במילת abstractהמפתח:
public abstract class A {

}
למחלקה מופשטת מספר תכונות:
  • אתה לא יכול ליצור אובייקט של מחלקה מופשטת
  • יכולות להיות לה שיטות מופשטות
  • ייתכן שגם אין לה שיטות מופשטות
דרושים שיעורים מופשטים להפשטה (סליחה על הטאוטולוגיה) שיש לה קבוצה של התנהגויות ומצבים משותפים (כלומר, שיטות ומשתנים). החיים האמיתיים מלאים בדוגמאות. הכל סביבנו. "חיה", "מכונית", "דמות גיאומטרית" וכן הלאה.

17. מה ההבדל בין String, StringBuilder ו-StringBuffer?

Stringערכים מאוחסנים במאגר מחרוזות קבוע. ברגע שנוצר מחרוזת, היא מופיעה במאגר הזה. ואי אפשר למחוק את זה. לדוגמה:
String name = "book";
המשתנה יצביע על מאגר המחרוזות הקבוע. 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 1 - 4הגדרת משתנה השם לערך אחר, יש לנו:
name = "pen";
מאגר המיתרים הקבוע נראה כך: 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 1 - 5במילים אחרות, שני הערכים נשארים שם. מאגר מיתרים:
  • Stringערכים מאוחסנים בערימה. אם ערך שונה, הערך החדש יחליף את הישן.
  • String Bufferהוא מסונכרן ולכן בטוח בשרשור.
  • בשל בטיחות החוטים, הביצועים שלו גרועים.
דוגמא:
StringBuffer name = “book”;
50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 1 - 6ברגע שהערך של משתנה השם משתנה, הערך בערימה משתנה: 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 1 - 7StringBuilder זהה לחלוטין ל- StringBuffer, רק שהוא לא בטוח ל-thread. כתוצאה מכך, הוא מהיר יותר מ- StringBuffer.

18. מה ההבדל בין מחלקה מופשטת לממשק?

שיעור מופשט:
  • למחלקות מופשטות יש בנאי ברירת מחדל. זה נקרא בכל פעם שנוצר צאצא של המעמד המופשט.
  • הם יכולים לכלול גם שיטות מופשטות וגם לא מופשטות. באופן כללי, מחלקה מופשטת לא חייבת להיות בעלת שיטות מופשטות.
  • מחלקה שיורשת אבסטרקט חייבת ליישם רק שיטות מופשטות.
  • למחלקה מופשטת יכולים להיות משתני מופע (ראה שאלה מס' 5).
מִמְשָׁק:
  • לממשק אין בנאי ולא ניתן לאתחל אותו.
  • ניתן להוסיף רק שיטות מופשטות (למעט שיטות ברירת מחדל).
  • מחלקות שמיישמות את הממשק חייבות ליישם את כל השיטות (למעט שיטות ברירת מחדל).
  • לממשקים יכולים להיות רק קבועים.

19. מדוע הגישה לאלמנט במערך היא O(1)?

שאלה זו נשאלה ממש בראיון האחרון שלי. כפי שלמדתי מאוחר יותר, מטרת השאלה הזו היא לראות איך אדם חושב. ברור שיש מעט ערך מעשי בידע הזה. רק לדעת את זה זה מספיק. ראשית, עלינו להבהיר ש-O(1) הוא סימון עבור מורכבות הזמן של אלגוריתם "זמן קבוע". במילים אחרות, ייעוד זה מציין את זמן הביצוע המהיר ביותר. כדי לענות על שאלה זו, עלינו לשקול את מה שאנו יודעים על מערכים. כדי ליצור intמערך, עלינו לכתוב את הדברים הבאים:
int[] intArray = new int[100];
ניתן להסיק מספר מסקנות מתחביר זה:
  1. כאשר מכריזים על מערך, הסוג שלו ידוע. אם הסוג ידוע, אז הגודל של כל תא במערך ידוע.
  2. גודל המערך כולו ידוע.
מכאן נובע שכדי להבין לאיזה תא לכתוב, אנחנו רק צריכים לחשב לאיזה אזור בזיכרון לכתוב. עבור מחשב, זה קל פיסי. המחשב יודע היכן מתחיל הזיכרון המוקצה, מספר האלמנטים וגודלו של כל תא. כל זה אומר שמקום הכתיבה יהיה שווה למקום ההתחלה של המערך + גודל כל תא כפול האינדקס.

אז איך מגיעים ל-O(1) כשניגשים לאובייקטים ב-ArrayList?

שאלה זו באה מיד אחרי הקודמת. האמת היא שכשעובדים עם מערך שמחזיק פרימיטיבים, אנחנו יודעים מראש (בזמן היצירה) את גודל סוג האלמנט. אבל מה אנחנו עושים אם יש לנו היררכיית ירושה כזו ואנחנו 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 1 - 8רוצים ליצור אוסף עבור אלמנטים מסוג A ולהוסיף יישומים שונים (B, C ו-D):
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
במצב זה, כיצד אנו מחשבים את גודלו של כל תא? אחרי הכל, כל אובייקט יהיה שונה, אולי עם שדות נוספים שונים. מה לעשות? כאן נשאלת השאלה בצורה שנועדה לבלבל אותך. אנו יודעים שהאוסף אינו מאחסן ישירות חפצים. זה מאחסן רק הפניות לאובייקטים. ולכל הפניות יש אותו גודל, וזה ידוע. כתוצאה מכך, אנו מחשבים כאן כתובות באותו אופן כמו בשאלה הקודמת.

21. אוטובוקסינג ו-unboxing

רקע היסטורי: אוטובוקסינג ו-unboxing הם חלק מהחידושים העיקריים ב-JDK 5. Autoboxing הוא תהליך של המרה אוטומטית מסוג פרימיטיבי למחלקת עטיפה מתאימה. Unboxing הוא ההיפך הגמור מ-autoboxing. זהו תהליך של המרת שיעור עטיפה לפרימיטיבי. אבל אם הערך של עטיפה הוא null, אז א NullPointerExceptionיזרק במהלך unboxing.

פרימיטיבים והעטיפות המתאימות להם

פְּרִימִיטִיבִי שיעור עטיפה
בוליאני בוליאנית
int מספר שלם
בייט בייט
לְהַשְׁחִיר אופי
לָצוּף לָצוּף
ארוך ארוך
קצר קצר
לְהַכפִּיל לְהַכפִּיל

// תיוג אוטומטי קורה:

  • בעת הקצאת פרימיטיבי להתייחסות לשיעור עטיפה:

    לפני Java 5:

    // Manual boxing (the way it was BEFORE Java 5).
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // And so on for other types
    }
    
    After Java 5:
    // Automatic boxing (the way it became in Java 5).
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // And so on for other types
    }
  • כאשר פרימיטיבי מועבר כטיעון לשיטה המצפה לעטיפה:

    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }

// Unboxing קורה:

  • כאשר אנו מקצים מופע של מחלקת עטיפה למשתנה פרימיטיבי:

    // BEFORE Java 5:
    int intValue = new Integer(4).intValue();
    double doubleValue = new Double(2.3).doubleValue();
    char c = new Character((char) 3).charValue();
    boolean b = Boolean.TRUE.booleanValue();
    
    // And after JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
  • במהלך פעולות חשבון. הפעולות חלות רק על טיפוסים פרימיטיביים, ולכן יש צורך ב-unboxing לפרימיטיביים.

    // BEFORE Java 5:
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // A comparison used to require this:
    integerBox1.intValue() > integerBox2.intValue()
    
    // In Java 5
    integerBox1 > integerBox2
  • כאשר מעבירים מופע של מחלקת עטיפה למתודה שלוקחת את הפרימיטיבי המתאים:

    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }

22. מהי מילת המפתח הסופית והיכן משתמשים בה?

ניתן להשתמש במילת finalהמפתח על משתנים, שיטות ומחלקות.
  1. לא ניתן לשנות את הערך של משתנה סופי לאחר אתחולו.
  2. כיתה אחרונה היא סטרילית :) היא לא יכולה להביא ילדים.
  3. לא ניתן לעקוף שיטה סופית על ידי צאצא.
כיסינו את הדברים ברמה גבוהה. עכשיו בואו לצלול עמוק יותר.

משתנים סופיים

Java נותנת לנו שתי דרכים להכריז על משתנה ולהקצות לו ערך:
  1. אתה יכול להכריז על משתנה ולאתחל אותו מאוחר יותר.
  2. אתה יכול להכריז על משתנה ולהקצות ערך מיד.
הנה דוגמה שמדגימה את השימושים האלה במשתנים סופיים:
public class FinalExample {

   // A static final variable that is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";

   // A final variable that is not initialized, but will only work if you
   // initialize it in the constructor:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // The final FinalExample.FINAL_EXAMPLE_NAME field cannot be accessed
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // The final Config.creationTime field cannot be accessed
//    finalExample.creationTime = 1L;
   }
}

האם משתנה סופי יכול להיחשב קבוע?

מכיוון שאיננו יכולים להקצות ערכים חדשים למשתנים סופיים, נראה כי מדובר במשתנים קבועים. אבל רק במבט ראשון: אם סוג הנתונים של המשתנה הוא immutable, אז כן, הוא קבוע. אבל אם סוג הנתונים הוא mutable, כלומר ניתן לשינוי, אז ניתן יהיה להשתמש בשיטות ומשתנים כדי לשנות את הערך של האובייקט שאליו משתנה הפניה final. בגלל זה, זה לא יכול להיקרא קבוע. הדוגמה הבאה מראה שמשתנים סופיים מסוימים הם באמת קבועים, בעוד שאחרים אינם, מכיוון שהם ניתנים לשינוי.
public class FinalExample {

   // Immutable final variables
   final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // Mutable final variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("Constant?");
}

משתנים סופיים מקומיים

כאשר finalמשתנה נוצר בתוך שיטה, הוא נקרא משתנה local final:
public class FinalExample {

   public static void main(String[] args) {
       // You can do this
       final int minAgeForDriveCar = 18;

       // Or you can do this, in a for-each loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
אנו יכולים להשתמש במילת המפתח הסופית בלולאה משופרת, מכיוון שמשתנה חדש נוצר לאחר כל איטרציה של הלולאה. זכור שזה לא חל על לולאה רגילה, ולכן נקבל שגיאת זמן קומפילציה.
// The final local j variable cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

שיעור אחרון

מחלקה שהוכרזה finalכבלתי ניתנת להארכה. במילים פשוטות יותר, אף כיתה אחרת לא יכולה לרשת את זה. דוגמה מצוינת למחלקה finalב-JDK היא String. הצעד הראשון ליצירת מחלקה בלתי ניתנת לשינוי הוא לסמן אותה כ- final, ובכך למנוע את הארכתה:
public final class FinalExample {
}

// Compilation error!
class WantsToInheritFinalClass extends FinalExample {
}

שיטות סופיות

כאשר שיטה מסומנת כסופית, היא נקראת שיטה סופית (הגיוני, נכון?). לא ניתן לעקוף שיטה סופית בכיתת ילד. אגב, המתודות wait() ו-notify() של המחלקה Object הן סופיות, כך שאין לנו את היכולת לעקוף אותן.
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // Compilation error!
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

איך ואיפה להשתמש ב-final ב-Java

  • השתמש במילת המפתח הסופית כדי להגדיר כמה קבועים ברמת הכיתה;
  • צור משתנים סופיים עבור אובייקטים שאתה לא רוצה שישתנו. לדוגמה, מאפיינים ספציפיים לאובייקט שאנו יכולים להשתמש בהם למטרות רישום.
  • אם אינך רוצה להאריך את השיעור, סמן אותו כסופי.
  • אם אתה צריך ליצור מחלקה בלתי ניתנת לשינוי, אתה צריך להפוך אותה לסופית.
  • אם אתה רוצה שהטמעה של שיטה לא תשתנה בצאצאיה, סמן את השיטה בתור final. זה מאוד חשוב כדי להיות בטוח שהיישום לא משתנה.

23. מהם טיפוסים ניתנים לשינוי ובלתי ניתנים לשינוי?

מִשְׁתַנֶה

אובייקטים הניתנים לשינוי הם אובייקטים שניתן לשנות את המצב והמשתנים שלהם לאחר היצירה. דוגמאות למחלקות הניתנות לשינוי כוללות StringBuilder ו-StringBuffer. דוגמא:
public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // This setter can change the name field
   public void setAddress(String address) {
       this.address = address;
   }

   public static void main(String[] args) {

       MutableExample obj = new MutableExample("First address");
       System.out.println(obj.getAddress());

       // We are updating the name field, so this is a mutable object
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

בלתי ניתן לשינוי

אובייקטים בלתי ניתנים לשינוי הם אובייקטים שלא ניתן לשנות את המצב והמשתנים שלהם לאחר יצירת האובייקט. מפתח נהדר עבור HashMap, אתה לא חושב? :) לדוגמה, מחרוזת, אינגר, כפול וכן הלאה. דוגמא:
// We'll make this class final so no one can change it
public final class ImmutableExample {

   private String address;

   ImmutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // We remove the setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("Old address");
       System.out.println(obj.getAddress());

       // There is no way to change this field, so it is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}
בחלק הבא נשקול שאלות ותשובות לגבי אוספים. הפרופיל שלי ב-GitHub 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core. חלק 2
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION