JavaRush /בלוג Java /Random-HE /50 השאלות והתשובות המובילות לראיון ליבת Java. חלק 2
Roman Beekeeper
רָמָה

50 השאלות והתשובות המובילות לראיון ליבת Java. חלק 2

פורסם בקבוצה
50 השאלות והתשובות המובילות לראיון ליבת Java. חלק 1 50 השאלות והתשובות המובילות לראיון ליבת Java.  חלק 2 - 1

אוספים

25. מה הכוונה באוספים בג'אווה?

אוסף הוא מסגרת שנועדה לאחסן ולתפעל חפצים. משמש לביצוע הפעולות הבאות:
  • לחפש;
  • מִיוּן;
  • מָנִיפּוּלָצִיָה;
  • חיבור;
  • מְחִיקָה.
כל השיעורים והממשקים למסגרת Collection נמצאים java.utilבחבילה.

26. אילו מחלקות וממשקים זמינים במסגרת האוסף?

ממשקים:
  • אוסף;
  • רשימה;
  • מַעֲרֶכֶת;
  • מַפָּה;
  • סט ממוין;
  • מפה ממוינת;
  • תוֹר.
שיעורים:
  • רשימות:
    1. רשימת מערך;
    2. רשימה מקושרת;
    3. וקטור (הוצא משימוש).
  • סטים:
    1. HashSet;
    2. LinkedHashSet;
    3. TreeSet.
  • מפות:
    1. מפת גיבוב
    2. מפת עץ
    3. HashTable (הוצא משימוש)
    4. LinkedHashMap
  • תוֹר
    1. תור עדיפות.

27. מה הכוונה במיון ומסודר באוספים?

הזמין:

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

מְמוּיָן:

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

28. אילו אוספים יש עם ממשק List? איך עובדים עם List?

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

רשימת מערך:

האוסף הנפוץ ביותר. בעיקרו של דבר, זהו מערך עם גודל מתרחב דינמית. התפקיד של ניהול גודל המערך מוטלת על האוסף. חשוב לנו להבין שברוב המקרים זה מה שאנחנו צריכים להשתמש בו. מוזרויות:
  • חיפוש מהיר וחיפוש אינדקס מהיר;
  • האוסף מסודר לפי אינדקס, אך אינו ממוין;
  • מיישמת את ממשק RandomAccess;
  • מוסיף לאט לאמצע הרשימה.
דוגמא:
public class A {

   public static void main(String[] args) {
       ArrayList names = new ArrayList<>();
       names.add("John");
       names.add("John");
       names.add("Roman");
       names.add("Ivan");
   }

}
>> פלט

   [John, John, Roman, Ivan]
הפלט מראה שמדובר באלמנטים שניתנים לחזרה. הם מוצגים לפי סדר הקלטתם. מה עוד לקרוא? כן, יש הרבה מידע, אתה אפילו לא צריך לעזוב את JavaRush:

רשימה מקושרת:

זהו אוסף שבו לכל אלמנט יש קישור לאלמנטים הקודמים והבאים. קישורים אלו מאפשרים לך לעבור מאלמנט אחד למשנהו. בעת הוספת אלמנט, הקישורים לרכיבים הקודמים והבאים פשוט משתנים: 50 השאלות והתשובות המובילות לראיון ליבת Java.  חלק 2 - 2
  • אלמנטים מחוברים זה לזה, כלומר מיושמת רשימה מקושרת כפול;
  • מהירות הפעולה הכוללת נמוכה באופן ניכר מאשר ב- ArrayList;
  • בחירה מצוינת למספר רב של הוספות ומחיקות באמצע מערך;
  • מיישם את ממשקי ה-Queue and Deque list, ולכן יש לו את שיטות העבודה שלהם.
דוגמא:
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("One");
linkedList.add("Two");
linkedList.add("Three");

29. ספר לנו על אוסף המפות והיישום שלו?

מפה היא אוסף ערך מפתח. יש מפתח ייחודי וערך התואם את הערך הזה. שיטות משמשות equals()גם hashcode()כדי לקבוע את הייחודיות של מפתח.

מפת גיבוב:

  • לא ממוין או מסודר;
  • משמש אם הסדר והמיון אינם חשובים;
  • תומך במפתח null.
דוגמא:
public class CollectionExample {

   public static void main(String[] args) {
       HashMap positions = new HashMap<>();
       positions.put("junior", "Ivan");
       positions.put("middle", "Roman");
       positions.put("senior", "Vasily");
       positions.put("team lead", "Anton");
       positions.put("arthitect", "Andrew");
       positions.put("senior", "John");
       System.out.println(positions);
   }
}

// вывод в консоль
// {junior=Ivan, middle=Roman, senior=John, team lead=Anton, arthitect=Andrew}
המפתחות תמיד ייחודיים, כך שרק בכיר אחד מתועד.

LinkedHashMap:

  • שומר על סדר ההכנסה;
  • איטי יותר מ- HashMap;
  • האיטרציה צפויה להיות מהירה יותר מאשר ב- HashMap.
דוגמא:
public class CollectionExample {

   public static void main(String[] args) {
       LinkedHashMap<String, String> positions = new LinkedHashMap<>();
       positions.put("junior", "Ivan");
       positions.put("middle", "Roman");
       positions.put("senior", "Vasily");
       positions.put("team lead", "Anton");
       positions.put("arthitect", "Andrew");
       positions.put("senior", "John");
       System.out.println(positions);
   }
}

// вывод в консоль
// {junior=Ivan, middle=Roman, senior=John, team lead=Anton, arthitect=Andrew}

מפת עץ:

מימוש מפה ששומר את הערכים ממוינים לפי הסדר הטבעי של המפתחות שלהם, או יותר טוב, באמצעות השוואת אם מסופק כזה בבנאי בעת יצירת המפה. דוגמא:
  1. בלי משווה

    public class CollectionExample {
    
       public static void main(String[] args) {
           TreeMap<Integer, String> positions = new TreeMap<>();
           positions.put(1, "Ivan");
           positions.put(3, "Roman");
           positions.put(2, "Vasily");
           positions.put(10, "Anton");
           positions.put(7, "Andrew");
           positions.put(1, "John");
           System.out.println(positions);
       }
    }
    
    // вывод в консоль
    // {1=John, 2=Vasily, 3=Roman, 7=Andrew, 10=Anton}
  2. עם משווה

    public class CollectionExample {
    
       public static void main(String[] args) {
           //используем реализацию Strategy Pattern'a и добавим компаратор:
           TreeMap<Integer, String> positions = new TreeMap<>(Comparator.reverseOrder());
           positions.put(1, "Ivan");
           positions.put(3, "Roman");
           positions.put(2, "Vasily");
           positions.put(10, "Anton");
           positions.put(7, "Andrew");
           positions.put(1, "John");
           System.out.println(positions);
       }
    }
    
    // вывод в консоль
    // {10=Anton, 7=Andrew, 3=Roman, 2=Vasily, 1=John}
אנו רואים שמיון בסדר עולה מיושם כסטנדרט, אך ניתן לשנות זאת על ידי הוספת השוואת לבנאי. TreeMap מתואר היטב כאן .

30. ספר לנו על אוסף הסט והטמעות שלו?

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

HashSet:

  • לא ממוין או מסודר. מתחת למכסה המנוע יש HashMap עם מציין מיקום לערך. תראה בעצמך ;)
  • משתמש ב-hashCode כדי להוסיף אובייקטים;
  • יש להשתמש בו כאשר אתה צריך חפצים ייחודיים והסדר שלהם אינו חשוב.
דוגמא:
public class CollectionExample {

   public static void main(String[] args) {
       HashSet<String> positions = new HashSet<>();
       positions.add("junior");
       positions.add("junior");
       positions.add("middle");
       positions.add("senior");
       positions.add("team lead");
       positions.add("architect");
       System.out.println(positions);
   }
}

// вывод в консоль
// [senior, middle, team lead, architect, junior]
כאן אתה יכול לראות שהאלמנט "ז'וניור", שנוסף פעמיים, קיים רק במופע בודד. והסדר אינו זהה להוספה.

LinkedHashSet:

  • גרסה מוזמנת של HashSet;
  • תומך ברשימה מקושרת כפולה עבור כל האלמנטים;
  • השתמש בו כאשר אתה צריך סדר באיטרציה שלך.
דוגמא:
public class CollectionExample {

   public static void main(String[] args) {
       LinkedHashSet<String> positions = new LinkedHashSet<>();
       positions.add("junior");
       positions.add("junior");
       positions.add("middle");
       positions.add("senior");
       positions.add("team lead");
       positions.add("architect");
       System.out.println(positions);
   }
}

// вывод в консоль
// [senior, middle, team lead, architect, junior]

סט עצים:

  • אחד משני אוספים ממוינים;
  • משתמש במבנה עץ אדום-שחור ומבטיח שהאלמנטים נמצאים בסדר עולה;
  • מתחת למכסה המנוע זה TreeMap עם בדל על הערכים. והאלמנטים של TreeSet הם המפתחות למפת TreeMap (ראה גם ;)).
דוגמא:
public class CollectionExample {

   public static void main(String[] args) {
       TreeSet<String> positions = new TreeSet<>();
       positions.add("junior");
       positions.add("junior");
       positions.add("middle");
       positions.add("senior");
       positions.add("team lead");
       positions.add("architect");
       System.out.println(positions);
   }
}

// вывод в консоль
// [architect, junior, middle, senior, team lead]

חריגים

31. מהו חריג?

חריגה היא בעיה שיכולה להתרחש בזמן ריצה. זהו מצב חריג שנוצר מסיבה כלשהי. דיאגרמת הירושה החריגה נראית כך (צריך לדעת אותה בעל פה ;)): 50 השאלות והתשובות המובילות לראיון ליבת Java.  חלק 2 - 3התרשים מראה שבאופן כללי כל החריגים מחולקים לשתי קבוצות - חריגים ושגיאות . שגיאה - JVMs משמשים להצגת שגיאות שלאחריהן האפליקציה כבר לא הגיונית. לדוגמה, StackOverFlowError, שאומר שהמחסנית מלאה והתוכנית לא יכולה יותר לפעול. חריג - חריגים שנוצרים באופן תכנותי בקוד. יש חריגים שונים, מסומנים ולא מסומנים, אבל העיקר שהם קיימים, וניתן לתפוס אותם והאפליקציה ממשיכה לתפקד. חריגים, בתורם, מחולקים עוד יותר לאלו היורשים מ-RuntimeException ומצאצאים אחרים של Exception. יש מספיק מידע על הנושא הזה. נדבר על החריגים המסומנים/לא מסומנים להלן.

32. כיצד ה-JVM מטפל בחריגים?

איך זה עובד? ברגע שיוצא חריג לאנשהו, זמן הריצה יוצר אובייקט חריג (מסומן כ-ExcObj). הוא מאחסן את כל המידע הדרוש לעבודה - החריג עצמו שנזרק והמקום שבו זה קרה. יצירה ExcObjושידור לזמן ריצה הם לא יותר מ"השלכת חריגה". ExcObjמכיל שיטות שניתן להשתמש בהן כדי להגיע למקום שבו הושלך החריג. סט שיטות זה נקרא Call Stack. בשלב הבא, מערכת זמן הריצה מחפשת שיטה ב-Call Stack שיכולה להתמודד עם החריגה שלנו. אם הוא אכן מוצא את המטפל המתאים, כלומר, סוג החריגה תואם את הטיפוס במטפל, הכל בסדר. אם הוא לא מוצא אותו, אז זמן הריצה מעביר הכל למטפל החריגים המוגדר כברירת מחדל, שמכין תגובה ויוצא. כך זה נראה:
/**
* Пример, в котором показываются две опции — когда находится обработчик для исключения и когда нет.
*/
class ThrowerExceptionExample {

   public static void main(String[] args) throws IllegalAccessException {

       ThrowerExceptionExample example = new ThrowerExceptionExample();

       System.out.println(example.populateString());
   }

   /**
    * Здесь происходит перехват одного из возможных исключений — {@link IOException}.
    * А вот второй будет пробрасываться дальше вверх по вызову.
    */
   private String populateString() throws IllegalAccessException {
       try {
           return randomThrower();
       } catch (IOException e) {
           return "Caught IOException";
       }
   }

   /**
    * Здесь две опции: or бросается {@link IOException} or {@link IllegalAccessException}.
    * Выбирается случайным образом.
    */
   private String randomThrower() throws IOException, IllegalAccessException {
       if (new Random().nextBoolean()) {
           throw new IOException();
       } else {
           throw new IllegalAccessException();
       }
   }
}
במקרה שלנו, CallStack ייראה באופן סכמטי כך:

randomThrower() => populateString() => main(String[] args)
ישנן שתי אפשרויות: חריגה אחת או אחרת תוזרק באופן אקראי. עבור IOException הכל בסדר, אם הוא נוצר, התוצאה של העבודה תהיה: "Caught IOException". אבל אם יש חריג שני שעבורו אין מטפל, התוכנית תפסיק עם הפלט הבא:

Exception in thread "main" java.lang.IllegalAccessException
  at ThrowerExceptionExample.randomThrower(CollectionExample.java:38)
  at ThrowerExceptionExample.populateString(CollectionExample.java:24)
  at ThrowerExceptionExample.main(CollectionExample.java:15)

33. איך מתכנתים מטפלים בחריגים?

בשאלות לעיל, מילות מפתח מסוימות כבר שימשו לעבודה עם חריגים; כעת עלינו לדבר עליהן ביתר פירוט. מהן מילות המפתח?
  • לְנַסוֹת
  • לתפוס
  • לזרוק
  • זורק
  • סוף כל סוף
חשוב לציין שניתן להשתמש בתפיסה, זריקה וזריקה רק עם java.lang.Throwable. זה לא יעבוד עם סוגים אחרים. עכשיו נדון לנסות, לתפוס ולבסוף.
  • try-catch-finallyהוא מבנה המאפשר לך לתפוס ולטפל בצורה נכונה בחריג.
  • try- יכול להיות רק פעם אחת, שם מתרחש ההיגיון;
  • catch- בלוק שמקבל סוג כלשהו של חריגים; יכולים להיות רבים מהם. לדוגמה, בלוק ניסיון יזרוק כמה חריגים שאין להם שום קשר אחד עם השני;
  • finally- "סוף סוף" הבלוק הזה. זהו בלוק שיבוצע בכל מקרה, ללא קשר למה שנעשה ב- try, catch.
כך זה נראה:
try {
   // сюда передают тот code, который может вызвать исключение.
} catch (IOException e) {
   // первый catch блок, который принимает IOException и все его подтипы(потомки).
   // Например, нет file при чтении, выпадает FileNotFoundException, и мы уже соответствующе
   // обрабатываем это.
} catch (IllegalAccessException e) {
   // если нужно, можно добавить больше одного catch блока, в этом нет проблем.
} catch (OneException | TwoException e) {
   // можно даже объединять несколько в один блок
} catch (Throwable t) {
   // а можно сказать, что мы принимаем ВСЁ))))
} finally {
   // этот блок может быть, а может и не быть.
   // и он точно выполнится.
}
קרא בעיון את תיאור הדוגמה והכל יהיה ברור)

34. זרוק וזורק בג'אווה

לזרוק

throwמשמש כאשר אתה צריך ליצור באופן מפורש חריג חדש. הוא משמש כדי ליצור ולזרוק חריגים מותאמים אישית. לדוגמה, חריגים הקשורים לאימות. בדרך כלל, לצורך אימות, הם יורשים מ- RuntimeException. דוגמא:
// пример пробрасывания исключения
throw new RuntimeException("because I can :D");
חשוב שהמבנה הזה יכול לשמש רק משהו שיורש מ Throwable. כלומר, אתה לא יכול להגיד את זה:
throw new String("How тебе такое, Илон Маск?");
לאחר מכן, עבודת השרשור מסתיימת והחיפוש מתחיל אחר מטפל שיכול לעבד אותו. כאשר הוא לא מוצא אותו, הוא עובר לשיטה שקראה לו, וכך החיפוש יעלה בשורת השיחות עד שהוא ימצא את המטפל המתאים או ישאיר את האפליקציה פועלת. בואו נראה:
// Пример, который демонстрирует работу throw
class ThrowExample {

   void willThrow() throws IOException {
       throw new IOException("Because I Can!");
   }

   void doSomething() {
       System.out.println("Doing something");
       try {
           willThrow();
       } catch (IOException e) {
           System.out.println("IOException was successfully handled.");
       }
   }

   public static void main(String args[]) {
       ThrowExample throwExample = new ThrowExample();
       throwExample.doSomething();
   }
}
אם נריץ את התוכנית, נקבל את התוצאה הבאה:

Doing something
IOException was successfully handled.

זורק

throws- מנגנון שבאמצעותו שיטה יכולה לזרוק חריג אחד או יותר. הם מתווספים מופרדים בפסיקים. בואו נראה כמה זה קל ופשוט:
private Object willThrow() throws RuntimeException, IOException, FileNotFoundException
יתרה מכך, חשוב לציין שיכולים להיות חריגים מסומנים וגם לא מסומנים. כמובן שלא ניתן להוסיף ל-, חריגים לא מסומנים throws, אבל נימוסים טובים אומרים אחרת. אם אלה ניתנים לבדיקה, אז באמצעות השיטה שיוצרת אותם, אתה צריך איכשהו לעבד את זה. ישנן שתי אפשרויות:
  1. כתוב try-catchעם חריג הירושה המתאים ומעלה.
  2. השתמש בו throwsבדיוק באותו אופן כדי שלמישהו אחר כבר יש את הבעיה הזו :D

35. חריגים מסומנים ובלתי מסומנים ב-Java

ישנם שני סוגים של חריגים ב-Java - מסומנים ולא מסומנים.

חריגים מסומנים:

אלו הם חריגים שנבדקים בזמן הקומפילציה. אם קוד כלשהו בשיטה זורק חריג מסומן במהלך חריגה, המתודה חייבת לטפל בו באמצעות try-catch, או להעביר אותו הלאה. לדוגמה, הקורא תמונה מהנתיב "/users/romankh3/image.png", מעדכן אותה בדרך כלשהי (עבורנו זה לא חשוב) וחוסך את זה בחזרה.
class CheckedImageExample {
   public static void main(String[] args) {
       File imageFile = new File("/users/romankh3/image.png");
       BufferedImage image = ImageIO.read(imageFile);
       updateAndSaveImage(image, imageFile);
   }

   private static void updateAndSaveImage(BufferedImage image, File imageFile) {
       ImageIO.write(image, "png", imageFile);
   }
}
קוד כזה לא יקמפל, מכיוון ששיטות סטטיות ImageIO.read()זורקות ImageIO.write()IOException, שנבדק ויש לטפל בו בהתאם. יש כאן שתי אפשרויות שכבר דנו בהן לעיל: או השתמש ב- try-catch, או העבר הלאה. להטמעה טובה יותר נעשה כך וכך. כלומר, updateAndSaveפשוט נעביר אותו בשיטה, ואז נשתמש בו בשיטה הראשית try-catch:
class CheckedImageExample {
   public static void main(String[] args) {
       File imageFile = new File("/users/romankh3/image.png");
       try {
           BufferedImage image = ImageIO.read(imageFile);
           updateAndSaveImage(image, imageFile);
       } catch (IOException e) {
           e.printStackTrace();
       }
   }

   private static void updateAndSaveImage(BufferedImage image, File imageFile) throws IOException {
       ImageIO.write(image, "png", imageFile);
   }
}

חריגים לא מסומנים:

אלו הם החריגים שלא נבדקים בשלב הקומפילציה. כלומר, שיטה יכולה ליצור RuntimeException, אבל המהדר לא יזכיר לך לטפל בזה איכשהו. כפי שמוצג להלן, יש לנו כל מה שיורש מ-RuntimeException ושגיאה לא מסומנים. 50 השאלות והתשובות המובילות לראיון ליבת Java.  חלק 2 - 4שקול את תוכנית Java הבאה. הקוד קומפילציה בסדר, אבל זורק חריג בעת הפעלה ArrayIndexOutOfBoundsException. המהדר מאפשר לו לבצע קומפילציה מכיוון ArrayIndexOutOfBoundsExceptionשהוא חריג לא מסומן. מצב נפוץ עם מערך, שיכול להיות:
class CheckedImageExample {
   public static void main(String[] args) {
       int[] array = new int[3];
       array[5] = 12;
   }
}
התוצאה תהיה:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
  at main(CheckedImageExample.java:12)
אגב, כבר שמתם לב שב-Java אף אחד לא נותן שמות קצרים? יותר גדול יותר טוב. הוא, Spring Framework, היה מאוד מוצלח בזה: פשוט קח איזה שיעור BeanFactoryPostProcessor )))

36. מה הם נסה עם משאבים?

זהו מנגנון שבאמצעותו יש לסגור את כל המשאבים בצורה נכונה. זה איכשהו לא ברור, נכון?) קודם כל, מה זה משאב... משאב הוא אובייקט, אחרי העבודה איתו צריך לסגור אותו, כלומר לקרוא ל- close(). משאב מתייחס לכל האובייקטים המיישמים ממשק AutoClosable, אשר בתורו מיישם ממשק Closeable. חשוב לנו להבין שהכל InputStreamהוא OutpuStreamמשאב וצריך לשחרר אותו בצורה נכונה ומוצלחת. זו בדיוק הסיבה שאנחנו צריכים להשתמש try-with-resourceבמבנה. כך זה נראה:
private void unzipFile(File zipFile) throws IOException {
   try(ZipInputStream zipOutputStream = new ZipInputStream(new FileInputStream(zipFile))) {
       ZipEntry zipEntry = zipOutputStream.getNextEntry();
       while (zipEntry != null) {

       }
   }
}

private void saveZipEntry(ZipEntry zipEntry) {
   // логика сохранения
}
בדוגמה זו, המשאב הוא ZipInputStream, לאחר העבודה איתו תצטרך לסגור אותו. וכדי לא לחשוב על קריאה למתודה close(), אנחנו פשוט מגדירים את המשתנה הזה בבלוק try, כפי שמוצג בדוגמה, ובתוך הבלוק הזה אנחנו עושים את כל מה שצריך. מה הדוגמה עושה? זה יפתח את ארכיון ה-zip. כדי לעשות זאת אתה צריך להשתמש InputStreamב-om. אתה יכול להגדיר יותר ממשתנה אחד; הם מופרדים על ידי נקודה-פסיק. מה הבעיה? אבל אתה יכול להשתמש finallyבבלוק, אתה יכול לומר. להלן מאמר המפרט את הבעיות בגישה זו. זה גם מתאר את כל רשימת הכשלים שיכולים לפקוד מישהו שמזניח להשתמש בעיצוב הזה. אני ממליץ לקרוא אותו ;) בחלק האחרון יש שאלות/תשובות בנושא Multithreading. פרופיל GitHub שלי
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION