CodeGym /בלוג Java /Random-HE /בחינת שאלות ותשובות מראיון עבודה למשרת מפתח Java. חלק 7
John Squirrels
רָמָה
San Francisco

בחינת שאלות ותשובות מראיון עבודה למשרת מפתח Java. חלק 7

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

62. מהי בריכת המיתרים, ולמה היא נחוצה?

חלק מהזיכרון הזמין לתוכנית Java נקרא ה-heap (עליו נדבר בהמשך), וחלק מה-heap נקרא ה- String pool . זה מיועד לאחסון ערכי מחרוזת. במילים אחרות, כאשר אתה יוצר מחרוזת, למשל, באמצעות מרכאות כפולות כמו זה:
String str = "Hello world";
ה-JVM בודק אם למאגר המחרוזות כבר יש את הערך שצוין. אם כן, אז למשתנה str מוקצה הפניה לערך זה במאגר. אם לא, נוצר ערך חדש במאגר, והפניה אליו מוקצית למשתנה str . הבה נשקול דוגמה:
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
true יוצג על המסך. זכור כי == משווה הפניות, ושני המשתנים הללו מצביעים על אותו ערך במאגר המחרוזות. זה עוזר להימנע מהפקת אובייקטי מחרוזת זהים רבים בזיכרון. אנו יכולים לעשות זאת מכיוון שכזכור, String היא מחלקה בלתי ניתנת לשינוי, כך שאין שום דבר רע בשמירה על מספר הפניות לאותו ערך. כעת לא ייתכן מצב ששינוי הערך במקום אחד מביא לשינויים בכמה התייחסויות אחרות. ובכל זאת, אם ניצור מחרוזת באמצעות new :
String str = new String("Hello world");
ואז נוצר אובייקט נפרד בזיכרון ומאחסן את ערך המחרוזת שצוין (זה לא משנה אם הערך הזה כבר נמצא במאגר המחרוזות). כדי לאשר קביעה זו, שקול זאת:
String firstStr = new String("Hello world");
String secondStr = "Hello world";
String thirdStr = new String("Hello world");
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
נקבל שתי שורות המציינות false , כלומר יש לנו שלוש מחרוזות נפרדות. בעיקרון, זו הסיבה שאתה צריך ליצור מחרוזות פשוט באמצעות מרכאות כפולות. עם זאת, ניתן להוסיף (או לקבל הפניה) ערכים במאגר המחרוזות גם בעת יצירת אובייקט באמצעות מילת המפתח החדשה . לשם כך, אנו משתמשים בשיטת intern() של המחלקה String . שיטה זו מבטיחה שאו שניצור את הערך במאגר המחרוזות או שנקבל הפניה אליו אם היא כבר קיימת. הנה דוגמה:
String firstStr = new String("Hello world").intern();
String secondStr = "Hello world";
String thirdStr = new String("Hello world").intern();
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
System.out.println(secondStr == thirdStr);
קוד זה יוצא נכון לקונסולה שלוש פעמים, מה שאומר לנו שכל שלושת המשתנים מתייחסים לאותה מחרוזת בזיכרון.

63. באילו דפוסי עיצוב GoF נעשה שימוש בבריכת המיתרים?

בבריכת המיתרים, דפוס העיצוב של GoF הוא דפוס במשקל זבוב . אם שמתם לב לתבנית עיצוב אחרת כאן, שתפו אותה בתגובות. כאן נדבר על דפוס העיצוב במשקל זבוב. זוהי דפוס עיצובי מבני שבו אובייקט המייצג את עצמו כמופע ייחודי במקומות שונים בתוכנית, למעשה אינו ייחודי. משקל הזבוב חוסך זיכרון על ידי אחסון המצב המשותף של אובייקטים במקום אחסון נתונים זהים בכל אובייקט. כדי להבין את העיקר, שקול את הדוגמה היסודית הזו. נניח שיש לנו ממשק לעובדים :
public interface Employee {
   void work();
}
ויש לזה כמה יישומים, כמו שיעור עורכי דין :
public class Lawyer implements Employee {

   public Lawyer() {
       System.out.println("A lawyer was hired.");
   }

   @Override
   public void work() {
       System.out.println("Settling legal issues...");
   }
}
וכיתת רואה חשבון :
public class Accountant implements Employee {

   public Accountant() {
       System.out.println("An accountant was hired.");
   }

   @Override
   public void work() {
       System.out.println("Keeping accounting records...");
   }
}
השיטות הן שרירותיות לחלוטין - עבור הדוגמה הזו, אנחנו רק צריכים לראות שהן מבוצעות. אותו הדבר נכון לגבי הקונסטרוקטור. פלט המסוף אומר לנו מתי נוצרים אובייקטים חדשים. יש לנו גם מחלקת משאבי אנוש שתפקידה להחזיר עובד מבוקש. אם העובד הזה עדיין לא בצוות, אז הוא יתקבל לעבודה ומחלקת משאבי אנוש מחזירה את העובד החדש:
public class HumanResourcesDepartment {
   private Map<String, Employee> currentEmployees = new HashMap<>();

   public Employee getEmployee(String type) throws Exception {
       Employee result;
       if (currentEmployees.containsKey(type)) {
           result = currentEmployees.get(type);
       } else {
           switch (type) {
               case "Accountant":
                   result = new Accountant();
                   currentEmployees.put(type, result);
                   break;
               case "Lawyer":
                   result = new Lawyer();
                   currentEmployees.put(type, result);
                   break;
               default:
                   throw new Exception("This employee is not on the staff!");
           }
       }
       return result;
   }
}
אז, ההיגיון פשוט: אם האובייקט הרצוי קיים, החזר אותו; אם לא, צור אותו, שים אותו באחסון (משהו כמו מטמון), והחזר אותו. עכשיו בואו נראה איך הכל עובד:
public static void main(String[] args) throws Exception {
   HumanResourcesDepartment humanResourcesDepartment = new HumanResourcesDepartment();
   Employee empl1 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl2 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl3 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl4 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl5 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl6 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl7 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl8 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
   Employee empl9 = humanResourcesDepartment.getEmployee("Lawyer");
   empl1.work();
   Employee empl10 = humanResourcesDepartment.getEmployee("Accountant");
   empl2.work();
}
הנה מה שנראה בקונסולה:
עורך דין נשכר. הסדרת סוגיות משפטיות... נשכר רואה חשבון. ניהול רישומים חשבונאיים... הסדרת סוגיות משפטיות... ניהול רישומים חשבונאיים... הסדרת סוגיות משפטיות... ניהול רישומי חשבונאות... הסדרת סוגיות משפטיות... ניהול רישומים חשבונאיים... הסדרת סוגיות משפטיות... ניהול חשבונות שיאים…
כפי שאתה יכול לראות, יצרנו רק שני אובייקטים ועשינו בהם שימוש חוזר פעמים רבות. הדוגמה פשוטה מאוד, אבל היא מדגימה כיצד דפוס עיצוב זה יכול לשמר את המשאבים שלנו. כפי שאולי שמתם לב, ההיגיון של הדפוס הזה דומה עד כאב להיגיון של מאגר ביטוח. בחינת שאלות ותשובות מראיון עבודה למשרת מפתח Java.  חלק 7 - 2

64. איך מפצלים מיתר לחלקים? תן דוגמה לקוד הרלוונטי

ברור, שאלה זו היא על שיטת הפיצול . למחלקה String יש שתי גרסאות של שיטה זו:
String split(String regex);
ו
String split(String regex);
הפרמטר regex הוא המפריד - ביטוי רגולרי כלשהו המשמש לפיצול המחרוזת למערך של מחרוזות, למשל:
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
  System.out.println(s);
}
הקונסולה תציג:
שלום, עולם זה אמיגו!
אז, המחרוזת שלנו פוצלה למערך של מחרוזות, תוך שימוש ברווח כמפריד (במקום הביטוי הרגיל "\\s" , יכולנו גם להשתמש בביטוי המחרוזת הרגיל " " ). לגרסה השנייה, העומס יתר על המידה, יש פרמטר מגבלה נוסף . limit הוא הגודל המרבי המותר של המערך המתקבל. במילים אחרות, ברגע שהמחרוזת פוצלה למספר המרבי המותר של תת-מחרוזות, הפיצול נעצר, והאלמנט האחרון יכיל כל "שאריות" מהמחרוזת הבלתי מפוצלת. דוגמא:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
  System.out.println(s);
}
פלט מסוף:
שלום, עולם זה אמיגו!
כפי שאנו יכולים לראות, אלמלא limit = 2 , ניתן היה לפצל את האלמנט האחרון של המערך לשלוש תת מחרוזות.

65. מדוע מערך תווים עדיף על מחרוזת לאחסון סיסמה?

ישנן מספר סיבות להעדפת מערך על פני מחרוזת בעת אחסון סיסמה:

1. בריכת המיתרים ואי-שינוי המיתר.

בעת שימוש במערך ( char[] ), אנו יכולים למחוק את הנתונים באופן מפורש לאחר שנסיים לעבוד איתו. אנחנו יכולים גם להחליף את המערך כמה שנרצה, ולבטל את הסיסמה מהמערכת עוד לפני איסוף האשפה (מספיק לשנות כמה תאים לערכים לא חוקיים). לעומת זאת, String הוא מחלקה בלתי ניתנת לשינוי. זה אומר שאם נרצה לשנות את הערך של אובייקט String , נקבל חדש, אבל הישן יישאר במאגר המחרוזות. אם ברצוננו להסיר את המחרוזת המכילה את הסיסמה, אנו עומדים בפני משימה מסובכת מכיוון שאנו צריכים את אוסף האשפה כדי להסיר את הערך הזה ממאגר המחרוזות, אך סביר להניח שהמחרוזת הזו תישאר שם לאורך זמן. כלומר, כשמדובר באחסון מאובטח של נתונים, String נחות ממערך char .

2. אם נוציא את ערך String לקונסולה (או יומן), אז נקבל:

String password = "password";
System.out.println("Password - " + password);
פלט מסוף:
סיסמה - סיסמה
ואם במקרה תדפיס את המערך לקונסולה:
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
הקונסולה תציג ג'יבריש לא מובן:
סיסמה - [C@7f31245a
למעשה, זה לא ג'יבריש. הנה איך להבין את מה שאתה רואה: [C הוא שם המחלקה - מערך char , @ הוא מפריד, ואז 7f31245a הוא קוד גיבוב הקסדצימלי.

3. המדריך הרשמי של Java Cryptography Architecture (JCA) מזכיר במפורש אחסון סיסמאות ב- char[] במקום במחרוזת :

"זה נראה הגיוני לאסוף ולאחסן את הסיסמה באובייקט מסוג java.lang.String . עם זאת, הנה האזהרה: אובייקטים מסוג String הם בלתי ניתנים לשינוי, כלומר, אין שיטות מוגדרות המאפשרות לך לשנות (החלפה) או אפס את התוכן של מחרוזת לאחר השימוש. תכונה זו הופכת את אובייקטי המחרוזת ללא מתאימים לאחסון מידע רגיש לאבטחה כגון סיסמאות משתמש. עליך תמיד לאסוף ולאחסן מידע רגיש לאבטחה במערך char במקום זאת." בחינת שאלות ותשובות מראיון עבודה למשרת מפתח Java.  חלק 7 - 3

Enum

66. תן תיאור קצר של Enum ב-Java

Enum הוא קיצור של Enumeration, שהוא קבוצה של קבועי מחרוזת המאוחדים על ידי סוג משותף. אנו מכריזים על אחד באמצעות מילת המפתח enum . הנה דוגמה עם enum : תפקידים מותרים בקמפוס בית ספר כלשהו:
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD
}
המילים הכתובות באותיות גדולות הן קבועי הספירה. הם מוכרזים בצורה פשוטה, ללא המפעיל החדש . שימוש בספירות הופך את החיים להרבה יותר קלים מכיוון שהם עוזרים למנוע שגיאות ובלבול בשמות (מאחר שהרשימה מגדירה את הערכים התקפים היחידים). עבורי, הם מאוד נוחים במבנה המתג .

67. האם Enum יכול ליישם ממשקים (להשתמש במילת מפתח implements)?

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

68. האם Enum יכול להרחיב מחלקה (השתמש במילת המפתח extends)?

לא, זה לא יכול, מכיוון ש-enum הוא תת-מחלקה של מחלקת ברירת המחדל Enum<T> , כאשר T הוא סוג ה-enum. זה לא יותר ממחלקת בסיס משותפת לכל סוגי ה-enum בשפת Java. ההמרה מ- enum למחלקה מתבצעת על ידי מהדר Java בזמן ההידור. ההרחבה אינה מצוינת במפורש בקוד, אך היא תמיד משתמעת.

69. האם ניתן ליצור Enum ללא מופעים של אובייקטים?

השאלה הזו קצת מבלבלת, ואני לא בטוח שהבנתי אותה עד הסוף. יש לי שני פירושים: 1. האם אתה יכול לקבל מנה ללא ערכים? כן, כמובן, אבל זה יהיה כמו כיתה ריקה - חסר טעם, למשל
public enum Role {
}
ואם נתקשר:
var s = Role.values();
System.out.println(s);
אנו מקבלים את הדברים הבאים בקונסולה:
[Lflyweight.Role;@9f70c54
(מערך ריק של ערכי תפקידים ) 2. האם ניתן ליצור enum ללא האופרטור החדש ? כן כמובן. כפי שאמרתי למעלה, אתה לא משתמש באופרטור החדש עבור ערכי enum, מכיוון שהם ערכים סטטיים.

70. האם נוכל לעקוף את שיטת toString() של Enum?

כן, כמובן שאתה יכול לעקוף את המתודה toString() על מנת להגדיר כיצד להציג את ה-enum שלך כאשר שיטת toString נקראת (בעת המרת enum למחרוזת רגילה, למשל, כדי להוציא אותו לקונסולה או ללוגים).
public enum Role {
   STUDENT,
   TEACHER,
   DIRECTOR,
   SECURITY_GUARD;

   @Override
   public String toString() {
       return "Selected role - " + super.toString();
   }
}
זה הכל בשבילי היום. עד החלק הבא!
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION