JavaRush /בלוג Java /Random-HE /ניתוח שאלות ותשובות מראיונות למפתח Java. חלק 15

ניתוח שאלות ותשובות מראיונות למפתח Java. חלק 15

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

Java Core

9. מה ההבדל בין כריכה סטטית לדינמית בג'אווה?

כבר עניתי על שאלה זו במאמר זה בשאלה 18 על פולימורפיזם סטטי ודינמי, אני ממליץ לך לקרוא אותה.

10. האם ניתן להשתמש בממשקים פרטיים או מוגנים בממשק?

לא אתה לא יכול. מכיוון שכאשר אתה מצהיר על ממשק, מהדר Java מוסיף אוטומטית את מילות המפתח הציבוריות והמופשטות לפני שיטות הממשק ואת מילות המפתח הציבוריות , הסטטיות והסופיות לפני חברי הנתונים. למעשה, אם תוסיף פרטי או מוגן , יתעורר קונפליקט, והמהדר יתלונן על משנה הגישה עם ההודעה: "השינוי '<משנה שנבחר>' אינו מותר כאן." מדוע המהדר מוסיף ציבורי , סטטי וסופי משתנים בממשק? בואו נבין את זה:
  • public - הממשק מאפשר ללקוח אינטראקציה עם האובייקט. אם המשתנים לא היו ציבוריים, ללקוחות לא הייתה גישה אליהם.
  • סטטי - לא ניתן ליצור ממשקים (או ליתר דיוק, את האובייקטים שלהם), ולכן המשתנה הוא סטטי.
  • final - מכיוון שהממשק משמש להשגת הפשטה של ​​100%, למשתנה יש את הצורה הסופית שלו (ולא ישונה).

11. מהו Classloader ולמה הוא משמש?

Classloader - או Class Loader - מספק טעינה של מחלקות Java. ליתר דיוק, הטעינה מובטחת על ידי צאצאיה - מעמיסים מחלקה ספציפית, מכיוון ClassLoader עצמו מופשט. בכל פעם שקובץ .class נטען, למשל, לאחר קריאה לבנאי או שיטה סטטית של המחלקה המתאימה, פעולה זו מבוצעת על ידי אחד מהצאצאים של המחלקה ClassLoader . ישנם שלושה סוגים של יורשים:
  1. Bootstrap ClassLoader הוא מטעין בסיסי, מיושם ברמת JVM ואין לו משוב מסביבת זמן הריצה, מכיוון שהוא חלק מקרנל JVM וכתוב בקוד מקורי. מטעין זה משמש כהורה של כל שאר המופעים של ClassLoader.

    אחראי בעיקר לטעינת מחלקות פנימיות של JDK, בדרך כלל rt.jar וספריות ליבה אחרות הממוקמות בספריית $JAVA_HOME/jre/lib . לפלטפורמות שונות עשויות להיות יישומים שונים של מטעין מחלקה זה.

  2. Extension Classloader הוא מעמיס הארכה, צאצא של מחלקת המעמיסים הבסיסית. דואג לטעינת הרחבה של מחלקות בסיס Java סטנדרטיות. נטען מספריית ההרחבות של JDK, בדרך כלל $JAVA_HOME/lib/ext או כל ספרייה אחרת המוזכרת במאפיין המערכת java.ext.dirs (אפשר להשתמש באפשרות זו כדי לשלוט בטעינת ההרחבות).

  3. System ClassLoader הוא מטעין מערכת המיושם ברמת JRE שדואג לטעון את כל המחלקות ברמת האפליקציה לתוך ה-JVM. הוא טוען קבצים שנמצאים במשתנה סביבת הכיתה -classpath או -cp אפשרות שורת הפקודה.

ניתוח שאלות ותשובות מראיונות למפתח Java.  חלק 15 - 2מעמיסי מחלקות הם חלק מזמן הריצה של Java. ברגע שה-JVM מבקש מחלקה, טוען המחלקה מנסה למצוא את המחלקה ולטעון את הגדרת המחלקה לתוך זמן הריצה באמצעות השם המלא של המחלקה. השיטה java.lang.ClassLoader.loadClass() אחראית לטעינת הגדרת המחלקה בזמן ריצה. הוא מנסה לטעון מחלקה על סמך השם המלא שלה. אם המחלקה עדיין לא נטענה, היא מאצילה את הבקשה למטעין מחלקות האב. תהליך זה מתרחש באופן רקורסיבי ונראה כך:
  1. System Classloader מנסה למצוא את המחלקה במטמון שלה.

    • 1.1. אם המחלקה נמצאה, הטעינה הושלמה בהצלחה.

    • 1.2. אם המחלקה לא נמצאת, הטעינה תועבר ל-Extension Classloader.

  2. Extension Classloader מנסה למצוא את המחלקה במטמון משלה.

    • 2.1. אם הכיתה נמצאה, היא תסתיים בהצלחה.

    • 2.2. אם המחלקה לא נמצאת, הטעינה תועבר ל- Bootstrap Classloader.

  3. Bootstrap Classloader מנסה למצוא את המחלקה במטמון משלה.

    • 3.1. אם המחלקה נמצאה, הטעינה הושלמה בהצלחה.

    • 3.2. אם המחלקה לא תימצא, Bootstrap Classloader הבסיסי ינסה לטעון אותה.

  4. אם טעינה:

    • 4.1. הצליח - טעינת הכיתה הסתיימה.

    • 4.2. אם זה נכשל, השליטה מועברת ל-Extension Classloader.

  5. 5. Extension Classloader מנסה לטעון את המחלקה, ואם טוען:

    • 5.1. הצליח - טעינת הכיתה הסתיימה.

    • 5.2. אם זה נכשל, השליטה מועברת ל-System Classloader.

  6. 6. System Classloader מנסה לטעון את המחלקה, ואם הוא טוען:

    • 6.1. הצליח - טעינת הכיתה הסתיימה.

    • 6.2. לא עבר בהצלחה - נוצר חריג - ClassNotFoundException.

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

12. מהם אזורי נתונים בזמן ריצה?

Run-Time Data Ares - אזורי נתוני זמן ריצה של JVM. ה-JVM מגדיר כמה אזורי נתוני זמן ריצה הדרושים במהלך הפעלת התוכנית. חלקם נוצרים כאשר ה-JVM מתחיל. אחרים הם שרשור מקומיים והם נוצרים רק כאשר השרשור נוצר (ונהרסים כאשר השרשור נהרס). אזורי נתוני זמן הריצה של JVM נראים כך: ניתוח שאלות ותשובות מראיונות למפתח Java.  חלק 15 - 3
  • PC Register הוא מקומי לכל שרשור ומכיל את הכתובת של הוראת ה-JVM שהשרשור מבצע כעת.

  • JVM Stack הוא אזור זיכרון המשמש כאחסון למשתנים מקומיים ותוצאות זמניות. לכל חוט יש מחסנית נפרדת משלו: ברגע שהשרשור מסתיים, גם מחסנית זו נהרסת. ראוי לציין שהיתרון של מחסנית על פני ערימה הוא ביצועים, בעוד של-heap בהחלט יש יתרון בקנה מידה אחסון.

  • Native Method Stack - אזור נתונים לכל חוט המאחסן רכיבי נתונים, בדומה לערימת ה-JVM, לביצוע שיטות מקוריות (שאינן Java).

  • Heap - משמש את כל השרשורים כמאגר שמכיל אובייקטים, מטא נתונים של מחלקות, מערכים וכו', שנוצרים בזמן ריצה. אזור זה נוצר כאשר ה-JVM מתחיל ונהרס כאשר הוא נכבה.

  • אזור השיטה - אזור זמן ריצה זה משותף לכל השרשורים ונוצר כאשר ה-JVM מתחיל. הוא מאחסן מבנים עבור כל מחלקה, כגון Runtime Constant Pool, קוד עבור בנאים ושיטות, נתוני שיטה וכו'.

13. מהו אובייקט בלתי ניתן לשינוי?

בחלק זה של המאמר, בשאלות 14 ו-15, כבר יש תשובה לשאלה הזו, אז תסתכל מבלי לבזבז את זמנך.

14. מה מיוחד בשיעור String?

מוקדם יותר בניתוח, דיברנו שוב ושוב על תכונות מסוימות של String (היה סעיף נפרד לזה). עכשיו בואו נסכם את התכונות של מחרוזת :
  1. זהו האובייקט הפופולרי ביותר בג'אווה ומשמש למגוון מטרות. מבחינת תדירות השימוש, הוא אינו נחות אפילו מטיפוסים פרימיטיביים.

  2. ניתן ליצור אובייקט ממחלקה זו מבלי להשתמש במילת המפתח החדשה - ישירות דרך מרכאות String str = "string"; .

  3. String היא מחלקה בלתי ניתנת לשינוי : בעת יצירת אובייקט של מחלקה זו, לא ניתן לשנות את הנתונים שלו (כאשר מוסיפים + "מחרוזת נוספת" למחרוזת מסוימת, כתוצאה מכך תקבל מחרוזת שלישית חדשה). הבלתי משתנה של מחלקת ה-String הופכת אותו לבטוח.

  4. המחלקה String הושלמה (יש לה את השינוי הסופי ), כך שלא ניתן לעבור אותה בירושה.

  5. למחרוזת יש בריכת מיתרים משלו, אזור של זיכרון בערימה ששומר את ערכי המיתר שהוא יוצר. בחלק זה של הסדרה , בשאלה 62, תיארתי את בריכת המיתרים.

  6. ל-Java יש אנלוגים ל-String , שנועדו גם הם לעבוד עם מחרוזות - StringBuilder ו- StringBuffer , אבל עם ההבדל שהם ניתנים לשינוי. אתה יכול לקרוא עליהם עוד במאמר זה .

ניתוח שאלות ותשובות מראיונות למפתח Java.  חלק 15 - 4

15. מהי שיתופיות סוג?

כדי להבין שיתופיות, נסתכל על דוגמה. נניח שיש לנו כיתת חיות:
public class Animal {
 void voice() {
   System.out.println("*тишина*");
 }
}
ואיזה כיתת כלבים שמרחיבה את זה :
public class Dog extends Animal {

 @Override
 public void voice() {
   System.out.println("Гав, гав, гав!!!");
 }
}
כזכור, אנו יכולים בקלות להקצות אובייקטים מסוג יורש לסוג האב:
Animal animal = new Dog();
זה לא יהיה יותר מפולימורפיזם. נוח, גמיש לא? ובכן, מה לגבי רשימת החיות? האם נוכל לתת רשימה עם חיה גנרית רשימה עם חפצי כלב ?
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs;
במקרה זה, השורה להקצאת רשימת הכלבים לרשימת החיות תסומן בקו תחתון באדום, כלומר. המהדר לא יעביר את הקוד הזה. למרות העובדה שהקצאה זו נראית הגיונית למדי (אחרי הכל, אנו יכולים להקצות אובייקט Dog למשתנה מסוג Animal ), לא ניתן לעשות זאת. הסיבה לכך היא שאם זה היה מותר, נוכל להכניס חפץ של בעלי חיים לרשימה שנועדה במקור להיות כלב , תוך מחשבה שיש לנו רק כלבים ברשימה . ואז, למשל, נשתמש בשיטת get() כדי לקחת אובייקט מרשימת הכלבים הזו , מתוך מחשבה שזה כלב, ונקרא לשיטה כלשהי של אובייקט הכלב שעליו, שאין ל- Animal . וכפי שאתה מבין, זה בלתי אפשרי - תתרחש שגיאה. אבל, למרבה המזל, המהדר לא מחמיץ את השגיאה הלוגית הזו בהקצאת רשימת צאצאים לרשימת הורים (ולהיפך). ב-Java, אתה יכול להקצות רק אובייקטי רשימה למשתנים ברשימה עם כללי התאמה. זה נקרא אינווריאציה. אם הם היו יכולים לעשות זאת, זה היה נקרא ונקרא שיתוף פעולה. כלומר, שיתופיות היא אם נוכל להגדיר אובייקט מסוג ArrayList<Dog> למשתנה מסוג List<Animal> . מסתבר ש-covariance לא נתמכת בג'אווה? לא משנה איך זה! אבל זה נעשה בדרך המיוחדת שלו. למה משמש העיצוב ? מרחיב חיה . הוא ממוקם עם גנרי של המשתנה שאליו אנו רוצים להגדיר את אובייקט הרשימה, עם גנרי של הצאצא. הבנייה הגנרית הזו אומרת שכל סוג שהוא צאצא מהסוג Animal יעשה (וגם הסוג Animal נופל תחת הכללה זו). בתורו, Animal יכול להיות לא רק מחלקה, אלא גם ממשק (אל תלך שולל על ידי מילת המפתח extends ). אנחנו יכולים לבצע את המשימה הקודמת שלנו כך: ניתוח שאלות ותשובות מראיונות למפתח Java.  חלק 15 - 5
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
כתוצאה מכך, תראה ב-IDE שהמהדר לא יתלונן על הבנייה הזו. בואו נבדוק את הפונקציונליות של עיצוב זה. נניח שיש לנו שיטה שגורמת לכל בעלי החיים המועברים אליה להשמיע קולות:
public static void animalsVoice(List<? extends Animal> animals) {
 for (Animal animal : animals) {
   animal.voice();
 }
}
בואו ניתן לו רשימה של כלבים:
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
animalsVoice(dogs);
במסוף נראה את הפלט הבא:
וופ וופ וופ!!! וופ וופ וופ!!! וופ וופ וופ!!!
משמעות הדבר היא שגישה זו לשיתוף פעולה פועלת בהצלחה. הרשו לי לציין שהגנרי הזה כלול ברשימה ? מרחיב את החיה איננו יכולים להוסיף נתונים חדשים מכל סוג שהוא: לא סוג הכלב , ואפילו לא סוג החיה :
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
animals.add(new Dog());
dogs.add(new Animal());
למעשה, בשתי השורות האחרונות המהדר ידגיש את הכנסת האובייקטים באדום. זה נובע מהעובדה שאנחנו לא יכולים להיות בטוחים במאה אחוז איזו רשימה של אובייקטים מאיזה סוג תוקצה לרשימה עם נתונים על ידי הגנרי <? מרחיב חיה> . ניתוח שאלות ותשובות מראיונות למפתח Java.  חלק 15 - 6ברצוני לדבר גם על קונטרה , מכיוון שבדרך כלל מושג זה תמיד הולך יחד עם שיתופיות, וככלל הם נשאלים עליהם יחד. המושג הזה הוא הפוך במקצת משותפות, שכן מבנה זה משתמש בסוג היורש. נניח שאנו רוצים רשימה שניתן להקצות לה רשימה של אובייקטי סוג שאינם אבות קדמונים של האובייקט Dog . עם זאת, איננו יודעים מראש אילו סוגים ספציפיים הם יהיו. במקרה זה, בנייה של הצורה ? סופר דוג , שכל הסוגים מתאימים לו - האבות של כיתת הכלב :
List<Animal> animals = new ArrayList<>();
List<? super Dog> dogs = animals;
dogs.add(new Dog());
dogs.add(new Dog());
אנחנו יכולים להוסיף בבטחה אובייקטים מסוג Dog לרשימה עם גנרי כזה , כי בכל מקרה יש לו את כל השיטות המיושמות של כל אחד מאבותיו. אבל לא נוכל להוסיף אובייקט מסוג Animal , שכן אין ודאות שיהיו בפנים אובייקטים מסוג זה, ולא, למשל, Dog . אחרי הכל, אנחנו יכולים לבקש מאלמנט של רשימה זו שיטה של ​​מחלקה Dog , אשר ל- Animal לא תהיה . במקרה זה, תתרחש שגיאת קומפילציה. כמו כן, אם רצינו ליישם את השיטה הקודמת, אבל עם הגנרי הזה:
public static void animalsVoice(List<? super Dog> dogs) {
 for (Dog dog : dogs) {
   dog.voice();
 }
}
נקבל שגיאת קומפילציה בלולאת for , מכיוון שאיננו יכולים להיות בטוחים שהרשימה המוחזרת מכילה אובייקטים מסוג Dog וחופשיות להשתמש בשיטות שלה. אם נקרא לשיטת dogs.get(0) ברשימה זו . - נקבל אובייקט מסוג Object . כלומר, כדי ששיטת animalsVoice() תעבוד , עלינו לפחות להוסיף מניפולציות קטנות עם צמצום נתוני הסוג:
public static void animalsVoice(List<? super Dog> dogs) {
 for (Object obj : dogs) {
   if (obj instanceof Dog) {
     Dog dog = (Dog) obj;
     dog.voice();
   }
 }
}
ניתוח שאלות ותשובות מראיונות למפתח Java.  חלק 15 - 7

16. איך יש מתודות במחלקה Object?

בחלק זה של הסדרה, בסעיף 11, כבר עניתי על השאלה הזו, אז אני ממליץ לך בחום לקרוא אותה אם עדיין לא עשית זאת. שם נסיים להיום. נתראה בחלק הבא! ניתוח שאלות ותשובות מראיונות למפתח Java.  חלק 15 - 8
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION