JavaRush /Java блог /Random UA /Розбір запитань та відповідей із співбесід на Java-розроб...
Константин
36 рівень

Розбір запитань та відповідей із співбесід на Java-розробника. Частина 11

Стаття з групи Random UA
Вітання! Навіть найшвидшехідне судно без курсу буде просто дрейфувати хвилями. Якщо ви зараз читаєте мою статтю, мета у вас безперечно є. Головне не сходити зі шляху, а гнути свою лінію до кінця – стати Java-розробником. Сьогодні я хочу продовжити свій аналіз 250+ питань для Java-розробників, який допоможе вам покрити деякі прогалини в теорії. Розбір запитань та відповідей із співбесід на Java-розробника.  Частина 11 - 1

97. Чи висуваються умови перевизначення угоди під час перевизначення Equals?

Перевизначений метод equals() повинен дотримуватись наступних умов (правила):
  • рефлексивність - для будь-якого значення x вираз виду x.equals(x) завжди має повертати true (коли при цьому x! = null ).

  • симетричність — для будь-яких значень x та y вираз вигляду x.equals(y) має повертати true тільки в тому випадку, якщо y.equals(x) повертає true .

  • транзитивність - для будь-яких значень x , y і z , якщо вираз x.equals(y) повертає true , при цьому y.equals(z) теж повертає true , тоді і x.equals(z) має повернути значення true .

  • узгодженість — для будь-яких значень x та y повторний виклик x.equals(y) завжди повертатиме значення попереднього виклику цього методу за умови, що поля, які використовуються для порівняння цих двох об'єктів, не були змінені між викликами.

  • порівняння null – для будь-якого значення x при викликі x.equals(null) буде повернуто – false .

98. Що буде, якщо не перевизначити Equals та HashCode?

У такому випадку hashCode() повертатиме число, згенероване на основі осередку пам'яті, в якому зберігається даний об'єкт. Тобто два об'єкти з абсолютно однаковими полями при виклику неперевизначеного hashCode() будуть отримувати різні значення (адже вони зберігаються у різних осередках пам'яті). Неперевизначений equals() порівнює посилання — чи вказують вони на той самий об'єкт чи ні. Тобто порівняння йде через == , і у випадку з об'єктами з однаковими полями завжди повертатиме false . Trueбуде тільки при порівнянні посилань на той самий об'єкт. Іноді є логіка, щоб не перевизначати ці методи. Наприклад, ви хочете, щоб усі об'єкти певного класу були унікальними і перевизначення цих методів тільки зіпсує логіку унікальності. Головне — розуміти нюанси перевизначених та неперевизначених даних методів та використовувати обидва підходи залежно від ситуації.

99. Чому симетричність виконується лише якщо x.equals(y) повертає true?

Трохи дивне питання. Якщо об'єкт A дорівнює об'єкту B, то об'єкт B дорівнює об'єкту А. Якщо ж B не дорівнює об'єкту A, то як можливо зворотне? Це проста логіка. Розбір запитань та відповідей із співбесід на Java-розробника.  Частина 11 - 2

100. Що таке колізія у HashCode? Як із нею боротися?

Колізія в hashCode - це ситуація, в якій два різних об'єкти мають однакове значення hashCode . Як це можливо? Справа в тому, що hashcode відображається в типі Integer , який, у свою чергу, має діапазон від -2147483648 до 2147483647, тобто приблизно 4 мільярди різних цілих чисел. Цей діапазон величезний, проте, не безкінечний. Тому можливі ситуації, коли два абсолютно різних об'єкти мають однаковий хеш-код. Це дуже малоймовірно, але можливо. Збільшувати частоту однакових хеш-кодів може ще погано реалізована хеш-функція, яка, наприклад, повертатиме числа в невеликому діапазоні, що збільшить шанс колізій. Для боротьби з колізією потрібно мати хорошу імплементацію методуhashCode , щоб розкидання значень було максимальним і шанс повторення значень був мінімальним.

101. Що буде, якщо елемент, який бере участь у контракті з HashCode, змінить своє значення?

Якщо елемент, який бере участь у підрахунку хеш-коду, був змінений, то і сам хеш-код об'єкта буде змінений (при хорошій хеш-функції). Тому в HashMap рекомендується використовувати іммутабельні (незмінювані) об'єкти як ключ, адже їх внутрішній стан (поля) неможливо змінити після створення. Відповідно, їх хеш-код також не перетворюється після створення. Якщо ж в якості ключа використовувати об'єкт, що змінюється, то при зміні полів даного об'єкта зміниться його хеш-код і як результат можна втратити цю пару в HashMap . Адже вона буде збережена в бакеті для початкового хеш-коду, а після зміни його пошук проводитиметься в іншому бакеті. Розбір запитань та відповідей із співбесід на Java-розробника.  Частина 11 – 3

102. Напишіть методи Equals and HashCode для класу Student, що складається з полів String name та int age

public class Student {
int age;
String name;

 @Override
 public boolean equals(final Object o) {
   if (this == o) {
     return true;
   }
   if (o == null || this.getClass() != o.getClass()) {
     return false;
   }

   final Student student = (Student) o;

   if (this.age != student.age) {
     return false;
   }
   return this.name != null ? this.name.equals(student.name) : student.name == null;
 }

 @Override
 public int hashCode() {
   int result = this.age;
   result = 31 * result + (this.name != null ? this.name.hashCode() : 0);
   return result;
 }
}
Equals:
  • Спершу ми порівнюємо безпосередньо посилання, адже якщо посилання на той самий об'єкт, який сенс продовжувати перевірку? Все і так буде true .

  • Перевірка на null і збіг типів класів, адже якщо об'єкт — аргумент null чи іншого типу, це означає, що об'єкти не рівні — false .

  • Приведення об'єкта до одного типу (раптом це був об'єкт батьківського типу).

  • Порівняння примітивного поля класу (адже для нього достатньо порівняння через =! ), Якщо поле не рівне - false .

  • Перевірка непримітивного поля на null і equalsString метод перевизначено і коректно порівнювати), якщо обидва поля null, чи equals , то перевірка закінчується і метод повертає true .

HashCode:
  • Завдання початкового значення хеш-коду дорівнює примітиву age об'єкта.

  • Помноження поточного хеш-коду на 31 (для більшого розкиду) та додавання до нього хеш-коду непримітивного рядкового поля (якщо воно не null).

  • Повернення результату.

  • В результаті такого перевизначення хеш-коду об'єкти з однаковими значеннями name і int завжди повертатимуть одне і те ж значення.

103. У чому різниця застосування if (obj instanceof Student) та if (getClass() == obj.getClass())?

Давайте розберемося, що робить кожен підхід:
  • instanceof перевіряє, чи є посилання на об'єкт з лівого боку екземпляром типу з правого боку або деяким його підтипом.

  • getClass() == ... перевіряє ідентичність типів.

Тобто, якщо getClass() перевіряє повну ідентичність класу, то instanceof поверне true навіть якщо об'єкт буде лише підтипом, що може дати нам велику гнучкість при активному використанні поліморфізму. Власне, обидва підходи хороші, якщо розуміти особливості їхньої роботи та застосовувати у потрібних місцях.

104. Дайте коротку характеристику методу clone().

Clone() - метод класу Object , призначенням якого - створення та повернення клону поточного об'єкта (копії поточного об'єкта). Розбір запитань та відповідей із співбесід на Java-розробника.  Частина 11 - 4Для його використання необхідно імплементувати інтерфейс маркер Cloneable :
Student implements Cloneable
І перевизначити сам метод clone() :
@Override
protected Object clone() throws CloneNotSupportedException {
 return super.clone();
}
Адже у класі Object він protected, тобто буде видно лише у самому класі Student , але не видно для класів ззовні.

105. У чому полягає особливість роботи методу clone() з полями об'єкта типу посилання?

При клонуванні об'єктів копіюються лише примітивні значення та значення посилань на об'єкти. Це означає, що якщо об'єкт має у внутрішньому полі посилання на інший об'єкт, то буде клоновано тільки це посилання, сам цей інший об'єкт клонований не буде. Власне, це і називають поверхневим клонуванням. Ну а що, якщо вам потрібне повноцінне клонування з клонуванням усіх вкладених об'єктів? Як зробити, щоб це були не копії посилань, а повноцінні клони об'єкти з іншими осередками пам'яті, що займаються, в купі? Насправді все досить просто - для цього вам потрібно в кожному класі цих внутрішніх об'єктів також перевизначити метод clone() і додати маркер інтерфейс - Cloneable. Тоді будуть скопійовані не посилання на об'єкти, а самі об'єкти, адже тепер вони також мають можливість копіювати себе.

Exceptions

106. У чому різниця між error та exception?

Як винятки, і помилки є підкласами класу Throwable . Проте вони мають свої відмінності. Помилка вказує на проблему, яка здебільшого виникає через брак системних ресурсів. І наша програма не повинна виявляти ці типи проблем. Деякі з прикладів помилок - збій системи та помилка нестачі пам'яті. Помилки переважно виникають під час виконання, оскільки вони відносяться до неперевіреного типу. Розбір запитань та відповідей із співбесід на Java-розробника.  Частина 11 - 5Винятки – це проблеми, які можуть виникнути під час виконання та під час компіляції. Як правило, це відбувається в коді, написаному розробниками. Тобто exception більш передбачувані та більш залежать від нас, як від розробників. У той же час errors більш випадкові і незалежніші від нас, а скоріше залежні від неполадок самої системи, в якій працює наша програма.

107. Яка різниця між checked та unchecked, exception, throw, throws.

Як я і сказав раніше, exception — це помилка під час виконання програми та під час компіляції, яка сталася в написаному розробнику коді (через якусь нештатну ситуацію). Checked – вид винятків, які потрібно завжди обробляти за допомогою використання механізму – try – catch або прокидати у методи вище. Throws використовується в заголовку методу для позначення можливих покинутих винятків даним методом. Тобто це і є механізм "прокидання" винятків у методи вище. Unchecked — вид винятків, які потрібно обробляти, вони, зазвичай, менш передбачувані і менш ймовірні. Тим не менш, за бажанням їх теж можна обробити. Throwвикористовується, при ручному кидку виключення, наприклад:
throw new Exception();

108. Яка ієрархія винятків?

Ієрархія винятків дуже велика, навіть занадто, щоб про неї ось тут так все розповісти. Тому, ми розглянемо лише ключові її ланки: Розбір запитань та відповідей із співбесід на Java-розробника.  Частина 11 - 6Тут на вершині ієрархії ми бачимо клас — Throwable — загальний клас, предок ієрархії винятків, що у свою чергу ділиться на:
  • Error — критичні помилки, що не перевіряються.
  • Exception - виключення, що перевіряються.
Exception ділиться на різні неперевірені runtime винятки та різноманітні виключення, що перевіряються.

109. Що таке checked та unchecked exception?

Як я раніше й казав:
  • Checked - винятки, які ви повинні якось обробити, тобто або обробити в блоці try - catch , або "прокинути" в спосіб вище. Для цього в сигнатурі методу після перерахування аргументів методу потрібно використовувати ключове слово trows <тип виключення> , яке вказує для користувачів методу, що метод може кинути цей виняток (щось на кшталт попередження) і передасть обов'язок обробляти виняток для користувачів цього методу.

  • Unchecked - винятки, які обробляти не потрібно, тому що вони не перевіряються під час компіляції і, як правило, непередбачуваніші. Тобто, основна різниця з checked у тому, що для них ці механізми try — catch або прокидання працюють також, але вони не є обов'язковими.

101. Напишіть приклад перехоплення та обробки виключення у блоці try — catch методу

try{                                                 // начало блока перехвата
 throw new Exception();                             // ручной бросок исключения
} catch (Exception e) {                              // данное исключение и его потомки будут перехватываться
 System.out.println("Упс, что-то пошло не так =("); // вывод некоторого исключения в консоль
}

102. Напишіть приклад перехоплення та обробки виключення з використанням власних винятків

Спочатку напишемо свій клас виключення, який успадковується від Exception і перевизначимо йому конструктор із повідомленням помилки:
public class CustomException extends Exception {

 public CustomException(final String message) {
   super(message);
 }
}
Ну а далі кинемо його вручну і перехопимо як і в попередньому питанні:
try{
 throw new CustomException("Упс, что-то пошло не так =(");
} catch (CustomException e) {
 System.out.println(e.getMessage());
}
І знову ж таки при запуску ви отримаєте висновок у консоль:
Упс, щось пішло не так =(
Розбір запитань та відповідей із співбесід на Java-розробника.  Частина 11 - 7Докладніше з винятками можна познайомитися ось тут . Ну, а на цьому сьогодні все! До зустрічі у наступній частині!
Інші матеріали серії:
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ