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

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

الذهاب إلى القناة على Telegram

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

إظهار المزيد
6 953
المشتركون
-324 ساعات
-27 أيام
+630 أيام
أرشيف المشاركات
⌨️ Какой хеш-код имеет 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.

👩‍💻 Программирование — В С Ё В 2026 году на кодинге уже не вывезешь, перспектива года - Информационная Безопасность. Ловите
👩‍💻 Программирование — В С Ё В 2026 году на кодинге уже не вывезешь, перспектива года - Информационная Безопасность. Ловите полезные каналы, которые помогут ворваться в новое направление. 👍 ZeroDay — Уроки, эксплуатация уязвимостей с нуля 👍 Белый Хакер — Свежие новости из мира ИБ 😎 Бункер Хакера — Статьи, книги, шпаргалки и хакинг 👨‍💻 Серверная Админа — Настройка и уроки по компьютерным сетям 📂 Вступай и изучай новое направление!

☕️ Интерфейсы в Java
В этом видео автор подробно разбирает, что такое интерфейсы в Java и чем они похожи на абстрактные классы. Вы узнаете об их ключевых отличиях, научитесь применять интерфейсы на практике и поймёте, в каких задачах они становятся незаменимым инструментом для создания гибкой и понятной архитектуры кода.
👉 Ссылка на первоисточник 🗣Запомни: интерфейсы — это контракты, которые определяют что должен делать класс, а не как. Это основа для написания слабосвязанного и тестируемого кода. 🤩 Java Фишки и трюки || #Видео

👩‍💻 Хватит учить только синтаксис, начинай делать реальные проекты! Python Ready — авторский канал, где Python перестаёт бы
+4
👩‍💻 Хватит учить только синтаксис, начинай делать реальные проекты! Python Ready — авторский канал, где Python перестаёт быть только теорией и становится рабочим инструментом. Мини-проекты, боты, советы, разборы задач, гайды и шпаргалки для каждого программиста. 🔥 Советую подписаться: @python_ready

📅 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 оставь для отчётов по неделям.

Docker и Kubernetes: основы разработки под облачную инфраструктуру Курс для тех, кто хочет держать свой стэк и знания актуаль
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

🧪 Тестируем приватные методы: хак или норма? (Спойлер: это запах) Тебе точно знакома ситуация: логика спрятана в приватном методе, а протестировать её «в лоб» нельзя. Первая мысль - рефлексия! Но это как использовать лом для починки часов. Сломаешь больше, чем починишь. Вот почему тестирование приватных методов - это сигнал о проблеме в дизайне класса, и как это исправить правильно 👇 🟢 Шаг 1: Типичный хак через рефлексию (как НЕ делать)
import java.lang.reflect.Method;

public class Calculator {
    private int superSecretFormula(int a, int b) {
        return (a + b) * (a - b);
    }
}

// В тесте:
Method method = Calculator.class.getDeclaredMethod("superSecretFormula", int.class, int.class);
method.setAccessible(true); // Взламываем приватность
int result = (int) method.invoke(new Calculator(), 5, 3);
➡️ Проблемы: 🔴 Код теста становится хрупким: переименовал метод — тесты сломались. 🔴 Нарушается инкапсуляция. Тест теперь знает о внутренней кухне класса. 🔴 Это просто некрасиво. Рефлексия — это крик отчаяния. 🟢 Шаг 2: Почему это «запах» кода? Если метод настолько сложный, что его нужно тестировать отдельно — он, скорее всего, делает что-то самостоятельное. Возможно, он:
1. Выполняет несколько задач (нарушает SRP). 2. Содержит логику, которая может пригодиться где-то ещё. 3. Слишком умный для своего класса.
➡️ Частный метод — это деталь реализации. Тестировать нужно публичное поведение, а не приватные детали. 🟢 Шаг 3: Решение №1 — Вынеси логику в отдельный класс Был:
public class ReportService {
    public String generateReport() {
        // ... подготовка данных
        String cleaned = cleanData(rawData); // приватный метод
        // ... формирование отчёта
    }

    private String cleanData(String data) {
        // сложная логика очистки
    }
}
✔️Стал:
// Новая публичная единица, которую легко тестировать
public class DataCleaner {
    public String clean(String data) {
        // та же логика, но теперь она публичная
    }
}

// В ReportService:
public class ReportService {
    private final DataCleaner cleaner;

    public String generateReport() {
        String cleaned = cleaner.clean(rawData);
        // ...
    }
}
➡️ Теперь DataCleaner можно и нужно тестировать напрямую. Это чистая архитектура. 🟢 Шаг 4: Решение №2 — Сделай метод package-private (если очень надо)
// Было: private void helper() { ... }
// Стало (в том же пакете):
class Service {
    void helper() { ... } // без модификатора = виден в пакете
}

// В тестах (которые лежат в том же test/java/... пакете):
@Test
void testHelper() {
    Service service = new Service();
    service.helper(); // Доступно!
}
➡️ Компромиссный вариант. Логика всё ещё скрыта от внешнего мира, но доступна для тестов. Используй осторожно. 🟢 Шаг 5: Решение №3 — Тестируй через публичный метод Просто протестируй публичный метод, который использует этот приватный. Если покрытие кажется недостаточным — возможно, это повод задуматься: а нужен ли этот тест? Может, приватный метод настолько прост, что не требует отдельного теста?
@Test
void generateReport_usesCleanDataLogic() {
    ReportService service = new ReportService();
    String report = service.generateReport();
    assertThat(report).contains("очищенные данные");
}
➡️ Ты тестируешь результат, а не реализацию. Это сильнее и надёжнее. 🗣️ Запомни: Рефлексия для тестов — как гаечный ключ в микросхеме. Если очень хочется протестировать приватное, значит, твой класс тайно просит, чтобы его разделили. Хороший дизайн рождает лёгкое тестирование.

📱Большинство Android-приложений ломаются не из-за UI, а из-за архитектуры. Неправильное разделение слоёв, хаотичная работа с
📱Большинство Android-приложений ломаются не из-за UI, а из-за архитектуры. Неправильное разделение слоёв, хаотичная работа с API и логикой — и проект перестаёт масштабироваться уже на старте. На открытом вебинаре OTUS вы пройдёте полный путь создания Android-приложения: от работы с API сервера до отображения данных на экране. Разберём сетевое взаимодействие, получение данных из интернета и разложим приложение по слоям в рамках чистой архитектуры и MVVM. Этот урок — практическая точка входа в современную Android-разработку. Вы увидите, как связать API, бизнес-логику и UI в целостное приложение, которое легко поддерживать и развивать. 📆Встречаемся 24 февраля в 20:00 МСК в преддверии старта курса «Android Developer». Регистрация открыта: https://otus.pw/oQe2/ Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru

🪄 Магия @SneakyThrows в Lombok: как она обманывает компилятор Когда видишь @SneakyThrows, кажется, что нашёл волшебную палочку: checked-исключения больше не нужно объявлять в throws и ловить в try-catch. Но эта магия — не настоящее волшебство, а тонкая иллюзия, которая может сломать твой код в самый неподходящий момент. Давай заглянем под капот и узнаем, как эта аннотация обманывает компилятор, и почему с ней нужно быть осторожным 👇 🟢 Шаг 1: Как работают checked-исключения (обычный путь)
// Без Lombok: нужно либо пробросить, либо поймать
public void readFile() throws IOException { // Объявляем throws
    Files.readAllBytes(Paths.get("file.txt"));
}

// Или так:
public void readFile() {
    try {
        Files.readAllBytes(Paths.get("file.txt"));
    } catch (IOException e) { // Ловим исключение
        throw new RuntimeException(e);
    }
}
➡️ Компилятор Java требует обработать checked-исключения (IOException, SQLException). Это проверка на этапе компиляции. 🟢 Шаг 2: Волшебство `@SneakyThrows` (как оно выглядит)
import lombok.SneakyThrows;

public class FileReader {
    @SneakyThrows
    public byte[] readFile() {
        return Files.readAllBytes(Paths.get("file.txt"));
    }
}
➡️ Никакого throws, никакого try-catch! Код компилируется, и ты можешь вызывать метод, как будто исключений не существует. Но они никуда не делись. 🟢 Шаг 3: Как Lombok это делает (разоблачение фокуса) Компилятор Lombok преобразует код примерно вот во что:
public byte[] readFile() {
    try {
        return Files.readAllBytes(Paths.get("file.txt"));
    } catch (IOException e) {
        throw Lombok.sneakyThrow(e); // Вот этот метод — ключ!
    }
}

// А метод sneakyThrow делает вот что:
public static RuntimeException sneakyThrow(Throwable t) {
    throw Lombok.<RuntimeException>sneakyThrow0(t);
}

@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
    throw (T) t; // Кастование к unchecked-исключению!
}
➡️ Фокус в двойном касте! Lombok «притворяется», что кидает unchecked-исключение (RuntimeException), но на самом деле прокидывает оригинальное checked-исключение. JVM разрешает это, потому что стирание типов (type erasure) в generics скрывает реальный тип исключения. 🟢 Шаг 4: Чем это опасно? Проблема №1 — отсутствие декларации
@SneakyThrows
public void connect() {
    Socket socket = new Socket("host", 9999); // Может кинуть IOException
    // ... работа с сокетом
}

// Где-то в другом месте:
public void init() {
    connect(); // Код не знает, что здесь возможен IOException!
    // И поэтому не готов его обработать
}
➡️ Контракт метода нарушен. Вызывающий код не знает, что метод может выбросить checked-исключение. Это сбивает с толку и ломает принцип явного объявления исключений. 🟢 Шаг 5: Проблема №2 — несовместимость с некоторыми конструкциями
// Попробуем использовать в лямбде (Consumer)
list.forEach(element -> {
    @SneakyThrows
    Thread.sleep(100); // Так не сработает!
});

// Нужно отдельно выносить:
list.forEach(this::sneakySleep);

@SneakyThrows
private void sneakySleep(Object element) {
    Thread.sleep(100);
}
➡️ @SneakyThrows не работает прямо на лямбдах. Это ограничивает его применение. 🟢 Шаг 6: Когда это можно использовать (редкие случаи) 1. В тестах, где checked-исключения маловероятны и только мешают. 2. В реализациях интерфейсов, которые не объявляют исключений, но твоя реализация может их кидать (например, Runnable). 3. Для избежания излишнего обертывания в RuntimeException, когда ты точно уверен в поведении метода.
// Пример с Runnable
public class Worker implements Runnable {
    @SneakyThrows
    @Override
    public void run() {
        Files.readAllBytes(Paths.get("config.json")); // IOException будет проброшен как unchecked
    }
}
➡️ Но даже здесь нужно понимать риски — исключение может «уплыть» и убить поток без возможности адекватной обработки. 🗣️ Запомни: @SneakyThrows - это не решение, а побег из тюрьмы checked-исключений через подкоп.

🔴 Завтра тестовое собеседование с Java-разработчиком 11 февраля(уже завтра!) в 19:00 по мск приходи онлайн на открытое собес
🔴 Завтра тестовое собеседование с Java-разработчиком 11 февраля(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика. Как это будет: 📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу 📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью 📂 В конце можно будет задать любой вопрос Сергею Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы. Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot Реклама. О рекламодателе.

⌨️ Какие существуют модификаторы доступа? В Java модификаторы доступа определяют, из каких частей программы можно получить доступ к классу, методу или переменной. Всего их четыре: public – предоставляет полный доступ к классу или его членам из любого другого класса в любом пакете. Применяется для методов, полей и классов, которые должны быть доступны всем. protected – доступ к члену класса разрешен в пределах того же пакета и для подклассов, даже если они находятся в других пакетах. Используется для полей и методов, которые могут быть полезны в наследовании. default (пакетный доступ) – если модификатор не указан явно, доступ к члену класса разрешен только внутри того же пакета. Этот доступ используется для внутренних структур, не предназначенных для использования в других пакетах. private – доступен только внутри того же класса. Поля и методы, помеченные как private, невидимы для других классов, включая подклассы. Эти модификаторы помогают контролировать видимость и инкапсуляцию данных в программе. #java #public #protected #default #private

🎭 Optional.ofNullable() - как не стрелять себе в ногу NullPointerException Кажется, что Optional должен спасать от null. Но если перепутать Optional.of() с Optional.ofNullable() - получишь NPE там, где меньше всего ожидал. Это как заряжать пистолет, думая, что он игрушечный. Вот разбор, как не попасть в эту ловушку и когда что использовать 👇 📂 Шаг 1: В чём разница? (Одно слово - null)

// Если value == null, то:
Optional.of(value);        // 🚨 Выбросит NPE
Optional.ofNullable(value); // ✅ Вернёт Optional.empty()
➡️ Optional.of создаёт контейнер только для не-null значений. Optional.ofNullable - для любых, сам проверит и сделает empty() при null. 📂 Шаг 2: Где стреляет `Optional.of()` (реальный пример) Представь, что метод может вернуть null, а ты оборачиваешь результат в Optional для красоты:

String findNameById(Long id) {
    // ... какой-то поиск, который может вернуть null
}

// Где-то в коде:
Optional<String> name = Optional.of(findNameById(42)); // БАМ! NPE
➡️ Кажется, что ты делаешь «безопасный» контейнер, но of() выстрелит раньше, чем ты успеешь его использовать. Типичная ошибка при рефакторинге старого кода. 📂 Шаг 3: Правильный паттерн для оборачивания неизвестных значений

// Входящее значение может быть null -> только ofNullable
Optional<String> safeName = Optional.ofNullable(findNameById(42));

// Теперь можно безопасно работать
String result = safeName.orElse("По умолчанию");
➡️ ofNullable - это шлюз, который пропускает и null, и значение, превращая первое в empty(). 📂 Шаг 4: А когда тогда нужен `Optional.of()`? Только когда ты на 100% уверен, что значение не null. Например:

// Константы, финальные строки
Optional<String> env = Optional.of("PROD");

// Результаты методов, которые никогда не возвращают null по контракту
Optional<String> uuid = Optional.of(UUID.randomUUID().toString());
➡️ Используй of() как assertion - «это точно не null». Если сомневаешься - ofNullable. 📂 Шаг 5: Тест на внимательность

public Optional<String> extract(boolean flag) {
    String value = flag ? "Java" : null;
    return Optional.of(value); // Что будет при flag = false?
}
➡️ Правильно - NPE при вызове метода, а не пустой Optional. Эту ошибку компилятор не подскажет. 🗣 Запомни: Optional.of() - это крик «я уверен!». Если не готов ловить NPE, как пулю, всегда используй ofNullable.

🔥 БЕСПЛАТНЫЙ КУРС ПО ВРЕМЕННЫМ РЯДАМ И AI 🔥 Ищете практический и углубленный курс, чтобы освоить временные ряды? Мы создали
🔥 БЕСПЛАТНЫЙ КУРС ПО ВРЕМЕННЫМ РЯДАМ И AI 🔥 Ищете практический и углубленный курс, чтобы освоить временные ряды? Мы создали курс из 5 объемных занятий. Это именно то, что нужно, чтобы прокачаться в одной из самых востребованных аналитических областей абсолютно бесплатно! 📌 Темы занятий: 1. Основы анализа временных рядов 2. Прогнозирование на основе временных рядов с помощью AI 3. Выявление аномалий в данных с помощью нейросетей 4. Применение временных рядов в рекомендационных системах 5. Тенденции и будущее анализа временных рядов с AI Почему временные ряды? Потому что это одна из центральных тем, они отличаются тем, что: 🧬 1. Очень нужны компаниям - прям прямая необходимость 🧬 2. Очень непредсказуемые - в отличие от CV, где всё понятно, тут итоговая точность нейронки вообще непредсказуемая 🤖 Присоединяйтесь к нашему бесплатному курсу и разберитесь в этой увлекательной теме с нами!

☕️ Построение ПК приложений. Java Swing JFrame
В этом видео автор переходит от консольных проектов к созданию графических интерфейсов. Вы научитесь разрабатывать полноценное ПК-приложение с использованием библиотеки Java Swing, добавляя в него кнопки, надписи, текстовые поля и другие графические компоненты на основе JFrame.
👉 Ссылка на первоисточник 🗣️Запомни: умение создавать GUI-приложения — это ключевой шаг от учебных задач к разработке реальных программ, которые видят и используют люди. 🤩 Java Фишки и трюки || #Видео

String.format() 🆚 конкатенация vs StringBuilder — кто быстрее на самом деле? Каждый день ты склеиваешь строки. Но если это происходит в горячем цикле или в высоконагруженном микроссервисе — разница в подходах может стоить миллисекунд, а то и секунд процессорного времени. Давай расставим всё по местам с цифрами в руках. Вот бенчмарк-ликбез, после которого ты больше не будешь гадать 👇 🗂 Шаг 1: Подопытные (4 основных способа)
// 1. Конкатенация (+)
String result1 = "Привет, " + name + "! Тебе " + age + " лет.";

// 2. StringBuilder
String result2 = new StringBuilder()
    .append("Привет, ")
    .append(name)
    .append("! Тебе ")
    .append(age)
    .append(" лет.")
    .toString();

// 3. String.format()
String result3 = String.format("Привет, %s! Тебе %d лет.", name, age);

// 4. String.concat() (редко, но бывает)
String result4 = "Привет, ".concat(name).concat("! Тебе ").concat(String.valueOf(age)).concat(" лет.");
➡️ Все они дают один результат. Но под капотом — абсолютно разная механика и производительность. 🗂 Шаг 2: Бенчмарк (условные замеры на 100_000 итераций) Представим результаты в наносекундах на операцию (меньше = лучше):
Конкатенация (+) ........... ~50 нс
StringBuilder .............. ~15 нс
String.format() ............ ~1200 нс
String.concat() ............ ~70 нс
➡️ Главный вывод: String.format() проигрывает в скорости в десятки раз. Он делает парсинг шаблона, проверку типов — это дорого. StringBuilder — чемпион по скорости. 🗂 Шаг 3: Почему «+» иногда не хуже StringBuilder? Важный нюанс! Компилятор сам оптимизирует простую конкатенацию в StringBuilder.
// Ты пишешь:
String s = a + b + c;

// Компилятор делает под капотом:
String s = new StringBuilder().append(a).append(b).append(c).toString();
➡️ Но! Это работает только для литералов и финальных выражений в одной строке кода. В циклах — оптимизации нет! 🗂 Шаг 4: Цикл — место, где «+» убивает производительность
// ❌ ПЛОХО (создаётся новый StringBuilder на КАЖДОЙ итерации)
String result = "";
for (String part : parts) {
    result += part; // Скрытый new StringBuilder() каждый раз!
}

// ✅ ХОРОШО (один StringBuilder на весь цикл)
StringBuilder sb = new StringBuilder();
for (String part : parts) {
    sb.append(part);
}
String result = sb.toString();
➡️ В цикле на 1000 элементов разница будет в сотни раз. Это та самая ошибка, которая съедает CPU. 🗂 Шаг 5: А когда тогда использовать String.format()? Когда читаемость и поддержка важнее скорости: 1. Сложные шаблоны с числами, датами, округлением:
    // Читаемо vs Нечитаемо
    String.format("Сумма: %.2f руб., дата: %tD", sum, date); // ✅
    // vs ручное склеивание с DecimalFormat и SimpleDateFormat — кошмар
    
2. Логирование, где скорость не критична (но даже там лучше шаблонизатор). 3. Конфигурационные сообщения, выносимые в properties-файлы. ➡️ String.format() — это Rolls-Royce: удобно, красиво, но для поездки в магазин за хлебом — избыточно. 🗂 Шаг 6: StringJoiner — твой друг для списков
// Когда нужно собрать строку через разделитель
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.add("Java");
joiner.add("Kotlin");
joiner.add("Scala");
String result = joiner.toString(); // [Java, Kotlin, Scala]
➡️ Под капотом использует StringBuilder, но даёт элегантный API для склейки с разделителями. Используй вместо ручных проверок «не первый ли элемент?». 🗣 Запомни: Выбирай инструмент не по привычке, а по делу.

🕰️ Java Time API: Почему java.util.Date должен умереть В старых версиях Java работа со временем была настоящим квестом. Класс java.util.Date — это, пожалуй, самый неудачный дизайн в истории JDK. 🤦‍♂️ Зал славы проблем Date и Calendar: 1️⃣ Они изменяемые (Mutable): Вы передаете дату в метод, а он может тихо поменять ей год. Это ад для многопоточности. 2️⃣ Нумерация месяцев: Январь — это 0. Декабрь — 11. Сколько багов было написано из-за этого! 3️⃣ Годы: new Date(2023, ...) создаст дату в 3923 году (потому что отсчет идет с 1900). 4️⃣ Нейминг: java.sql.Date наследуется от java.util.Date, но не содержит времени. Путаница неизбежна. ✅ Спасение в Java 8+ (java.time) Java переняла опыт библиотеки Joda-Time и внедрила JSR 310. Теперь у нас есть строгие, неизменяемые и понятные типы. Шпаргалка, что выбрать: 1️⃣ Instant — Точка на временной шкале (Unix Timestamp). Для кого: Для машин, логов и баз данных. Внутри это просто количество секунд с 1970 года (UTC). Пример: Instant.now() 2️⃣ LocalDate / LocalTime — "Дата в календаре" и "Время на часах". Без часового пояса. Для кого: Дни рождения, праздники ("Новый год всегда 1 января", неважно, где вы). Пример: LocalDate.of(2023, Month.JANUARY, 1) 3️⃣ ZonedDateTime — Полный фарш: дата + время + часовой пояс. Для кого: Для организации встреч звонков между странами. Учитывает переход на летнее время! 💎 Неизменяемость (Immutability): Больше никаких сюрпризов. Методы изменения времени всегда возвращают новый объект.

LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1); // today остался прежним!
💡 Лайфхак: Если вам нужно посчитать разницу между датами, используйте Period (для дней/месяцев) или Duration (для секунд/наносекунд).

long days = ChronoUnit.DAYS.between(date1, date2);

🤖Уведомления в Android — это важный инструмент взаимодействия с пользователем, который напрямую влияет на вовлечённость, ret
🤖Уведомления в Android — это важный инструмент взаимодействия с пользователем, который напрямую влияет на вовлечённость, retention и пользовательский опыт. Ошибки в работе с уведомлениями — частая проблема у начинающих Android-разработчиков. 📆На открытом вебинаре OTUS разберём все виды уведомлений в Android: от базовых до более продвинутых сценариев. Покажем, какие типы уведомлений существуют, чем они отличаются и в каких случаях используются. Все примеры — с разбором кода и практической реализацией. За один вебинар вы получите системное понимание уведомлений в Android и научитесь внедрять их в свои проекты осознанно, а не методом проб и ошибок. Это базовый навык, без которого невозможно двигаться дальше в Android-разработке. 💥Встречаемся 27 января в 20:00 МСК в преддверии старта курса «Android Developer». Регистрация открыта: https://otus.pw/8KD0/ Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576

⌨️ Java CompletableFuture: Прокачанный Future Помните интерфейс Future из Java 5? Он был похож на чемодан без ручки. Вы могли отправить задачу в другой поток, но чтобы получить результат, приходилось делать future.get() и блокировать текущий поток, ожидая ответа. Смысл асинхронности терялся. В Java 8 появился `CompletableFuture`. Это Future, который можно "программировать". 🔗 Цепочки вызовов (Pipelining) Вместо того чтобы ждать результат, вы говорите Java: *"Когда результат будет готов, сделай с ним вот это, а потом вот это"*.

CompletableFuture.supplyAsync(() -> {
    return fetchUser(123); // 1. Идем в базу (в другом потоке)
}).thenApply(user -> {
    return user.getEmail(); // 2. Преобразуем (когда данные пришли)
}).thenAccept(email -> {
    sendEmail(email);       // 3. Отправляем письмо
});

System.out.println("Я не заблокирован!"); // Эта строка выполнится мгновенно
💪 Суперсила: Комбинация задач Представьте, что вам нужно получить данные пользователя И его последние заказы из разных сервисов одновременно.

var userFuture = CompletableFuture.supplyAsync(() -> getUser());
var ordersFuture = CompletableFuture.supplyAsync(() -> getOrders());

// Ждем оба, комбинируем результаты и возвращаем отчет
userFuture.thenCombine(ordersFuture, (user, orders) -> {
    return new Report(user, orders);
}).thenAccept(report -> show(report));
Оба запроса выполняются параллельно. Время выполнения равно времени самого медленного запроса, а не их сумме. ⚠️ Опасная ловушка По умолчанию CompletableFuture использует общий пул потоков ForkJoinPool.commonPool(). Если вы запустите там "тяжелую" блокирующую задачу (например, долгий запрос к БД без асинхронного драйвера), вы можете забить все потоки и "повесить" всё приложение. Совет: Всегда передавайте свой собственный Executor вторым аргументом:

CompletableFuture.supplyAsync(() -> heavyTask(), myCustomExecutor);

🔴 Завтра тестовое собеседование с Java-разработчиком 21 января(уже завтра!) в 19:00 по мск приходи онлайн на открытое собесе
🔴 Завтра тестовое собеседование с Java-разработчиком 21 января(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика. Как это будет: 📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу 📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью 📂 В конце можно будет задать любой вопрос Сергею Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы. Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot Реклама. О рекламодателе.

👻 Java `volatile`: Призрак в памяти Спросите на собеседовании: «Зачем нужен volatile?», и 80% ответят: «Чтобы потоки не конфликтовали». Это не совсем так. volatile не спасает от конфликтов (race conditions). Он решает проблему видимости. Давайте разберемся, почему ваш код может зависнуть навечно без этого слова. 🛑 Ситуация: Бесконечный цикл Представьте простой код:

public class Worker {
    private boolean running = true; // ❌ Забыли volatile

    public void run() {
        while (running) {
            // Крутимся в цикле и ждем команды стоп
        }
        System.out.println("Stopped!");
    }

    public void stop() {
        running = false;
    }
}
Вы запускаете run() в Потоке А, а через секунду вызываете stop() из Потока Б. Ожидание: Цикл остановится. Реальность: Программа может работать вечно. Почему? (Кэши процессора) Современные процессоры супер-быстрые, а оперативная память (RAM) — медленная. Чтобы не ждать память, каждое ядро процессора имеет свой локальный кэш (L1/L2 Cache). 1. Поток А (Ядро 1) скачал переменную running = true в свой кэш. Он проверяет её там. 2. Поток Б (Ядро 2) меняет значение на false в *своем* кэше и даже записывает в RAM. 3. Но Поток А этого не видит! Он продолжает читать true из своего изолированного кэша. Это называется проблема видимости. ✨ Магия `volatile` Если мы напишем:

private volatile boolean running = true;
Мы говорим JVM и процессору: «Эту переменную нельзя кэшировать локально». 1. Любая запись в volatile переменную сразу пробрасывается (flush) в общую память (RAM). 2. Любое чтение volatile переменной идет напрямую из общей памяти. Поток А сразу увидит изменение, и цикл остановится. ⚠️ Главная ловушка: Атомарность Многие новички думают: «О, volatile синхронизирует данные! Значит можно делать счетчик».

volatile int count = 0;
count++; // ⛔️ ОПАСНО!
НЕТ! Операция count++ — это три действия: считать, прибавить, записать. volatile гарантирует, что вы считаете свежее значение, но он не блокирует других от изменения этой переменной в тот же момент. Два потока могут одновременно считать 5, оба добавят 1 и запишут 6. Мы потеряем данные. Итог: 👉 Используйте volatile только для флагов состояния (вкл/выкл) или когда запись делает только один поток. 👉 Для счетчиков используйте AtomicInteger или synchronized.