❓ Чем List.copyOf отличается от Collections.unmodifiableList?
Ответ: Collections.unmodifiableList(source) создаёт представление исходного списка, запрещающее изменения через это представление. Если исходный список изменить напрямую, изменения будут видны и через обёртку.
List.copyOf(source) возвращает список, который нельзя изменить и чьё содержимое не зависит от последующих изменений исходной коллекции. При этом метод не обязан физически создавать новый объект: подходящий неизменяемый список может быть переиспользован.
Например, после добавления элемента в source обёртка unmodifiableList увидит новый элемент, а результат copyOf — нет.
Есть ещё два нюанса. List.copyOf не допускает null-элементы. И ни один из этих способов не делает глубокую копию объектов внутри списка: если элемент изменяемый, его состояние всё ещё можно изменить.
❓ Чем операции Stream API с состоянием отличаются от операций без состояния?
Ответ: Операции без состояния обрабатывают очередной элемент независимо от ранее обработанных. Типичные примеры — filter и map, если переданные им функции сами не зависят от изменяемого внешнего состояния.
Операциям с состоянием нужна информация о других элементах. Например, distinct должен учитывать уже встреченные значения, а sorted — порядок элементов всей конечной последовательности, прежде чем выдавать отсортированный результат.
Это влияет на память и выполнение конвейера. После filter подходящий элемент часто можно сразу передать дальше. sorted обычно требует накопить данные; distinct может выдавать новые уникальные элементы постепенно.
Не путайте состояние операции Stream API с изменением общей переменной внутри лямбды. Счётчик, который вы увеличиваете из map, не превращает его в корректную замену встроенной операции с состоянием и особенно опасен при параллельном выполнении.
❓ Чем ClassNotFoundException отличается от NoClassDefFoundError?
Ответ: ClassNotFoundException — проверяемое исключение, возникающее, когда приложение загружает класс по имени, например через Class.forName(), а загрузчик не находит его определение.
NoClassDefFoundError относится к LinkageError. Он может возникнуть, когда JVM требуется определение класса при обычном выполнении программы, но оно недоступно. Например, приложение скомпилировали с зависимостью, а при запуске нужный JAR отсутствует.
Но объяснение «NoClassDefFoundError всегда означает отсутствующий JAR» неполное. Он также возникает при повторной попытке инициализировать класс, чья предыдущая инициализация завершилась ошибкой.
Например, статический инициализатор выбросил исключение: первое обращение может завершиться ExceptionInInitializerError, а последующие — NoClassDefFoundError.
Поэтому при диагностике важно смотреть не только имя ошибки, но и её причину, а также самый первый сбой инициализации.
❓ Можно ли объявить интерфейс sealed и что это ограничивает?
Ответ: Да. sealed можно применять и к классам, и к интерфейсам. Для интерфейса он ограничивает круг непосредственных подтипов: кто может его реализовать или расширить.
Например, sealed interface Payment permits CardPayment, CashPayment разрешает только перечисленные непосредственные подтипы. При этом permits можно опустить, если компилятор может вывести список из объявлений в той же единице компиляции.
Обычный класс, напрямую реализующий такой интерфейс, должен быть final, sealed или non-sealed. Подынтерфейс — sealed или non-sealed. У record и enum есть собственные правила неявных модификаторов.
Важно: non-sealed снова открывает соответствующую ветку для дальнейшего наследования. Поэтому sealed у корневого интерфейса не означает, что во всей иерархии может существовать только несколько конкретных классов.
Два равных объекта — две записи. В какой map это возможно?
Создали два разных объекта String со значением key. Они равны по equals, но ссылки различаются. Оба использовали как ключи. Какой размер получится у IdentityHashMap и у обычной HashMap?
Ответ:
2 и 1. IdentityHashMap сравнивает ключи по тождеству ссылок, обычная HashMap — по equals. Она нужна в редких задачах вроде обхода графа объектов или глубокой копии, где два равных по содержимому узла нельзя слить в один. Это специальная семантика, а не универсальная замена HashMap. В примере явно создаются два разных объекта, одинаковые строковые литералы для проверки не подходят.