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

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

Статья из группы Random
Привет! Даже самое быстроходное судно без курса будет просто дрейфовать по волнам. Если вы сейчас читаете мою статью, цель у вас определенно есть. Главное не сходить с пути, а гнуть свою линию до конца — стать 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Подробнее с исключениями можно познакомиться вот тут. Ну а на этом сегодня все! До встречи в следующей части!
Другие материалы серии:
Комментарии (4)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Sekator Уровень 41
13 октября 2022
В таком случае hashCode() будет возвращать число, сгенерированное на основе ячейки памяти, в которой хранится данный объект. Це не так !
hidden #2460969 Уровень 2
12 июля 2021
Дело в том, что hashcode отображается в типе Integer. метод hashCode возвращает тип int 102. Напишите методы Equals and HashCode для класса Student, который состоит из полей String name и int age

   if (o == null || this.getClass() != o.getClass()) {
     return false;
   }
Джошуа Блох говорит это bad pracrices. надо использовать instanceof only

return this.name != null ? this.name.equals(student.name) : student.name == null;
опять же Блох говорит тут использовать Objects.equals() туда же 103. В чем разница применения if (obj instanceof Student) и if (getClass () == obj.getClass ())? Собственно, оба подхода хороши, если понимать особенности их работы и применять в нужных местах. не одинаково хороши они с точки зрения правильного equals()