Джун вызывает Stream.iterate и его приложение намертво зависает. Он не учёл, что Stream.iterate генерирует бесконечный поток, пока кто-то его не остановит. Сеньор знает: iterate нужно ограничивать всегда.
Почему так происходит?
Stream.iterate (с двумя аргументами, как в Java 8) создаёт последовательность, применяя функцию к предыдущему элементу. Если не задать предела, стрим будет бесконечно порождать новые элементы. Терминальная операция вроде forEach пытается обработать все элементы, что никогда не закончится. В Java 9 появился перегруженный iterate с предикатом-ограничителем, который останавливает генерацию при выполнении условия.
⚠️ Нюанс: takeWhile тоже может остановить стрим, но он работает с уже сгенерированными элементами, а не с условием генерации. Если поток бесконечный, takeWhile без прерывания не поможет.
Ключевое слово volatile. Этот пример с двумя потоками и счётчиком вскрывает опасное заблуждение. Выбери один из четырёх вариантов, а завтра я выложу правильный разбор.
Сохрани пост, чтобы не ошибиться в реальном проекте.
Напиши в комментариях свой вариант — интересно, сколько разрабов попадётся в эту ловушку.
Почему String.format лучше +?
Конкатенация через + заставляет смешивать текст и переменные, превращая код в нечитаемую кашу. String.format разделяет шаблон и данные: все неизменные части строки лежат в одном месте, а переменные подставляются через %s, %d, %f и другие спецификаторы. Это упрощает чтение, редактирование и локализацию — достаточно заменить шаблон, не трогая логику подстановки переменных. Кроме того, можно задать формат чисел и дат.
✅ Как правильно?
• Сложные сообщения, логи, UI-тексты → String.format("Привет, %s! Баланс: %.2f руб.", name, balance).
• Простая склейка двух частей → "Имя: " + name.
• Локализация с множественным числом → MessageFormat.format("Найдено {0} товаров", count).
⚠️ Нюанс: String.format использует java.util.Formatter и поддерживает множество спецификаторов для чисел, дат, флагов и ширины. Если нужно форматировать с учётом локали (например, запятая вместо точки), передайте Locale первым аргументом.
Блок finally славен тем, что выполняется всегда. В видео — короткий пример и четыре варианта ответа. Выбери свой, а завтра я покажу правильный разбор.
Сохрани пост, чтобы не попасться на этом на собеседовании.
Напиши в комментариях, какой вариант выбрал — интересно узнать, кто помнит нюанс.
Джун проверяет тип через instanceof и следом выполняет ручное приведение. Код работает, но он многословен и содержит дублирование: тип String упоминается трижды в трёх строках. Сеньор использует pattern matching из Java 16+ и делает всё в одной строке — без лишнего шума.
Почему это круто?
Pattern matching for instanceof (JEP 394) позволяет объявить переменную прямо в условии. Если проверка проходит, переменная автоматически получает значение с нужным типом. Это сокращает код, убирает потенциальные ошибки при касте и улучшает читаемость. Переменная видна только внутри блока if, что ограничивает её область видимости.
⚠️ Нюанс: Pattern matching работает и в условиях ||, но осторожно: if (obj instanceof String s || s.length() = 5) не скомпилируется, потому что во второй части s может быть не определена.
Удаление элемента из коллекции прямо в for-each — одна из самых частых ошибок новичков. Но поведение может быть не таким очевидным, как кажется. В видео — короткий пример и четыре варианта ответа. Выбери свой, а завтра я покажу правильный разб ор.
Сохрани пост, чтобы не попасться на этом в реальном коде.
Напиши в комментариях, какой вариант выбрал — интересно узнать, кто знает нюанс.
Почему enum мощнее, чем кажется?
Каждая константа enum — это public static final экземпляр самого enum-класса. Компилятор автоматически делает его финальным, невидимо наследует от java.lang.Enum и гарантирует, что других экземпляров не будет. Но главное — вы можете добавить собственные поля, конструкторы и методы. Более того, можно объявить абстрактный метод, и тогда каждая константа обязана будет предоставить свою реализацию прямо в фигурных скобках — идеально для паттернов «Стратегия» или «Команда» в рамках ограниченного набора вариантов.
✅ Как правильно?
• Используй enum, если у тебя фиксированный набор значений (дни недели, статусы заказа, операции).
• Добавляй поля и методы, если каждой константе нужно хранить данные или поведение.
• Используй абстрактные методы в enum, чтобы заставить каждую константу реализовать свою логику.
⚠️ Нюанс: enum не может наследовать другой класс. Зато они сериализуемы «из коробки» и устойчивы к рефлексии, поэтому идеальны для Singleton.
Деление на ноль в Java ведёт себя совершенно по-разному для целых и вещественных чисел. Этот нюанс часто упускают, пока не столкнутся на собеседовании или в реальном баге. В видео — короткий пример и четыре вариан та ответа. Выбери свой, а правильный разбор выйдет завтра.
Сохрани пост — пригодится, когда меньше всего ждёшь.
Напиши в комментариях, какой вариант выбрал — интересно посмотреть, кто попадётся.
Джун пишет logger.info("value: " + expensiveOp()) и не понимает, почему приложение тормозит, даже когда уровень INFO выключен. Сеньор знает, что конкатенация происходит до вызова метода, поэтому дорогая операция выполняется в любом случае. Он использует ленивое логирование.
Почему конкатенация вычисляется всегда?
В Java аргументы метода вычисляются до того, как метод будет вызван. expensiveOp() выполнится, чтобы получить строку, которая передаётся в logger.info(). Даже если логгер настроен на WARN и INFO-сообщения не пишутся, операция уже совершена.
⚠️ Нюанс: Плейсхолдеры {} в SLF4J (logger.debug("value: {}", expensiveOp())) не ленивы — аргумент всё равно вычисляется до вызова. Для ленивого вычисления нужно использовать проверку isEnabled или лямбду-супплера.
Перегрузка методов — базовая тема, но передача null способна запутать даже опытных. В видео — короткий код и четыре варианта ответа. Выбери тот, который считаешь верным, а правильный разб ор появится завтра.
Сохрани пост, чтобы вспомнить этот нюанс перед собеседованием.
Напиши в комментариях свой вариант — интересно сравнить догадки
Почему try-with-resources?
Ресурсы (потоки, соединения, блокировки) должны закрываться в любом случае, иначе возможны утечки памяти и файловых дескрипторов. Ручное закрытие требует finally и проверок на null, а исключение при close() может перезаписать оригинальную ошибку. try-with-resources решает эти проблемы: он гарантированно вызывает close() у всех объявленных в скобках объектов (реализующих AutoCloseable), даже если в блоке try произошло исключение. Более того, подавленные исключения от close() не теряются и доступны через getSuppressed().
⚠️ Нюанс: Ресурсы должны быть final или effectively final в блоке try. Если нужно управлять закрытием вручную, можно использовать try-с-ресурсами с уже существующей переменной, но лучше объявлять их прямо в заголовке.
Блок finally выполняется всегда, но влияет ли он на уже готовый return? Этот пример встречается на собеседованиях и ставит в тупик даже опытных. Выбери один из четырёх вариантов, а завтра я разберу правильный ответ.
Сохрани пост, чтобы не забыть эту тонкость.
Напиши в комментариях свой вариант — интересно, сколько человек попадётся.
Почему EnumMap быстрее и компактнее?
EnumMap специально спроектирован для ключей типа enum. Внутри он использует массив, размер которого равен количеству значений enum. Индекс в этом массиве определяется ординалом (ordinal()) enum-константы, поэтому доступ O(1), и не нужно вычислять хеш и разрешать коллизии. Это экономит память и увеличивает скорость по сравнению с HashMap, который хранит узлы с хешами и указателями.
⚠️ Нюанс: EnumMap не может использовать null в качестве ключа — при попытке добавить null выбрасывается NullPointerException. Это плата за компактность.
Что такое интерфейс?
Интерфейс в Java — это ссылочный тип, который определяет «контракт»: набор методов (сигнатур), которые класс обязан реализовать. До Java 8 интерфейсы не могли содержать код, но сейчас могут иметь default-методы с реализацией и статические методы. Интерфейс позволяет достичь абстракции и слабой связанности: код зависит от контракта, а не от конкретной реализации.
✅ Ключевые идеи:
• Контракт: интерфейс говорит «что нужно сделать», класс — «как это сделать».
• Множественная реализация: класс может реализовать несколько интерфейсов, обходя ограничение на множественное наследование классов.
• Функциональные интерфейсы: если в интерфейсе один абстрактный метод, его можно использовать с лямбда-выражениями (Runnable, Comparator, Function).
• Default-методы: позволяют добавлять новую функциональность в интерфейсы без поломки существующих реализаций (Java 8+).
⚠️ Нюанс: Интерфейс не может иметь состояние (поля, кроме статических констант) и конструкторы. Для общего состояния и частичной реализации используй абстрактный класс.
Казалось бы, простой пример с ++, но на собеседованиях на нём спотыкаются даже опытные разработчики. В видео — короткий сниппет и четыре варианта ответа. Выбери тот, который считаешь верным, а правильный разбор появится завтра.
Сохрани пост, чтобы не забыть этот нюанс.
Напиши в комме нтариях свой вариант — интересно сравнить ответы.
Джун вызывает save(), идёт проверять данные в базе — и удивляется, что их там нет. Он думает, что save() немедленно отправляет SQL в базу, но это не так. Данные теряются для других запросов, пока транзакция не завершится.
Почему так происходит?
save() не выполняет немедленную вставку. Он только переводит сущность в управляемое состояние (persist) внутри текущего persistence context. Фактический SQL-запрос (INSERT или UPDATE) выполняется либо при вызове flush(), либо автоматически при коммите транзакции. Пока транзакция открыта, другие транзакции (в зависимости от уровня изоляции) могут не видеть изменения.
✅ Как правильно?
Сеньор знает, когда нужны данные в БД немедленно. Если действительно необходимо отправить SQL прямо сейчас (например, чтобы получить сгенерированный ID или чтобы другие транзакции увидели изменения), он вызывает flush().
⚠️ Нюанс: flush() не коммитит транзакцию! Данные станут видны другим транзакциям только после коммита. Исключение — если в БД настроен уровень изоляции READ_UNCOMMITTED.
Одна из первых ловушек, которую ловят Java-джуны на String Pool.
Пиши свой ответ в комментах, чуть позже я опубликую пояснение
Отметь друга-джависта
#java #javadeveloper #javacode
В чём разница?
String — неизменяемый (immutable). Каждая операция конкатенации через + или concat() создаёт новый объект в памяти. В цикле это превращается в тысячи ненужных объектов и падение производительности.
StringBuilder — изменяемый (mutable). Он работает с одним внутренним буфером, который расширяется при необходимости. Не синхронизирован, поэтому быстрее в однопоточной среде.
StringBuffer — полный аналог StringBuilder, но все его методы синхронизированы. Это даёт потокобезопасность ценой скорости.
✅ Правила:
• Одиночная конкатенация ("a" + "b") — используй +.
• Циклы и однопоточка — только StringBuilder.
• Многопоточка — StringBuffer, но лучше рассмотреть StringBuilder с внешней синхронизацией.
⚠️ Нюанс: Компилятор Java оптимизирует простые конкатенации через +, заменяя их на StringBuilder под капотом. Но в цикле эта оптимизация не спасает.
Джун запускает CompletableFuture.runAsync() с блокирующей операцией (например, Thread.sleep()), не указывая исполнитель. Вся JVM начинает тормозить, потому что общий ForkJoinPool перегружен и не может обслуживать другие задачи.
Почему так происходит?
CompletableFuture без явного Executor использует общий ForkJoinPool.commonPool(). Он рассчитан на неблокирующие CPU-bound задачи. Блокирующая операция (сон, сетевые вызовы, работа с БД) занимает поток из этого пула, и другим задачам (включая внутренние операции JVM) может не хватить ресурсов.
⚠️ Нюанс: Для I/O-интенсивных операций лучше использовать Executors.newCachedThreadPool() или настроить пул динамически. Главное правило: никогда не блокируйте общий ForkJoinPool — это может привести к deadlock'ам.
#джава #javaprogramming
Джун хранит даты в Date или LocalDateTime и теряет часовой пояс. Сеньор использует ZonedDateTime — класс, который знает всё о временных зонах и летнем времени.
Зачем нужен ZonedDateTime?
LocalDateTime хранит только дату и время без привязки к поясу — это как циферблат, у которого не указано, в каком городе он висит. ZonedDateTime добавляет к этому зону (ZoneId), а значит, понимает, что такое UTC+3, переход на летнее время и как корректно сравнивать моменты в разных точках мира. Без него невозможно правильно посчитать время стыковки рейсов или отправить уведомление в нужный час.
✅ Правило от сеньора:
• Встречи, рейсы, события с временем и поясом → ZonedDateTime.
• Только дата (день рождения) → LocalDate.
• Дата и время без привязки к поясу (локальный будильник) → LocalDateTime.
• Момент в UTC для логов и меток → Instant.
⚠️ Нюанс: ZonedDateTime.now() без аргументов использует системный часовой пояс сервера. В распределённых системах лучше всегда явно указывать ZoneId, чтобы избежать сюрпризов.
#джава #javatutorial
