en
Feedback
Java | Фишки и трюки

Java | Фишки и трюки

Open in Telegram

Java: примеры кода, интересные фишки и полезные трюки Купить рекламу: https://telega.in/c/java_tips_and_tricks ✍️По всем вопросам: @Pascal4eg Менеджер по рекламе: @shmyzna

Show more
6 953
Subscribers
-324 hours
-27 days
+630 days
Posts Archive
ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ Айтишники поймут ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠ ᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠᅠ

💎 Clean Code на Java — идиоматические приёмы, которые реально работают Хочешь писать Java-код, который приятно читать и легко сопровождать? Вот не теория с книжек, а настоящие идиомы, шаблоны и фишки, которые делают код чистым, понятным и «живым». 👍 1. Именование — полдела ❌ Плохо:
double d = get(a, b);
✔️Лучше:
double distance = calculateDistance(startPoint, endPoint);
Именуй переменные и методы так, чтобы код сам говорил, что он делает. 👍 2. if без else — код легче читатьБыло:
if (isAdmin) {
    grantAccess();
} else {
    denyAccess();
}
✔️ Стало:
if (!isAdmin) {
    denyAccess();
    return;
}
grantAccess();
Меньше вложенности → легче читать. 👍 3. Меньше булевых флагов Неочевидно:
public void start(boolean silent) {
    if (!silent) {
        System.out.println("Starting...");
    }
    // ...
}
✔️ Читаемо:
public void startLoud() {
    System.out.println("Starting...");
    start();
}

public void start() {
    // ...
}
Выноси поведение в отдельные методы, а не в булевы флаги. 👍 4. Используй Optional вместо null ❌ Потенциальный NPE:
String name = user.getName();
✔️ Безопаснее:
Optional<String> name = user.getNameOptional();
name.ifPresent(System.out::println);
Optional показывает, что значение может отсутствовать. 👍 5. Выражай намерение через enumМагические числа:
send(2);
✔️ Ясно:
send(Priority.HIGH);
enum передаёт смысл. 👍 6. Строим объекты читаемо: builder Плохо читается:
User u = new User("Ann", 25, true, "Russia", null, false);
✔️ Круто:
User user = User.builder()
    .name("Ann")
    .age(25)
    .active(true)
    .country("Russia")
    .build();
Именованные параметры через builder — читабельность x10. 👍 7. Используй switch-expression в Java 14+ Старый подход:
int days;
switch (month) {
    case JANUARY: days = 31; break;
    case FEBRUARY: days = 28; break;
    // ...
}
✔️ Современный:
int days = switch (month) {
    case JANUARY, MARCH, MAY -> 31;
    case FEBRUARY -> 28;
    default -> throw new IllegalArgumentException();
};
Чётко, лаконично, без лишнего кода. 👍 8. Чистые методы = маленькие и простые Монстр:
public void registerUser(UserDto dto) {
    validate(dto);
    saveToDatabase(dto);
    sendWelcomeEmail(dto);
    logRegistration(dto);
}
✔️ Разбиваем:
public void registerUser(UserDto dto) {
    validate(dto);
    persist(dto);
    notify(dto);
}

private void validate(UserDto dto) { ... }
private void persist(UserDto dto) { ... }
private void notify(UserDto dto) { ... }
Каждый метод делает одну вещь. Легко тестировать, легко читать. 👍 9. Предпочитай Streams + map/filter/collect Старый for:
List<String> active = new ArrayList<>();
for (User u : users) {
    if (u.isActive()) {
        active.add(u.getName().toUpperCase());
    }
}
✔️ Современно:
List<String> active = users.stream()
    .filter(User::isActive)
    .map(u -> u.getName().toUpperCase())
    .collect(Collectors.toList());
Чисто, выразительно, без шума. 👍 10. Старайся не ловить Exception без причины Так себе:
try {
    Thread.sleep(100);
} catch (Exception e) {
    // игнорируем
}
✔️ Так лучше:
try {
    Thread.sleep(100);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    throw new RuntimeException(e);
}
Нельзя просто проглатывать ошибки — это превращается в хаос. 🗣️ Запомни: Clean code — это не строгость, а удобство. Читаемый код — это код, который ты поймёшь через 3 месяца в 2 часа ночи. Не бойся переписывать, выносить, называть по-человечески — в этом сила Java-разработчика.

Онлайн-доски теперь в Битрикс24 Можно не только вести проекты и общаться по видео, но и визуализировать идеи вместе с командо
Онлайн-доски теперь в Битрикс24 Можно не только вести проекты и общаться по видео, но и визуализировать идеи вместе с командой. Бесплатно, удобно, без лишних движений. Стратегии больше не будут прежними. Зарегистрироваться #реклама 16+ bitrix24.ru О рекламодателе

⚙️ Оптимизация Collections и Stream API — как это делать профессионально Используешь Stream в Java 8+? Только не думай, что э
⚙️ Оптимизация Collections и Stream API — как это делать профессионально Используешь Stream в Java 8+? Только не думай, что это всегда просто и быстро. В этой статье — реальные кейсы и трюки, которые позволят ускорить обработку коллекций и избежать типичных ошибок. В статье ты узнаешь: 📌 Лучшие приёмы работы со Stream, чтобы избежать лишней аллокации 📌 Когда Stream быстрее цикла, а когда цикл — всё же король производительности 📌 Практические советы: избегать лишних объектов, использовать правильные методы сборки 📌 Ускорение благодаря профайлингу и тому, как выжать максимум из JIT ➡️ Читайте и наслаждайтесь 🗣️ Java всегда сильна в коллекциях — сделай свой код быстрее и чище уже сегодня! 🤩 Java Фишки и трюки || #Cтатья

Просто напиши тему — например, Spring Boot — и @TinyInterviewerBot завали тебя вопросами. Ответы — под катом! чтобы не подглядывать. ⚠️ Нужно точечно? Укажи узкую тему — хоть JVM memory model, хоть Transactional propagation, хоть Garbage Collector tuning — и бот будет гонять тебя только по ней. 📈 Повторяй десятки вопросов каждый день, по любой теме — от core Java до фреймворков и продвинутых тем. 🎯 Идеально для: • Быстрой самопроверки • Подготовки к собесам • Ревизии старых знаний 👉 Попробуй сам — @TinyInterviewerBot И больше никакой «эээ… щас вспомню» на интервью 😎

🚀 Java Stream API: продвинутые приёмы и подводные камни Stream API — мощный инструмент, но легко попасть в ловушку, если не знать нюансов. ✅ Фишки, которые нужно знать 🟢 Фильтрация и преобразование в одну строку:
List<String> names = users.stream()
    .filter(u -> u.isActive())
    .map(User::getName)
    .collect(Collectors.toList());
🟢 Собрать в Map без дублирующих ключей:
Map<Long, String> map = users.stream()
    .collect(Collectors.toMap(
        User::getId,
        User::getName,
        (a, b) -> a  // если дубликат — оставить первый
    ));
🟢 Группировка и сортировка:
Map<String, List<User>> grouped = users.stream()
    .collect(Collectors.groupingBy(User::getRole));
⚠️ Подводные камни 🟢 Не перезапускается!
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
// stream.forEach(...) // ❌ выбросит исключение — уже использован
🟢 Ленивая природа:
Stream.of("a", "b", "c")
    .filter(s -> {
        System.out.println("filter: " + s);
        return true;
    });
// ничего не произойдёт, пока не вызвать .collect() или .forEach()
🟢 Null внутри Stream — плохая идея:
Stream.of("a", null, "b");  // ❌ NPE при map/filter
Лучше фильтровать:
Stream.of("a", null, "b")
    .filter(Objects::nonNull)
    .map(String::toUpperCase)
    .forEach(System.out::println);
🔍 Продвинутый пример Фильтруем активных, сортируем по дате, берём 10 последних:
List<Post> recent = posts.stream()
    .filter(Post::isActive)
    .sorted(Comparator.comparing(Post::getCreatedAt).reversed())
    .limit(10)
    .collect(Collectors.toList());
🗣️ Запомни: Stream API — кайф, если понимаешь, что делаешь. Всегда помни про однократность, лень и null — и всё будет летать.

🤩Нашли для вас топовые каналы, которые реально научат вас программировать! Free Znanija — огромная база курсов по ИТ, которы
🤩Нашли для вас топовые каналы, которые реально научат вас программировать! Free Znanija — огромная база курсов по ИТ, которые можно скачать совершенно бесплатно; IT библиотека — самая огромная библиотека книг по IT во всём телеграмме Hacking & InfoSec Base — канал действующего белого хакера. Подробные уроки по безопасности, эксплуатации уязвимостей, социальной инженерии. 🫵Подпишись, чтобы не потерять самые полезные материалы!

🌀 Pattern Matching в Java — прощай `instanceof + cast` Раньше, чтобы проверить тип и привести его, писали вот так:
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toLowerCase());
}
👎 Шумно, опасно, повторяется. С Java 16+ можно проще и безопаснее — паттерн-матчинг для `instanceof`: ✔️ Как это работает:
if (obj instanceof String s) {
    System.out.println(s.toLowerCase());
}
✅ Если objString, то переменная s уже приведена к нужному типу. Больше никаких ручных кастов. 🛠 Примеры на практике: ☑️ Проверка нескольких типов
void printInfo(Object obj) {
    if (obj instanceof String s) {
        System.out.println("Строка: " + s.toUpperCase());
    } else if (obj instanceof Integer i) {
        System.out.println("Число: " + (i * 2));
    } else {
        System.out.println("Что-то другое");
    }
}
🔍 Код стал чище: без каста, без дублирования obj. 🚀 Использование внутри switch С Java 21 (preview) можно использовать switch с типами:
static String format(Object obj) {
    return switch (obj) {
        case String s -> "Строка: " + s;
        case Integer i -> "Целое: " + i;
        case null -> "null";
        default -> "Неизвестно";
    };
}
➡️ Это называется Pattern Matching for switch. 🧩 Совмещение с guard'ами
if (obj instanceof String s && s.length() > 5) {
    System.out.println("Длинная строка: " + s);
}
⚠️ s будет доступна только если оба условия выполняются. Это безопасно. 🧪 Более сложные условия
if (!(obj instanceof String s)) return;
System.out.println("Обрабатываем строку: " + s.toLowerCase());
Можно комбинировать с !instanceof, return, continue, break и др. 🗣️ Запомни: Используй instanceof с привязкой переменной — особенно в if-else, парсерах, логике обработки событий и switch.

🧠 Java и AI: как использовать LangChain4J в 2025 Считаешь Java не для AI? Серьёзно ошибаешься! LangChain, фреймворк для разр
🧠 Java и AI: как использовать LangChain4J в 2025 Считаешь Java не для AI? Серьёзно ошибаешься! LangChain, фреймворк для разработки с языковыми моделями, теперь уже доступен и для Java — через LangChain4J. В статье ты узнаешь: 📌 Как использовать LangChain4J для создания цепочек запросов с LLM 📌 Взаимодействие с векторными хранилищами и RAG 📌 Примеры кода на Java для NLP-задач 📌 Почему Java‑специалистам уже пора в AI ➡️ Читайте и наслаждайтесь 🗣️ Java всё ближе к языкам будущего. Попробуй новое направление уже сейчас! 🤩 Java Фишки и трюки || #Cтатья

🧵 Используй try-with-resources с потоками правильно (и продвинуто) Если ты до сих пор закрываешь потоки руками или думаешь, что try-with-resources — это только для файлов, смотри: 🛠 Пример 1: чтение и закрытие автоматически
try (BufferedReader reader = Files.newBufferedReader(Path.of("data.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
}
☑️ Поток закрывается автоматически — даже если внутри случится исключение. 🧪 Пример 2: кастомный ресурс (любой AutoCloseable)
public class TempResource implements AutoCloseable {
    public void doSomething() {
        System.out.println("Работаем с ресурсом");
    }

    @Override
    public void close() {
        System.out.println("Ресурс закрыт");
    }
}

try (TempResource res = new TempResource()) {
    res.doSomething();
}
🧠 Всё, что реализует AutoCloseable, можно оборачивать в try-with-resources. Это не только файлы, но и сетевые соединения, стримы, логи, сессии Hibernate и др. 🕸 Пример 3: несколько ресурсов
try (
    InputStream in = new FileInputStream("input.txt");
    OutputStream out = new FileOutputStream("output.txt")
) {
    in.transferTo(out);
}
🔒 Все ресурсы закроются в обратном порядке. 🗣️ Запомни: try-with-resources — не просто синтаксический сахар. Это способ писать надёжный код без утечек ресурсов и без лишнего шаблонного кода. Особенно полезно при работе с сетью, файлами, базами данных и внешними API.

erid: 2W5zFHCHQjJ Канал про собеседования, алгоритмы, System Design, процессы, культуру и инженерию в FAANG/BigTech Автор кан
erid: 2W5zFHCHQjJ Канал про собеседования, алгоритмы, System Design, процессы, культуру и инженерию в FAANG/BigTech Автор канала — разработчик с 18-летним опытом, из которых 8 лет он провёл в FAANG (3,5 года — в Amazon). Работал и жил в России, Германии, Люксембурге и Великобритании, провёл более 100 технических интервью в FAANG-компании. На канале разбираю реальные задачи с собеседований в FAANG по алгоритмам и System Design. Рассматриваю задачи из не-FAANG компаний на Java, делая акцент на многопоточность. Делюсь опытом работы в FAANG, рассказываю о процессах, технологиях и инженерной культуре, обсуждаю особенности релокации и жизни разработчика в разных странах. Если вам интересны эти темы, подписывайтесь: t.me/faangmaster

☕️ List.of() в Java — быстро, но не всегда безопасно С Java 9 появился удобный способ создать список:
List<String> names = List.of("Alice", "Bob", "Charlie");
Выглядит лаконично, работает быстро — но есть нюанс. ⚠️ Подводные камни 1️⃣.📛 Список неизменяемый
List<String> list = List.of("a", "b");
list.add("c");       // 💥 UnsupportedOperationException
list.remove("a");    // 💥 тоже ошибка
Любая попытка изменить список — ошибка во время выполнения. Это не ArrayList, а immutable List. 2️⃣.💣 Нельзя null
List<String> list = List.of("hello", null);  // 💥 NullPointerException
Даже один null — и всё падает. Потому что List.of() не допускает null ни в одном элементе. 3️⃣.🎭 Не всегда List, который ты ожидаешь
System.out.println(List.of("a", "b").getClass());
// class java.util.ImmutableCollections$ListN
Это не ArrayList, не LinkedList. Это внутренний тип, оптимизированный под неизменяемость. Если твой код ждёт ArrayList и будет кастить — ловишь ClassCastException. 🧪 Где безопасно использовать: 🟢 Константные списки (List.of(DAYS)) 🟢 Аргументы в методах, где ничего не модифицируется 🟢 Когда 100% уверен, что null нет ✅ Альтернатива с возможностью изменения:
List<String> modifiable = new ArrayList<>(List.of("a", "b", "c"));
modifiable.add("d");  // 👍 теперь можно
🗣️ Запомни: List.of() — круто для коротких, неизменяемых данных. Но если планируешь менять список или есть шанс на nullлучше явно используй `ArrayList`. Не пались на удобстве — знай, что лежит под капотом.

Вчера QA.GURU анонсировали вебинар с Head of QA в Dodo Engineering Дмитрием Тучс! Занять место бесплатно еще можно, регистрация здесь ▶ Встреча для тех, кто уже пишет на Java и хочет прокачаться в архитектуре автотестов. Спикер, Дмитрий Тучс — Head of QA в Dodo Engineering, инженер с многолетним опытом и член программного комитета конференций CodeFest, CodeTalks, EpicHey!, E-CODE. Помимо QA — бэкграунд в Java-разработке, аналитике и проектном менеджменте с 2009 года. Что будет на вебинаре? — Познакомитесь с учебным проектом Niffler: вместе взглянем на микросервисную архитектуру и технические решения проекта, с которым предстоит работать. — Разберетесь, чем «тесты на Google» (black box) отличаются от white box. А еще на занятии вы: — Напишете свой первый JUnit Extension для создания тестовых данных через API. И тест, показывающий элегантность такого решения. — Создадите полноценный «каркас» будущего проекта с E-2-E тестами: сразу напишем конфиги, page-objects, API-клиенты, DTO и многое другое! Занять место ▶▶▶ Реклама. Рекламодатель: ИП Васенков Станислав Олегович, ИНН 774335827403, erid: 2Vtzqw2ar9b

☕️ Что такое Project Loom?
Этот курс объясняет, что такое Project Loom в Java, включая виртуальные потоки, улучшение масштабируемости и новые возможности многозадачности. Подходит для разработчиков, интересующихся современными подходами к многозадачности в Java.
🤩 Java Фишки и трюки || #Видео

Крупнейший университет искусственного интеллекта Учим использовать ChatGPT в профессиональных целях, создавать нейро-сотрудни
Крупнейший университет искусственного интеллекта Учим использовать ChatGPT в профессиональных целях, создавать нейро-сотрудников и зарабатывать на искусственном интеллекте. ✨ 8 000+ студентов со всего мира ✨ 600+ AI-проектов, созданных студентами ✨ Сборная Университета — победители крупнейших AI-хакатонов России ✨ Стажировки в крупнейших компаниях России (РЖД, Ростелеком, РУДН, Совкомбанк, Самолет и другие) ✨ Трудоустраиваем выпускников в крупнейшие компании (Яндекс, ВТБ, Сбербанк, Роскосмос и другие) Будем рады видеть тебя в наших рядах! Узнать больше #реклама 16+ neural-university.ru О рекламодателе

🌐 WebClient — современная замена RestTemplate в Java RestTemplate давно знаком, но теперь Spring рекомендует использовать WebClient. Это не просто новый способ сделать HTTP-запросы — это полноценный инструмент, который умеет работать асинхронно, поддерживает реактивность и даёт гораздо больше контроля над процессом. Вот как выглядит простой GET-запрос:
WebClient client = WebClient.create();

String body = client.get()
    .uri("https://api.github.com/users/octocat")
    .retrieve()
    .bodyToMono(String.class)
    .block();

System.out.println(body);
Здесь bodyToMono(String.class) возвращает реактивный тип Mono, который содержит результат. Если вызвать .block(), запрос будет выполняться синхронно, так что можно легко внедрять WebClient даже в привычный код. Для POST-запроса с телом используем:
WebClient client = WebClient.create("https://httpbin.org");

String response = client.post()
    .uri("/post")
    .header("Content-Type", "application/json")
    .bodyValue("{\"name\":\"PyLinux\"}")
    .retrieve()
    .bodyToMono(String.class)
    .block();

System.out.println(response);
Ошибки обрабатываются через onStatus, например, для 4xx:
client.get()
    .uri("https://api.github.com/404")
    .retrieve()
    .onStatus(status -> status.is4xxClientError(), resp -> {
        System.out.println("Ошибка клиента: " + resp.statusCode());
        return Mono.error(new RuntimeException("Client error"));
    })
    .bodyToMono(String.class)
    .block();
Если нужно передать заголовки или параметры:
WebClient client = WebClient.builder()
    .baseUrl("https://api.example.com")
    .defaultHeader("Authorization", "Bearer your_token")
    .build();

String data = client.get()
    .uri(uriBuilder -> uriBuilder
        .path("/data")
        .queryParam("limit", 10)
        .build())
    .retrieve()
    .bodyToMono(String.class)
    .block();
Таким образом, WebClient позволяет легко делать запросы с любыми параметрами и заголовками, при этом можно управлять асинхронностью и реактивностью без лишних сложностей. 🗣️ Запомни: WebClient — это не просто новый класс. Это современный подход к HTTP в Java, который ты сможешь использовать и в обычных приложениях, и в реактивных. Если хочешь гибкости и мощи — забудь про RestTemplate и сразу учись WebClient.

Роадмап подготовки к Java собеседованиям Цель роадмапа – предоставить список тем и источников для быстрой подготовки к собесе
Роадмап подготовки к Java собеседованиям Цель роадмапа – предоставить список тем и источников для быстрой подготовки к собеседованиям. Темы: – Java (архитектура jvm, gc, многопоточность) – Spring (aop, transaction, cloud) – SQL/NoSQL (acid, base, уровни изоляций, explain) – Kafka/Docker/Kubernetes – Паттерны проектирования, ООП, SOLID – Алгоритмы и структуры данных – Системный дизайн Полная версия роадмапа со всеми темами и источниками лежит в канале @backend_interviewer

☕️ Хватит писать циклы! Java 8 дала тебе больше С 8-й версии Java предлагает мощные инструменты для работы с коллекциями — и
☕️ Хватит писать циклы! Java 8 дала тебе больше С 8-й версии Java предлагает мощные инструменты для работы с коллекциями — и пора ими воспользоваться. Забудь про for и while, когда можно писать элегантно и декларативно. В статье ты найдёшь: 📌 10 лучших методов из Stream API, которые экономят строки кода 📌 Как заменить громоздкие циклы одной строкой 📌 Примеры filter(), map(), collect() и других трюков 📌 Объяснение, почему функциональный стиль — это не боль, а кайф 📌 Практические сценарии: фильтрация, преобразование, группировка, подсчёты ➡️ Читайте и наслаждайтесь 🗣️ Пиши меньше, делай больше. Stream API — must-have для современного джависта. 🤩 Java Фишки и трюки || #Cтатья

Как тебе живется, когда тебя прослушивают? Или смс которую вы удаляете и думаете, что никто не видит. Например главные герои
Как тебе живется, когда тебя прослушивают? Или смс которую вы удаляете и думаете, что никто не видит. Например главные герои этого поста: 15 переписок знаменитостей которые мало кто видел смотреть переписки Прослушка звонков от хакеров интернета разговор на 24 минуты База городов которые переполнены плохой продукцией и просрочкой Новосибирск лидирует, а твой город? Вступай, тут все о твоей защите — @Код Безопасности

🔄 Observer на чистом Java — реакция без Spring и магии Нужно, чтобы один объект реагировал на изменения другого? Без тайных фреймворков, просто на Java? Решение — паттерн Наблюдатель (Observer). Он позволяет подписчикам автоматически узнавать об изменениях. 📦 Минимальный пример:
// Observable (Subject)
class NewsAgency {
    private List<NewsListener> listeners = new ArrayList<>();

    public void addListener(NewsListener l) {
        listeners.add(l);
    }

    public void publish(String news) {
        for (NewsListener l : listeners) {
            l.update(news);
        }
    }
}

// Observer (Listener)
interface NewsListener {
    void update(String news);
}

// Конкретный подписчик
class TelegramBot implements NewsListener {
    public void update(String news) {
        System.out.println("🔔 Telegram получил: " + news);
    }
}
▶️ Использование:
public class Main {
    public static void main(String[] args) {
        NewsAgency agency = new NewsAgency();
        agency.addListener(new TelegramBot());

        agency.publish("Java 21 уже вышел!");
        agency.publish("🧵 Loom теперь в preview!");
    }
}
📤 Вывод:
🔔 Telegram получил: Java 21 уже вышел!
🔔 Telegram получил: 🧵 Loom теперь в preview!
🛠 Можно сделать гибче: Добавь нескольких подписчиков:
class EmailNotifier implements NewsListener {
    public void update(String news) {
        System.out.println("📧 Email-рассылка: " + news);
    }
}
agency.addListener(new EmailNotifier());
Теперь все слушатели получают событие одновременно. 🗣️ Запомни: Паттерн Observer — отличный способ организовать реактивное поведение без зависимости от GUI, фреймворков или событийных систем. Работает в CLI, бэкенде, десктопе и даже Arduino. Просто и гибко.