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

Java Talks

1 подписчик

3просмотра

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

1просмотр

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

5просмотров

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

1просмотр

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

191просмотр

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

2просмотра

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

6просмотров

💡 Какие состояния есть у потока? Java определяет шесть состояний в Thread.State: • NEW — поток создан, но start() ещё не вызван. • RUNNABLE — поток готов к выполнению или выполняется. Важно: это не значит, что он прямо сейчас на процессоре — он может ждать своей очереди у планировщика. • BLOCKED — поток ждёт освобождения монитора (вход в synchronized блок). • WAITING — поток ждёт бессрочно, пока другой поток не вызовет notify() или notifyAll() (через wait(), join() без таймаута). • TIMED_WAITING — поток ждёт с таймаутом (sleep(ms), wait(ms), join(ms)). • TERMINATED — метод run() завершён. Поток нельзя перезапустить. ✅ Что важно помнить: • BLOCKED и WAITING — принципиально разные состояния. BLOCKED связан с монитором, WAITING — с ожиданием сигнала. • getState() возвращает текущее состояние. • Поток в TERMINATED нельзя запустить заново — только создать новый. ⚠️ Нюанс: RUNNABLE объединяет два подсостояния: «готов к запуску» и «выполняется». Разделить их из Java нельзя, но операционная система это делает.

1просмотр

CompletableFuture.allOf — удобный способ дождаться нескольких асинхронных задач, но если одна из них завершится с ошибкой, поведение может удивить. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не пропустить исключения при использовании allOf — это частая ошибка в асинхронном коде. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает тонкости.

275просмотров

Джун читает весь файл целиком через Files.readAllBytes и получает OutOfMemoryError на больших данных. Сеньор использует буферизованное чтение и обрабатывает файл порциями. Разбираем за 16 секунд. 💡 Почему readAllBytes опасен? Files.readAllBytes загружает всё содержимое файла в массив байт. Если файл больше доступной памяти, приложение падает с OutOfMemoryError. Даже если файл меньше, но таких файлов много, память быстро заканчивается. Для больших файлов нужно использовать потоковое чтение: BufferedReader, Files.newInputStream с буфером, или lines() для текстовых файлов. Это позволяет обрабатывать данные по мере поступления, не загружая всё сразу. ✅ Как правильно? • Для текстовых файлов: Files.newBufferedReader(path).lines() — ленивый стрим строк. • Для бинарных: try (InputStream is = Files.newInputStream(path)) { byte[] buf = new byte[8192]; ... } • Для очень больших: FileChannel с ByteBuffer. ⚠️ Нюанс: Даже Files.readAllLines загружает все строки в память. Для больших файлов используйте BufferedReader.lines() — он читает лениво и не держит весь файл в памяти.

2просмотра

Ссылки на методы — удобная штука, но checked-исключения могут всё испортить. Один метод с throws — и компилятор не пускает. В видео — короткий пример и четыре варианта ответа. Выбери свой, а завтра я покажу правильный разбор. 📌 Сохрани пост, чтобы не спотыкаться о checked-исключения в стримах — это происходит чаще, чем хотелось бы. 💬 Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает ограничения method references.

232просмотра

💡 Почему String.join лучше ручного цикла? String.join — это статический метод класса String, добавленный в Java 8. Он принимает разделитель и коллекцию (или массив) строк, и возвращает единую строку, в которой элементы соединены этим разделителем. Метод сохраняет порядок элементов, а внутри использует StringBuilder, что делает его эффективным. Ручной цикл с конкатенацией не только многословен, но и создаёт лишние промежуточные объекты, если использовать + в цикле. String.join снимает эти проблемы. ✅ Как правильно? • Для склейки коллекции: String.join(", ", list). • Для массива: String.join("-", arr). • Для произвольного набора: String.join(":", "A", "B", "C"). ⚠️ Нюанс: String.join не пропускает null — он превращает его в строку "null". Если нужно игнорировать null-элементы, используйте stream().filter(Objects::nonNull).collect(Collectors.joining(", ")).

4просмотра

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

220просмотров

💡 Почему record неизменяемый? Record (введён в Java 14, стабилизирован в Java 16) спроектирован как носитель неизменяемых данных. Все поля неявно объявлены как private final, поэтому присвоить им новое значение после создания невозможно. Это гарантирует потокобезопасность и предсказуемость. Чтобы "изменить" данные, нужно создать новый объект с обновлёнными значениями. Для этого добавляют методы-хелперы, которые возвращают новый экземпляр, например withAge(int) или withName(String). ✅ Как правильно? Сеньор добавляет в record метод, который возвращает копию с изменённым полем: record Person(String name, int age) { Person withAge(int newAge) { return new Person(name, newAge); } } Теперь обновление выглядит как person.withAge(31), а исходный объект остаётся неизменным. ⚠️ Нюанс: В Java нет встроенного механизма "withers", но вы можете сгенерировать такие методы вручную или через библиотеки (например, Lombok @With). Альтернатива — использовать обычный класс с сеттерами, если изменяемость действительно нужна.

2просмотра

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

138просмотров

💡 Что такое байткод? Байткод — это промежуточный язык инструкций для виртуальной машины Java (JVM). Когда вы запускаете javac, он компилирует исходный код в .class файл, содержащий эти инструкции. В отличие от машинного кода, который привязан к конкретному процессору и ОС, байткод универсален: он работает на любой платформе, где установлена JVM. JVM может интерпретировать байткод или компилировать его в машинный код «на лету» с помощью JIT-компиляции для повышения производительности. ✅ Что важно помнить: • javac создаёт байткод, а не исполняемый файл. • Байткод обеспечивает кроссплатформенность. • JVM выполняет байткод, переводя его в машинный код в рантайме (интерпретация или JIT). ⚠️ Нюанс: Наличие байткода не означает, что Java всегда медленнее нативных языков. Современные JIT-компиляторы могут выдавать очень эффективный машинный код, иногда приближающийся к нативному.

2просмотра

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

198просмотров

Джун автоматически добавляет .boxed() для любых операций с примитивными стримами, создавая кучу объектов Integer. Сеньор знает, что IntStream уже имеет свои методы max(), sum(), average(), и не тратит ресурсы на упаковку. Разбираем за 16 секунд. 💡 Почему boxed() вреден без необходимости? Примитивные стримы (IntStream, LongStream, DoubleStream) оптимизированы для работы с числами без создания объектов-обёрток. Метод boxed() преобразует их в объектный Stream Integer, что приводит к автоупаковке каждого элемента. Это увеличивает потребление памяти и снижает скорость, особенно на больших массивах. Многие операции, такие как поиск минимального/максимального значения, суммы или среднего, можно выполнять напрямую через методы примитивного стрима. ✅ Как правильно? Сеньор работает с IntStream напрямую: • Для максимума: Arrays.stream(arr).max() возвращает OptionalInt. • Для суммы: Arrays.stream(arr).sum(). • Для среднего: Arrays.stream(arr).average(). Используйте boxed() только тогда, когда вам действительно нужен объектный стрим, например, для сбора в List Integer через collect(Collectors.toList()), но даже в этом случае можно избежать упаковки через специальные коллекторы. ⚠️ Нюанс: Если нужно собрать примитивные значения в коллекцию, используйте collect с Collectors.toList() только после boxed(), но помните, что это создаёт объекты. В некоторых случаях лучше работать с примитивными массивами или специализированными коллекциями (например, IntArrayList из библиотек).

5просмотров

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

163просмотра

Что такое CyclicBarrier? CyclicBarrier — это синхронизатор из пакета java.util.concurrent, который позволяет нескольким потокам ждать друг друга в определённой точке выполнения. При создании указывается количество участников (например, 3). Каждый поток, дойдя до барьера, вызывает await() и блокируется. Когда все три потока вызвали await(), барьер срабатывает: выполняется опциональное действие (Runnable), переданное в конструктор, и все потоки одновременно продолжают работу. Барьер циклический: после срабатывания его можно использовать снова для следующего этапа. ✅ Когда использовать? • Для разбиения задачи на этапы, где каждый этап требует полного завершения предыдущего всеми потоками. • Для многопоточных алгоритмов, например, в матричных вычислениях или симуляциях. • Когда нужно, чтобы потоки одновременно стартовали после общей подготовки. ⚠️ Нюанс: Если один из потоков завершился с ошибкой или не дошёл до барьера, остальные потоки, вызвавшие await(), могут блокироваться навсегда. Чтобы избежать зависаний, используйте await(timeout, TimeUnit) с разумным таймаутом. Также барьер можно «сломать» через reset(), но это пробуждает потоки с исключением BrokenBarrierException.

4просмотра

Собираешь стрим в Map через Collectors.toMap и не ожидаешь подвоха? А он есть: дубликаты ключей могут всё сломать. В видео — короткий пример и четыре варианта. Выбери свой, а завтра я покажу правильный разбор. Сохрани пост, чтобы не ловить IllegalStateException в проде — это классическая ошибка при конвертации списков в карты. Напиши в комментариях, какой вариант выбрал — посмотрим, кто знает про merge-функцию.