JuniorJava Developer > Java Core > Основы Java
Короткий ответ Оператор == для ссылочных типов сравнивает ссылки: результат true, только если переменные указывают на один и тот же объект в памяти; для примитивов он сравнивает значения. Метод equals() предназначен для сравнения содержимого объектов, но в базовом Object он реализован через ==, поэтому классы, где важно логическое равенство, — прежде всего String — переопределяют его. Как это работает подробнее == для примитивов — сравнение значений, например 5 == 5 верно. == для ссылок — reference equality: проверка, что ссылки ведут на один объект; сравнение с null делается именно так. equals() — content equality: сравнение содержимого; переопределение требует согласованного hashCode(). Object.equals() — по умолчанию делегирует ==, поэтому без переопределения разные объекты с одинаковыми данными считаются неравными. Пример кода Что использовать на практике Сравнивай строки и другие объекты по содержимому через equals(), а не через ==. == используй для примитивов, проверки на null и случаев, когда важно, что
comparisonequals
JuniorJava Developer > Java Core > Collections
Короткий ответ Различие — во внутренней структуре. ArrayList построен на динамическом массиве Object[]: доступ по индексу — O(1), вставка или удаление в середине — O(n) из-за сдвига, добавление в конец — амортизированно O(1). LinkedList — двусвязный список: каждый элемент обёрнут в узел со ссылками на соседей; вставка в найденную позицию сводится к перестановке ссылок, но поиск позиции идёт перебором за O(n). Как это работает подробнее ArrayList хранит элементы в массиве, который автоматически расширяется при заполнении; элементы лежат подряд, поэтому итерация быстрая и хорошо используется кэш процессора. LinkedList на каждый элемент создаёт отдельный объект-узел со ссылками на предыдущий и следующий; узлы разбросаны по памяти, поэтому структура проигрывает и ArrayList, и более современным коллекциям. Доступ по индексу: у ArrayList O(1), у LinkedList — перебор от начала или конца, O(n). Вставка в середину: у ArrayList сдвиг последующих элементов — O(n); у LinkedList после поиска узла — O(1), но сам поиск стои
arraylistbig-o
JuniorJava Developer > Java Core > Строки
Короткий ответ Все три класса работают со строками, но различаются неизменяемостью и потокобезопасностью. String неизменяем: любой «изменяющий» метод создаёт новую строку. StringBuilder и StringBuffer изменяемы: они модифицируют внутренний буфер через append, insert, replace, не создавая новые объекты. Методы StringBuffer объявлены synchronized, поэтому он потокобезопасен, но медленнее StringBuilder. Как это работает подробнее StringBuffer существует с Java 1.0, его методы синхронизированы; StringBuilder появился в Java 5 как несинхронизированный аналог. Вне циклов компилятор сам превращает конкатенацию через + в вызовы StringBuilder, но внутри цикла это не спасает от множества промежуточных объектов. String — неизменяемый, подходит для строк, которые не меняются; переиспользуется в пуле. StringBuilder — изменяемый, без синхронизации, самый быстрый в однопоточном коде; появился в Java 5. StringBuffer — изменяемый, методы synchronized, потокобезопасный, но медленнее; существует с Java 1.0. Компилятор — простая
immutabilityjava
MiddleJava Developer > Java Core > Core Java
Сборщик мусора находит недостижимые объекты и освобождает занимаемую ими память, работая по поколениям.
JavaVirtual Machine
MiddleJava Developer > Java Core > Core Java
При возникновении коллизий (когда два ключа возвращают одинаковый бакет-индекс), HashMap использует метод цепочек. Начиная с Java 8, если длина связного списка в бакете превышает 8 элементов, он преобразуется в сбалансированное красно-черное дерево...
Collections
MiddleJava Developer > Java Core > Collections
Короткий ответ HashMap внутри — это массив бакетов: массив узлов Node[] table, где каждый узел хранит hash, key, value и ссылку next на следующий узел при коллизии. Индекс корзины вычисляется из hashCode() ключа, а коллизии разрешаются цепочками узлов. Как это работает подробнее По умолчанию создаётся 16 корзин, индекс вычисляется как (n - 1) & hash, где n — текущая длина массива. Когда число записей превышает ёмкость × load factor (по умолчанию 0,75), массив увеличивается примерно вдвое и записи перераспределяются по новым корзинам. Узел: хранит четыре поля — вычисленный хэш ключа, ключ, значение и ссылку next на следующий узел той же корзины. Коллизии: разные ключи с одним индексом выстраиваются в цепочку; с Java 8 цепочка длиннее 8 узлов превращается в красно-чёрное дерево, и худший случай поиска становится O(log n) вместо O(n). null: HashMap допускает один null-ключ — он попадает в корзину с индексом 0 без вызова hashCode — и сколько угодно null-значений. Потокобезопасность: класс не синхронизирован,
big-ocollections
MiddleJava Developer > Java Core > Collections
Короткий ответ Контракт Java: если два объекта равны по equals(), их hashCode() обязаны быть одинаковыми. Обратное не требуется: одинаковый хэш-код не гарантирует равенства — это коллизия. Связь критична для HashMap и HashSet: по hashCode определяется корзина, а точное совпадение внутри корзины проверяется через equals. Как это работает подробнее Если переопределить только equals и не тронуть hashCode, контракт нарушается: равные объекты разъедутся по разным корзинам, поиск не найдёт элемент, а в Set или Map появятся дубликаты. Поэтому методы переопределяют всегда вместе и на основе одного и того же набора значимых полей. Требования к equals: рефлексивность (объект равен себе), симметричность, транзитивность, консистентность и возврат false для null. Генерация: используйте средства IDE или Objects.equals и Objects.hash — это синхронизирует поля в обоих методах. Типичный баг: нарушение контракта — одна из самых частых причин неочевидных ошибок при работе с коллекциями. Пример кода Что использовать на практике
collectionsequals
JuniorJava Developer > Java Core > Строки
Короткий ответ Строка в Java неизменяема, потому что после создания объекта String его содержимое изменить нельзя: методы, которые выглядят как изменение (concat, replace, substring, toUpperCase), возвращают новый объект, а исходный остаётся нетронутым. Технически это обеспечивают final-массив символов с инкапсуляцией и final-класс, запрещающий наследование. Как это работает подробнее Неизменяемость — продуманное решение разработчиков языка: она делает строки безопасными для кэширования, разделения между потоками и хранения чувствительных данных. Хэш-код вычисляется один раз и сохраняется, строки надёжно интернируются и переиспользуются в пуле, а ключ коллекции не меняется после вставки. Кэширование hashCode — значение вычисляется один раз и сохраняется в поле, вызовы при работе с HashMap и HashSet не пересчитывают его. Пул строк и интернирование — неизменяемые литералы безопасно переиспользовать: одна «испорченная» строка не повлияет на все места использования. Стабильные ключи — ключ не меняется после встав
immutabilityjava
JuniorJava Developer > Java Core > Исключения
Короткий ответ Разница — в том, проверяет ли исключение компилятор. Checked-исключения проверяются на этапе компиляции: их нужно обработать в try-catch или объявить в throws, иначе код не соберётся. Unchecked-исключения (RuntimeException и её наследники, а также Error) компилятор не проверяет: они возникают в рантайме, и обрабатывать их не обязательно. Как это работает подробнее Checked-исключения — подклассы Exception, кроме RuntimeException: IOException, SQLException, ClassNotFoundException, InterruptedException и другие. Они обычно сигнализируют о проблемах внешней среды (файл не найден, сеть недоступна), поэтому язык заставляет разработчика обрабатывать их явно. Unchecked-исключения — RuntimeException и все её наследники (NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException, NumberFormatException, ClassCastException), а также Error и его наследники. Они, как правило, означают ошибки логики или программиста: обработка и объявление в throws не обязательны. Пример кода Что использовать н
checked-exceptionsexceptions
JuniorJava Developer > Java Core > Тестирование
Короткий ответ Unit-тест проверяет отдельный компонент — один класс или метод — в изоляции от остальной системы: это «белый ящик», такие тесты быстрые и обычно пишутся самим разработчиком. Интеграционный тест проверяет взаимодействие модулей или слоёв приложения (сервиса с базой, контроллера с сервисом) и корректность работы с внешними зависимостями; он выполняется после unit-тестов и работает медленнее. Как это работает подробнее Коротко: unit-тест отвечает на вопрос «корректно ли работает этот класс сам по себе», а интеграционный — «корректно ли модули работают вместе». Тестирование нужно, чтобы находить дефекты на ранних стадиях (это дешевле исправления в проде), проверять соответствие требованиям, снижать риск отказов и повышать уверенность при изменении кода. JUnit — основной фреймворк unit-тестирования; в JUnit 5 используются @Test, @BeforeEach/@AfterEach, @BeforeAll/@AfterAll и классы-ассерты. Mockito — библиотека моков: when(...).thenReturn(...) задаёт ожидаемое поведение, verify(...) проверяет, что м
integration-testingjunit
MiddleJava Developer > Java Core > Spring
Короткий ответ @Transactional — аннотация декларативного управления транзакциями: все операции с базой данных внутри помеченного метода (или класса) выполняются в рамках одной транзакции и либо целиком фиксируются (commit), либо при ошибке откатываются (rollback). Аннотация применяется на уровне метода или класса. Как это работает подробнее Spring оборачивает бин в AOP-прокси. Когда помеченный метод вызывается через этот прокси, Spring перед вызовом открывает транзакцию — берёт соединение из пула — а после успешного завершения метода выполняет commit. Если метод бросает исключение, транзакция откатывается, и база данных остаётся в согласованном состоянии. Откат по умолчанию: происходит только при непроверяемых исключениях — RuntimeException и Error. Checked-исключения: если метод бросает проверяемое исключение, нужно явно указать rollbackFor, например @Transactional(rollbackFor = Exception.class). Self-invocation: когда один метод бина вызывает другой @Transactional-метод того же бина через this, вызов идёт в
aopjava
MiddleJava Developer > Java Core > SQL и БД
Короткий ответ Индекс — объект базы данных, который ускоряет поиск и выборку строк: по аналогии с алфавитным указателем в книге он позволяет СУБД быстро находить нужные данные, не сканируя всю таблицу подряд. Индексы ускоряют SELECT с условиями в WHERE, с JOIN и с сортировкой ORDER BY. Как это работает подробнее Плата за индекс — дополнительное место для хранения структур и нагрузка на операции записи: при каждом INSERT, UPDATE и DELETE индексы нужно обновлять. Индекс эффективен на колонках, которые часто используются в WHERE, условиях JOIN и ORDER BY, и когда колонка имеет хорошую селективность — много различных значений. Кластеризованный: определяет физический порядок хранения строк, может быть только один на таблицу, обычно строится на первичном ключе. Некластеризованный: отдельная структура с указателями на строки; таких индексов может быть несколько. Уникальный и составной: уникальный гарантирует отсутствие дубликатов, составной строится по нескольким колонкам. Когда индекс не сработает: функция над коло
databaseindexes
JuniorJava Developer > Java Core > ООП
Короткий ответ ООП — объектно-ориентированное программирование: методология, в которой программа строится как совокупность взаимодействующих объектов. Каждый объект — экземпляр класса — объединяет состояние (поля) и поведение (методы). В Java выделяют четыре принципа: инкапсуляцию, наследование, полиморфизм и абстракцию. Как это работает подробнее Принципы связаны между собой: инкапсуляция прячет данные, наследование выстраивает иерархии, полиморфизм позволяет единому коду работать с разными типами, абстракция отделяет «что делать» от «как». Инкапсуляция. Сокрытие внутреннего устройства объекта: поля делают private, а доступ к ним дают через публичные геттеры и сеттеры. Это защищает данные от некорректного изменения. Наследование. Новый класс создаётся на основе существующего через extends: наследник получает поля и методы родителя и может расширять или переопределять их. Например, Cat наследуется от Animal. Полиморфизм. Одноимённые методы ведут себя по-разному: вызов animal.voice() для Dog даст «Гав», а для
abstractionencapsulation
MiddleJava Developer > Архитектура и данные > Design Patterns
Singleton гарантирует единственный экземпляр класса, но усложняет тестирование и скрывает зависимости.
CommonDesign Patterns
MiddleJava Developer > Java Core > SQL и БД
Короткий ответ Транзакция — последовательность операций с базой данных, которая выполняется как единое целое (unit of work): либо все операции успешно применяются, либо, если что-то пошло не так, все изменения откатываются, и база остаётся в прежнем состоянии. ACID — набор свойств, которым должна удовлетворять надёжная транзакция: Atomicity, Consistency, Isolation, Durability. Как это работает подробнее Классический пример — банковский перевод: если не прошло ни списание, ни зачисление, перевод целиком откатывается, и остатки не теряются. ACID особенно важен для систем с параллельным доступом и финансовыми операциями, где целостность данных критична. Atomicity — атомарность: транзакция выполняется полностью или не выполняется вовсе; при ошибке одной операции все изменения откатываются. Consistency — согласованность: после завершения база остаётся в валидном состоянии, соблюдены правила и ограничения (например, остаток на складе не отрицательный). Isolation — изоляция: параллельные транзакции не влияют друг на
aciddatabase
MiddleJava Developer > Java Core > JPA и Hibernate
Короткий ответ JPA (Java Persistence API) — это стандарт (спецификация) ORM в Java: он определяет API и набор аннотаций — @Entity, EntityManager, JPQL — но сам по себе реализацией не является. Hibernate — самая популярная реализация этой спецификации: он реализует интерфейсы JPA и добавляет собственные расширения: HQL, тонкую настройку кэша второго уровня и дополнительные возможности. Как это работает подробнее Разница в уровне абстракции: JPA — это правила и интерфейсы, у которых есть несколько реализаций, а Hibernate — одна из них, наиболее функциональная. Если писать код только на стандартном JPA-API, он будет переносимым между ORM-провайдерами; расширенные же возможности доступны через Hibernate. JPA — стандарт: набор правил, интерфейсов и аннотаций; реализации — Hibernate, EclipseLink, OpenJPA. Hibernate — реализация JPA с самым широким набором возможностей и расширений. HQL — собственный язык запросов Hibernate, работающий на высоком уровне абстракции. На практике: в Spring Boot по умолчанию JPA-провайд
hibernatejava
JuniorJava Developer > Java Core > Spring
Короткий ответ IoC (Inversion of Control) — принцип, по которому управление созданием и жизненным циклом объектов передаётся контейнеру. DI (Dependency Injection) — паттерн внедрения зависимостей и один из способов реализации IoC: контейнер сам предоставляет объекту его зависимости. Как это работает подробнее В Spring инверсию контроля реализует IoC-контейнер — базовый BeanFactory или расширенный ApplicationContext. Он создаёт бины, связывает их и управляет их временем жизни, поэтому приложение не вызывает new, а лишь описывает нужные ему объекты. Именно поэтому подход называется инверсией контроля: контроль над созданием объектов переходит от кода приложения к контейнеру. IoC шире, чем DI: IoC — общий принцип инверсии управления, а DI — конкретная техника, один из способов его реализации. Способы DI: внедрение через конструктор, через сеттер и напрямую в поле (field injection). Для запоминания: «Spring создаёт бин» — это IoC, а «Spring внедряет один бин в другой» — это DI. Пример кода Что использовать на пра
containerdependency-injection
JuniorJava Developer > Java Core > SQL и БД
Короткий ответ JOIN — операция объединения строк двух или более таблиц на основе условия соответствия, обычно по ключам. INNER JOIN возвращает только совпавшие строки; LEFT JOIN — все строки левой таблицы, а при отсутствии совпадения поля правой заполняются NULL; RIGHT JOIN — зеркальный вариант для правой таблицы; FULL JOIN возвращает все строки обеих таблиц, заполняя NULL стороны без пары. Как это работает подробнее Подзапрос (subquery) — это запрос, вложенный в другой запрос: он используется в WHERE через IN, EXISTS или сравнение со значением, а также в списке выборки для фильтрации и вычислений. Главное отличие от JOIN — в результате: JOIN «расширяет» выборку колонками второй таблицы, а подзапрос вычисляет отдельный результат, который внешний запрос использует как условие или источник данных. Объём результата: INNER JOIN — пересечение; LEFT и RIGHT JOIN — все строки одной стороны с NULL на другой; FULL JOIN — все строки обеих таблиц. Подзапрос: коррелированный подзапрос выполняется для каждой строки внешне
databasejoin
JuniorJava Developer > Java Core > JVM и память
Короткий ответ JVM — виртуальная машина Java, которая исполняет байт-код и обеспечивает переносимость программ. JRE — среда выполнения: JVM плюс стандартные библиотеки, нужна только для запуска приложений. JDK — комплект разработчика: всё из JRE плюс компилятор javac, утилиты и отладчик. Отношение вложенности: JDK содержит JRE, JRE содержит JVM. Как это работает подробнее Компоненты различаются составом и назначением, но работают вместе: вы пишете код, инструменты JDK превращают его в байт-код, а JVM внутри JRE исполняет этот байт-код на конкретной платформе. JVM. Загружает и проверяет class-файлы, интерпретирует инструкции байт-кода, а горячие участки компилирует JIT-компилятором в машинный код. Существует отдельная реализация под каждую платформу: Windows, Linux, macOS. JRE. Содержит JVM и стандартные библиотеки классов (java.lang, java.util и другие). Достаточна пользователю, который только запускает готовое приложение. JDK. Нужен разработчику: включает компилятор javac, утилиты java, jar, javadoc и отладч
bytecodejava
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Состояние гонки — это ситуация, когда результат многопоточной программы зависит от порядка и скорости выполнения потоков и становится непредсказуемым. Гонка возникает, когда несколько потоков без синхронизации обращаются к одним и тем же разделяемым данным и хотя бы один из них выполняет запись; итог зависит от того, какой поток «добежал» первым. Как это работает подробнее Типичный пример — несколько потоков увеличивают общий счётчик. Операция count++ состоит из трёх шагов: прочитать текущее значение, прибавить единицу, записать результат обратно. Два потока могут прочитать одно и то же значение, каждый вычислит следующее число, и в поле будет записано то же значение — одно увеличение потеряно (lost update). Гонка коварна тем, что проявляется не сразу: она часто «выстреливает» только под нагрузкой или на другом железе, из-за чего баги нерегулярно воспроизводятся. Три основных способа борьбы: атомарные классы (AtomicInteger) для одной составной операции; synchronized или Lock для более крупной к
atomicjava
JuniorJava Developer > Java Core > REST API
Короткий ответ REST (REpresentational State Transfer) — архитектурный стиль, основанный на веб-стандартах: он использует протокол HTTP для обмена данными. Всё содержимое рассматривается как ресурсы: каждый ресурс идентифицируется URI и доступен через единый интерфейс HTTP-методов, а клиент получает его представление — обычно JSON или XML. Веб-сервисы, построенные по этому стилю, называют RESTful; ключевой принцип — отсутствие состояния на сервере (statelessness). Как это работает подробнее Идемпотентность означает, что повторное выполнение одной и той же операции даёт тот же результат: состояние ресурса не меняется от количества вызовов. Статус-коды HTTP сообщают клиенту об исходе запроса. Методы: GET — получение ресурса (безопасный); POST — создание или добавление данных; PUT — полная замена или обновление; DELETE — удаление; PATCH — частичное обновление; также есть OPTIONS и HEAD. Идемпотентны: GET, PUT и DELETE. POST не идемпотентен: повторный POST может создать несколько ресурсов. Статус-коды: 200 OK; 201
apihttp
MiddleJava Developer > Java Core > Collections
LinkedHashMap хранит дополнительный двусвязный список, который задает порядок обхода элементов.
Collections
MiddleJava Developer > Java Core > ООП
Короткий ответ Абстрактный класс — это класс, который нельзя инстанцировать: он задаёт шаблон наследникам и может содержать абстрактные методы, обычные методы с реализацией, поля, конструкторы и статические члены. Интерфейс описывает контракт поведения: до Java 8 в нём были только абстрактные методы и константы, с Java 8 появились default- и static-методы, с Java 9 — private-методы. Как это работает подробнее Оба механизма описывают абстракции, но решают разные задачи. Класс может наследовать только один абстрактный класс, зато реализует сколько угодно интерфейсов — это частично компенсирует отсутствие множественного наследования. На практике их часто комбинируют: класс наследует абстрактную базу и дополнительно реализует один или несколько интерфейсов. Состояние — абстрактный класс может иметь поля и конструкторы (вызываются через super()); в интерфейсе только константы public static final. Методы — в абстрактном классе абстрактные и обычные методы; в интерфейсе с Java 8 — default, static, а с Java 9 — priva
abstract-classinterface
JuniorJava Developer > Java Core > Spring
Короткий ответ @Controller — MVC-контроллер классических веб-приложений: его методы возвращают имя представления (JSP или шаблон Thymeleaf), в которое передаётся модель. @RestController — специализированная аннотация для REST-сервисов: комбинация @Controller + @ResponseBody, поэтому методы сразу возвращают данные. Как это работает подробнее Методы @RestController возвращают данные (как правило, JSON или XML), которые автоматически сериализуются в тело ответа с помощью HttpMessageConverter (например, Jackson). Ни @ResponseBody на каждом методе, ни view resolver для этого не нужны. Ключевое отличие: @Controller возвращает представление (view), а @RestController — данные (JSON/XML). Сфера применения: @Controller — классические веб-приложения на Spring MVC, @RestController — REST API и микросервисы. Возврат данных из @Controller: чтобы отдать JSON или XML, к методу нужно добавить @ResponseBody. Пример кода Что использовать на практике Для REST API и микросервисов всегда используйте @RestController — в Spring Boot
javamvc
MiddleJava Developer > Java Core > Multithreading
Короткий ответ wait() — метод класса Object, а sleep() — статический метод класса Thread. wait() служит для межпоточной коммуникации, вызывается только из синхронизированного кода и при вызове освобождает монитор объекта. sleep() просто приостанавливает текущий поток на заданное время и не освобождает удерживаемые блокировки. Как это работает подробнее wait() вне синхронизированного метода или блока бросит IllegalMonitorStateException. Поток переходит в ожидание, пока другой поток не вызовет notify()/notifyAll() на том же объекте либо не истечёт указанный таймаут; без таймаута ожидание может длиться «вечно». sleep() можно вызывать где угодно, он всегда принимает время задержки и удобен для имитации пауз. Пара wait()/notify() — классический механизм схемы «производитель — потребитель» для ожидания наступления условия. Оба метода бросают проверяемое InterruptedException, которое нужно обрабатывать. Пример кода Что использовать на практике Используй wait()/notifyAll(), когда поток должен ждать наступления услови
javamultithreading
MiddleJava Developer > Java Core > Collections
Короткий ответ Comparable задаёт «естественный» порядок самого класса: класс реализует интерфейс и метод compareTo(Object o), в котором зашито правило «я больше или меньше другого». Так устроены стандартные классы — String, Integer, Date. Comparator — отдельный класс или лямбда с методом compare(a, b), который не требует изменять сравниваемый класс, поэтому для одного типа можно завести много стратегий сортировки. Как это работает подробнее Отсортировать коллекцию можно через Collections.sort(...) или list.sort(...): если передан только список, элементы должны реализовывать Comparable; если нужен особый порядок — вторым аргументом передаётся Comparator. У Comparator есть удобные дефолтные методы comparing(...), thenComparing(...), reversed(), из которых собирается составная сортировка. Comparable: один естественный порядок в самом классе — например, по id. Comparator: порядок зависит от контекста, или класс чужой и менять его нельзя. Пример кода Что использовать на практике Comparable берите, когда у класса е
collectionscomparable
MiddleJava Developer > Java Core > Collections
Короткий ответ Fail-fast итератор работает с самой коллекцией и при любом структурном изменении (добавление или удаление элементов), выполненном не через сам итератор, немедленно выбрасывает ConcurrentModificationException. Fail-safe — точнее, слабо согласованные (weakly consistent) итераторы из java.util.concurrent — не бросают исключение при параллельных изменениях, но не гарантируют, что увидят изменения, сделанные после создания итератора. Как это работает подробнее Механизм fail-fast реализован через счётчик модификаций modCount: итератор запоминает его при создании и сравнивает при каждом вызове next(); если значения разошлись — коллекция менялась «на стороне» — итератор «быстро падает» с исключением. Так ведут себя коллекции пакета java.util: ArrayList, HashMap, LinkedList. Fail-safe итераторы либо проходят по копии коллекции, либо устроены так, что не бросают исключение; плата — дополнительные расходы памяти на копирование или менее строгие данные. Примеры fail-safe: итераторы ConcurrentHashMap, CopyO
collectionsconcurrentmodificationexception
JuniorJava Developer > Java Core > Исключения
Короткий ответ throw — оператор, который в точке выполнения кода непосредственно выбрасывает исключение: после него указывается объект-исключение, например throw new IllegalArgumentException(). throws — объявление в сигнатуре метода о том, какие исключения метод может выбросить: void read() throws IOException. Первый пишется в теле метода, второй — в его сигнатуре. Как это работает подробнее throw используется внутри тела метода. Бросить можно только объект класса, наследующего Throwable. Один вызов throw бросает ровно одно исключение, после чего управление уходит на поиск подходящего catch или выше по стеку. throws стоит в сигнатуре и сообщает компилятору и вызывающему коду, что метод может выбросить перечисленные исключения. Если метод способен выбросить checked-исключение, он обязан либо перехватить его в try-catch, либо объявить в throws — иначе код не скомпилируется. Для unchecked-исключений (RuntimeException и Error) объявление не обязательно. throw работает с объектом исключения и пишется в теле метода
checked-exceptionsexceptions
JuniorJava Developer > Java Core > Core Java
Константа объявляется с модификатором final: её значение нельзя переприсвоить после инициализации.
Basics
MiddleJava Developer > Java Core > Multithreading
sleep() приостанавливает поток и не освобождает монитор, wait() освобождает монитор и ждет notify().
Multithreading
JuniorJava Developer > Java Core > ООП
Короткий ответ Перегрузка (overloading) — наличие в одном классе нескольких методов с одинаковым именем и разным списком параметров; нужный вариант выбирает компилятор по типам аргументов, поэтому это полиморфизм времени компиляции. Переопределение (overriding) — изменение реализации унаследованного метода в классе-наследнике при сохранении сигнатуры; реализацию выбирает JVM по фактическому типу объекта на этапе выполнения. Как это работает подробнее Механизмы удобно сравнивать по нескольким осям: где происходит, как связаны сигнатуры и когда выбирается реализация. Где происходит. Перегрузка — внутри одного класса; переопределение — между родителем и наследником. Сигнатура. При перегрузке параметры обязаны отличаться; при переопределении — обязаны совпадать, а возвращаемый тип должен быть тем же или ковариантным (подтипом). Момент выбора. Перегрузка — на этапе компиляции; переопределение — на этапе выполнения, по фактическому типу объекта. Ограничения. Переопределить можно только унаследованный не-static, не-
javaoop
MiddleJava Developer > Java Core > Исключения
Короткий ответ Exception и Error — наследники Throwable, но это разные классы проблем. Exception — событие, нарушающее нормальный ход программы, которое в большинстве случаев можно перехватить через try-catch и восстановить работу. Error — серьёзная проблема уровня JVM или окружения: приложение обычно не в силах с ней справиться, поэтому ловить Error не принято. Как это работает подробнее Exception описывает события, нарушающие нормальный ход программы, но в большинстве случаев восстановимые: IOException, SQLException (checked), а также RuntimeException и её наследники. Перехватив такое исключение через try-catch, программа обычно может продолжить работу. Error — это критические проблемы уровня JVM или окружения: OutOfMemoryError, StackOverflowError, NoClassDefFoundError, AssertionError. Дальнейшая стабильная работа часто невозможна, поэтому «чинить» их в коде не принято — нужны другие настройки JVM, исправление кода или инфраструктуры. Технически перехватить Error можно (он наследник Throwable), но делать эт
errorexceptions
MiddleJava Developer > Java Core > Collections
Короткий ответ HashMap хранит пары в хэш-таблице: порядок обхода не определён и зависит от хэш-кодов и размера таблицы, операции put, get, remove в среднем O(1); допускается один null-ключ и null-значения. TreeMap хранит записи в красно-чёрном дереве: ключи всегда отсортированы — по естественному порядку (Comparable) или заданному Comparator, а все операции занимают O(log n). Как это работает подробнее TreeMap — это самобалансирующееся бинарное дерево поиска: в каждой вершине хранятся ключ, значение, ссылки на потомков и цвет (красный или чёрный); инварианты балансировки гарантируют высоту O(log n). null-ключ TreeMap не допускает, так как при вставке ключ нужно с чем-то сравнивать. Главный козырь — работа с упорядоченными данными: firstKey(), lastKey(), headMap(), tailMap(), subMap(). HashMap: порядок ключей не важен и нужна максимальная скорость. TreeMap: нужен отсортированный обход, поиск ближайших ключей или диапазонные выборки. Пример кода Что использовать на практике Используйте HashMap, когда порядок кл
big-ocollections
MiddleJava Developer > Java Core > Collections
Короткий ответ HashSet основан на HashMap (элементы — ключи карты) и не гарантирует порядка: элементы распределяются по корзинам по хэш-коду, операции добавления и поиска в среднем O(1), допускается один null. LinkedHashSet наследует HashSet и дополнительно прошивает элементы связным списком в порядке вставки. TreeSet хранит элементы отсортированными в красно-чёрном дереве за O(log n), а null не допускает. Как это работает подробнее Порядок в LinkedHashSet задаётся порядком добавления: при итерации элементы приходят именно в нём, цена — чуть больший расход памяти и немного более медленные операции. TreeSet сортирует по естественному порядку (Comparable) либо по переданному Comparator; при вставке элемент сразу сравнивается с другими, поэтому null даёт NullPointerException. Дерево позволяет отдавать диапазоны: subSet, headSet, tailSet. HashSet: максимальная скорость, порядок не важен. LinkedHashSet: нужен порядок вставки. TreeSet: нужен отсортированный обход и диапазонные операции. Пример кода Что использовать
collectionshashset
JuniorJava Developer > Java Core > Collections
HashTable синхронизирован целиком и не допускает null, HashMap быстрее и допускает один null-ключ.
Collections
SeniorJava Developer > Java Core > Multithreading
Короткий ответ Оба механизма обеспечивают взаимное исключение и реентерабельны: поток, уже владеющий монитором или замком, может захватить его повторно. synchronized — встроенная конструкция языка: монитор захватывается при входе в метод или блок и автоматически освобождается при выходе, даже при исключении. ReentrantLock из java.util.concurrent.locks требует явного lock() и освобождения в finally, но даёт больше контроля. Как это работает подробнее Возможности synchronized ограничены: нельзя прервать поток, ожидающий блокировку, нельзя установить таймаут ожидания, нет честной (fair) очереди, а захват возможен только в пределах одного метода или блока. ReentrantLock предоставляет tryLock() (захват без блокировки или с таймаутом), lockInterruptibly() (прерываемое ожидание), честные замки и несколько объектов Condition для тонкого управления ожиданием. Именно tryLock с таймаутом позволяет писать код, устойчивый к deadlock: не получилось захватить блокировку — поток освобождает уже захваченные ресурсы и повторяе
javalock
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Синхронизированные коллекции — это обёртки над обычными коллекциями, создаваемые через Collections.synchronizedList(), synchronizedMap() и т. п., а также исторические Vector и Hashtable: в них каждый публичный метод синхронизирован целиком, все операции захватывают один общий монитор коллекции. Concurrent-коллекции из java.util.concurrent (ConcurrentHashMap, CopyOnWriteArrayList и др.) спроектированы для высокой конкуренции: они используют гранулярную синхронизацию, CAS и слабо консистентные итераторы. Как это работает подробнее Synchronized-коллекции гарантируют потокобезопасность, но создают сильную конкуренцию: чем больше потоков обращается одновременно, тем больше времени они проводят в ожидании общей блокировки, поэтому масштабируемость плохая. Итерироваться по ним нужно внутри собственного блока synchronized, иначе поведение при параллельном изменении недетерминировано. ConcurrentHashMap в современных версиях синхронизирует доступ по корзинам таблицы: операции над разными корзинами не меш
concurrentcopyonwritearraylist
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Синхронизированный нестатический метод захватывает монитор экземпляра this на всё время выполнения всего метода; статический синхронизированный метод блокирует монитор объекта класса (SomeClass.class). synchronized-блок позволяет указать, какой объект использовать как монитор, и ограничить синхронизацию только нужным фрагментом кода. Как это работает подробнее Пока поток внутри synchronized-метода на this, другой поток не сможет войти ни в один другой synchronized-метод того же объекта. Статические методы синхронизируются между собой на объекте Class, поэтому говорят про «класс-лок» в отличие от «объект-лока». Блок уменьшает время удержания блокировки и снижает конкуренцию (contention), поэтому считается более гибким и, как правило, более производительным: остальной код метода выполняется без блокировки. Монитор в Java реентерабелен: поток, уже владеющий монитором, может повторно войти в синхронизированный код по тому же объекту без взаимной блокировки самого себя. Пример кода Что использовать
javalock
SeniorJava Developer > Java Core > Multithreading
Короткий ответ volatile решает проблему видимости: чтение и запись volatile-переменной выполняются напрямую из основной памяти, а между записью одним потоком и чтением другим устанавливается отношение happens-before. Однако volatile не обеспечивает атомарность: составная операция «прочитать — изменить — записать» вроде counter++ остаётся неатомарной. Атомарные классы из java.util.concurrent.atomic дают атомарные операции без блокировок — на базе аппаратной инструкции CAS. Как это работает подробнее volatile идеален для флагов состояния вроде boolean running, которые один поток выставляет, а другой читает. При counter++ два потока могут одновременно прочитать старое значение и записать результат, «потеряв» одно из увеличений, — даже если поле объявлено volatile. Атомарные классы AtomicInteger, AtomicLong, AtomicReference предоставляют методы getAndIncrement(), addAndGet(), compareAndSet(expected, newValue), реализованные на аппаратной инструкции CAS (Compare-And-Swap). CAS действует оптимистично: compareAndSet
atomiccas
JuniorJava Developer > Java Core > Основы Java
Короткий ответ Java — строго pass-by-value. При вызове метода создаётся копия значения аргумента. Для примитивных типов копируется само значение, для ссылочных — копия ссылки на тот же объект. Поэтому изменения состояния объекта внутри метода видны снаружи, а переприсваивание параметра — нет. Как это работает подробнее Разница между передачей по ссылке (как в C++) и по значению проявляется в двух типовых сценариях. Примитивный аргумент. Изменение параметра внутри метода никак не влияет на переменную в вызывающем коде: метод работает с копией значения. Ссылочный аргумент. По значению передаётся копия ссылки, но объект тот же самый: вызов list.add(...) или изменение поля будет видно снаружи. Переприсваивание. Если внутри метода выполнить param = new Something(), изменится только локальная копия ссылки, а исходная переменная продолжит указывать на прежний объект. Пример кода Что использовать на практике Помнить, что классический «обмен значений двух переменных» через метод в Java не работает — меняются лишь копи
javamethods
MiddleJava Developer > Java Core > Исключения
Короткий ответ Да, такое возможно. Блок finally не выполнится, если JVM завершается до выхода из try — при вызове System.exit() или аварийном завершении (сбой JVM, OutOfMemoryError, kill процесса), — либо если поток застревает внутри try (бесконечный цикл, вечное ожидание на мониторе) и просто не доходит до блока. Как это работает подробнее finally гарантированно выполняется при обычном завершении try, при исключении и даже перед return — управление передаётся в finally до выхода из метода. Это ключевая гарантия языка. Блок не выполняется только тогда, когда завершается сама JVM или поток не может выйти из try. System.exit() — завершает виртуальную машину, выполнение до finally не доходит. Аварийное завершение — сбой JVM, критическая ошибка вроде OutOfMemoryError, принудительное завершение процесса (kill). Застревание потока — бесконечный цикл или вечное ожидание на мониторе внутри try: код не доходит до finally; в старых версиях к этому приводила и принудительная остановка потока Thread.stop(). Пример кода Ч
control-flowexceptions
MiddleJava Developer > Java Core > ООП
Короткий ответ Полиморфизм (дословно «много форм») — принцип ООП, при котором одно и то же имя метода (или один и тот же код) работает с объектами разных типов, проявляя себя по-разному. В Java различают два вида: полиморфизм времени компиляции — перегрузка методов, и полиморфизм времени выполнения — переопределение методов, который называют также динамической диспетчеризацией. Как это работает подробнее Механизм runtime-полиморфизма: переменная имеет тип родительского класса, а указывает на объект наследника. При вызове переопределённого метода JVM определяет фактический тип объекта во время выполнения и вызывает реализацию именно этого класса. Перегрузка (overloading). В одном классе несколько методов с одинаковым именем и разными параметрами; нужный вариант выбирает компилятор по типам переданных аргументов. Переопределение (overriding). Наследник заменяет реализацию унаследованного метода, сохраняя сигнатуру; выбор реализации происходит на этапе выполнения. Практический эффект. Один метод, принимающий Ani
javaoop
MiddleJava Developer > Java Core > Сборка мусора
Короткий ответ Сборка мусора — автоматический процесс управления памятью: JVM сама находит объекты в куче, до которых нельзя добраться по цепочке ссылок от корневых точек (корней), и освобождает их память. Программисту не нужно вручную удалять объекты, как через free или delete в C/C++. Как это работает подробнее Базовый алгоритм — mark-and-sweep: на первом шаге GC обходит граф объектов от корней и помечает достижимые, на втором удаляет непомеченные. В современных HotSpot JVM куча разделена на поколения: молодые объекты «умирают» быстро, для них применяется копирующая сборка с частыми небольшими паузами, а выжившие со временем переходят в старое поколение, где работают другие алгоритмы. Корни (GC roots) — локальные переменные активных потоков, статические поля и другие точки входа в граф объектов. Достижимость — объект жив, пока до него можно добраться от корней; недостижимые объекты удаляются. Поколения — young и old generation: для молодых объектов частая копирующая сборка, для старых — отдельные стратегии.
gcheap
MiddleJava Developer > Java Core > Исключения
Короткий ответ Неперехваченное исключение всплывает вверх по стеку вызовов: текущий метод немедленно завершается, и исключение передаётся методу, который его вызвал, — процесс повторяется, пока не найдётся подходящий catch или исключение не достигнет main. По пути на каждом уровне выполняются блоки finally, а при отсутствии обработчика JVM завершает поток и печатает stack trace. Как это работает подробнее Распространение (exception propagation) начинается с места выброса исключения: текущий метод завершается, и исключение передаётся вызвавшему методу. Если подходящего catch нет, процесс идёт выше — по всему стеку вплоть до main. При прохождении каждого уровня выполняются все блоки finally, даже если исключение на этом уровне не перехватывается. Когда catch найден, выполнение продолжается после этого блока. Для checked-исключений распространение «узаконено» объявлением throws в сигнатурах методов на пути; unchecked-исключения (RuntimeException и Error) проходят через любые методы без объявлений. Блоки catch ра
callstackexceptions
MiddleJava Developer > Java Core > Collections
Короткий ответ put(key, value) сначала вычисляет хэш ключа и индекс корзины, затем ищет в ней узел с равным ключом: если такой ключ уже есть — значение заменяется на новое, а старое возвращается; если нет — в корзину добавляется новый узел. Высчитывать позицию бакета нужно, чтобы не перебирать все записи: ключ сразу приводит к месту хранения, поэтому в среднем операции занимают O(1). Как это работает подробнее Индекс вычисляется как (n - 1) & hash. Если корзина пуста, в неё просто помещается новый узел. Если занята, узлы проверяются: сначала сверяется хэш, затем ключи через equals(). При совпадении значение заменяется, при несовпадении узел добавляется в цепочку корзины; в Java 8+, если цепочка превысила порог, она превращается в дерево, и вставка идёт по его правилам. После вставки проверяется порог ёмкость × load factor — при превышении выполняется resize с пересчётом позиций. Метод get работает симметрично: корзина по хэшу, затем сравнение ключей через equals. Зачем бакет: адресация по хэшу избавляет от ли
big-obucket
SeniorJava Developer > Java Core > Core Java
ClassLoader загружает байт-код классов в JVM по принципу делегирования родителю.
JVM
SeniorJava Developer > Java Core > JVM и память
Короткий ответ ClassLoader — компонент JVM, отвечающий за загрузку классов: он находит бинарное представление класса (байт-код из .class-файла, JAR-архива, сети или БД) и создаёт объект java.lang.Class. Загрузка выполняется лениво — при первом использовании класса. Встроенные загрузчики работают по модели делегирования родителю: запрос уходит вверх по иерархии, и класс загружает первый загрузчик, который сможет его найти. Как это работает подробнее В JVM три встроенных загрузчика: Bootstrap ClassLoader (написан на нативном коде, загружает ядро JDK: rt.jar в Java 8 или базовые модули runtime image в Java 9+), Platform (в Java 8 — Extension) ClassLoader для расширений и платформенных библиотек и Application (System) ClassLoader для классов приложения из classpath. По модели делегирования загрузчик сначала передаёт запрос родительскому загрузчику, и только если родитель вплоть до Bootstrap не нашёл класс, ищет его сам. Если класс не найден нигде — выбрасывается ClassNotFoundException. Из делегирования следуют ви
bootstrapbytecode
MiddleJava Developer > Java Core > Сборка мусора
Короткий ответ Garbage Collector автоматически управляет памятью кучи: он находит объекты, которые больше не нужны программе, и освобождает занимаемую ими память. Мусором считается объект, до которого нельзя добраться по цепочке ссылок от корней GC (GC Roots). Современные JVM используют трассирующие (tracing) алгоритмы, а не подсчёт ссылок (reference counting), из-за проблемы циклических ссылок. Как это работает подробнее Reference counting — исторический подход: у каждого объекта ведётся счётчик ссылок; когда он уменьшается до нуля, объект удаляется сразу. Минус — не справляется с циклическими ссылками: если A ссылается на B, а B на A, и извне ссылок нет, счётчики не обнуляются и память утекает. Tracing-сборщик стартует от корней GC (локальные переменные потоков, статические поля и т. д.) и обходит граф объектов, помечая (mark) все достижимые. Всё непомеченное считается мусором и удаляется (sweep), при необходимости выполняется уплотнение (compact) против фрагментации. Для скорости куча делится на поколения:
gcheap
SeniorJava Developer > Java Core > Multithreading
ConcurrentHashMap обеспечивает потокобезопасность на уровне отдельных сегментов (в Java 7) или через CAS и synchronized бакетов (в Java 8+)...
CollectionsConcurrency
JuniorJava Developer > Java Core > Collections
Короткий ответ Несмотря на похожие названия, это разные сущности. java.util.Collection — это интерфейс, корень иерархии коллекций: от него наследуются List, Set и Queue, а сам он задаёт базовый контракт — size(), isEmpty(), add(E), remove(Object), contains(Object), iterator(). java.util.Collections — это утилитный класс со статическими методами для работы с коллекциями. Как это работает подробнее Создать «объект Collection» напрямую нельзя: интерфейс можно лишь реализовать или использовать одного из наследников. Класс Collections не участвует в иерархии коллекций и содержит только готовые алгоритмы и обёртки. Алгоритмы: сортировка sort, перемешивание shuffle, реверс reverse, бинарный поиск binarySearch, поиск максимума и минимума. Обёртки: неизменяемые представления unmodifiableList, unmodifiableMap и синхронизированные synchronizedList, synchronizedMap. Создание: пустые коллекции через emptyList(), коллекция из одного элемента — через singletonList(). Пример кода Что использовать на практике В полях и сигнат
collectionsinterface
SeniorJava Developer > Java Core > Сборка мусора
Короткий ответ В HotSpot JVM исторически сменяли друг друга несколько сборщиков: Serial GC (-XX:+UseSerialGC), Parallel GC, он же Throughput Collector (-XX:+UseParallelGC), CMS (Concurrent Mark Sweep), затем G1 (-XX:+UseG1GC), ставший сборщиком по умолчанию с Java 9, и ZGC для сверхнизких пауз. Все они останавливают приложение на время сборки (stop-the-world), но современные выполняют основную работу конкурентно. Как это работает подробнее Serial GC — самый простой: сборка выполняется одним потоком, подходит для небольших приложений с маленькой кучей и одним-двумя ядрами. Parallel GC (Throughput Collector) — сборщик по умолчанию до Java 8: молодое и старое поколения чистит несколькими рабочими потоками, давая максимальную пропускную способность, но его паузы могут быть заметными. CMS выполнял большую часть пометки параллельно с работающим приложением ради уменьшения пауз, но не уплотнял кучу (отсюда фрагментация) и был помечен устаревшим в JDK 9, а позже удалён. G1 (Garbage First) делит кучу на регионы одинак
cmsg1
MiddleJava Developer > Java Core > Spring
Короткий ответ Autowiring — механизм автоматического связывания бинов, который выполняет контейнер Spring, обычно по аннотации @Autowired; он избавляет от ручной настройки связей и способствует слабой связанности. Выделяют три способа внедрения: через конструктор, через сеттер и напрямую в поле класса. Как это работает подробнее Рекомендуемый подход — конструкторная инъекция, особенно для обязательных зависимостей: объект создаётся полностью инициализированным, зависимости нельзя поменять после создания, исключается частично инициализированное состояние и связанные с ним ошибки, а класс легко тестировать, передавая моки в конструктор. Сеттерная инъекция уместна для опциональных зависимостей, которые могут меняться в процессе жизни объекта. Инъекция в поле через @Autowired проста в написании, но скрывает зависимости, мешает тестированию (сложно подставить мок без контейнера) и нарушает неизменяемость. Важный нюанс: если у бина один конструктор, явная @Autowired на нём не обязательна — контейнер использует его
autowiredconstructor-injection
JuniorJava Developer > Java Core > Основы Java
Короткий ответ Значения по умолчанию присваиваются полям класса (переменным экземпляра и статическим переменным), если им явно ничего не присвоено. Числовые примитивы получают ноль в своём виде, char — нулевой символ, boolean — false, ссылочные поля (включая String) — null. Локальные переменные исключение: значений по умолчанию у них нет. Как это работает подробнее JVM инициализирует поля в момент создания объекта или загрузки класса — ещё до вызова конструктора. То же правило действует для элементов массива: при его создании все ячейки заполняются значениями по умолчанию. Числа: byte, short, int → 0; long → 0L; float → 0.0f; double → 0.0d. Символы и логика: char → нулевой символ; boolean → false. Ссылки: любые поля объектных типов (включая String) → null, то есть ссылка «ни на что». Локальные переменные внутри метода не инициализируются автоматически: обращение до присваивания — ошибка компиляции. Пример кода Что использовать на практике Локальные переменные инициализировать до использования — иначе код не с
default-valuesjava
MiddleJava Developer > Java Core > Spring
Короткий ответ Область видимости (scope) определяет, сколько экземпляров бина создаёт контейнер и как они используются. По умолчанию все бины — singleton: один экземпляр на весь контейнер. Скоуп prototype создаёт новый экземпляр при каждом запросе из контейнера. Как это работает подробнее Spring поддерживает шесть скоупов: singleton (один экземпляр на весь контейнер), prototype (новый экземпляр на каждый запрос), request (на каждый HTTP-запрос), session (на каждую HTTP-сессию), application (общий для всего веб-приложения) и websocket (на каждую WebSocket-сессию). Скоупы request, session, application и websocket доступны только в веб-приложениях. Singleton: контейнер создаёт один экземпляр и полностью управляет его жизненным циклом, включая вызов метода уничтожения при завершении приложения; экономит память и подходит для stateless-бинов — сервисов и репозиториев. Prototype: при каждом запросе создаётся новый объект; после передачи вызывающему коду контейнер больше не управляет им, поэтому метод уничтожения дл
bean-scopejava
MiddleJava Developer > Java Core > Collections
Короткий ответ Главное отличие — потокобезопасность: у Hashtable синхронизированы все методы, и из-за глобальной синхронизации на каждый вызов она медленнее; HashMap не синхронизирован и безопасен только для одного потока. Второе важное отличие — работа с null: HashMap допускает один null-ключ и любое количество null-значений, а Hashtable выбрасывает NullPointerException на ключ или значение null. Как это работает подробнее В HashMap для null предусмотрен специальный путь: такой ключ попадает в корзину с индексом 0 без вызова hashCode, поэтому null разрешён; дизайн Hashtable такой поддержки не предполагает. Наследования между классами нет: HashMap унаследован от AbstractMap, а Hashtable — от Dictionary, это разные ветви иерархии. Итераторы и HashMap, и Hashtable являются fail-fast. Современная замена: ConcurrentHashMap даёт более высокую конкурентность за счёт сегментации, а в Java 8+ — CAS-операций с точечной синхронизацией отдельных корзин. Общее: и Hashtable, и ConcurrentHashMap не допускают null-ключи и n
collectionsconcurrent
MiddleJava Developer > Java Core > JPA и Hibernate
Короткий ответ В Hibernate сущность проходит четыре состояния. Transient — объект только что создан через new, не связан с Session и не имеет соответствия в базе. Persistent — связан с открытой Session и имеет запись в БД, изменения отслеживаются. Detached — был persistent, но Session закрыта или вызваны evict()/detach(). Removed — помечен на удаление и будет удалён при синхронизации. Как это работает подробнее Переходы выполняются операциями Session: save()/persist() переводят transient в persistent, закрытие сессии или evict() — в detached, а update(), saveOrUpdate() или merge() возвращают объект в persistent. От состояния зависит, какие операции Hibernate выполнит при коммите и какие исключения могут возникнуть. Transient: объект не управляется Hibernate, записи в базе ещё нет. Persistent: работает dirty checking — Hibernate сам синхронизирует изменения с БД при flush или commit, явный update() не нужен. Detached: изменения больше не отслеживаются; чтобы сохранить их, объект нужно присоединить заново. Remo
detachedentity-lifecycle
JuniorJava Developer > Java Core > Multithreading
Короткий ответ Первый способ — создать подкласс Thread и переопределить метод run(). Второй — реализовать интерфейс Runnable и передать экземпляр задания в конструктор Thread. Запускает поток всегда метод start(): именно он создаёт новый поток и внутри него вызывает run(). Как это работает подробнее Наследование от Thread смешивает задачу («что делать») с потоком («где выполнять») и расходует единственное место под наследование. Реализация Runnable считается предпочтительной: Java поддерживает только одиночное наследование, а интерфейс такой проблемы не создаёт; один и тот же объект Runnable можно передать в несколько потоков или в пул. Родственный интерфейс Callable: метод call() возвращает результат и может бросать проверяемые исключения в отличие от void run(). Задачи Callable выполняются не через new Thread(...), а через ExecutorService.submit(), который возвращает Future для получения результата. Пример кода Что использовать на практике Runnable — стандартный выбор: задача отделена от потока, её мо
callableexecutor
JuniorJava Developer > Java Core > Основы Java
Короткий ответ В Java два семейства типов. Примитивные: их ровно восемь — byte, short, int, long, float, double, char, boolean; переменная хранит значение прямо «в себе» и методов у него нет. Ссылочные: классы, интерфейсы, массивы и перечисления; переменная хранит ссылку на объект, который размещается в куче. Как это работает подробнее Главное различие — что именно копируется при присваивании и где хранятся данные. Примитив. При присваивании копируется само значение. Локальные примитивы хранятся в стеке, а примитивные поля — внутри объекта в куче. Ссылочный тип. Переменная хранит адрес области памяти, где лежит объект, созданный оператором new. При присваивании копируется ссылка, и обе переменные указывают на один и тот же объект. Значения по умолчанию. Поле-примитив по умолчанию получает 0 или false, неинициализированная ссылка — null. Когда примитив нужно представить как объект (например, положить в коллекцию), используют классы-обёртки: Integer для int. Пример кода Что использовать на практике Для содержим
javaobject
JuniorJava Developer > Java Core > Collections
Начальная емкость (initial capacity) по умолчанию составляет 16 бакетов, а коэффициент загрузки (load factor) равен 0.75...
BasicsCollections
MiddleJava Developer > Java Core > ООП
Короткий ответ Статические члены принадлежат классу, а не отдельному экземпляру: static-поле хранится в единственном экземпляре и разделяется всеми объектами класса, а static-метод вызывается через имя класса, без создания объекта. Назначение — общие данные (счётчик созданных объектов, конфигурация) и вспомогательные операции, которым состояние не нужно: Math.max(), Integer.parseInt(). Как это работает подробнее Статическое поле инициализируется один раз при загрузке класса, и из любого объекта через него видно одно и то же значение. Статический метод не привязан к объекту, поэтому внутри него нельзя напрямую обращаться к нестатическим полям и методам экземпляра — у метода просто нет ссылки this. Перегружать статические методы можно, переопределять — нельзя. Статическое поле — общее состояние для всех экземпляров: счётчик созданных объектов, разделяемая конфигурация, кэш. Статический метод — операция без состояния объекта: математические вычисления, парсинг, фабричные и служебные методы. Сокрытие (hiding) — о
class-membersjava
JuniorJava Developer > Java Core > Java 8+
Короткий ответ Главная особенность нового Date and Time API (пакет java.time, Java 8) — неизменяемость: все классы, такие как LocalDate, LocalTime, LocalDateTime, immutable, а значит, потокобезопасны. API построен по образцу библиотеки Joda-Time и на основе календарной системы ISO-8601. Он разделяет понятия даты, времени и зоны, предоставляет фабричные методы и fluent-интерфейс для вычислений с датами. Как это работает подробнее API разделяет разные понятия времени: LocalDate — дата без времени и зоны; LocalTime — время без даты и зоны; LocalDateTime — дата и время без зоны; ZonedDateTime и OffsetDateTime — с зоной или смещением; Instant — момент на временной шкале; Duration и Period — промежутки времени. Методы сдвига (plusDays(), minusMonths(), plus(Period)) возвращают новый экземпляр, не меняя исходный, — отсюда неизменяемость и потокобезопасность. Вместо конструкторов с запутанными параметрами используются фабричные методы: now(), of(), parse(). Форматирование и разбор выполняет DateTimeFormatter, который
datetimeimmutable
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Главное преимущество — эффективное использование процессорного времени: пока один поток заблокирован (ждёт ввода-вывода, сети или базы данных), другие продолжают выполняться, и CPU не простаивает. На многоядерных машинах потоки выполняются по-настоящему параллельно, что ускоряет вычисления, разбиваемые на независимые части. Как это работает подробнее Второй плюс — отзывчивость приложения: в GUI тяжёлая работа выносится в фоновый поток, и интерфейс не «зависает», а серверные приложения обслуживают тысячи клиентов за счёт пулов потоков, а не отдельного процесса на каждый запрос. Потоки экономичнее процессов: они разделяют память процесса и создаются с меньшими накладными расходами. Асинхронные сценарии — задачи, выполняемые в фоне с последующим получением результата, — естественно реализуются через потоки, ExecutorService и Future. Обратная сторона — гонки за данные, взаимные блокировки (deadlock), накладные расходы на переключение контекста и сложность отладки и тестирования многопоточного кода.
concurrencyjava
JuniorJava Developer > Java Core > Collections
Короткий ответ Ответ зависит от того, что понимается под «поиском». В ArrayList доступ по индексу get(i) занимает O(1): массив хранится непрерывно. Поиск по значению — contains(element) — требует линейного прохода с проверкой через equals, поэтому в худшем случае это O(n). В HashSet contains вычисляет хэш-код, находит корзину и сравнивает элементы внутри неё — в среднем O(1) независимо от размера множества. Как это работает подробнее Худший случай HashSet — когда из-за плохих хэш-кодов все элементы «свалились» в одну корзину: поиск деградирует до O(n). В Java 8+ длинные цепочки превращаются в сбалансированное дерево, и тогда худший случай ограничен O(log n). Для корректной работы HashSet элементы должны правильно переопределять equals и hashCode. Практический вывод: частые проверки «содержится ли элемент» быстрее делать через HashSet, чем через список. Пример кода Что использовать на практике Для частых проверок «содержится ли элемент» используйте HashSet — в среднем O(1) против O(n) у списка. ArrayList берит
arraylistbig-o
MiddleJava Developer > Java Core > ООП
Короткий ответ Нет. Переопределить можно только метод, который класс унаследовал от родителя и который наследнику виден, а private-метод виден лишь внутри своего класса и подклассам не передаётся. Метод с той же сигнатурой, объявленный в наследнике, — это новый самостоятельный метод, а аннотация @Override над ним вызовет ошибку компиляции. Как это работает подробнее Такое поведение называют сокрытием (hiding), а не переопределением. Аналогично ведут себя статические методы: одноимённый статический метод в наследнике лишь скрывает родительский, и какой из них вызовется, зависит от типа ссылки, а не от фактического типа объекта. Переопределение возможно только для унаследованных и видимых методов: не private, не static, не final. Аннотация @Override — компилятор проверяет, что метод действительно переопределяется; для private-метода это ошибка компиляции. Доступ private — метод не становится частью API наследника: для него он просто не существует. Пример кода Что использовать на практике Переопределяй только ун
hidingjava
JuniorJava Developer > Java Core > JVM и память
Короткий ответ Потому что в момент запуска приложения у JVM ещё нет ни одного объекта класса, а метод main() нужно вызвать до создания каких-либо экземпляров. Объявление static позволяет JVM обратиться к методу через сам класс, без объекта, — это и есть вход в программу. Как это работает подробнее Каноническая сигнатура: public static void main(String[] args). Здесь public — доступность метода для JVM извне, void — метод ничего не возвращает, а String[] args — массив аргументов командной строки. Если убрать static, компиляция пройдёт, но при запуске JVM не найдёт подходящий метод и завершится ошибкой вида Main method is not static. Точка входа — JVM находит метод с канонической сигнатурой и вызывает его, не создавая экземпляр класса. Перегрузка — другие перегруженные варианты main() допустимы, но запускается только вариант с массивом String. Инициализация — первые объекты приложения создаются уже внутри main(), что решает проблему «курицы и яйца». Пример кода Что использовать на практике Всегда объявляй точку
entry-pointjava
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Потому что эти методы работают с монитором объекта, а не с потоком: поток ожидает на конкретном объекте-«замке», и уведомление тоже отправляется через объект. Встроенный монитор есть у любого наследника Object, поэтому естественно разместить методы ожидания и уведомления в самом Object, чтобы их можно было вызывать для любого объекта-монитора. Как это работает подробнее Механика такая: поток вызывает someObject.wait() из синхронизированного блока по someObject, освобождает его монитор и засыпает, а другой поток вызывает notify()/notifyAll() на том же объекте, чтобы разбудить ожидающих. Именно привязка к объекту позволяет независимо синхронизироваться на разных «замках»: ожидание на одном объекте не мешает потокам работать с другим. notify() будит один произвольный поток из очереди ожидания на мониторе, notifyAll() — все ожидающие, после чего они конкурируют за монитор. notifyAll() безопаснее в большинстве сценариев: он снижает риск «потерянного уведомления». Пример кода Что использовать на прак
javamonitor
JuniorJava Developer > Java Core > JVM и память
Короткий ответ Программу на Java компилируют один раз в промежуточный байт-код, а исполняет его JVM, реализация которой существует под каждую платформу: Windows, Linux, macOS. Поэтому один и тот же .class-файл работает без перекомпиляции везде, где есть подходящая JVM. Этот принцип называют «Write Once, Run Anywhere». Как это работает подробнее Секрет — в разделении труда между компилятором и средой исполнения: компилятор не привязывается к конкретному «железу», а JVM делает это за него. Компиляция. javac превращает исходный код не в машинный код процессора, а в промежуточный байт-код (.class-файлы), одинаковый для всех платформ. Исполнение. Каждая реализация JVM «знает», как превратить байт-код в команды именно своей ОС и архитектуры. Переносимым является байт-код, а JVM платформозависима — её устанавливают отдельно под каждую систему. Фиксированные примитивы. В отличие от C/C++, где размер int зависит от архитектуры, спецификация Java задаёт размеры жёстко: например, int — всегда 32 бита. Пример кода Что ис
bytecodecross-platform
JuniorJava Developer > Java Core > ООП
Короткий ответ Наследование — механизм ООП, при котором подкласс (наследник) создаётся на основе другого класса — суперкласса (родителя) — через ключевое слово extends и получает доступ к его не-private членам. Наследник может добавлять свои члены и переопределять унаследованные методы. В Java у класса может быть только один непосредственный родитель. Как это работает подробнее Отношение между наследником и родителем описывается как «является» (IS-A): Cat — это Animal. Наследование уменьшает дублирование кода и служит фундаментом полиморфизма. Переиспользование кода. Общие поля и методы выносятся в базовый класс, и каждый наследник не дублирует их, а просто наследует. IS-A и полиморфизм. Там, где ожидается Animal, можно передать Cat: ссылка родительского типа указывает на объект наследника, и вызывается реализация метода именно наследника. Конструкторы не наследуются. Наследник обязан вызвать конструктор родителя через super(); компилятор добавляет такой вызов автоматически, если у родителя есть конструктор б
extendsinheritance
MiddleJava Developer > Java Core > JVM и память
Короткий ответ Стек создаётся для каждого потока отдельно и хранит кадры методов: локальные переменные, параметры и служебную информацию; работает по принципу LIFO. Куча — общая для всех потоков область, в которой размещаются объекты и массивы, созданные через new. Локальные переменные-примитивы хранятся в стеке; если примитив — поле объекта, он лежит в куче вместе с объектом. Как это работает подробнее При вызове метода в стеке выделяется кадр, при возврате из метода он разрушается. Стек живёт недолго и имеет фиксированный, обычно небольшой размер; его переполнение приводит к StackOverflowError. Куча обычно значительно больше стека, её размер задаётся параметрами -Xmx и -Xms. Объект в куче живёт, пока на него есть ссылки, а освобождением памяти занимается сборщик мусора. Для работы GC куча делится на поколения: молодое (Eden и survivor-области) и старое. Если ссылка на объект — локальная переменная, сама ссылка лежит в стеке потока, а объект — в куче. В памяти JVM есть и другие области: Metaspace (метаданные
heapjava
MiddleJava Developer > Java Core > JPA и Hibernate
Короткий ответ Кэширование в Hibernate хранит часто запрашиваемые данные в памяти, уменьшая обращения к базе. Кэш первого уровня (First-Level Cache, Session Cache) включён по умолчанию и привязан к конкретной Session: повторно запрошенный в той же сессии объект возвращается из памяти без запроса к БД. Кэш второго уровня (Second-Level Cache, SessionFactory Cache) — общий для всех сессий фабрики; по умолчанию выключен и требует явной настройки. Как это работает подробнее Разница — в границе действия. Первый уровень живёт в пределах одной Session: он обеспечивает согласованность данных на уровне транзакции, но недоступен другим сессиям. Второй уровень действует на уровне всего приложения, поддерживает параллельный доступ нескольких сессий и может хранить данные не только в памяти, но и во внешнем хранилище. Первый уровень: всегда включён, не настраивается, хранится в памяти сессии. Второй уровень: опционален и конфигурируем: подключается провайдер кэша (например, Ehcache или Redis), включается свойство hibernate
cachingfirst-level-cache
SeniorJava Developer > Java Core > Сборка мусора
Короткий ответ Разница между ссылками — в моменте, когда GC может удалить объект. SoftReference удерживает объект, пока JVM не столкнётся с нехваткой памяти: сборщик удаляет soft-доступные объекты обычно перед OutOfMemoryError. WeakReference ещё слабее: объект, доступный только по слабой ссылке, может быть удалён уже при ближайшей сборке, как только исчезнут сильные ссылки на него. Как это работает подробнее Мягкие ссылки удобны для кэшей, где данные можно пересоздать: они живут максимально долго, но не приводят к переполнению памяти. Слабые ссылки подходят для вспомогательных данных, потерю которых можно пережить; классический пример — WeakHashMap, где записи исчезают, когда их ключи больше не используются сильными ссылками. PhantomReference — крайний случай: объект, доступный только по фантомной ссылке, фактически уже недоступен, метод get() всегда возвращает null. Фантомные ссылки применяются с ReferenceQueue, чтобы выполнить действия «перед удалением» объекта или надёжно отследить, что объект действительн
gcjava
JuniorJava Developer > Java Core > Collections
Короткий ответ Исторический класс Vector (Java 1.0) делает все свои методы synchronized, то есть каждый вызов защищён монитором на уровне объекта; ArrayList появился позже (Java 2) и не синхронизирован, поэтому в однопоточном коде работает заметно быстрее. Vector при переполнении удваивает размер массива (или растёт на capacityIncrement), а ArrayList увеличивает ёмкость примерно в полтора раза: oldCapacity + (oldCapacity >> 1). Как это работает подробнее Оба класса реализуют интерфейс List и внутри используют обычный массив, поэтому логика работы похожа. Разница в производительности — следствие синхронизации: у Vector она глобальная и приходится на каждый вызов, у ArrayList её нет вовсе. Из-за этого Vector считается устаревшим (legacy), хотя и не удалён из стандартной библиотеки. Потокобезопасность: Vector синхронизирован, но такая «безопасность» покупается блокировкой на каждой операции. Современная замена: при необходимости синхронизации берут ArrayList с Collections.synchronizedList(...) либо потокобезопас
arraylistcollections
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Процесс — это программа в состоянии выполнения с собственным изолированным адресным пространством памяти. Поток — «лёгковесная» единица выполнения внутри процесса; в Java сам процесс — это запущенная JVM, внутри которой работают десятки потоков: main, потоки сборщика мусора, пользовательские потоки. Как это работает подробнее Потоки одного процесса разделяют кучу и другие общие области памяти, поэтому могут общаться через общие объекты и переменные, но при этом каждый имеет собственный стек вызовов и программный счётчик — локальные переменные у потоков не общие. Из-за разделяемой памяти и возникает риск гонок за данные. Процессы полностью изолированы: крах одного процесса не «роняет» другой, однако обмен данными между ними требует специальных механизмов — сокетов, файлов, разделяемой памяти. Создание и переключение потоков обходятся заметно дешевле, чем у процессов, поэтому многопоточность эффективнее использует ресурсы системы и многоядерные процессоры. Пример кода Что использовать на практике
javamemory
MiddleJava Developer > Java Core > Spring
Короткий ответ @SpringBootApplication — основная аннотация класса запуска Spring Boot-приложения. Она является составной и объединяет три аннотации: @Configuration, @EnableAutoConfiguration и @ComponentScan. Как это работает подробнее Запуск выполняет метод SpringApplication.run(...): создаётся ApplicationContext, выполняется сканирование компонентов, применяется авто-конфигурация, загружаются свойства конфигурации и запускается встроенный веб-сервер. Авто-конфигурация анализирует, какие зависимости есть в classpath (какие стартеры подключены), и на их основе сама создаёт и настраивает нужные бины — например, DataSource или EntityManagerFactory. Составляющие аннотации: @Configuration помечает класс как источник бинов, @EnableAutoConfiguration включает авто-конфигурацию, а @ComponentScan сканирует пакет главного класса и подпакеты в поисках @Component, @Service, @Repository, @Controller. Условные аннотации — @ConditionalOnClass, @ConditionalOnBean, @ConditionalOnMissingBean — включают конфигурацию только при в
autoconfigurationcomponentscan
MiddleJava Developer > Java Core > ООП
Короткий ответ Ключевое слово final означает запрет на изменение и используется в трёх основных смыслах: final-переменной значение присваивается только один раз, final-метод нельзя переопределить в наследнике, а от final-класса нельзя создавать подклассы. Именно так устроены String, Integer и другие классы-обёртки. Как это работает подробнее Если final стоит у ссылки на объект, менять саму ссылку нельзя, но состояние объекта по ней — можно (если объект сам не неизменяемый). final применяется и к параметрам методов: такой параметр нельзя переприсваивать внутри метода. Неизменяемые объекты, все поля которых объявлены final, безопасно публиковать и использовать в многопоточной среде без синхронизации. final-переменная — константа: присваивание при объявлении или в конструкторе, далее только чтение; переприсваивание даёт ошибку компиляции. final-метод — реализация зафиксирована в родительском классе и не может быть переопределена в подклассах. final-класс — наследование запрещено, поэтому поведение класса гаранти
finalimmutability
JuniorJava Developer > Java Core > Основы Java
Короткий ответ Компилятор автоматически добавит так называемый конструктор по умолчанию — конструктор без параметров, тело которого состоит из вызова конструктора родителя super(). Благодаря этому объект класса по-прежнему можно создать оператором new, а цепочка конструкторов корректно строится от базового класса к производному. Как это работает подробнее Конструктор по умолчанию добавляется только в одном случае: если в классе нет вообще ни одного конструктора. Если программист объявил хотя бы один конструктор, например Book(String title), автоматический конструктор не создаётся, и для new Book() понадобится явный конструктор без параметров. Видимость сгенерированного конструктора совпадает с видимостью класса. Условие генерации — в классе не объявлено ни одного конструктора; иначе компилятор не добавляет ничего. Вызов super() — у родителя должен быть доступный конструктор без параметров; иначе наследник обязан явно вызвать подходящий super(...). Роль — корректно проинициализировать объект и построить цепочк
constructorsdefault-constructor
SeniorJava Developer > Java Core > Дженерики
Короткий ответ При компиляции дженерик-информация стирается (type erasure): компилятор проверяет типы, а затем убирает их из байт-кода. Поэтому List и List после компиляции — один и тот же класс List: узнать параметр типа в рантайме нельзя. Raw type — параметризованный тип без параметра (List вместо List): он оставлен для совместимости со старым кодом, но типобезопасность теряется. Как это работает подробнее Переменная типа T заменяется своей левой границей: без указанной границы — Object, для — Animal. Параметризованный тип превращается в сырой List, а не в List. Стирание введено ради обратной совместимости: код, написанный до Java 5, должен был продолжать работать и корректно взаимодействовать с новым. Из стирания вытекают следствия: нельзя создать массив параметризованного типа (new List[1] запрещено), нельзя проверить параметр типа в рантайме; чтобы передать тип в метод, используется дополнительный аргумент Class. Raw types: компилятор выдаёт
genericsjava
MiddleJava Developer > Java Core > Java 8+
Короткий ответ Optional — контейнерный класс из пакета java.util, появившийся в Java 8: он содержит либо одно ненулевое значение, либо пуст. Его задача — сделать возможное отсутствие значения явным в сигнатуре метода и избавить код от бесконечных проверок на null и от NullPointerException: вместо возврата null метод возвращает Optional.empty(). Как это работает подробнее Создать Optional можно тремя способами: Optional.of(value) — только для заведомо ненулевого значения, иначе сразу NullPointerException; Optional.ofNullable(value) — допускает null и превращает его в пустой Optional; Optional.empty() — пустой контейнер. Для проверки наличия значения есть isPresent() и isEmpty() (с Java 11). Для безопасного получения — orElse(default), orElseGet(supplier), когда значение по умолчанию вычисляется лениво, и orElseThrow(). Методы map(), filter() и flatMap() позволяют преобразовывать значение внутри контейнера без проверок. Optional активно используется в Stream API: findFirst(), max() и другие терминальные операци
javajava-8
JuniorJava Developer > Java Core > Основы Java
Короткий ответ Класс-обёртка — это ссылочный тип, который упаковывает примитив в объект: int → Integer, long → Long, char → Character, boolean → Boolean. Обёртки нужны там, где требуется объект, а не значение: коллекции и дженерики хранят только объекты. Преобразование автоматическое: автобоксинг упаковывает примитив в обёртку, распаковка делает обратное. Как это работает подробнее Помимо «объектности», обёртки дают полезную функциональность: константы вроде Integer.MAX_VALUE, методы преобразования Integer.parseInt("42") и Integer.valueOf(...), а также возможность хранить null для обозначения отсутствия значения. Автобоксинг — автоматическая упаковка примитива при присваивании или добавлении в коллекцию. Распаковка — автоматическое извлечение примитива; для null-обёртки даёт NullPointerException. Кэш значений — Integer от -128 до 127 кэшируется, поэтому == для обёрток даёт неожиданные результаты. Функциональность — константы, парсинг и преобразование типов, например Integer.parseInt() и valueOf(). Пример кода
autoboxinginteger
SeniorJava Developer > Java Core > JPA и Hibernate
Короткий ответ EAGER и LAZY — две стратегии загрузки ассоциаций, определяющие, когда данные связей извлекаются из базы. При жадной загрузке связанные данные приходят сразу вместе с основной сущностью; при ленивой — Hibernate подставляет прокси или коллекцию-обёртку, а запрос выполняется только в момент реального обращения к свойству. Стратегия задаётся атрибутом fetch аннотации связи. Как это работает подробнее Ленивая загрузка экономит память и число запросов, когда связь может не понадобиться, но требует открытой сессии в момент обращения к свойству. Если обратиться к ленивой связи, когда Session или persistence context уже закрыт, Hibernate выбрасывает LazyInitializationException. По умолчанию: @ManyToOne — EAGER, @OneToMany и @ManyToMany — LAZY. LazyInitializationException: «could not initialize proxy - no Session» — возникает, например, при чтении сущности в контроллере вне границ @Transactional, когда сессия из сервиса уже закрыта. Решения: обращаться к ленивым связям внутри транзакции или открытой сесс
eagerhibernate
JuniorJava Developer > Java Core > Java 8+
Короткий ответ Лямбда-выражение — это анонимная функция: блок кода без имени, который можно передавать как значение, например в аргументы метода. В Java 8 оно записывается как список параметров, стрелка и тело. Лямбда реализует единственный абстрактный метод функционального интерфейса, а типы параметров и возвращаемого значения компилятор выводит автоматически (type inference) из контекста. Как это работает подробнее Функциональный интерфейс (SAM-интерфейс) — это интерфейс ровно с одним абстрактным методом; в нём могут быть также статические и default-методы. Аннотация @FunctionalInterface необязательна, но позволяет компилятору проверить, что интерфейс действительно функциональный. До Java 8 такое поведение реализовывали анонимными внутренними классами; лямбда — более компактная альтернатива им. Встроенные примеры — Runnable, Comparator, Callable, а в Java 8 добавлены Function, Consumer, Supplier, Predicate и другие интерфейсы пакета java.util.function. Лямбда не создаёт новый уровень вложенности, как аноним
anonymous-classfunctional-interface
MiddleJava Developer > Java Core > ООП
Короткий ответ Множественное наследование — ситуация, когда один класс может иметь более одного непосредственного родителя и наследовать поля и методы сразу от нескольких классов, например class Child extends Mother, Father. Java его не поддерживает: наследование классов одиночное. Зато класс может реализовывать любое количество интерфейсов. Как это работает подробнее Главная причина запрета — проблема ромба (diamond problem): если два родительских класса имеют метод с одинаковой сигнатурой, непонятно, какую реализацию должен унаследовать потомок и как корректно разрешить конфликт. Интерфейсы. Класс может реализовывать несколько интерфейсов: implements A, B, C. Начиная с Java 8 интерфейсы могут содержать default-методы с реализацией. Разрешение конфликта. Если два интерфейса предоставляют default-методы с одинаковой сигнатурой, класс обязан переопределить метод явно, например вызвав A.super.method(). Композиция как альтернатива. Вместо жёсткого наследования поведение часто встраивают через объекты других клас
default-methodsdiamond-problem
SeniorJava Developer > Java Core > Multithreading
Короткий ответ Модель памяти Java — это набор формальных правил и гарантий, определяющих, как потоки взаимодействуют через память и какие результаты чтения и записи они обязаны «видеть», чтобы программа вела себя предсказуемо на разных процессорах и операционных системах. Центральное понятие — отношение happens-before: если действие A happens-before действия B, то результат A гарантированно виден действию B, и A не будет переупорядочено после B. Как это работает подробнее Без JMM компилятор, JIT и процессор могли бы свободно переупорядочивать операции и кэшировать значения, из-за чего поведение многопоточной программы зависело бы от конкретного железа. JMM определяет, какие переупорядочивания запрещены и какие гарантии видимости даёт Java. Основные правила happens-before: порядок действий внутри одного потока (program order); освобождение монитора happens-before последующий захват того же монитора другим потоком; запись в volatile-переменную happens-before последующее чтение той же переменной другим потоком;
happens-beforejava
JuniorJava Developer > Java Core > ООП
Короткий ответ Объект — это экземпляр класса: если класс это шаблон или чертёж, описывающий поля и методы, то объект — конкретная реализация шаблона с собственным набором значений полей. Например, класс Car описывает свойства машины, а объект — конкретную машину с моделью и цветом. Объект создаётся оператором new, также его можно получить рефлексией, клонированием или десериализацией. Как это работает подробнее Каждый объект занимает собственную область памяти в куче и имеет состояние, отличное от состояния других объектов того же класса. Важно различать объект и ссылку: переменная объектного типа хранит не сам объект, а ссылку — «адрес», по которому объект лежит в памяти. Ссылку можно переназначить на другой объект или в null, а на один объект могут одновременно указывать несколько ссылок. Класс — шаблон: какие поля и методы есть у сущности. Объект — экземпляр с конкретными значениями полей; размещается в куче. Ссылка — переменная, указывающая на объект; может быть переназначена или равна null. java.lang.Obj
classheap
SeniorJava Developer > Java Core > JPA и Hibernate
Короткий ответ Проблема N+1 — это 1 + N запросов вместо одного оптимального запроса с join: сначала выполняется один SELECT для получения N родительских записей, а затем для каждой из них — ещё по одному запросу за связанными данными. Чаще всего проявляется при ленивой загрузке ассоциаций, когда мы обходим список и у каждой сущности обращаемся к ленивой коллекции. Как это работает подробнее На небольших объёмах проблема незаметна, но с ростом данных производительность резко падает из-за множества обращений к базе. Все техники решения направлены на сокращение числа запросов, а выбор конкретного способа зависит от структуры запроса и сценария чтения. Fetch join: в HQL/JPQL связанные данные подгружаются в том же запросе через JOIN FETCH. @EntityGraph: в Spring Data JPA декларативно описывается граф загрузки — метод репозитория подтягивает указанные атрибуты одним запросом. Batch fetching: @BatchSize(size = 10) на сущности или коллекции грузит связи пачками, сокращая число запросов с N до N/size. EAGER: для связе
entitygraphfetch-join
MiddleJava Developer > Java Core > Spring
Короткий ответ Профили (Profiles) — механизм Spring, позволяющий поддерживать разные конфигурации приложения для разных окружений: разработки (dev), тестирования (test) и продакшена (prod). Бин или класс конфигурации, помеченный @Profile, загружается только тогда, когда соответствующий профиль активирован. Как это работает подробнее Профили разделяют окружение-специфичные настройки: URL базы данных, уровень логирования, параметры интеграций. Это избавляет от ручного изменения конфигурации при переходе между окружениями и помогает защитить продакшен-настройки. Для отдельных профилей создаются файлы вида application-{profile}.properties или application-{profile}.yml, которые переопределяют общие настройки. Способы активации профиля: свойство spring.profiles.active=prod в application.properties, аргумент командной строки --spring.profiles.active=prod или переменная окружения SPRING_PROFILES_ACTIVE. Основной файл конфигурации Spring Boot — application.properties: порт сервера (server.port), подключение к базе дан
configurationenvironment
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Пул потоков — это набор заранее созданных рабочих потоков, которые переиспользуются для выполнения множества задач. Вместо создания нового потока на каждую задачу вы отправляете задачу в пул, её выполняет свободный поток, после чего поток не умирает, а возвращается в пул и ждёт следующую задачу. Пул убирает накладные расходы на создание и уничтожение потоков и ограничивает максимальное число одновременно работающих потоков. Как это работает подробнее В Java пулы реализованы через интерфейс ExecutorService и его основную реализацию ThreadPoolExecutor. Фабричные методы класса Executors: newFixedThreadPool(n) — фиксированное число потоков; newCachedThreadPool() — потоки по мере необходимости с переиспользованием простаивающих; newSingleThreadExecutor() — один рабочий поток с неограниченной очередью; newScheduledThreadPool() — для отложенных и периодических задач. Под капотом ThreadPoolExecutor настраивается параметрами: corePoolSize — основные потоки, maximumPoolSize — максимум, keepAliveTime — вр
executorexecutorservice
JuniorJava Developer > Java Core > ООП
Короткий ответ Спецификаторы (модификаторы) доступа определяют, из каких мест программы видны классы, поля, методы и конструкторы. В Java четыре уровня: private, package-private (по умолчанию, без модификатора), protected и public. Это практическая реализация инкапсуляции: наружу остаётся только безопасный публичный интерфейс. Как это работает подробнее Уровни доступа перечисляют по нарастанию видимости. private. Самый строгий: член доступен только внутри того же класса; ни наследники, ни другие классы его не видят. package-private (по умолчанию). Модификатор не пишется: член доступен всем классам внутри того же пакета и не виден за его пределами. protected. Член доступен внутри пакета, а также наследникам класса из других пакетов. public. Доступен отовсюду — из любого класса любого пакета, при условии что доступен сам класс. Классы верхнего уровня. Для них применимы только public и package-private: класс не может быть private или protected. Пример кода Что использовать на практике Начинать с private и открыв
access-modifiersencapsulation
MiddleJava Developer > Java Core > Multithreading
Короткий ответ BlockingQueue — интерфейс из пакета java.util.concurrent, описывающий потокобезопасную очередь с блокирующими операциями: put() ждёт, пока в заполненной очереди освободится место, а take() ждёт появления элемента в пустой. Это готовая модель «производитель — потребитель», которая избавляет от ручной синхронизации через wait/notify. Как это работает подробнее Блокирующие операции приостанавливают поток до выполнения условия: put() не завершится, пока в ограниченной очереди не освободится место, а take() — пока не появится элемент. Помимо них есть неблокирующие offer() и poll(): они сразу возвращают false или null, а версии с таймаутом ждут строго ограниченное время. Интерфейс избавляет от ручной синхронизации: не нужны ни явные блокировки, ни wait/notify, ни бесконечные циклы опроса. Очередь сама координирует потоки производителя и потребителя. ArrayBlockingQueue — ограниченная очередь на массиве с фиксированной ёмкостью. LinkedBlockingQueue — очередь на связных узлах, опционально ограниченная.
blockingqueueconcurrent
JuniorJava Developer > Java Core > JVM и память
Короткий ответ Байт-код — это промежуточное представление Java-программы: набор инструкций не для конкретного процессора, а для абстрактной вычислительной машины — JVM. Компилятор javac превращает исходный файл Hello.java в файл Hello.class, внутри которого и находится байт-код. Как это работает подробнее Байт-код компактнее исходного текста и уже содержит результат синтаксического и семантического анализа, но машинным кодом ещё не является — его исполняет JVM. Интерпретация и JIT. JVM либо интерпретирует байт-код, либо компилирует часто исполняемые участки в машинный код с помощью JIT-компилятора (Just-In-Time), что ускоряет выполнение. Связующее звено. Программу компилируют в байт-код один раз, а дальше каждая реализация JVM сама решает, как исполнить его на своей платформе. Пример кода Что использовать на практике Помнить, что результатом javac всегда является .class-файл с байт-кодом. Использовать javap -c, чтобы посмотреть реальные инструкции байт-кода. Рассматривать байт-код как единицу переносимости: о
bytecodeclass-file
JuniorJava Developer > Java Core > Core Java
JVM исполняет байт-код, classpath указывает, где искать классы и ресурсы приложения.
BasicsJVM
MiddleJava Developer > Java Core > Multithreading
Короткий ответ CompletableFuture — класс из пакета java.util.concurrent, появившийся в Java 8 и реализующий интерфейсы Future и CompletionStage. Он представляет результат асинхронной операции и позволяет писать асинхронный, событийно-ориентированный код: строить цепочки зависимых шагов, обрабатывать ошибки и комбинировать несколько операций без блокировки потока. Как это работает подробнее Классический Future ограничен: результат получается только блокирующим вызовом get(), future нельзя завершить вручную, несколько future нельзя соединить в цепочку или скомбинировать, а исключения обрабатывать неудобно. CompletableFuture можно завершить явно методом complete(value) или завершить с ошибкой через completeExceptionally(). Задачи запускаются статическими фабриками runAsync() (без результата) и supplyAsync() (с результатом). Конвейер шагов: thenApply() преобразует результат, thenAccept() потребляет его, thenCompose() соединяет future-операции, а exceptionally()/handle() обрабатывают ошибки. Несколько независимых
asynccompletablefuture
MiddleJava Developer > Java Core > Multithreading
Короткий ответ CountDownLatch позволяет одному или нескольким потокам ждать, пока набор других потоков не выполнит операции: он работает на счётчике, который уменьшается вызовами countDown(), а ожидающие блокируются в await(), пока счётчик не станет нулевым. CyclicBarrier заставляет фиксированную группу потоков встретиться в одной точке и после отпускания сбрасывается, поэтому пригоден для повторного использования. Semaphore хранит счётчик разрешений и через acquire()/release() ограничивает число потоков, одновременно работающих с ресурсом. Как это работает подробнее CountDownLatch создаётся с начальным значением счётчика и используется один раз: когда счётчик достигает нуля, все ожидающие потоки освобождаются, но восстановить latch нельзя. Классический сценарий — главный поток ждёт, пока несколько фоновых задач загрузят данные. CyclicBarrier создаётся на фиксированное число участников (parties); опционально в конструктор передаётся Runnable — барьерное действие, которое выполняется, когда все потоки достигли
countdownlatchcyclicbarrier
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Deadlock (взаимная блокировка) — это ситуация, когда два или более потоков навсегда блокируют друг друга: каждый поток удерживает ресурс (монитор, блокировку), который нужен другому, и одновременно ждёт ресурс, удерживаемый другим. В результате программа «зависает», хотя потоки не завершились и не упали с ошибкой. Как это работает подробнее Классический сценарий в Java: поток 1 захватывает монитор объекта A и хочет войти в блок по объекту B, а поток 2 одновременно захватывает B и хочет войти в блок по A. Для возникновения deadlock нужно четыре условия Коффмана: взаимное исключение, удержание и ожидание, отсутствие вытеснения и циклическое ожидание. Достаточно устранить хотя бы одно из них, чтобы взаимной блокировки не было. Похожее явление — livelock: потоки не заблокированы, активно меняют состояние, но из-за взаимных уступок не продвигаются вперёд. Пример кода Что использовать на практике Захватывай блокировки во всех потоках в едином фиксированном порядке (и освобождай в обратном) — это разр
concurrencydeadlock
MiddleJava Developer > Java Core > Java 8+
Короткий ответ Default-метод — это метод интерфейса, у которого есть реализация, помеченная ключевым словом default. Он нужен для обратной совместимости: до Java 8 методы интерфейсов были только абстрактными, и добавление нового метода ломало все классы-реализации. Теперь в существующий интерфейс можно добавить метод с готовой реализацией, и классы продолжат компилироваться без изменений, а при желании переопределят его. Как это работает подробнее Именно благодаря default-методам в Java 8 в старые интерфейсы стандартной библиотеки добавили новые возможности: в Collection появился метод stream(), в List — sort(). Это дало и второе преимущество — множественное «поведенческое» наследование: класс может получить готовую реализацию сразу от нескольких интерфейсов, если между ними нет конфликтов. Важное правило: если класс реализует два интерфейса с одинаковыми default-методами, возникает конфликт, и класс обязан переопределить метод явно, например вызвав SomeInterface.super.method(). Реализация из суперкласса всег
backward-compatibilitydefault-methods
MiddleJava Developer > Java Core > Spring
Короткий ответ DispatcherServlet — фронт-контроллер Spring MVC: он принимает все входящие HTTP-запросы, направляет их к нужным контроллерам, координирует обработку и возвращает ответ клиенту. Архитектурно Spring MVC делит приложение на Model, View и Controller, а DispatcherServlet служит единой точкой входа для всех запросов. Как это работает подробнее Полная цепочка типового запроса в Spring Boot-приложении: клиент → DispatcherServlet → Controller → Service → Repository → база данных, а результат возвращается через контроллер клиенту в виде представления или JSON. В Spring Boot DispatcherServlet настраивается автоматически при подключении spring-boot-starter-web: встроенный Tomcat и авто-конфигурация Spring MVC уже включены. Приём запроса: клиент отправляет HTTP-запрос, и он попадает в DispatcherServlet. Маршрутизация: по маппингам запросов (@RequestMapping, @GetMapping) DispatcherServlet определяет, какой контроллер обработает запрос. Обработка: контроллер взаимодействует с моделью и сервисным слоем и возвр
controllerdispatcherservlet
MiddleJava Developer > Java Core > Дженерики
Короткий ответ Generics — это параметризованные типы: классы, интерфейсы и методы работают с типом, который задаётся при использовании, а не «зашит» в код. Главная решаемая проблема — типобезопасность и раннее обнаружение ошибок: компилятор проверяет типы на этапе компиляции, а не в рантайме, и убирает ручные приведения к Object. Как это работает подробнее До дженериков коллекции хранили Object, и при извлечении требовалось явное приведение, например к строке. Если в список по ошибке попал элемент другого типа, программа падала с ClassCastException в рантайме, часто далеко от места добавления. С дженериками неверный тип не проходит компиляцию — проверки выполняются на этапе компиляции. Механизм работает с классами, интерфейсами и методами: тип-параметр задаётся при использовании (класс с параметром T, дженерик-методы и дженерик-интерфейсы). Параметром может быть только ссылочный тип — примитив нужно оборачивать в класс-обёртку (Integer вместо int). Информация о типе-параметре существует только до компиляции:
collectionscompilation
MiddleJava Developer > Java Core > Collections
При возникновении коллизий HashMap использует метод цепочек. С версии Java 8, если длина списка превышает 8 элементов, он преобразуется в дерево...
CollectionsJava
JuniorJava Developer > Java Core > JPA и Hibernate
Короткий ответ ORM (Object-Relational Mapping) — техника отображения объектной модели на реляционную базу: класс отображается на таблицу, объект — на строку, поля — на колонки, поэтому разработчик работает с объектами, а не пишет вручную SQL и код преобразования. Hibernate — самый популярный open-source ORM-фреймворк для Java, автоматизирующий CRUD и управляющий персистентностью объектов. Как это работает подробнее Hibernate — это надстройка над JDBC: под капотом он использует JDBC-драйвер базы данных, но берёт на себя низкоуровневую работу — соединения, подготовку и выполнение SQL, генерацию запросов и отслеживание изменений объектов. Основной выигрыш — меньше ручного кода, выше переносимость и поддерживаемость приложения. Маппинг и генерация SQL: объекты автоматически отображаются на таблицы, не нужно вручную писать большое количество SQL. HQL — собственный объектно-ориентированный язык запросов, похожий на SQL, но работающий с классами и их свойствами. Производительность: встроенное кэширование (кэш первог
hibernatejava
SeniorJava Developer > Java Core > Дженерики
Короткий ответ PECS (Producer Extends, Consumer Super) — мнемоническое правило выбора wildcard в параметрах методов: если метод только читает коллекцию («производитель»), объявляйте ? extends T; если только добавляет элементы («потребитель») — ? super T. Правило нужно из-за инвариантности дженериков: List не является подтипом List. Как это работает подробнее «Producer Extends»: метод только читает элементы — коллекция производит данные. Параметр ? extends T задаёт верхнюю границу: Collection примет и коллекцию Animal, и коллекцию любого наследника (Cat, Dog). Читать можно как Animal, а добавлять нельзя (кроме null): компилятор не знает фактический тип элементов. «Consumer Super»: метод только добавляет элементы — коллекция потребляет данные. Параметр ? super T задаёт нижнюю границу: Collection принимает Collection, Collection или Collection. Добавлять можно Cat и его наследников, а при чтении гарантирован только Object. Эталон из JDK: Collections.c
collectionsgenerics
MiddleJava Developer > Java Core > JPA и Hibernate
Короткий ответ SessionFactory — «тяжёлая» потокобезопасная фабрика: она создаётся один раз на всё приложение на основе конфигурации, хранит метаданные маппингов и кэш второго уровня и порождает объекты Session. Session — основной интерфейс работы с базой: единица работы (unit of work) с методами CRUD, управления транзакциями, кэшем первого уровня и ленивой загрузкой. В JPA роли совпадают: EntityManagerFactory — аналог SessionFactory, а EntityManager — аналог Session. Как это работает подробнее Session не потокобезопасна: каждый поток или единица работы должна использовать собственную Session, полученную через openSession() или getCurrentSession() для сессии, привязанной к текущей транзакции. Поскольку Hibernate реализует спецификацию JPA, его Session и SessionFactory реализуют стандартные интерфейсы EntityManager и EntityManagerFactory. SessionFactory: создаётся один раз (например, из hibernate.cfg.xml), дорога в создании, потокобезопасна, общая для всех сессий. Session: открывается на единицу работы, предост
entitymanagerhibernate
SeniorJava Developer > Java Core > Spring
Короткий ответ Spring AOP (Aspect Oriented Programming) — парадигма, дополняющая ООП: сквозные задачи — логирование, безопасность, управление транзакциями, аудит — выносятся из бизнес-логики в отдельные модули. Если в ООП основная единица — класс, то в AOP это аспект. Как это работает подробнее По моменту выполнения советы делятся на before (до метода), after returning (после успешного завершения), after throwing (если метод бросил исключение), after (finally) — в любом случае, и around, который выполняется и до, и после метода и может управлять его выполнением. Основные применения AOP: логирование, безопасность и аутентификация, декларативные транзакции, обработка исключений, сбор метрик API. Код аспектов собирается в одном месте, переиспользуется, а бизнес-код остаётся чистым. Aspect (аспект) — класс, который реализует сквозную функциональность, затрагивающую сразу несколько классов, например логирование или проверку прав. Join Point (точка соединения) — место в ходе выполнения программы, в котором может бы
adviceaop
MiddleJava Developer > Java Core > Spring
Короткий ответ Spring Bean — это обычный Java-объект (POJO), который создаётся, настраивается и управляется IoC-контейнером Spring. В отличие от объекта, созданного через new, бин получает автоматически внедрённые зависимости, а его жизненным циклом от создания до уничтожения управляет контейнер. Как это работает подробнее Бины регистрируются в контексте приложения через стереотипные аннотации — @Component, @Service, @Repository, @Controller — либо декларацией @Bean внутри @Configuration-класса. Далее контейнер использует их для автоматического внедрения в другие компоненты. Этапы цикла: запуск контейнера и загрузка конфигурации, создание экземпляра бина, внедрение зависимостей, инициализация бина (например, @PostConstruct или init-method), выполнение бизнес-методов, затем вызов метода уничтожения (@PreDestroy или destroy-method) при остановке контейнера. Зачем это нужно: централизованное управление объектами и зависимостями упрощает сопровождение и тестирование, а ресурсы — соединения с БД, сокеты, файлы — г
beancontainer
JuniorJava Developer > Java Core > Spring
Короткий ответ Spring Boot Starters — предварительно сконфигурированные наборы зависимостей: вместо ручного добавления и согласования версий десятков JAR достаточно подключить один стартер, и нужные библиотеки подтянутся автоматически. Это устраняет конфликты версий и сокращает конфигурацию сборки. Как это работает подробнее Стартер — это один артефакт, который включает все библиотеки для конкретной функциональности или типа приложения. Присутствие стартеров в classpath во многом определяет, какие авто-конфигурации применит Spring Boot. Совместимость версий транзитивных зависимостей обеспечивают родительский POM spring-boot-starter-parent и BOM spring-boot-dependencies. spring-boot-starter-web — веб-приложения и REST API: Spring MVC и встроенный Tomcat. spring-boot-starter-data-jpa — работа с базой данных через JPA и Hibernate. spring-boot-starter-security — аутентификация и авторизация. spring-boot-starter-test — тестирование: JUnit, Mockito и Spring Test. spring-boot-starter-actuator — мониторинг и health-п
dependenciesjava
MiddleJava Developer > Java Core > Spring
Короткий ответ Spring Boot — фреймворк, построенный поверх Spring Framework и упрощающий разработку автономных (stand-alone) приложений, готовых к продакшену. Его ключевые механизмы — авто-конфигурация, стартовые зависимости (starters) и встроенный веб-сервер. Как это работает подробнее Spring — это общий фреймворк: контейнер IoC/DI, AOP, Spring MVC, управление транзакциями; он требует довольно большой ручной настройки и, как правило, внешнего сервера. Spring Boot — расширение, которое работает «поверх» Spring и использует те же Spring Core, Spring AOP и Spring MVC, но сильно упрощает их применение. Авто-конфигурация: убирает большую часть шаблонной ручной конфигурации, в первую очередь XML-настройку. Стартеры: берут на себя управление зависимостями — не нужно вручную подбирать версии библиотек. Встроенный сервер: Tomcat, Jetty или Undertow позволяют запускать приложение как обычный исполняемый JAR без внешнего сервера. Production-возможности: например, Actuator для мониторинга; Boot удобен для облачных прило
autoconfigurationjava
MiddleJava Developer > Java Core > Spring
Короткий ответ Spring Data JPA — модуль Spring Framework, работающий поверх спецификации JPA (внутри по умолчанию Hibernate): достаточно объявить интерфейс-репозиторий, и Spring Data сам предоставит его реализацию с готовыми CRUD-операциями — без написания SQL и DAO-классов. Как это работает подробнее Центральный интерфейс — JpaRepository, расширяющий CrudRepository и PagingAndSortingRepository. Особенность Spring Data JPA — производные методы запросов (derived query methods): по имени метода, то есть по префиксам findBy, countBy, existsBy, deleteBy и именам полей сущности, Spring Data парсит имя и генерирует нужный JPQL или SQL-запрос. Готовые методы: save(), findById(), findAll(), deleteById(), flush(), а также пагинация через Pageable и сортировка через Sort. Derived-метод: findByName(String name) превращается в поиск по полю name, а findByDepartmentName строит запрос через связанную сущность. @Query: для сложных запросов запрос задаётся вручную на JPQL или нативном SQL при nativeQuery = true, параметры св
javajpa
MiddleJava Developer > Java Core > Java 8+
Короткий ответ Stream в Java 8 — это абстракция для декларативной обработки последовательностей данных: вы описываете, что нужно сделать с элементами (отфильтровать, преобразовать, агрегировать), а не как это сделать с помощью циклов. java.util.stream.Stream не хранит данные и не является коллекцией — это конвейер операций над источником (коллекцией, массивом, файлом, генератором), который обрабатывает элементы лениво и при необходимости параллельно. Как это работает подробнее Пайплайн стрима состоит из трёх частей: источник, промежуточные операции и терминальная операция. Промежуточные операции (filter, map, flatMap, sorted, limit, distinct, peek) ленивы: они не выполняются, пока к пайплайну не применена терминальная операция, и каждая возвращает новый стрим. Терминальные операции (collect, forEach, reduce, count, anyMatch) запускают обработку и производят результат; после терминальной операции стрим использовать нельзя. Исходная коллекция при работе стрима не изменяется. map() преобразует каждый элемент оди
collectorsfunctional-interface
JuniorJava Developer > Java Core > Строки
Короткий ответ String Pool (пул строк) — специальная область памяти в куче, где JVM хранит строковые литералы, чтобы одинаковые строки не дублировались. Когда код встречает литерал "java", JVM проверяет пул: если такая строка уже есть, переменная получает ссылку на существующий объект, если нет — строка создаётся и помещается в пул. Поэтому сравнение == двух одинаковых литералов даёт true. Как это работает подробнее Создание через new String("java") работает иначе: конструктор всегда создаёт новый объект в обычной части кучи, даже если такая строка уже есть в пуле, поэтому new String("java") == "java" даёт false. Переместить строку в пул можно вручную методом intern(), который возвращает канонический объект из пула. До Java 7 пул находился в PermGen, с Java 7 его перенесли в обычную кучу — теперь сборщик мусора может удалять неиспользуемые интернированные строки. Интернирование литералов — одинаковые литералы по всей программе разделяют один объект и экономят память. new String(...) — всегда отдельный объект
internjava
SeniorJava Developer > Java Core > Core Java
Четыре типа ссылок определяют, при каких условиях сборщик мусора может освободить объект.
Управление памятью
MiddleJava Developer > Java Core > Исключения
Короткий ответ Try-with-resources — форма оператора try из Java 7: ресурсы объявляются в круглых скобках после try и автоматически закрываются по завершении блока — нормальном или с исключением — вызовом close() в порядке, обратном их объявлению. Это работает для любого объекта, реализующего AutoCloseable (или его наследника Closeable). Как это работает подробнее Ресурс объявляется в круглых скобках после try — это может быть любой объект, реализующий java.lang.AutoCloseable (метод close()). Интерфейс java.io.Closeable расширяет AutoCloseable: его метод close() объявлен с throws IOException. В старом стиле try-catch-finally ресурсы закрывались вручную, и при исключениях и в теле try, и в close() они могли «потечь», а исходное исключение — затеряться. В try-with-resources наружу уходит исключение из тела try, а исключения из close() добавляются как подавленные (suppressed) — их можно получить через Throwable.getSuppressed(). Закрытие выполняется в порядке, обратном порядку объявления ресурсов. После блока try-
autocloseableexceptions