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, то як можливо зворотне? Це проста логіка.100. Що таке колізія у HashCode? Як із нею боротися?
Колізія в hashCode - це ситуація, в якій два різних об'єкти мають однакове значення hashCode . Як це можливо? Справа в тому, що hashcode відображається в типі Integer , який, у свою чергу, має діапазон від -2147483648 до 2147483647, тобто приблизно 4 мільярди різних цілих чисел. Цей діапазон величезний, проте, не безкінечний. Тому можливі ситуації, коли два абсолютно різних об'єкти мають однаковий хеш-код. Це дуже малоймовірно, але можливо. Збільшувати частоту однакових хеш-кодів може ще погано реалізована хеш-функція, яка, наприклад, повертатиме числа в невеликому діапазоні, що збільшить шанс колізій. Для боротьби з колізією потрібно мати хорошу імплементацію методуhashCode , щоб розкидання значень було максимальним і шанс повторення значень був мінімальним.101. Що буде, якщо елемент, який бере участь у контракті з HashCode, змінить своє значення?
Якщо елемент, який бере участь у підрахунку хеш-коду, був змінений, то і сам хеш-код об'єкта буде змінений (при хорошій хеш-функції). Тому в HashMap рекомендується використовувати іммутабельні (незмінювані) об'єкти як ключ, адже їх внутрішній стан (поля) неможливо змінити після створення. Відповідно, їх хеш-код також не перетворюється після створення. Якщо ж в якості ключа використовувати об'єкт, що змінюється, то при зміні полів даного об'єкта зміниться його хеш-код і як результат можна втратити цю пару в HashMap . Адже вона буде збережена в бакеті для початкового хеш-коду, а після зміни його пошук проводитиметься в іншому бакеті.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 і equals (у String метод перевизначено і коректно порівнювати), якщо обидва поля null, чи equals , то перевірка закінчується і метод повертає true .
-
Завдання початкового значення хеш-коду дорівнює примітиву age об'єкта.
-
Помноження поточного хеш-коду на 31 (для більшого розкиду) та додавання до нього хеш-коду непримітивного рядкового поля (якщо воно не null).
-
Повернення результату.
-
В результаті такого перевизначення хеш-коду об'єкти з однаковими значеннями name і int завжди повертатимуть одне і те ж значення.
103. У чому різниця застосування if (obj instanceof Student) та if (getClass() == obj.getClass())?
Давайте розберемося, що робить кожен підхід:-
instanceof перевіряє, чи є посилання на об'єкт з лівого боку екземпляром типу з правого боку або деяким його підтипом.
-
getClass() == ... перевіряє ідентичність типів.
104. Дайте коротку характеристику методу clone().
Clone() - метод класу Object , призначенням якого - створення та повернення клону поточного об'єкта (копії поточного об'єкта). Для його використання необхідно імплементувати інтерфейс маркер 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 . Проте вони мають свої відмінності. Помилка вказує на проблему, яка здебільшого виникає через брак системних ресурсів. І наша програма не повинна виявляти ці типи проблем. Деякі з прикладів помилок - збій системи та помилка нестачі пам'яті. Помилки переважно виникають під час виконання, оскільки вони відносяться до неперевіреного типу. Винятки – це проблеми, які можуть виникнути під час виконання та під час компіляції. Як правило, це відбувається в коді, написаному розробниками. Тобто exception більш передбачувані та більш залежать від нас, як від розробників. У той же час errors більш випадкові і незалежніші від нас, а скоріше залежні від неполадок самої системи, в якій працює наша програма.107. Яка різниця між checked та unchecked, exception, throw, throws.
Як я і сказав раніше, exception — це помилка під час виконання програми та під час компіляції, яка сталася в написаному розробнику коді (через якусь нештатну ситуацію). Checked – вид винятків, які потрібно завжди обробляти за допомогою використання механізму – try – catch або прокидати у методи вище. Throws використовується в заголовку методу для позначення можливих покинутих винятків даним методом. Тобто це і є механізм "прокидання" винятків у методи вище. Unchecked — вид винятків, які потрібно обробляти, вони, зазвичай, менш передбачувані і менш ймовірні. Тим не менш, за бажанням їх теж можна обробити. Throwвикористовується, при ручному кидку виключення, наприклад:throw new Exception();
108. Яка ієрархія винятків?
Ієрархія винятків дуже велика, навіть занадто, щоб про неї ось тут так все розповісти. Тому, ми розглянемо лише ключові її ланки: Тут на вершині ієрархії ми бачимо клас — Throwable — загальний клас, предок ієрархії винятків, що у свою чергу ділиться на:- Error — критичні помилки, що не перевіряються.
- Exception - виключення, що перевіряються.
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());
}
І знову ж таки при запуску ви отримаєте висновок у консоль:
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ