Java Core

Java Core #

1. Расскажи мне про контракт и свойства Equals & Hashcode #

Контракт:

  • Если hashCode() двух объектов возвращает разные значения, то они не могут быть равны
  • Если equals() объектов true, то и хеш-коды должны быть равны
  • Переопределив equals(), всегда переопределять hashCode()
  • Если у объектов одинаковый hashCode(), то они не обязательно equals()
  • При каждом вызове hashCode() для одного и того же объекта должен возвращаться один и тот же хеш-код

Свойства equals(): Метод equals() реализует отношение равенства (эквивалентности), которое обладает следующими свойствами:

  • Рефлексивность. Объект должен быть равен самому себе (x.equals(x) == true)
  • Симметричность. Любые два объекта должны иметь одно и то же мнение относительно своего равенства или неравенства (x.equals(y) == true и y.equals(x) == true)
  • Транзитивность. Если один объект равен второму, а второй равен третьему, то и первый объект должен быть равен третьему (x.equals(y) == true и y.equals(z) == true и x.equals(z) == true)
  • Непротиворечивость. Если два объекта равны, они должны быть равны всегда, пока один из них (или оба они) не будет изменен
  • Для любой ненулевой ссылки на значение х выражение х.equals(null) должно возвращать false.

Свойства hashCode():

  • Согласованность. При каждом вызове hashCode() для одного и того же объекта в рамках одной программы должно возвращаться одно и то же значение, если поля объекта, участвующие в расчете хеш-кода, не изменились
  • Равенство и хеш-код. Если два объекта равны по методу equals(), их хеш-коды тоже должны быть равны. Однако обратное не обязательно — объекты с одинаковым хеш-кодом могут быть не равны
  • Распределение. Реализация hashCode(), возвращающая разный хеш-код для неравных объектов позволяет увеличить эффективность работы с хеш-структурами данных (такими как HashMap или HashSet) за счёт распределения объектов по разным корзинам хеш-таблицы


2. Что такое коллизия? #

Коллизия возникает, когда у разных объектов одинаковые хеш-коды


Что будет, если переопределить equals() не переопределяя hashCode()?

  • Объект, который по equals() равен существующему ключу в HashMap, может не быть найден, если их хеш-коды различаются. Это связано с тем, что HashMap сначала использует hashCode() для поиска корзины, а уже потом сравнивает объекты по equals().
  • При попытке удалить ключ, который равен другому ключу по equals(), но имеет другой hashCode(), удаление может не сработать.


3. Какие типы ссылок существуют в Java? #

Типы ссылок в Java:

  • Сильная ссылка - объект не будет удален сборщиком мусора, пока на него есть хотя бы одна сильная ссылка Object a = new Object()
  • Мягкая ссылка - используется для кеширования. Объект, на который ссылается мягкая ссылка, будет удален сборщиком мусора только при нехватке памяти SoftReference<MyObject> softRef = new SoftReference<>(new MyObject())
  • Слабая ссылка - объект, на который она ссылается, может быть удалён при следующем вызове сборщика мусора WeakReference<MyObject> weakRef = new WeakReference<>(new MyObject())
  • Фантомная ссылка - используется для отслеживания, когда объект был удалён из памяти. Фантомная ссылка не позволяет получить доступ к объекту напрямую, она только сигнализирует, что объект скоро будет удален PhantomReference<MyObject> phantomRef = new PhantomReference<>(new MyObject(), referenceQueue)


4. Какой класс называют Immutable? #

Иммутабельный класс - класс, экземпляры которого не могут быть изменены после создания. Принципы создания:

  • модификатор final у класса
  • все поля private final
  • отсутствие сеттеров
  • все поля задаются через конструктор


5. Основная идея языка #

Написано однажды - работает везде


6. За счет чего обеспечена кроссплатформенность? #

За счёт того, что исходный код компилируется в байт-код, который затем исполняется виртуальной машиной Java (JVM).


7. Плюсы и минусы Java? #

Плюсы:

  • Объектно-ориентированное программирование. Структура данных становится объектом, которым можно управлять, создавать отношения между различными объектами.

  • Язык высокого уровня с простым синтаксисом и плавной кривой обучения. Синтаксис Java основан на C ++, поэтому Java похожа на C. Тем не менее, синтаксис Java проще, что позволяет новичкам быстрее учиться и эффективнее использовать код для достижения конкретных результатов.

  • Стандарт для корпоративных вычислительных систем. Корпоративные приложения — главное преимущество Java с 90-х годов, когда организации начали искать надежные инструменты программирования не на C.

  • Безопасность. Благодаря отсутствию указателей и Security Manager (политика безопасности, в которой можно указать правила доступа, позволяет запускать приложения Java в “песочнице”).

  • Независимость от платформы. Можно создать Java-приложение на Windows, скомпилировать его в байт-код и запустить его на любой другой платформе, поддерживающей виртуальную машину Java (JVM). Таким образом, JVM служит уровнем абстракции между кодом и оборудованием.

  • Язык для распределенного программирования и комфортной удаленной совместной работы. Специфическая для Java методология распределенных вычислений называется Remote Method Invocation (RMI). RMI позволяет использовать все преимущества Java: безопасность, независимость от платформы и объектно-ориентированное программирование для распределенных вычислений. Кроме того, Java также поддерживает программирование сокетов и методологию распределения CORBA для обмена объектами между программами, написанными на разных языках.

  • Автоматическое управление памятью. (вопрос спорный: плюс или минус) Разработчикам Java не нужно вручную писать код для управления памятью благодаря автоматическому управлению памятью (AMM).

  • Многопоточность. Поток — наименьшая единица обработки в программировании. Чтобы максимально эффективно использовать время процессора, Java позволяет запускать потоки одновременно, что называется многопоточностью.

  • Стабильность и сообщество. Сообщество разработчиков Java не имеет себе равных. Около 45% респондентов опроса StackOverflow 2018 используют Java.

Минусы:

  • Платное коммерческое использование (с 2019)
  • Многословный код


8. JVM, JRE, JDK, JIT #

JVM (Java Virtual Machine) - виртуальная машина Java, исполняющая байт-код. JRE (Java Runtime Environment) - JVM + набор стандартных библиотек, необходимых для запуска приложений Java:

  • java.lang - String, System, Object
  • java.io - InputStream, OutputStream
  • java.util - коллекции (List, Set, Map) JDK (Java Development Kit) - набор разработчика, включает JRE + компилятор и другие инструменты JIT(C) (Just-In-Time Compilation) - компиляция байт-кода в машинный во время выполнения программы. Позволяет ускорить выполнение программы за счёт компиляции наиболее часто выполняемых участков кода в машинный код и хранения его в памяти


9. Что такое байт-код? #

Байт-код - это набор инструкций, исполняемый JVM, получаются из исходного кода путём компиляции.


10. Что такое сборщик мусора (garbage collector)? #

Сборщик мусора - это механизм JVM, отвечающий за управление объектами в памяти. Программист выделяет память под объекты, поиском и очищением памяти от неиспользуемых объектов (объектов, на которые нет прямых или косвенных ссылок) занимается сборщик.

Версия JavaGC по умолчаниюПримечания
Java 8Parallel GCОптимизирован для пропускной способности.
Java 9G1G1 стал сборщиком по умолчанию, заменив Parallel GC.
Java 10G1Улучшения в G1, включая параллельную полную сборку мусора.
Java 11G1Введены ZGC и Shenandoah (экспериментальные).
Java 12G1Улучшения в G1, включая возврат неиспользуемой памяти.
Java 13G1Улучшения в ZGC и Shenandoah.
Java 14G1ZGC и Shenandoah стали стабильными.
Java 15G1ZGC стал кроссплатформенным.
Java 16G1Улучшения в ZGC и Shenandoah.
Java 17G1CMS окончательно удален.
Java 21G1Введен Generational ZGC, улучшения в G1, ZGC и Shenandoah.

Когда использовать другие GC?

  • ZGC - для приложений с огромными кучами (терабайты) и требованиями к минимальным паузам (менее 10 мс).
  • Shenandoah - для приложений с низкими задержками и большими кучами.
  • Parallel GC - для приложений, где важна пропускная способность, а не низкие задержки.
  • Serial GC - для однопоточных приложений или приложений с небольшими кучами.


11. Как работает GC? #

  1. Поиск недостижимых объектов - проверяет, есть ли ссылки на объект.
  2. Очистка - если объект недостижим, его память освобождается.
  3. Обновление памяти - освобождённая память становится доступной для новых объектов.

Основные алгоритмы GC:

  • Mark-and-Sweep - отмечает используемые объекты и удаляет неиспользуемые.
  • Generational GC - разделяет память на молодое, старшее и постоянное поколение для оптимизации работы.


12. Какие виды GC знаешь? #

  • Serial GC — простой однопоточный сборщик мусора, который хорошо подходит для маленьких приложений и небольшого объёма памяти.
  • Parallel GC (Throughput GC) — многопоточный сборщик, который делает упор на максимальную производительность приложения.
  • CMS (Concurrent Mark Sweep) — сборщик с низкими паузами, который пытался выполнять часть работы параллельно с приложением, но сейчас считается устаревшим.
  • G1 GC (Garbage First) — универсальный сборщик, который балансирует между скоростью работы и предсказуемыми паузами.
  • ZGC — современный сборщик для приложений, где важны ультранизкие паузы, обычно в пределах миллисекунд.
  • Shenandoah — low-latency сборщик, похожий по назначению на ZGC, тоже старается минимизировать паузы приложения.
  • Epsilon GC — специальный сборщик, который вообще не освобождает память и используется в основном для тестов и бенчмарков.


13. Области памяти Java. Stack и heap. Что это такое? #

На рисунке структура памяти JVM согласно спецификации

Разбираем с т.з. спецификации и реализации в HotSpot JVM.

Нативная память (native memory) - это вся память, которую процесс JVM получает напрямую от операционной системы. Внутри у неё есть heap, все остальные области - в нативной памяти.

Heap (куча) - используется для выделения памяти под объекты и классы. Создание нового объекта также происходит в куче. Любой объект, созданный в куче, имеет глобальный доступ и на него могут ссылаться из любой части приложения. Управляется сборщиком мусора. В реализации HotSpot делится на:

  • Young Generation - для недавно созданных объектов
    • Eden Space - сюда попадают новосозданные объекты, GC проходит здесь чаще всего
    • Survivor Space - делится надвое (S1, S2), из Eden “выжившие” объекты переходят в S1, пережившие несколько сборок в S1 переходят в S2
  • Old Generation - для долго живущих объектов
  • (до Java 8) Perm Generation - хранение статических объектов, размер ограничен

Method Area (MetaSpace для HotSpot) - хранит структуру классов, такую как метаданные классов, пул констант, статические переменные и код методов. Доступна всем потокам в JVM. В Java 8 MetaSpace заменила PermGen (который был частью кучи) и, в отличие от него, её размер увеличивается автоматически и не имеет явного ограничения

Stack Area - это область хранения стеков (Thread Stack). Стек создаётся при старте потока и доступен только из своего потока. Стек хранит фреймы (те самые строки в нижнем левом углу в режиме отладки в IntelliJ), которые создаются при вызове метода и удаляются при выходе из него. Фреймы хранятся в стеке по принципу LIFO (последний вошёл — первый вышел)

Фрейм содержит в себе все локальные переменные, требующиеся для выполнения методов. Все локальные переменные примитивных типов полностью хранятся в стеке, локальные переменные-ссылки на объект также хранятся в стеке, но сам объект хранится в куче

Регистры (PC Registers) - у каждого потока есть свой регистр, он хранит адрес текущей инструкции байткода и позволяет JVM продолжать выполнение потока после переключений

Native Method Stack - это область памяти, в которой хранятся стеки для вызова нативных методов, то есть методов, написанных на других языках программирования, таких как C или C++

В дополнение к спецификации в HotSpot есть

  • Code Cache - хранит код, оптимизированный JIT-компилятором для быстрого выполнения, используется в процессе “прогрева” JVM. Отдельная область в нативной памяти
  • Shared Libs - это нативные библиотеки, подключаемые JVM (libc, libjvm.so, JNI-библиотеки)


14. Типы данных в Java #

В Java есть примитивные и ссылочные типы данных.

Примитивные типы данных:

  • byte (0) (8 бит)
  • short (0) (16 бит)
  • int (0) (32 бит)
  • long (0L) (64 бит)
  • float (0.0f) (32 бит)
  • double (0.0d) (64 бит)
  • char (\u0000) (16 бит)
  • boolean (false) (зависит от JVM)


15. Что такое класс-обертка? #

Класс-обёртка - это immutable-класс, который представляет примитивный тип данных в виде объекта. Такие классы позволяют хранить null, в отличие от примитивов, где null вызывает NullPointerException. Обёртки содержат полезные статические поля (MIN_VALUE, MAX_VALUE) упрощающие работу с данными.


16. Автоупаковка и автораспаковка #

Автоупаковка (autoboxing) - примитивный тип -> класс-обёртка

int x = 10;  
Integer obj = x;

Автораспаковка (unboxing) - класс-обёртка -> примитивный тип

Integer obj = 10;  
int x = obj;


17. Строки/String в Java? Из чего состоит? Нюансы #

String в Java - это объект. Сравнение через оператор == сравнивает строки по ссылкам объектов - если ссылки равны, то строки равны, в остальных случаях не равны. Нужно использовать equals() и производные методы для сравнения содержимого.

Класс String - immutable из-за модификатора final и отсутствия сеттера. При редактировании строк будет создаваться новая строка.


18. Что такое пул строк/String pool? #

Пул строк — это особая область кучи (heap), где хранятся объекты строк. Служит в первую очередь для оптимизации работы со строковыми литералами. При объявлении литерала (строки в двойных кавычках) в пуле происходит поиск этой строки:

  • Если такой строки в пуле нет ⇒ создается строка, возвращается ссылка
  • Если есть ⇒ возвращается ссылка найденной строки

Строки, созданные с помощью new String() не помещаются в пул строк и хранятся в куче как остальные объекты.


Что делает метод intern() в классе String?

  • Помещает строку в пул строк


19. Почему не рекомендуется изменять строки в цикле? #

Строка является неизменной и финализированной в Java, поэтому все наши манипуляции со строкой всегда будут создавать новую строку. Манипуляции со строками ресурсоемкие, поэтому Java обеспечивает два полезных класса для манипуляций со строками – StringBuffer и StringBuilder.


20. Разница между String, StringBuffer, StringBuilder #

String - неизменяемый, потокобезопасный StringBuilder - изменяемый, оптимизирован для конкатенации строк (новые строки не создаются от каждой конкатенации) StringBuffer - потокобезопасная версия StringBuilder, хуже производительность


21. Каким образом переменные передаются в методы — по значению или по ссылке? #

По значению. Всегда. При передаче объектов передаётся копия ссылки на них. Т.е. внутри метода можно обращаться к объекту и изменять его состояние, но нельзя изменить область памяти, на которую ссылается внешняя переменная


22. Что такое enum? #

Enum - специализированный класс в Java, представляющий из себя набор логически связанных констант. От него нельзя наследоваться.


23. Что такое конструктор? #

Конструктор - это специализированный метод класса, предназначенный для инициализации экземпляра этого класса.

Конструктор по умолчанию - это конструктор без параметров, который автоматически создаётся компилятором, если не было объявлено других конструкторов.

Для чего нужны приватные/private конструкторы? Приватный конструктор запрещает создание экземпляра класса вне методов самого класса. Нужен для реализации паттернов singleton

public class Singleton {
    // Статическое поле для хранения единственного экземпляра класса
    private static Singleton instance;

    // Приватный конструктор, предотвращающий создание экземпляров извне
    private Singleton() {}

    // Публичный метод для доступа к единственному экземпляру класса
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Чем отличаются конструкторы по умолчанию, конструктор копирования и конструктор с параметрами?

  • Конструктор по умолчанию - без параметров, дефолтный
  • Конструктор копирования - принимает объект того же типа, копирует его поля
  • Конструктор с параметрами - принимает на вход параметры для конфигурации объекта при создании


24. Какие есть модификаторы доступа? Какие применимы к классам? #

  • private - доступ только из класса, в котором объявлен
  • default - компонент доступен для любого другого класса в том же пакете
  • protected - компонент доступен всем классам внутри пакета, а так же всем классам-наследникам вне пакета
  • public - доступ к компоненту из любой точки программы

protected и private в контексте классов применимы только к внутренним (вложенным), потому что в других protected классах нет смысла - default видимость делает то же самое, а к private внешнему классу нельзя было бы обратиться из любого другого класса.


25. Что означает static? #

  • static переменная - переменная, принадлежащая классу, а не объекту
  • static класс - вложенный класс, который может обращаться только к статическим полям класса-обертки
  • static метод - метод, принадлежащий классу, а не объекту. Статические методы обращаются только к другим статическим методам и переменным

Может ли статический метод быть переопределен?

Нельзя переопределять статические методы. Если объявить такой же метод в классе-наследнике, то мы лишь «спрячем» метод суперкласса вместо переопределения. Это называется сокрытие методов.

Прим. Перегружать можно, переопределять нет. В случае со статическими методами это не переопределение (overriding), а сокрытие (hiding).


Могут ли нестатические методы перегружать статические?

Да. В итоге получится два разных метода. Статический будет принадлежать классу и будет доступен через его имя, а нестатический будет принадлежать конкретному объекту и доступен через вызов метода этого объекта.


Могут ли классы быть статическими?

Да, вложенные классы


26. Можно ли сузить уровень доступа или тип возвращаемого значения при переопределении метода? #

Можно расширить модификатор доступа и сузить тип возвращаемого значения (если они совместимы)


27. Что можно изменить в сигнатуре метода при переопределении? Можно ли менять модификаторы (throws и т. п.)? #

Согласно спецификации Java, сигнатура состоит из имени метода, списка параметров и их типов (порядок типов важен). Сюда не входит возвращаемое значение, модификатор доступа и перечисление выбрасываемых исключений.

Поэтому именно в самой сигнатуре при переопределении методов ничего менять нельзя. Можно:

  • расширить уровень доступа
  • сузить тип возвращаемого значения
  • не указывать исключения, которые были объявлены в родительском классе
  • добавить новые исключения, которые являются наследниками родительского, либо те, что наследуются от RuntimeException


28. Что означает модификатор final? К чему он может быть применим? #

  • final у класса - запрещает наследование
  • final у примитива - запрещает изменение значения после присвоения
  • final у ссылочной переменной - запрещает изменение ссылки на объект
  • final у массива - запрещает изменение ссылки на массив (но можно менять элементы внутри)
  • final у метода - запрещает переопределять данный метод в наследниках класса


29. Что такое абстрактные классы? #

Абстрактный класс - это класс, который не может быть инстанциирован (нельзя создать экземпляр этого класса). Используется для того, чтобы выделить общую логику (абстракцию) и переиспользовать в наследниках.

Может ли быть абстрактный класс без абстрактных методов? Да, может

Могут ли быть конструкторы у абстрактных классов? Для чего они нужны? Да, могут, если они нужны для наследников


30. Что такое интерфейсы? #

Интерфейс — это особый абстрактный тип, описывающий поведение (контракт), которое классы должны реализовать

  • Методы интерфейса - public abstract
  • Поля - public static final

Какие модификаторы по умолчанию/по дефолту имеют поля и методы интерфейсов?

Default методы - это методы в интерфейсе, которые имеют тело и помечены default. Классы, реализующие такой интерфейс могут не переопределять метод и использовать код из интерфейса.

Начиная с Java 8 в интерфейсах разрешается размещать реализацию методов по умолчанию default и статических static методов.

По умолчанию все методы в интерфейсе фактически имеют модификатор public. Однако начиная с Java 9 мы также можем определять в интерфейсе методы с модификатором private. Они могут быть статическими и нестатическими, но обязаны иметь реализацию (т.е. тело метода).

Интерфейс также может содержать и поля. В этом случае они автоматически являются публичными public, статическими static и неизменяемыми final.


Может ли один интерфейс наследоваться от другого? От двух других?

Да


Зачем нужны интерфейсы маркеры?

Интерфейсы-маркеры (marker interfaces) — это интерфейсы без методов, используемые для предоставления метаинформации о классе.

Примеры:

Serializable — сообщает, что объект может быть сериализован.
Cloneable — разрешает создание копий объекта через clone().

Плюсы:
✅ Простота реализации.
✅ Используется в стандартной библиотеке Java (Serializable, Cloneable).
✅ Позволяет использовать instanceof для проверки типа.

Минусы:
❌ Неявная логика — тяжело понять, что делает интерфейс.
❌ Сложно поддерживать, если нужно добавить новую функциональность.
❌ Нельзя контролировать правильность использования (например, Cloneable не гарантирует корректную реализацию clone()).


Плюсы и минусы дефолтных методов

default-методы позволяют добавлять реализацию методов в интерфейсы без необходимости изменять существующие реализации классов.

Пример:

interface Animal {
    default void makeSound() {
        System.out.println("Some sound...");
    }
}
class Dog implements Animal {}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.makeSound(); // Выведет "Some sound..."
    }
}

Плюсы:
✅ Позволяют добавлять новые методы в интерфейсы без ломания существующего кода.
✅ Уменьшают дублирование кода в классах, реализующих интерфейс.
✅ Можно предоставлять базовую реализацию метода.

Минусы:
❌ Может усложнять иерархию классов, если использовать слишком активно.
❌ Конфликты default-методов, если несколько интерфейсов содержат метод с одинаковой сигнатурой.
❌ Нарушает принцип единственной ответственности (SRP), так как интерфейс начинает содержать логику.


Плюсы и минусы множественной имплементации

Множественная имплементация означает, что класс может реализовать несколько интерфейсов.

Пример:

interface A {
    default void show() {
        System.out.println("A");
    }
}
interface B {
    default void show() {
        System.out.println("B");
    }
}
class C implements A, B {
    @Override
    public void show() {  // Разрешаем конфликт
        A.super.show();
    }
}

Плюсы:
✅ Позволяет избежать проблем, связанных с множественным наследованием (алмазная проблема).
✅ Повторное использование кода через default-методы.
✅ Гибкость — можно комбинировать поведение разных интерфейсов.

Минусы:
❌ Конфликты default-методов, если несколько интерфейсов содержат метод с одинаковой сигнатурой.
❌ Возможна сложность в отладке и поддержке кода.
❌ Нельзя использовать super для вызова конструктора интерфейса.


31. Отличия интерфейса от абстрактных классов? #

  • Абстрактные классы используются, когда есть отношение “is a”, а интерфейс может быть реализован классами, вовсе не связанными друг с другом
  • Унаследовать можно только один класс, а реализовать интерфейсов — сколько угодно
КритерийАбстрактный классИнтерфейс
Когда использоватьЕсли есть общая логика, которую наследники могут переопределятьДля определения контрактов, которые реализуют классы
Модификаторы доступа по умолчаниюprotecteddefaultВсе методы public
Наличие конструктораДаНет
Наследование/реализацияКласс может наследовать только один абстрактный классКласс может реализовать несколько интерфейсов
Методы с реализациейМожет содержать методы с реализациейМожет содержать default и static (с Java 8), а с Java 9и private методы с реализацией


32. Каков порядок вызова конструкторов и блоков инициализации с учетом иерархии классов? #

  1. Статические блоки у родителя
  2. Статический блок у наследника
  3. Нестатический блок у родителя
  4. Конструктор родителя
  5. Нестатические блоки у наследника
  6. Конструктор наследника


33. Зачем нужны и какие бывают блоки инициализации? #

Существуют статические и нестатические блоки инициализации.
Статический блок инициализации используется для инициализации статических данных класса. Он выполняется только один раз, когда класс загружается в память.
Нестатический блок инициализации, также известный как блок инициализации экземпляра, используется для инициализации переменных экземпляра класса. Он выполняется каждый раз, когда создается новый экземпляр класса.


34. Класс Object и его методы? #

Object - базовый класс для всех объектов в Java. Любой класс так или иначе наследуется от Object, и, соответственно, наследует его методы (11 штук):

  • equals() - определяет, равен ли один объект другому.
  • hashCode() - возвращает хэш-код, связанный с вызывающим объектом.
  • toString() - возвращает символьную строку, описывающую объект.
  • getClass() - получает класс объекта во время выполнения.
  • clone() - создает новый объект, не отличающийся от клонируемого.
  • finalize() - вызывается перед удалением неиспользуемого объекта.

А так же методы для многопоточности:

  • notify() - возобновляет исполнение потока, ожидающего вызывающего объекта.
  • notifyAll() - возобновляет исполнение всех потоков, ожидающих вызывающего объекта.
  • wait(), wait(long timeout), wait(long timeout, int nanos) - ожидает другого потока исполнения.

Метод finalize()

finalize() — это метод в классе Object, который вызывается перед уничтожением объекта сборщиком мусора (GC). Помечен как @Deprecated с Java 9 с возможностью дальнейшего удаления.

Современная альтернатива:
Java 9+: использовать Cleaner или try-with-resources.


35. Что такое сериализация и для чего она нужна? #

Сериализация - это механизм преобразования объектов в байтовую строку. В Java реализована через интерфейс-маркер Serializable. Нужна для компактного сохранения состояния объекта, передачи по сети и считывания этого состояния


36. Разница между версиями Java #

  • Java 8 - лямбда-выражения, Stream API, Optional API, новый Date and Time API, default методы в интерфейсах
  • Java 9 - модульная система (JPMS), улучшения в Stream API и методы Optional.ifPresentOrElse, фабричные методы для коллекций (List.of)
  • Java 10 - локальная типизация (var), улучшения в GC, API для создания неизменяемых коллекций (List.copyOf, Set.copyOf, Map.copyOf)
  • Java 11 - поддержка var в лямбдах, новый клиент HTTP
  • Java 17 -  recordsealed классы, улучшения в GC и performance, pattern matching для instanceof
  • Java 21 - виртуальные потоки, pattern matching для switch, поддержка sealed-иерархий для record, улучшения в GC
  • Java 23 - flexible constructor, javadoc комменты в markdown формате, улучшения по ZGC
  • Java 25 - улучшения JIT, scoped values вместо ThreadLocal, structurred concurrency, stable value вместо синглтона


37. Может ли абстрактный класс быть final? #

Нет, т.к. суть этого класса - в вынесении общей логики наследников, нельзя создать экземпляр абстрактного класса.


38. Integer pool #

Integer Pool - это область памяти в хипе, предназначенная для кэширования Integer в диапазоне от -128 до 127 для уменьшения количества создаваемых объектов (по аналогии с String Pool)


39. Какие виды загрузчиков классов ты знаешь? #

  • Bootstrap ClassLoader: Загружает стандартные библиотеки (например, rt.jar).
  • Extension ClassLoader: Загружает дополнительные библиотеки (ext).
  • System ClassLoader: Загружает классы из classpath.
  • Custom ClassLoader: Пользовательские загрузчики классов.


40. Record-ы с Java 17. Для чего и зачем? #

Record — это компактная декларация неизменяемого класса, удобная для хранения данных.

  • Преимущества:
    • Автоматическое создание конструктора, getter-ов (методы именуется без префикса get), equals, hashCode, toString.
    • Неизменяемость полей.
public record Point(int x, int y) {}
Point p = new Point(1, 2);
System.out.println(p.x()); // 1


41. Что такое Анонимный класс #

Анонимный класс - это класс без имени, реализация которого описана in-place в момент создания

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello!");
    }
};

Требования к переменным, на которые ссылаемся внутри анонимного класса Переменные, используемые внутри анонимного класса, должны быть effectively final (не изменяться после инициализации)


42. Расскажи что делает JIT компилятор #

JIT (Just-In-Time) компилятор преобразует байткод в машинный код для особо “горячих” (часто вызывающихся) частей кода во время выполнения программы для увеличения производительности


43. Расскажи про прогрев JVM #

Прогрев JVM - это оптимизация работы приложения в первые минуты его запуска. JIT анализирует исполняемый код и применяет оптимизации, такие как инлайнинг или устранение лишних проверок, escape-анализ (проверка на локальные объекты метода с возможностью размещать их в стеке, а не в куче) https://docs.oracle.com/en/java/javase/25/vm/java-virtual-machine-guide.pdf


44. break vs continue в цикле #

break - выход из цикла continue - немедленный переход к следующей итерации


45. Что такое аннотация? Зачем нужна? Как создать свою аннотацию? #

Аннотация - это механизм пометки классов, методов и полей дополнительной информацией для компилятора, инструментов разработки и фреймворков

Основные аннотации в Java

АннотацияОписание
@OverrideПроверяет, что метод переопределяет метод суперкласса.
@DeprecatedОтмечает метод/класс устаревшим, чтобы не использовать его в будущем.
@SuppressWarningsПодавляет определенные предупреждения компилятора.
@FunctionalInterfaceПоказывает, что интерфейс является функциональным (с одним абстрактным методом).
@SafeVarargsПодавляет предупреждения о небезопасных операциях с аргументами переменной длины (varargs).
@NativeИспользуется для констант, предназначенных для взаимодействия с нативным кодом.

Метаданные аннотаций (RetentionTarget)
Когда мы создаем свою аннотацию, важно понимать, как она будет использоваться. Это задается с помощью мета-аннотаций:

АннотацияОписание
@RetentionОпределяет, на каком этапе жизненного цикла аннотация доступна (SOURCE, CLASS, RUNTIME).
@TargetУказывает, к чему можно применять аннотацию (методы, поля, классы и т. д.).
@DocumentedВключает аннотацию в Javadoc.
@InheritedНаследуется ли аннотация подклассами.
@RepeatableПозволяет применять одну и ту же аннотацию несколько раз.

Как создать свою аннотацию?

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnnotation {
    String value();
    int count() default 1;
}

class Example {
    @MyAnnotation(value = "Hello", count = 3)
    public void test() {
        System.out.println("Метод с аннотацией");
    }
}

Чтение аннотации с помощью рефлексии

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        Method method = Example.class.getMethod("test");
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        System.out.println("value: " + annotation.value());  // "Hello"
        System.out.println("count: " + annotation.count());  // 3
    }
}

Где используются аннотации в реальных проектах? Lombok, JUnit, Spring


46. Переопределение и перегрузка методов #

Переопределение метода - это замена реализации метода класса-родителя в классе-наследнике. Правила

  • метод должен иметь точно такое же имя, тип возвращаемого значения и аргументы.;
  • метод в родительском классе не должен быть private;
  • должен происходить в наследуемом классе;
  • аннотация @Override рекомендуется (не обязательна, но помогает избежать ошибок).

Перегрузка метода - это создание в классе нескольких методов с одним именем, но различными сигнатурами. Правила:

  • имена методов одинаковые;
  • различие - в количестве, типах или порядке аргументов (любая комбинация этих способов);
  • возвращаемый тип может быть любым, но не используется для различия перегрузки.


47. ClassLoader в Java #

ClassLoader (загрузчик классов) - это специальный компонент JVM, предназначенный для загрузки классов в память во время выполнения программы. Загружает классы, соблюдая иерархию и принцип делегирования.

Иерархия загрузчиков:

  1. Bootstrap - загружает базовые классы (java.lang.*, java.util.*)
  2. Platform (Extension до Java 9) - загружает модули платформы
  3. Application/System - загружает классы из classpath
  4. Custom - зависит от реализации

Принцип делегирования - перед тем как загрузить класс, загрузчик обращается в вышестоящему по иерархии и спрашивает, не загрузил ли он уже такой класс, чтобы не загружать класс дважды.


48. В чем разница сравнений по equals и "=="? #

Оператор == сравнивает ссылки на объекты. Метод equals() сравнивает объекты по содержимому, если правильно переопределён, потому что по умолчанию (наследуется от Object) реализация метода сводится к

public boolean equals(Object obj) {  
	return (this == obj);  
}