Java | Фишки и трюки
رفتن به کانال در Telegram
Java: примеры кода, интересные фишки и полезные трюки Купить рекламу: https://telega.in/c/java_tips_and_tricks ✍️По всем вопросам: @Pascal4eg Менеджер по рекламе: @shmyzna
نمایش بیشتر6 963
مشترکین
اطلاعاتی وجود ندارد24 ساعت
+537 روز
+1430 روز
آرشیو پست ها
6 964
JVM-инженеры, общий сбор: 28 марта Яндекс проведет Я.Субботник по JVM-языкам
Это митап для разработчиков, которые работают с языками Java Virtual Machine. Встреча пройдет в московском офисе Яндекса и в онлайн. Будем обсуждать новые подходы, делиться практическим опытом и нетворкать в неформальной обстановке.
В программе — доклады про ускорение Java-фреймворков на примере Quarkus, устройство трассирующего профайлера (Qubership Profiler), реальные подводные камни java.time и оптимизация GraphQL API на базе GraphQL Java и Spring Framework. А еще круглые столы, активность от Яндекс Вертикалей и афтерпати.
Подать заявку на митап можно здесь.
6 964
🕳 NPE без номера строки: расследование анонимного убийства
Бывает: приходит исключение, а в stacktrace — только имя класса и метода. Ни номера строки, ни намёка, где именно случился
NullPointerException. Код молчит, логи не помогают, а прод падает. Это как найти труп без документов — придётся вызывать детектива.
Почему так происходит и как вычислить убийцу — разбираемся 👇
📉 Почему пропадает номер строки?
В Java номера строк хранятся в специальном атрибуте LineNumberTable внутри .class-файла. Если при компиляции отключить отладочную информацию (флаг -g:none), атрибут не создаётся. Также строки могут отсутствовать у динамически генерируемых классов — лямбд, прокси, байткод-библиотек.
// Пример с лямбдой (частая причина)
List<String> list = null;
list.stream().forEach(s -> System.out.println(s.length()));
// NPE здесь, но стек покажет lambda$... без строки
➡️ Код скомпилирован, лямбда превращена в анонимный класс — отладочной информации в нём нет. Получаем глухой стек.
🔍 Методы расследования
1. Изучи имя метода в stacktrace
Если фрейм называется lambda$process$1, значит, беда в первой или второй лямбде внутри метода process. Посмотри на все лямбды в этом методе и проанализируй, что могло быть null.
2. Логируй входные данные перед подозрительным местом
log.debug("Processing order: order={}, customer={}", order, order.getCustomer());
order.getCustomer().applyDiscount(); // потенциальный NPE
➡️ Если после этого лога NPE повторится, ты увидишь последние успешные значения.
3. Используй `Objects.requireNonNull` для явной проверки
public void process(Order order) {
Objects.requireNonNull(order, "order must not be null");
Objects.requireNonNull(order.getCustomer(), "customer must not be null");
// ...
}
➡️ Такой код кинет NPE с понятным сообщением и, если повезёт, с номером строки. А если нет — хотя бы будет текст.
4. Воспроизведи локально с дебагом
Запусти приложение в IDE — скорее всего, локально номера строк появятся, потому что разработчик обычно компилирует с отладкой. Тогда точно определишь место.
⚡️ Профилактика: не доводи до убийств
🟡 Всегда компилируй с отладочной информацией (в Maven это <debug>true</debug>). 🟡 Используй Optional для явного обозначения возможного null. 🟡 Пиши тесты, которые проверяют граничные случаи (null, пустые коллекции). 🟡 Включай логирование ключевых параметров на входе в методы.🗣️ Запомни: NPE без строки — не тупик, а вызов твоей дедукции. Ищи подсказки в именах методов, логируй входные данные, проверяй
null явно.6 964
☕️ Java с нуля. Классы
В этом видео автор продолжает знакомство с классами в Java. Вы узнаете, что классы могут быть не только обычными, но и вложенными, локальными и даже анонимными. Главная задача урока — понять, что класс — это просто шаблон для будущих объектов, и описывать их можно совершенно по-разному в зависимости от задачи.➡️ Ссылка на первоисточник 🗣️Запомни: не пытайтесь объять необъятное в одном уроке. Сосредоточьтесь на главном — класс — это шаблон, а способы его описания — лишь инструменты для разных ситуаций. 🤩 Java Фишки и трюки || #Видео
6 964
⌨️ В чём разница между TreeSet и HashSet?
TreeSet и HashSet — это два класса, реализующих интерфейс Set, но они имеют разные внутренние механизмы и свойства.
Структура данных
✔️ HashSet использует хеш-таблицу для хранения элементов. Это позволяет быстро добавлять, удалять и искать элементы, не обеспечивая никакого порядка их хранения.
✔️ TreeSet использует красно-черное дерево (Red-Black Tree), что позволяет хранить элементы в отсортированном порядке (естественном порядке элементов или порядке, определённом компаратором).
Порядок элементов
✔️ HashSet не гарантирует какого-либо порядка элементов. Порядок может быть случайным, и он не сохраняется при добавлении/удалении элементов.
✔️ TreeSet хранит элементы в отсортированном порядке. Если элементы реализуют интерфейс Comparable, то сортировка будет основана на их естественном порядке (например, для чисел — по возрастанию). Также можно задать пользовательский порядок с помощью объекта Comparator.
Скорость операций
✔️ HashSet обеспечивает O(1) для операций добавления, удаления и поиска (в среднем, если хеш-функция работает эффективно и коллизий мало).
✔️ TreeSet обеспечивает O(log n) для операций добавления, удаления и поиска из-за использования сбалансированного дерева.
Поддержка дополнительных операций
✔️ HashSet не поддерживает дополнительные методы для работы с диапазонами или порядком элементов.
✔️ TreeSet предоставляет такие методы, как subSet(), headSet(), tailSet(), которые позволяют работать с диапазонами элементов в отсортированном множестве.
Null-значения
✔️ HashSet может хранить одно null значение.
✔️ TreeSet не позволяет хранить null значения, так как при добавлении null неясно, как его сравнивать с другими элементами.
Когда использовать
✔️ Используйте HashSet, если вам не важен порядок элементов и вам нужна максимальная производительность.
✔️ Используйте TreeSet, если нужно поддерживать элементы в отсортированном порядке.
#java #HashSet #TreeSet6 964
⌨️ Суть лямбда-выражений
Лямбда-выражения на первый взгляд могут показаться чем-то сложным и загадочным, но на самом деле они просты и интуитивно понятны.
Лямбда-выражение — это лаконичный способ описания анонимной функции, которую можно передать в качестве параметра или сохранить в переменной для последующего использования.
Если говорить ещё проще, лямбда-выражение — это просто другой способ создания и реализации объекта определённого типа. Рассмотрим это на примере создания нового потока.
У класса
Thread есть конструктор:
public Thread(Runnable target) {
...
}
То есть в конструктор нужно передать объект типа Runnable. До лямбда-выражений мы сделали бы так:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();
Здесь мы создаём анонимный класс, реализующий интерфейс Runnable, с определённым методом run.
Если использовать лямбда-выражение, тот же код будет выглядеть следующим образом:
Runnable r = () -> System.out.println("Hello World");
new Thread(r).start();
Или проще:
new Thread(() -> System.out.println("Hello World")).start();
Лямбда-выражение заменяет собой анонимный класс, который раньше был бы необходим для реализации Runnable. Лямбда-выражение может использоваться только там, где ожидается реализация функционального интерфейса — интерфейса с единственным абстрактным методом.
А интерфейс Runnable именно такой:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Функциональный интерфейс должен содержать только один абстрактный метод, чтобы компилятор мог точно определить, какой метод реализует лямбда-выражение. В противном случае возникли бы неоднозначности и ошибки.
#java #lambda #Runnable6 964
Бесплатно: 5 дней Python для новичков → 4 мини-проекта 🐍
Телеграм-боты, парсер данных в таблицу и простой сайт. Без математики, только практика.
Запись до 15 марта → тут
Реклама. ЧОУ ДПО "ОБРАЗОВАТЕЛЬНЫЕ ТЕХНОЛОГИИ "СКИЛБОКС (КОРОБКА НАВЫКОВ)". ИНН 9704088880.
6 964
🎯
try-with-resources: закрывать за собой — признак взрослого программиста
Все мы знаем, что ресурсы (файлы, сокеты, соединения с БД) нужно закрывать. Но сколько раз ты видел код, где close() забыт, а ресурсы текут? Java 7 придумала try-with-resources, чтобы мы не зависели от человеческой памяти. Но даже в этой конструкции есть нюансы, о которых нужно знать.
Вот как правильно закрывать за собой и не оставлять следов 👇
🟢 Шаг 1: Древний способ (как делали пещерные люди)
InputStream input = null;
try {
input = new FileInputStream("file.txt");
// читаем данные
} catch (IOException e) {
e.printStackTrace();
} finally {
if (input != null) {
try {
input.close(); // ещё один try-catch внутри finally!
} catch (IOException e) {
e.printStackTrace();
}
}
}
➡️ Проблемы:
🟢 Кода больше, чем логики.
🟢 Легко забыть close().
🟢 Если и в try, и в finally летят исключения — одно подавляется.
🟢 В реальном проекте таких конструкций могут быть сотни.
🟢 Шаг 2: try-with-resources (современный подход)
try (InputStream input = new FileInputStream("file.txt")) {
// читаем данные
} catch (IOException e) {
e.printStackTrace();
}
// input автоматически закрыт
➡️ Всё! Ресурс закрывается автоматически, даже если вылетело исключение. Кода в 5 раз меньше, а надёжность — 100%.
🟢 Шаг 3: Не только файлы — любые ресурсы
Любой класс, реализующий AutoCloseable (или старый Closeable), можно использовать в try-with-resources:
class DatabaseConnection implements AutoCloseable {
public void query(String sql) { /* ... */ }
@Override
public void close() {
System.out.println("Закрываем соединение с БД");
}
}
// Использование:
try (DatabaseConnection conn = new DatabaseConnection()) {
conn.query("SELECT * FROM users");
} // close() вызовется автоматически
➡️ Создавай свои ресурсы, которые нужно закрывать, и просто реализуй AutoCloseable. Всё будет работать как часы.
🟢 Шаг 4: Несколько ресурсов — порядок закрытия
try (
FileInputStream fis = new FileInputStream("in.txt");
FileOutputStream fos = new FileOutputStream("out.txt")
) {
// копирование
}
➡️ Важно: ресурсы закрываются в обратном порядке создания. Сначала fos, потом fis. Как стопка тарелок — последний положил, первый забрал.
🟢 Шаг 5: Скрытая ловушка — подавленные исключения
Что если и в блоке try, и при закрытии ресурса летят исключения?
class ExplosiveResource implements AutoCloseable {
public void work() { throw new RuntimeException("Ошибка в работе"); }
@Override
public void close() { throw new RuntimeException("Ошибка при закрытии"); }
}
try (ExplosiveResource r = new ExplosiveResource()) {
r.work();
} catch (RuntimeException e) {
System.out.println(e.getMessage()); // "Ошибка в работе"
Throwable[] suppressed = e.getSuppressed();
System.out.println(suppressed[0].getMessage()); // "Ошибка при закрытии"
}
➡️ Исключение из close() не теряется! Оно становится подавленным (suppressed) и прикрепляется к основному исключению. Получить их можно через e.getSuppressed().
🟢 Шаг 6: Можно и без catch — просто закрытие
try (Resource r = new Resource()) {
// используем
}
// catch не обязателен! Ресурс закроется в любом случае
➡️ Если тебя не интересуют исключения в работе (например, при записи в лог), можно опустить catch. Ресурс всё равно закроется.
🟢 Шаг 7: Ресурс, объявленный до try (final variable)
Resource r = new Resource();
try (r) { // Работает с Java 9+
r.use();
}
➡️ Если переменная эффективно final (не меняется после инициализации), можно использовать её в try-with-resources. Удобно, когда ресурс создаётся заранее.
🗣️ Запомни: Используй try-with-resources всегда, когда есть что закрывать. Если пишешь свой класс с ресурсами — реализуй AutoCloseable.6 964
🔴 КАК ИДЕАЛЬНО ПРОЙТИ СОБЕС? ПОКАЖЕМ ЗАВТРА!
Каждый, проходя интервью, думал: «Ну что они хотят услышать? Я же правильно ответил! Почему меня не взяли?»
4 марта(уже завтра!) в 19:00 по мск приходи онлайн на открытое интервью, где будут собеседовать МЕНТОРА ШОРТКАТ
Как это будет:
📂 Виктор Анохин, старший разработчик из WildBerries, будет задавать реальные вопросы и задачи старшему разработчику Сергею Чамкину
📂 Сергей будет отвечать на каждый вопрос так, как это ожидает сам от вас на собеседованиях
📂 В конце можно будет задать любой вопрос Сергею и Виктору
Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.
Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot
Реклама.
О рекламодателе.
6 964
🧵 ThreadLocal: как не устроить коммунальную квартиру в потоках
Кажется, что
ThreadLocal — идеальный способ хранить данные, принадлежащие конкретному потоку: контекст пользователя, соединение с БД, ID запроса. Но когда ты используешь пул потоков (как в любом серьёзном веб-приложении), твои переменные могут внезапно переехать к соседям. Ты думал, у каждого клиента своя уютная комната, а они живут в коммуналке, где всё общее.
Вот почему это происходит и как не пустить посторонних в свои данны👇
🟢 Шаг 1: Что такое ThreadLocal и как он работает
public class UserContext {
private static ThreadLocal<String> currentUser = new ThreadLocal<>();
public static void set(String user) { currentUser.set(user); }
public static String get() { return currentUser.get(); }
public static void clear() { currentUser.remove(); }
}
// В потоке A:
UserContext.set("Алиса");
System.out.println(UserContext.get()); // Алиса
// В потоке B:
System.out.println(UserContext.get()); // null (свой контекст)
➡️ ThreadLocal хранит значение в самом объекте потока. У каждого потока свой «ящичек», и другие потоки в него не заглядывают.
🟢 Шаг 2: Пулы потоков — когда начинается коммуналка
Веб-серверы (Tomcat, Jetty) используют пул потоков: один поток обрабатывает много запросов последовательно.
// Контроллер
@GetMapping("/user")
public String getUser() {
// Предположим, middleware установил UserContext.set(...)
String user = UserContext.get();
// ... логика ...
return user;
}
➡️ Проблема: если ты не очистил ThreadLocal после обработки запроса, при следующем запросе (который пойдёт через тот же поток) в get() вернётся старый пользователь! Чужой пользователь получит данные предыдущего. Это называется утечка контекста.
🟢 Шаг 3: Визуализация проблемы
ExecutorService pool = Executors.newFixedThreadPool(1);
// Запрос 1 (Алиса)
pool.submit(() -> {
UserContext.set("Алиса");
System.out.println(UserContext.get()); // Алиса
// Забыли очистить!
});
// Запрос 2 (Боб) — тот же поток!
pool.submit(() -> {
System.out.println(UserContext.get()); // Алиса?! Вот и коммуналка
});
➡️ Поток переиспользовался, а данные остались. Боб получил контекст Алисы. Это критично для безопасности и корректности.
🟢 Шаг 4: Правильный паттерн — очистка в finally
@GetMapping("/user")
public String getUser() {
try {
// Установили (например, из фильтра)
UserContext.set(getCurrentUserFromRequest());
return process();
} finally {
UserContext.clear(); // Гарантированно чистим
}
}
➡️ finally гарантирует очистку даже если вылетело исключение. Никаких чужих данных следующему запросу.
🟢 Шаг 5: Элегантный способ — try-with-resources (AutoCloseable)
Можно сделать ThreadLocal обёртку, которая чистит автоматически:
public class ThreadLocalScope implements AutoCloseable {
private static ThreadLocal<String> current = new ThreadLocal<>();
public ThreadLocalScope(String value) {
current.set(value);
}
public static String get() { return current.get(); }
@Override
public void close() {
current.remove();
}
}
// Использование:
try (ThreadLocalScope scope = new ThreadLocalScope("Алиса")) {
String user = ThreadLocalScope.get(); // Алиса
// ...
} // автоматически удалится
➡️ Не надо думать о finally. Java сама вызовет close().
🟢 Шаг 6: Проверка на утечки в тестах
Можно добавить тест, который после выполнения проверяет, что ThreadLocal пуст:
@Test
void testCleanup() {
// Выполняем запрос
userController.getUser();
// В том же потоке проверяем:
assertThat(UserContext.get()).isNull();
}
➡️ Помогает отловить забытые очистки.
🗣️ Запомни: ThreadLocal в пуле потоков без очистки — это как не закрыть кран: рано или поздно затопишь соседей. Всегда чисти за собой, иначе чужой пользователь увидит данные твоего клиента. А в коммуналке и так тесно.6 964
Бесплатная конференция по PostgreSQL
Регистрируйся на бесплатную конференцию по PostgreSQL — 19.03.2026.📅
Эксперты расскажут о разработке, администрировании и новинках мира Postgres.
✅ Бесплатное участие
✅ Опытные спикеры
✅ 25+ тематических докладов
✅ Рабочие кейсы
Каждый участник получает именной Сертификат участника мероприятия.
Одни из немногих спикеров конференции:
— Андрей Бородин
PostgreSQL Major contributor, руководитель подразделения разработки РСУБД с открытым исходным кодом Yandex Cloud
— Родион Хабибов
DevOps-инженер «СберСервис»
— Андрей Овчаренко
Руководитель отдела Java-разработки Московской биржи
... и многие другие.
Регистрируйся, будет интересно!
И бесплатно!
Зарегистрироваться
#реклама 16+
pgbootcamp.ru
О рекламодателе
6 964
🗑
ConcurrentModificationException: удалять на ходу — как менять колесо у едущей машины
Всё работает, коллекция полна данных, ты решил почистить её во время обхода... и получаешь ConcurrentModificationException. Знакомая боль?
Это Java кричит: «Нельзя менять колесо, пока машина едет!». Давай разберём, почему так происходит и как делать правильно, чтобы не попадать в аварию 👇
🟢 Шаг 1: Типичная ошибка (как НЕ надо)
List<String> fruits = new ArrayList<>(List.of("яблоко", "банан", "апельсин", "груша"));
// Пытаемся удалить все фрукты короче 6 букв
for (String fruit : fruits) { // for-each использует итератор
if (fruit.length() < 6) {
fruits.remove(fruit); // 🚨 ConcurrentModificationException!
}
}
➡️ Код падает с исключением. Почему? Потому что for-each создаёт итератор, а ты напрямую вызываешь remove() у самой коллекции. Итератор теряет ориентацию и бунтует.
🟢 Шаг 2: Что происходит под капотом (fail-fast итератор)
// Упрощённая логика итератора ArrayList:
class Itr implements Iterator<E> {
int expectedModCount = modCount; // запоминаем версию коллекции
public E next() {
checkForComodification(); // проверяем, не изменилась ли коллекция
// ...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
➡️ Каждая модификация коллекции (add, remove) увеличивает счётчик modCount. Итератор при создании запоминает это число, а при каждом вызове next() проверяет, не изменилось ли оно. Если да — вылетает исключение. Это защита от непредсказуемого поведения.
🟢 Шаг 3: Правильный способ №1 — через итератор
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
if (fruit.length() < 6) {
iterator.remove(); // ✅ Безопасно! Итератор сам обновляет счётчик
}
}
System.out.println(fruits); // [апельсин]
➡️ У итератора есть свой метод remove(), который удаляет последний возвращённый элемент и корректирует внутренний expectedModCount. Итератор остаётся валидным.
🟢 Шаг 4: Современный способ №2 — Collection.removeIf() (Java 8+)
fruits.removeIf(fruit -> fruit.length() < 6);
System.out.println(fruits); // [апельсин]
➡️ removeIf — встроенный метод коллекций, который делает то же самое, но в одну строку. Под капотом он использует итератор, но ты не видишь этой возни. Самый чистый и читаемый вариант.
🟢 Шаг 5: Альтернативы — копирование или стримы
Если нужно не удалять, а создать новую коллекцию (иммутабельность):
// Через стримы
List<String> filtered = fruits.stream()
.filter(fruit -> fruit.length() >= 6)
.collect(Collectors.toList());
// Или через новый список
List<String> result = new ArrayList<>();
for (String fruit : fruits) {
if (fruit.length() >= 6) result.add(fruit);
}
➡️ Иногда проще собрать новую коллекцию, чем мутировать старую. Особенно если исходная коллекция потом не нужна.
🗣️ Запомни: Удалять элементы из коллекции во время for-each — это как менять колесо на полном ходу.6 964
🔠 Слили 4 ТБ по IT, хакингу и разработке.
⚪️ Все лучшие инструкции, гайды, книги и инструменты — без воды.
🖥 Курсы & GitHub — 579GB
☁️ Хакинг & ИБ — 756GB
🤒 OSINT — 315GB
⌨️ Python — 955GB
🙃 Linux & Bash — 459GB
😦 Работа в IT — 278GB
🖥 Общий архив — 946GB
➡️ Присоединяйтесь и скачивайте.
Пост будет удален через 48 часов.
6 964
☕️ Java с нуля. Введение в ООП
В этом видео автор предлагает нестандартный взгляд на объектно-ориентированное программирование. Он честно признаёт, что будет учить «неправильному» ООП, но именно с таким подходом вы столкнётесь в реальной индустрии — от работы с фреймворками до корпоративных проектов. Это прикладное введение, которое подготовит вас к реалиям разработки, а не к академической теории.➡️ Ссылка на первоисточник 🗣️Запомни: в программировании важно не только знать, «как правильно», но и понимать, «как работает» в реальных проектах. Готовьтесь к индустрии, а не к идеальному миру. 🤩 Java Фишки и трюки || #Видео
6 964
🔀 Stream.peek: подсматривать в замочную скважину опасно
Кажется, что
peek() — идеальный способ заглянуть внутрь стрима, посмотреть, что там происходит на каждом этапе, и ничего не сломать. Но Java — язык хитрый: иногда простое подсматривание меняет результат. Заглянул — и сломал.
Разбираемся, когда peek() безобиден, а когда начинает колдовать 👇
🟢 Шаг 1: Что такое peek()?
Stream.of("яблоко", "банан", "апельсин")
.peek(s -> System.out.println("Обрабатываем: " + s))
.map(String::toUpperCase)
.forEach(System.out::println);
➡️ peek() принимает Consumer и выполняет его для каждого элемента, не меняя элемент. Создан для отладки — чтобы смотреть, что течёт по трубе.
🟢 Шаг 2: Когда peek() работает (и это полезно)
List<String> result = Stream.of("one", "two", "three")
.peek(System.out::println) // видим все элементы
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
// Вывод: one, two, three
// Результат: ["three"]
➡️ Пока стрим заканчивается терминальной операцией (здесь collect), peek() выполняется для каждого элемента, проходящего через него. Идеально для быстрой отладки.
🟢 Шаг 3: Ловушка №1 — ленивость стримов
Stream.of("один", "два", "три")
.peek(System.out::println)
.map(String::length);
// Ничего не выведется! Терминальной операции нет.
➡️ Стримы ленивы. Промежуточные операции (включая peek) не выполняются, пока не вызвана терминальная операция. Если забыть collect или forEach, твой peek молчит — и ты не понимаешь, почему.
🟢 Шаг 4: Ловушка №2 — peek() может изменить состояние (и сломать результат)
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
List<String> result = list.stream()
.peek(s -> list.add(s.toUpperCase())) // МОДИФИЦИРУЕМ ИСХОДНЫЙ СПИСОК
.collect(Collectors.toList());
System.out.println(result); // ??
➡️ В зависимости от реализации стрима (последовательный/параллельный) это может привести к ConcurrentModificationException или просто к непредсказуемому результату. peek не предназначен для изменения данных — только для чтения.
🟢 Шаг 5: Ловушка №3 — оптимизации стримов
Stream.of("a", "bb", "ccc")
.peek(System.out::println) // ожидаем увидеть все?
.filter(s -> s.length() > 2)
.findFirst(); // терминальная операция, но...
➡️ Вывод: "a", "bb", "ccc"? Нет! Стрим оптимизирует: findFirst() может перестать перебирать элементы, как только найдет подходящий. peek выполняется только для тех, кто реально проходит через него до момента остановки. Здесь: "a" (не прошла фильтр), "bb" (не прошла), "ccc" (прошла — выведется, потом стрим останавливается). Итого вывод только "a", "bb", "ccc". Но если изменить порядок — может быть меньше.
🟢 Шаг 6: Когда peek() безопасен и полезен
🟢 Только для отладки во время разработки.
🟢 Только с терминальной операцией, которая обрабатывает все элементы (например, forEach, collect в список).
🟢 Без изменения внешнего состояния (только чтение, логирование).
🟢 Шаг 7: Чем заменить peek() для реальных действий
// Если нужно логировать + модифицировать — используй map и верни тот же объект
.map(s -> { System.out.println(s); return s; })
➡️ Но это костыль. Лучше использовать полноценный отладчик или логирование вне стрима.
🗣️ Запомни: peek() — это замочная скважина: подсматривать можно, но дверь открывать нельзя. Если нужно что-то сделать с элементами, делай это в map или в терминальной операции.6 964
⌨️ Какой хеш-код имеет
null?
null не имеет хеш-кода, потому что null не является объектом. Однако, при использовании null в структурах данных, таких как HashMap или HashSet, применяется специальная логика:
➡️ Что происходит с null в HashMap и HashSet?
✔️ В HashMap ключ null всегда попадает в первый бакет (bucket 0).
✔️ В HashSet (который использует HashMap внутри) null также хранится в этом же бакете.
✔️ Проверки выполняются в Objects.hashCode(), который для null всегда возвращает 0.
➡️ Пример:
System.out.println(Objects.hashCode(null)); // 0
📌 Вывод: null не имеет собственного хеш-кода, но в хеш-структурах ему назначается 0.6 964
👩💻 Программирование — В С Ё
В 2026 году на кодинге уже не вывезешь, перспектива года - Информационная Безопасность.
Ловите полезные каналы, которые помогут ворваться в новое направление.
👍 ZeroDay — Уроки, эксплуатация уязвимостей с нуля
👍 Белый Хакер — Свежие новости из мира ИБ
😎 Бункер Хакера — Статьи, книги, шпаргалки и хакинг
👨💻 Серверная Админа — Настройка и уроки по компьютерным сетям
📂 Вступай и изучай новое направление!
6 964
☕️ Интерфейсы в Java
В этом видео автор подробно разбирает, что такое интерфейсы в Java и чем они похожи на абстрактные классы. Вы узнаете об их ключевых отличиях, научитесь применять интерфейсы на практике и поймёте, в каких задачах они становятся незаменимым инструментом для создания гибкой и понятной архитектуры кода.👉 Ссылка на первоисточник 🗣Запомни: интерфейсы — это контракты, которые определяют что должен делать класс, а не как. Это основа для написания слабосвязанного и тестируемого кода. 🤩 Java Фишки и трюки || #Видео
6 964
👩💻 Хватит учить только синтаксис, начинай делать реальные проекты!
Python Ready — авторский канал, где Python перестаёт быть только теорией и становится рабочим инструментом. Мини-проекты, боты, советы, разборы задач, гайды и шпаргалки для каждого программиста.
🔥 Советую подписаться: @python_ready
6 964
📅
LocalDate и DateTimeFormatter: вечная путаница с YYYY и yyyy
Каждый Новый год эта ошибка просыпается и незаметно портит данные в логах, отчётах и экспортах. Разница между YYYY и yyyy - всего одна буква, но она может сдвинуть дату на целый год вперёд или назад в первую неделю января.
Вот как не попасть в эту ловушку и почему это происходит 👇
📁 Шаг 1: Наглядный пример ошибки (31 декабря 2024 года)
DateTimeFormatter wrongFormatter = DateTimeFormatter.ofPattern("dd.MM.YYYY");
DateTimeFormatter correctFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
LocalDate date = LocalDate.of(2024, 12, 31);
System.out.println("Неправильно (YYYY): " + date.format(wrongFormatter));
// 31.12.2025 ← вот он, сдвиг на год!
System.out.println("Правильно (yyyy): " + date.format(correctFormatter));
// 31.12.2024
➡️ Да, 31 декабря 2024 года форматируется как... 2025 год. Это не баг, а фича, о которой мало кто знает.
📁 Шаг 2: В чём разница? (Год vs Год недели)
- `yyyy` — это обычный календарный год (year-of-era). То, что мы все понимаем под годом.
- `YYYY` — это год недели (week-based-year) по стандарту ISO 8601.
➡️ YYYY используется вместе с номером недели (ww) и днём недели (E). Он нужен для отображения года, к которому относится неделя, а не календарная дата.
📁 Шаг 3: Почему происходит сдвиг? (Магия первой недели года)
По ISO 8601:
1. Неделя начинается с понедельника.
2. Первая неделя года — это неделя, содержащая первый четверг года.
3. Год недели (YYYY) — это год, к которому относится эта неделя.
// Пример: 1 января 2025 года — это среда
LocalDate jan1 = LocalDate.of(2025, 1, 1);
System.out.println("Год недели: " + jan1.format(DateTimeFormatter.ofPattern("YYYY")));
// 2025 — потому что неделя (30 дек - 5 янв) содержит первый четверг 2025 года (2 января)
// А 30 декабря 2024 года — это понедельник, начало недели 1 (2025 года!)
LocalDate dec30 = LocalDate.of(2024, 12, 30);
System.out.println("Год недели: " + dec30.format(DateTimeFormatter.ofPattern("YYYY")));
// 2025 — потому что эта неделя (30 дек - 5 янв) содержит первый четверг 2025 года
➡️ Критические периоды: последние 3-4 дня декабря и первые 3-4 дня января. Именно здесь YYYY и yyyy чаще всего расходятся.
📁 Шаг 4: Где используется `YYYY` (правильно)
Только вместе с номером недели!
// Для отображения номера недели и года недели (отчёты, планирование)
DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("'Неделя' ww, YYYY");
LocalDate date = LocalDate.of(2024, 12, 30);
System.out.println(date.format(weekFormatter));
// "Неделя 01, 2025" ← это правильно для систем, работающих по неделям
➡️ Используй YYYY только если ты точно понимаешь, что такое год недели, и используешь его с ww (номер недели).
📁 Шаг 5: Где НЕЛЬЗЯ использовать `YYYY` (опасные случаи)
1. Логирование — дата в логах внезапно станет 2025 годом 30 декабря.
2. Экспорт в CSV/Excel для отчётности.
3. Формирование имён файлов с датами.
4. Любые даты, которые потом будут парситься обратно.
// ❌ Опасный парсинг (может сломаться)
String wrong = "31.12.2024";
LocalDate parsed = LocalDate.parse(wrong, DateTimeFormatter.ofPattern("dd.MM.YYYY"));
// Может выкинуть исключение или спарсить не ту дату
📁 Шаг 6: Как избежать ошибки навсегда
1. Используй готовые форматеры вместо ручных паттернов:
DateTimeFormatter.ISO_LOCAL_DATE; // yyyy-MM-dd
DateTimeFormatter.BASIC_ISO_DATE; // yyyyMMdd
2. Пиши тесты на граничные даты:
@Test
void dateFormatting_aroundNewYear() {
LocalDate dec30 = LocalDate.of(2024, 12, 30);
LocalDate jan1 = LocalDate.of(2025, 1, 1);
assertThat(dec30.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")))
.isEqualTo("30/12/2024");
assertThat(jan1.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")))
.isEqualTo("01/01/2025");
}
3. Включи в code review проверку всех DateTimeFormatter на наличие YYYY.
🗣️ Запомни: YYYY - это не опечатка, а отдельная концепция. Используй yyyy для всего, что связано с календарными датами. А YYYY оставь для отчётов по неделям.6 964
Docker и Kubernetes: основы разработки под облачную инфраструктуру
Курс для тех, кто хочет держать свой стэк и знания актуальными и глубоко разбираться, как устроены Docker, Kubernetes, и современная облачная инфраструктура в целом.
🌐 Чему вы научитесь:
🤩 Создавать облачную инфраструктуру «с нуля» управление и конфигурация серверов с Terraform, Ansible, cloud‑init
🤩 Уверенно работать с Docker: Dockerfile, слои, кэш, многоступенчатые сборки, реестры, безопасность, air‑gapped
🤩 Проектировать многоконтейнерные приложения: паттерны Sidecar, Ambassador, Adapter, проверки (liveness/readiness), DaemonSet и поды
🤩 Настраивать сеть и балансировку в Kubernetes
ClusterIP, Services, Ingress, MetalLB, TLS/SNI, сервис‑меши (Istio)
🤩 Организовывать хранение данных: PersistentVolumes / PVC, StorageClasses, резервное копирование. Упаковка в Helm и поддержка через Operator
🥸 Кто мы: R&D-центр Devhands. Автор курса — Николай Ихалайнен, эксперт по СУБД и бекенду (ex-Percona), со-основатель MyDB, энтузиаст открытого ПО.
🗓 Старт курса: 25 февраля, 6 недель обучения.
Изучить программу и записаться можно здесь.
Ждем вас!
Реклама. ИП Рыбак А.А. ИНН 771407709607 Erid: 2Vtzqvh26Qk
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
