Джун создаёт новый массив и вручную перекладывает элементы в цикле — лишний код, который можно заменить одной строкой. Сеньор знает про Arrays.copyOf и System.arraycopy — быстрые и нативные способы копирования.
Почему ручной цикл хуже?
Ручное копирование массива через for понятно, но многословно и легко приводит к ошибкам (например, неправильная длина или выход за границы). System.arraycopy — это нативный метод JVM, который копирует блок памяти максимально эффективно. Arrays.copyOf — обёртка над System.arraycopy, которая дополнительно создаёт новый массив нужной длины и может усекать или расширять его. Использование встроенных методов не только короче, но и потенциально быстрее за счёт оптимизаций JIT и низкоуровневых инструкций.
✅ Как правильно?
Сеньор выбирает подходящий метод:
• Простое копирование всего массива → Arrays.copyOf(source, source.length).
• Копирование части массива → Arrays.copyOfRange(source, from, to).
• Копирование с вставкой в существующий массив → System.arraycopy(source, srcPos, dest, destPos, length).
⚠️ Нюанс: Arrays.copyOf возвращает новый массив, а System.arraycopy требует, чтобы массив назначения уже был создан. Выбирайте в зависимости от задачи.
Ключевое слово final часто понимают слишком широко, путая ссылку и сам объект. Этот простой пример развенчивает популярный миф. В видео — короткий сниппет и четыре варианта ответа. Выбери свой, а завтра я покажу правильный разбор.
Сохрани пост, чтобы не путаться в базовых вещах.
Напиши в комментариях, какой вариант выбрал — интересно, кто помнит нюанс.
Почему parallelStream обычно не нужен?
parallelStream работает на общем ForkJoinPool, который рассчитан на неблокирующие CPU-задачи. Если вы вызываете parallelStream для I/O или Thread.sleep, вы рискуете заблокировать потоки, на которых работают другие параллельные стримы, включая внутренние операции JVM. Кроме того, разделение данных и сборка результата требуют ресурсов, и на небольших объёмах (менее десятков тысяч элементов) эти накладные расходы съедают весь выигрыш. На практике parallelStream оправдан только для вычислительно тяжёлых операций на очень больших коллекциях, да и то с обязательными замерами.
✅ Как правильно?
Сеньор применяет parallelStream только когда:
• Данных много и операция CPU-тяжёлая.
• Нет I/O и блокировок.
• Использует собственный ForkJoinPool, чтобы изолировать задачи.
• Замеряет время до и после — и часто убеждается, что обычный stream быстрее.
⚠️ Нюанс: Если вы всё же используете parallelStream, никогда не полагайтесь на общий пул. Передавайте свой ForkJoinPool и контролируйте количество потоков.
Массивы в Java — это объекты, и сравнение через == таит в себе подвох, о котором многие забывают после первых лекций. В видео — простой пример и четыре варианта ответа. Выбери свой, а завтра выйдет правильный разбор.
Сохрани пост, чтобы не путаться в будущем.
Напиши в комментариях, какой вариант выбрал — интересно увидеть, кто помнит нюанс.
Джун вставляет System.gc() в критичных местах, искренне веря, что это спасёт от OutOfMemoryError. Приложение всё равно падает. Сеньор знает: это не решение, а отчаянный жест.
Почему System.gc() не работает?
System.gc() — это всего лишь рекомендация JVM запустить сборку мусора. Виртуальная машина может её проигнорировать, особенно если используется -XX:+DisableExplicitGC. Даже если сборщик отработает, он не исправит утечку памяти — объекты, которые всё ещё достижимы (например, из-за забытых ссылок в коллекциях или кэшах), не будут удалены. Вызов System.gc() в продакшене часто только замедляет приложение, вызывая полную остановку мира (Full GC), но не решает корневую проблему.
✅ Как правильно?
Сеньор действует системно:
1. Анализирует дамп кучи (VisualVM, Eclipse MAT) в момент утечки, чтобы понять, какие объекты заполнили память.
2. Настраивает сборщик мусора через JVM-параметры (-XX:+UseG1GC, -XX:MaxGCPauseMillis=200), а не полагается на явные вызовы.
3. Ищет логические ошибки: удерживаемые ссылки, незакрытые ресурсы, бесконтрольно растущие кэши.
⚠️ Нюанс: Единственный оправданный сценарий для System.gc() — это бенчмарки или тесты, где нужно измерить чистое время работы алгоритма без влияния случайных сборок мусора. В продакшене — никогда.
BigDecimal — стандарт для точных вычислений, но в нём кроется тонкость, которую часто упускают даже опытные разработчики. В видео — короткий пример и четыре варианта ответа. Выбери свой вариант, а завтра я выложу правильный разбор.
Сохрани пост, чтобы не забыть этот нюанс.
Напиши в комментариях, какой вариант выбрал — интересно увидеть статистику.
Что такое пул строк и зачем он нужен?
Пул строк (String Pool) — это область в куче (начиная с Java 7), где хранятся уникальные строковые литералы. Когда вы пишете "hello", JVM автоматически помещает эту строку в пул. Когда другой код пишет "hello", он получает ту же ссылку из пула. Это экономит память и ускоряет сравнение через ==. Но new String("hello") создаёт новый объект в куче, минуя пул. intern() заставляет строку провериться в пуле: если такая уже есть — возвращает её, если нет — помещает в пул и возвращает ссылку.
✅ Как правильно?
• Используйте intern() для долгоживущих, часто повторяющихся строк (например, ключи из БД, enum-значения), чтобы экономить память и сравнивать через ==.
• Не интернируйте всё подряд — пул имеет ограниченный размер, и его переполнение замедлит приложение.
• Для обычного сравнения строк всегда используйте equals().
⚠️ Нюанс: intern() не изменяет исходную ссылку! Обязательно присваивайте результат: s = s.intern(). Иначе вы продолжите работать с объектом в куче.
Сравнение enum через == и equals() — практически близнецы, но есть один коварный случай, где они ведут себя по-разному. В видео — короткий пример и четыре варианта ответа. Выбери свой, а завтра я покажу правильный разбор.
Сохрани пост, чтобы не забыть этот нюанс — он пригодится на каждом втором код-ревью.
Напиши в комментариях, какой вариант выбрал — интересно, кто в курсе тонкости.
Джун вызывает 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? Этот пример встречается на собеседованиях и ставит в тупик даже опытных. Выбери один из четырёх вариантов, а завтра я разберу правильный ответ.
Сохрани пост, чтобы не забыть эту тонкость.
Напиши в комментариях свой вариант — интересно, сколько человек попадётся.
