Java Talks
Иконка канала Java Talks

Java Talks

3 подписчика

8просмотров

💡 Что такое рехеширование? HashMap внутри хранит элементы в массиве бакетов. Когда количество элементов достигает порога capacity * loadFactor (по умолчанию 16 * 0.75 = 12), происходит resize: внутренний массив удваивается, а все элементы пересчитываются и перекладываются. Это O(n) операция, которая вызывает паузы. Фактор загрузки — компромисс между памятью и производительностью: ниже значение → меньше коллизий, но больше памяти; выше → экономия памяти, но чаще resize. ✅ Как правильно? • Если знаешь будущее количество элементов, задавай new HashMap(expectedSize / 0.75f + 1), чтобы избежать resize. • Для большинства случаев дефолтные 16 и 0.75 подходят. • Если критична скорость и не жалко памяти, можно снизить loadFactor до 0.5. ⚠️ Нюанс: Частые ресайзы особенно болезненны при добавлении миллионов элементов. Лучше один раз выделить массив с запасом, чем много раз копировать данные.

1просмотр

Stream.iterate — мощный инструмент, но у него есть тёмная сторона. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не забывать про limit и takeWhile при работе с бесконечными стримами. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает коварство iterate.

256просмотров

Джун вешает @Scheduled(fixedRate = 3600_000) и ждёт, что задача запустится через час. Но Spring запускает её сразу после старта контекста. Приложение ещё не прогрето: пулы потоков не заполнены, соединения с БД не установлены, кэши пустые. Задача либо падает, либо забивает ресурсы в самый неподходящий момент. Сеньор добавляет initialDelay. Разбираем за 16 секунд. 💡 Зачем нужен initialDelay? @Scheduled без initialDelay означает: первый запуск произойдёт немедленно после инициализации планировщика Spring, то есть в первые секунды старта приложения. Это опасно по нескольким причинам: • Прогрев ещё не завершён. Пул соединений с БД может быть пуст, Hibernate ещё не поднял кэши, а @PostConstruct других бинов может не выполниться. • Конкуренция за ресурсы. Старт приложения и так ресурсоёмкий: миграции, загрузка конфигов, warm-up. Дополнительная тяжёлая задача усугубляет. • Каскадные ошибки. Если задача падает на старте, Spring может откатить инициализацию контекста или залогировать ошибку, которая маскирует реальные проблемы. initialDelay задаёт паузу перед первым запуском. Это даёт приложению время прогреться: установить соединения, заполнить кэши, стабилизироваться. Периодичность (fixedRate или fixedDelay) начинает работать только после первой задержки. ⚠️ Нюанс: initialDelay не влияет на fixedRate и fixedDelay — они начинают отсчёт только после первого запуска. Если initialDelay больше периода, первый запуск всё равно произойдёт через initialDelay, а не через период. Также в Spring Boot можно задать глобальную задержку через spring.task.scheduling.initial-delay, но лучше указывать явно на каждом методе — разные задачи требуют разного времени прогрева.

3просмотра

OutOfMemoryError — это Error, а не Exception. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы понимать разницу между Error и Exception — это важно для написания надёжного кода. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает про Throwable.

143просмотра

💡 Что такое ExecutorService? ExecutorService — это интерфейс из java.util.concurrent, который представляет пул потоков. Вместо того чтобы создавать Thread вручную, ты отправляешь задачи в пул, а он распределяет их между потоками. Потоки переиспользуются, что экономит ресурсы. Пул можно создать через фабрики Executors. По умолчанию задачи ставятся в очередь, если все потоки заняты. ✅ Когда использовать? • Когда есть много независимых задач (обработка запросов, вычисления). • Когда нужно ограничить количество одновременно работающих потоков. • Когда нужен результат выполнения задачи (Future). ⚠️ Нюанс: ExecutorService не завершается автоматически. Если не вызвать shutdown() или shutdownNow(), приложение может не остановиться. shutdown() перестаёт принимать новые задачи и ждёт завершения текущих, shutdownNow() пытается прервать выполняющиеся задачи. Также не забывай про awaitTermination, если нужно дождаться полного завершения пула.

4просмотра

Лямбды реализуют функциональные интерфейсы, и не все исключения можно просто так пробросить. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не ловить ошибки компиляции при работе со стримами и checked-исключениями. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает ограничения лямбд.

257просмотров

Джун тащит HttpServletRequest в метод и парсит заголовок через getHeader. Сеньор ставит @RequestHeader на параметр — Spring делает всё сам. Разбираем за 16 секунд. 💡 Почему @RequestHeader лучше? @RequestHeader — это аннотация Spring MVC, которая связывает значение HTTP-заголовка с параметром метода контроллера. Spring сам достаёт заголовок, конвертирует в нужный тип (String, int, enum, UUID, дата и т.д.) и, если заголовка нет, возвращает 400 Bad Request — без ручных проверок на null. Преимущества: • Меньше кода: одна аннотация вместо ручного парсинга и null-проверок. • Типобезопасность: Spring сконвертирует значение в нужный тип. • Слабая связанность: метод контроллера не зависит от HttpServletRequest, его проще тестировать. • Валидация: можно совместить с @Validated, @NotNull, @Pattern. ⚠️ Нюанс: Для нескольких заголовков одного имени используйте @RequestHeader MultiValueMap headers или HttpHeaders. А для заголовков, которые часто используются (например, Authorization), Spring Security даёт готовые механизмы — не парсите его руками.

1просмотр

try без catch, только finally. Внутри бросается исключение. Многие думают, что finally его «съест». Но Java так не работает. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не терять исключения из-за finally — это классическая ловушка. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает порядок выполнения.

4просмотра

💡 Что такое CopyOnWriteArrayList? CopyOnWriteArrayList — потокобезопасная реализация List из пакета java.util.concurrent. Его главная особенность: при каждой модификации (add, set, remove) создаётся новая копия внутреннего массива. Старый массив остаётся неизменным, и все текущие итераторы продолжают работать с ним. Это даёт fail-safe поведение: изменение списка во время итерации не вызывает ConcurrentModificationException. Чтение (get, итерация) работает без блокировок и очень быстро — массив не меняется, пока не произойдёт запись. Запись, наоборот, дорогая: копирование всего массива занимает O(n) и создаёт нагрузку на память. Итератор работает со snapshot и не видит элементов, добавленных после его создания — чтобы получить актуальные данные, нужно получить новый итератор. ✅ Когда использовать? • Много чтений, мало записей. • Список слушателей событий, подписчиков, обработчиков. • Кэш конфигураций, который обновляется редко. • Ситуации, когда итерация не должна прерываться при модификации. ⚠️ Нюанс: Не используй CopyOnWriteArrayList при частых записях — копирование массива на каждый add убьёт производительность. Для частых изменений подойдут ConcurrentLinkedQueue, ConcurrentHashMap.newKeySet() или Collections.synchronizedList(new ArrayList()). Также помни, что fail-safe итератор — это не «магическая защита»: если логика зависит от актуальных данных, snapshot может ввести в заблуждение.

4просмотра

Pattern matching для instanceof появился в Java 16. Как он ведёт себя с null? Многие ждут NPE, но Java удивляет. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не бояться instanceof с null — этот паттерн безопасен. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает про pattern matching.

300просмотров

Джун загружает 100 пользователей и их заказы. Hibernate делает 101 запрос. Сеньор ставит @BatchSize(size = 50) и получает 3 запроса. 💡 Что такое N+1 и как @BatchSize его лечит? Когда ты загружаешь список сущностей, а у каждой есть ленивая коллекция, Hibernate по умолчанию идёт в базу за каждой коллекцией отдельно. Это N+1: один запрос на список + N запросов на каждую коллекцию. @BatchSize(size = N) говорит Hibernate: «Когда один из пользователей запросит свои заказы, подгрузи заказы сразу для N пользователей одним запросом». То есть вместо 100 отдельных SELECT-ов уйдёт 2 пачки по 50. Плюс один изначальный — итого 3. Настройка действует на уровне коллекции (или на уровне класса для сущностей). ⚠️ Нюанс: @BatchSize не заменяет JOIN FETCH. Если данные нужны сразу в одном запросе — используй JOIN FETCH или @EntityGraph. @BatchSize — когда коллекции нужны лениво и хочется избежать N+1 без дублирования строк. Комбинация обоих подходов даёт лучший результат.

2просмотра

Параллельный стрим обычно не гарантирует порядок. Но что, если использовать forEachOrdered? В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не путать forEach и forEachOrdered — это важно при параллельной обработке. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает тонкости Stream API.

4просмотра

💡 В чём разница? throw — это оператор, который создаёт и выбрасывает объект исключения. Например, throw new IOException("..."). throws — это ключевое слово в сигнатуре метода, которое объявляет, что метод может выбросить одно или несколько checked-исключений. Например, void readFile() throws IOException. Оно информирует вызывающий код о необходимости обработки (try-catch или проброс выше). Для unchecked-исключений (наследников RuntimeException) throws не обязателен, потому что компилятор не требует их обработки. ✅ Как правильно? • Бросаешь исключение в теле метода? Используй throw. • Метод может выбросить checked-исключение? Объяви через throws. • Для unchecked throws опционален, но может быть добавлен для документирования. ⚠️ Нюанс: Один метод может объявлять несколько исключений: throws IOException, SQLException. Если метод переопределяет метод родителя, он не может объявлять более широкие checked-исключения, чем родительский.

2просмотра

Константный hashCode не сломает HashMap сразу, но заставит её работать как LinkedList. Сколько элементов сохранится и почему это плохо? В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не убить производительность своих мап — это классическая ловушка. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает про коллизии.

137просмотров

Джун блокирует поток через Thread.sleep для каждой отложенной задачи. Сеньор использует DelayQueue — задачи ждут своего времени, не мешая другим. Разбираем за 16 секунд. 💡 Почему sleep — плохо для отложенных задач? Thread.sleep(ms) блокирует весь поток на указанное время. Если нужно выполнить 10 задач с задержкой в 5 секунд, получишь 50 секунд полного простоя. Поток не может делать ничего другого. Кроме того, sleep не даёт точного порядка: задачи выполняются строго по очереди, даже если их время пришло одновременно. DelayQueue — это очередь, где каждый элемент реализует интерфейс Delayed с методом getDelay(). Поток вызывает take() и блокируется только до момента, когда первый элемент станет готов. Если элементы ещё не готовы, поток ждёт, но как только первый «созрел» — получает его. Одновременно готовые задачи извлекаются в порядке getDelay(). Это эффективно и не блокирует поток зря. ⚠️ Нюанс: DelayQueue не гарантирует порядок для элементов с одинаковым временем — только по getDelay(). Если нужна строгая последовательность, добавьте счётчик или используйте PriorityQueue с Delayed.

1просмотр

Массивы в Java ведут себя не так, как дженерики. Одно безобидное присваивание может уронить программу в рантайме. В видео — короткий пример и четыре варианта ответа. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не попасться на ArrayStoreException — это частая проблема при рефакторинге и наследовании. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает про ковариантность.

125просмотров

💡 Что такое Serial GC? Serial GC — это самый базовый сборщик мусора в HotSpot JVM. Он выполняет сборку мусора в одном потоке, что означает полную остановку всех потоков приложения (stop-the-world) на время очистки. Отсутствие многопоточности уменьшает накладные расходы на синхронизацию и делает его эффективным для небольших куч (примерно до 100-200 МБ). Включается флагом -XX:+UseSerialGC. ✅ Когда использовать? • Маленькие клиентские приложения, где куча невелика. • Встраиваемые системы, микроконтроллеры (если Java там вообще используется). • Контейнеры с жёсткими ограничениями памяти и CPU. • Тестирование или простые скрипты, где паузы не важны. ⚠️ Нюанс: Для серверных приложений с большой кучей и многопоточностью Serial GC не подходит: паузы будут слишком длинными. В таких случаях выбирают Parallel GC, G1, ZGC или Shenandoah, которые распределяют работу между потоками или делают паузы минимальными.

2просмотра

PriorityQueue — отличный выбор для очереди с приоритетом, но есть нюанс с null. Один вызов add может уронить программу. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не наступить на NPE при работе с очередями — это случается чаще, чем хотелось бы. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает ограничения коллекций.

192просмотра

Джун создаёт новый BigInteger для единицы каждый раз, когда она нужна. Сеньор использует константу BigInteger.ONE. Разбираем за 16 секунд. 💡 Почему new BigInteger("1") — плохо? BigInteger — неизменяемый класс. Это значит, что объект BigInteger.ONE можно безопасно переиспользовать в любом месте, где нужна единица. Создание нового объекта через new BigInteger("1") каждый раз приводит к лишним аллокациям, особенно в циклах или часто вызываемых методах. В больших системах это увеличивает нагрузку на сборщик мусора. Кроме того, BigInteger.ONE — это стандартная константа, которая делает код понятнее. ✅ Как правильно? Используй готовые константы: • BigInteger.ZERO • BigInteger.ONE • BigInteger.TEN Они создаются при загрузке класса и доступны везде.

3просмотра

remove в ArrayList перегружен: один метод принимает int, другой Object. Когда в списке Integer, вызов remove(1) может удалить не то, что вы ожидали. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не путать удаление по индексу и по значению — это классическая ловушка с автоупаковкой. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает приоритет сигнатур.