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

Java Talks

1 подписчик

1просмотр

Джун вызывает Stream.iterate и его приложение намертво зависает. Он не учёл, что Stream.iterate генерирует бесконечный поток, пока кто-то его не остановит. Сеньор знает: iterate нужно ограничивать всегда. Почему так происходит? Stream.iterate (с двумя аргументами, как в Java 8) создаёт последовательность, применяя функцию к предыдущему элементу. Если не задать предела, стрим будет бесконечно порождать новые элементы. Терминальная операция вроде forEach пытается обработать все элементы, что никогда не закончится. В Java 9 появился перегруженный iterate с предикатом-ограничителем, который останавливает генерацию при выполнении условия. ⚠️ Нюанс: takeWhile тоже может остановить стрим, но он работает с уже сгенерированными элементами, а не с условием генерации. Если поток бесконечный, takeWhile без прерывания не поможет.

1просмотр

Ключевое слово 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 славен тем, что выполняется всегда. В видео — короткий пример и четыре варианта ответа. Выбери свой, а завтра я покажу правильный разбор. Сохрани пост, чтобы не попасться на этом на собеседовании. Напиши в комментариях, какой вариант выбрал — интересно узнать, кто помнит нюанс.

2просмотра

Джун проверяет тип через 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.

2просмотра

Деление на ноль в Java ведёт себя совершенно по-разному для целых и вещественных чисел. Этот нюанс часто упускают, пока не столкнутся на собеседовании или в реальном баге. В видео — короткий пример и четыре варианта ответа. Выбери свой, а правильный разбор выйдет завтра. Сохрани пост — пригодится, когда меньше всего ждёшь. Напиши в комментариях, какой вариант выбрал — интересно посмотреть, кто попадётся.

1просмотр

Джун пишет logger.info("value: " + expensiveOp()) и не понимает, почему приложение тормозит, даже когда уровень INFO выключен. Сеньор знает, что конкатенация происходит до вызова метода, поэтому дорогая операция выполняется в любом случае. Он использует ленивое логирование. Почему конкатенация вычисляется всегда? В Java аргументы метода вычисляются до того, как метод будет вызван. expensiveOp() выполнится, чтобы получить строку, которая передаётся в logger.info(). Даже если логгер настроен на WARN и INFO-сообщения не пишутся, операция уже совершена. ⚠️ Нюанс: Плейсхолдеры {} в SLF4J (logger.debug("value: {}", expensiveOp())) не ленивы — аргумент всё равно вычисляется до вызова. Для ленивого вычисления нужно использовать проверку isEnabled или лямбду-супплера.

2просмотра

Перегрузка методов — базовая тема, но передача null способна запутать даже опытных. В видео — короткий код и четыре варианта ответа. Выбери тот, который считаешь верным, а правильный разбор появится завтра. Сохрани пост, чтобы вспомнить этот нюанс перед собеседованием. Напиши в комментариях свой вариант — интересно сравнить догадки
Почему try-with-resources? Ресурсы (потоки, соединения, блокировки) должны закрываться в любом случае, иначе возможны утечки памяти и файловых дескрипторов. Ручное закрытие требует finally и проверок на null, а исключение при close() может перезаписать оригинальную ошибку. try-with-resources решает эти проблемы: он гарантированно вызывает close() у всех объявленных в скобках объектов (реализующих AutoCloseable), даже если в блоке try произошло исключение. Более того, подавленные исключения от close() не теряются и доступны через getSuppressed(). ⚠️ Нюанс: Ресурсы должны быть final или effectively final в блоке try. Если нужно управлять закрытием вручную, можно использовать try-с-ресурсами с уже существующей переменной, но лучше объявлять их прямо в заголовке.

9просмотров

Блок 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.

1просмотр

Одна из первых ловушек, которую ловят Java-джуны на String Pool. Пиши свой ответ в комментах, чуть позже я опубликую пояснение Отметь друга-джависта #java #javadeveloper #javacode
В чём разница? String — неизменяемый (immutable). Каждая операция конкатенации через + или concat() создаёт новый объект в памяти. В цикле это превращается в тысячи ненужных объектов и падение производительности. StringBuilder — изменяемый (mutable). Он работает с одним внутренним буфером, который расширяется при необходимости. Не синхронизирован, поэтому быстрее в однопоточной среде. StringBuffer — полный аналог StringBuilder, но все его методы синхронизированы. Это даёт потокобезопасность ценой скорости. ✅ Правила: • Одиночная конкатенация ("a" + "b") — используй +. • Циклы и однопоточка — только StringBuilder. • Многопоточка — StringBuffer, но лучше рассмотреть StringBuilder с внешней синхронизацией. ⚠️ Нюанс: Компилятор Java оптимизирует простые конкатенации через +, заменяя их на StringBuilder под капотом. Но в цикле эта оптимизация не спасает.

1просмотр

Джун запускает 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