Java | Фишки и трюки
7.21K subscribers
182 photos
29 videos
6 files
40 links
Java: примеры кода, интересные фишки и полезные трюки

Купить рекламу: https://telega.in/c/java_tips_and_tricks

✍️По всем вопросам: @Pascal4eg
Download Telegram
☕️ CI/CD с Jenkins Pipelines — как Java проект летит в прод

Сборка руками? Заливка через FTP?
⛔️ Это не девелопмент, а тетрис в продакшене.

Jenkins автоматизирует всё — от сборки до деплоя, пока ты пьёшь кофе ☕️
Вот как это работает на Java 👇

⚙️ Что такое Jenkins Pipeline

Jenkinsfile — это сценарий, который описывает, что и как делать с кодом.
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './gradlew build'
}
}
stage('Test') {
steps {
sh './gradlew test'
}
}
stage('Deploy') {
steps {
sh 'scp build/libs/app.jar user@server:/app/'
}
}
}
}

🟢 Всё по шагам: сначала билд, потом тесты, потом деплой
➡️ Каждый коммит триггерит этот сценарий

🔥 Плохо без CI/CD:
🟢Костыльные .bat скрипты и ручной scp
🟢Кто-то залил баг — тесты никто не запускал
🟢Ночью прод лег, никто не понял, почему


✔️ Хорошо с Jenkins:
🟢git push → Jenkins билдит и пушит сам
🟢Ошибся в тестах? Не пройдёшь дальше
🟢Всё логируется, откатывается и работает одинаково везде


👍 CI — это контроль. CD — это доставка без боли.

Сборка Java проекта в пайплайне
stage('Build') {
steps {
sh './mvnw package -DskipTests'
}
}

➡️ Jenkins билдит .jar или .war сам
🟢 Поддерживает Gradle, Maven, Ant — что угодно

🧪 Тесты — обязательный рубеж
stage('Test') {
steps {
sh './mvnw test'
}
}

➡️Никаких «ну оно у меня работало»
🟢 Прогон JUnit и Coverage на каждый коммит

🚀 Деплой на сервер — без доступа к ssh
stage('Deploy') {
steps {
sh 'scp target/app.jar user@host:/srv/'
}
}

➡️ Jenkins сам подключается, копирует, рестартит
🟢 Или делает docker build + push → K8s deploy

🗣️ Запомни: CI/CD нужен не когда всё плохо. А чтобы плохо не стало. Jenkins спасёт от багов, усталости и вечного "вручную".
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥65
☕️ ArrayList & HashMap: быстрые коллекции, которые решают всё

В Java массивы — фиксированные, а коллекции умеют расти, менять размер и работать с разными типами данных.
ArrayList и HashMap — два столпа, на которых держатся 80% задач.

🔢. Создание и добавление
import java.util.ArrayList;

ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");

👉 ArrayList хранит элементы в порядке добавления и умеет расти автоматически.

🔢. Доступ по индексу
String lang = list.get(0);
System.out.println(lang); // Java

👉 Как в массиве: быстрый доступ по номеру, но индекс должен быть в пределах размера.

🔢. Перебор элементов
for (String l : list) {
System.out.println(l);
}

👉 Удобный синтаксис for-each для чтения всех значений без возни с индексами.

🔢. HashMap: хранение по ключу
import java.util.HashMap;

HashMap<String, Integer> map = new HashMap<>();
map.put("Apples", 5);
map.put("Oranges", 3);

👉 HashMap — это ключ-значение. Ключи уникальны, значения можно повторять.

🔢. Получение по ключу
int count = map.get("Apples");
System.out.println(count); // 5

👉 Быстрое извлечение: доступ почти моментальный.

🔢. Проверка наличия
if (map.containsKey("Oranges")) {
System.out.println("Есть апельсины!");
}

👉 Нет ключа — вернёт false, а не ошибку.

🗝 🔢. Перебор пар
for (var entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}

👉 Легко пройтись сразу и по ключам, и по значениям.


🗣️ Запомни: массивы — это база, но ArrayList и HashMap дают гибкость. Используй массивы, когда важна скорость и фиксированный размер, а коллекции — когда нужно удобно, динамично и без лишней возни.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍115🔥4
Media is too big
VIEW IN TELEGRAM
☕️Условные конструкции (if-else, switch-case)

Это видео объясняет, как работают условные операторы в Java. Ты узнаешь, как с помощью if, else if, else и switch-case принимать решения в коде и управлять логикой программы.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43🔥2
⌨️ Дженерики (generics) — это механизм, который позволяет создавать классы, интерфейсы и методы, работающие с различными типами данных, при этом сохраняя строгую типизацию. Они были введены в Java начиная с версии 5 для повышения безопасности типов и повторного использования кода.

Дженерики позволяют определить параметр типа, который будет заменен конкретным типом данных при создании экземпляра класса или вызове метода. Например, List<T> может использоваться как List<String>, List<Integer> и т.д.

Благодаря дженерикам, ошибки типа (например, попытка вставить объект неправильного типа) обнаруживаются на этапе компиляции, а не во время выполнения программы.

Один универсальный класс или метод может работать с различными типами данных, что позволяет избежать дублирования кода.

Во время компиляции информация о типах стирается, и вместо этого используется базовый тип, что позволяет сохранять совместимость с кодом, написанным до появления дженериков.


public class Box<T> {
private T item;

public void setItem(T item) {
this.item = item;
}

public T getItem() {
return item;
}
}

Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String str = stringBox.getItem();

В этом примере класс Box<T> является обобщенным и может работать с любым типом данных, который заменит T при создании объекта.

#java #generics #T
Please open Telegram to view this post
VIEW IN TELEGRAM
👍165🔥4
⌨️ StringJoiner - это класс, который предназначен для объединения строк с использованием разделителя между ними. Этот класс был введен в Java 8 в пакете java.util. Он облегчает создание текстовых последовательностей, объединяя строки и вставляя разделители между ними.

Вызов метода toString() возвращает объединенную строку с разделителями и окружающими символами.

Вы также можете использовать метод setEmptyValue() для определения значения, которое будет использоваться, если StringJoiner остается пустым.


StringJoiner sj = new StringJoiner(", ", "[" , "]");
sj.setEmptyValue("No items");
System.out.println(sj.toString()); // No items

sj.add("Apple").add("Banana").add("Cherry");
System.out.println(sj.toString()); // [Apple, Banana, Cherry]


#java #StringJoiner
Please open Telegram to view this post
VIEW IN TELEGRAM
12👍9🔥4
☕️Distributed Tracing — Zipkin без боли

Когда у тебя микросервисы, баги превращаются в квест.
Клиент сделал запрос ➡️ прошёл через API ➡️ сервис авторизации ➡️ сервис платежей ➡️ сервис нотификаций.
А потом где-то что-то упало. Где искать?
Логов тонны, grep не спасает.

Тут появляется Zipkin — Sherlock Holmes твоей системы.

📦 Поднять Zipkin проще простого

Через Docker:
docker run -d -p 9411:9411 openzipkin/zipkin

➡️ Открываешь [http://localhost:9411](http://localhost:9411) — готово. У тебя веб-интерфейс для трассировок.

🧠 Как это работает

Zipkin использует концепцию trace и span:

🟢trace = весь путь запроса от начала до конца
🟢span = отдельный шаг внутри пути (запрос к БД, вызов другого сервиса)

Каждый сервис при вызове следующего передаёт trace-id и span-id в заголовках (обычно X-B3-TraceId, X-B3-SpanId).
➡️ В итоге: в UI ты видишь красивую диаграмму, где висит каждый шаг.

⚙️ Интеграция в Spring Boot

Добавляешь зависимости:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>


А в application.yml:
spring:
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0

➡️ Теперь каждый запрос автоматически получает trace-id, а все логи будут содержать [traceId, spanId].

🔍 Пример лога
2025-08-22 12:42:01 [traceId=4af3, spanId=1a2b] Calling PaymentService

➡️ Видишь traceId? Теперь ты можешь в Zipkin найти все вызовы, связанные с этим запросом.

🎯 Зачем это реально нужно

👍 🕵️ Находить медленные места (Bottlenecks)
👍 🐛 Локализовать баг в цепочке сервисов
👍 📊 Смотреть статистику по задержкам и ошибкам


🗣️ Запомни: В микросервисах без трассировки ты как водитель без фар ночью.
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍7
⚡️ Reactive Streams в Java — WebFlux + Project Reactor

Когда у тебя миллион запросов и сервер начинает задыхаться — обычный Spring MVC с потоками умирает. Тут выходит на сцену Spring WebFlux и движок Project Reactor. Асинхронщина, backpressure, и код, который реально дышит под нагрузкой.

📦 Подключаем WebFlux в pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

➡️ Всё. Теперь у тебя вместо старого DispatcherServlet — реактивный движок Netty.

🧠 Минимальный контроллер на WebFlux:
@RestController
public class HelloController {
@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("👋 Привет, реактивный мир!");
}
}

➡️ Mono = 0..1 значение (как Future). Flux = поток значений.

📛 Пример со стримом чисел:
@GetMapping("/numbers")
public Flux<Integer> numbers() {
return Flux.range(1, 5).delayElements(Duration.ofSeconds(1));
}

➡️ Клиент получает числа по одному каждую секунду. Не JSON сразу, а настоящий поток.

🔄 Реактивные операторы (Project Reactor):
Flux.just("A", "B", "C")
.map(s -> s.toLowerCase())
.filter(s -> !s.equals("b"))
.subscribe(System.out::println);

➡️ Конвейер: map → filter → подписка. Реактивщина — это не if’ы и for, а цепочки.

💥 Backpressure — контроль нагрузки:
Flux.interval(Duration.ofMillis(10))
.onBackpressureDrop()
.subscribe(i -> System.out.println("⚡️ " + i));

➡️ Если потребитель не успевает — лишние данные отбрасываются. Ты управляешь потоком, а не он тобой.

🔐 Интеграция с БД через R2DBC:
@Repository
public interface UserRepo extends ReactiveCrudRepository<User, String> {}

@GetMapping("/users")
public Flux<User> getUsers() {
return userRepo.findAll();
}

➡️ Даже база работает реактивно. Никаких блокировок потоков.

Чем отличается WebFlux 🆚 Project Reactor?

👍 Project Reactor = библиотека реактивных стримов (Mono, Flux, операторы).
👍 WebFlux = фреймворк на базе Reactor, чтобы писать контроллеры, API и сервисы.


📚 Документация:
👉 Reactor 👈

👉 WebFlux 👈


🗣️ Запомни: Reactive Streams — это не магия ради хайпа. Это способ выжить под нагрузкой, где обычный синхронный код падает. Если хочешь сервер, который тащит десятки тысяч соединений — без WebFlux и Reactor ты уже динозавр.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥105👍5
Media is too big
VIEW IN TELEGRAM
☕️ Массивы данных. Одномерные и многомерные массивы

В этом видео разбираются основы работы с массивами в Java. Автор показывает, как создавать и использовать одномерные и многомерные массивы, как обращаться к их элементам и перебирать данные. Это базовый инструмент, без которого невозможно работать с большими наборами информации.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍4
🧹 Advanced GC Tuning — ZGC в Java

Если ты думаешь, что GC — это просто «собрал мусор и пошёл дальше», то добро пожаловать в ад продакшена с кучей тредов и 200 ГБ heap’а.
Обычный GC может стопнуть мир на секунду — и твой сервис превращается в «слайдшоу». Для маркетплейса или финтеха — смерть.
Тут на сцену выходит ZGC — сборщик мусора, который обещает паузы меньше 1 мс, даже на огромных хипах.

📦 Как включить ZGC

Запускаешь Java-приложение с флагами:

java -XX:+UseZGC -Xmx16g -Xms16g -jar app.jar

➡️ -XX:+UseZGC активирует Z Garbage Collector.
➡️ Работает начиная с JDK 11 (продакшен-стабил с 15).

🧠 В чём магия ZGC?

Обычный GC работает так: стопнул мир → пересобрал кучу → отпустил.
ZGC же:

🟢 Делает конкурентную маркировку и компактификацию (почти весь GC идёт параллельно с твоим кодом).
🟢 Использует цветные указатели (colored pointers), чтобы отслеживать состояние объектов.
🟢 Работает без длинных стопов — паузы \~0.5–2 мс даже при 100+ ГБ heap.

➡️ Идеален для real-time систем, API с миллионами RPS, финансовых транзакций.

⚙️ Настройки для тюнинга

Базовые:
-XX:+UseZGC
-XX:ZUncommitDelay=300
-XX:SoftMaxHeapSize=12g


🟢 ZUncommitDelay — через сколько секунд освобождать неиспользуемую память (по умолчанию 300).
🟢 SoftMaxHeapSize — мягкий лимит кучи (GC будет стараться держаться в нём, даже если Xmx больше).


Мониторинг:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintGC -Xlog:gc*:gc.log

➡️ Логи GC покажут, как часто идут паузы и сколько памяти реально отжимается.


🔍 Пример GC-лога с ZGC
[2.345s][info][gc,start    ] GC(0) Pause Mark Start
[2.345s][info][gc ] GC(0) Concurrent Mark
[2.348s][info][gc ] GC(0) Pause Mark End 0.2ms
[2.349s][info][gc ] GC(0) Concurrent Relocate

➡️ Видишь? Паузы меньше миллисекунды, всё остальное уходит в concurrent.

Когда брать ZGC, а когда нет

🟢 Бери:

🟢 У тебя огромные хипы (16–500 ГБ).
🟢 Важны низкие latency (игры, трейдинг, API).
🟢 Ты не хочешь тратить дни на ручной тюнинг старых GC.


🔴 Не бери:

🟢Если у тебя микросервис на 512 МБ heap (Overkill).
🟢Если нужен максимальный throughput (G1 иногда быстрее на маленьких heap’ах).


🗣️ Запомни:Он не делает чудес — память всё равно надо оптимизировать, но он гарантирует, что GC-паузы не убьют твой SLA.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍54🔥4👏1
Media is too big
VIEW IN TELEGRAM
☕️ Коллекции в Джава (Collections Framework)

В этом видео автор подробно объясняет, как работает Collections Framework в Java.
Пошагово демонстрируются основные интерфейсы и реализации: List, Set, Map, а также важные классы вроде ArrayList, HashSet, HashMap. Показано, как правильно выбирать подходящую коллекцию, когда использовать список, а когда — отображение, как добавлять, удалять и перебирать элементы.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍3🔥3😁1
⚡️ Reactive Streams: поток вместо цикла — и это работает

В обычном Java-коде всё блокируется: запрос ждёт ответ, поток простаивает.
В реактивщине (Spring WebFlux) — данные текут как река.
Ты подписался на поток → получил элементы по мере готовности.


📛 Пример: возвращаем поток данных
import reactor.core.publisher.Flux;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
@GetMapping("/users")
public Flux<String> getUsers() {
return Flux.just("Alice", "Bob", "Charlie");
}
}

➡️ Не List, а Flux.
Клиент получит поэлементно: Alice → Bob → Charlie.

🔄 Mono vs Flux

👍 Mono<T> → 0..1 элемент
👍 Flux<T> → 0..N элементов
@GetMapping("/user")
public Mono<String> getUser() {
return Mono.just("Alice");
}

➡️ Реактивщина различает одиночный результат и поток.

🧪 Преобразования прямо в цепочке
Flux.range(1, 5)
.map(i -> i * 2)
.filter(i -> i > 5)
.subscribe(System.out::println);

➡️ Поток как коллекция, но лениво и неблокирующе.

📦 Асинхронные запросы
@GetMapping("/external")
public Mono<String> callApi(WebClient client) {
return client.get()
.uri("https://api.example.com/data")
.retrieve()
.bodyToMono(String.class);
}

➡️ Без блокировок. Пока API думает — поток свободен для других задач.

🔗 Комбинация потоков
Mono<String> u1 = Mono.just("Alice");
Mono<String> u2 = Mono.just("Bob");

Flux<String> merged = Flux.merge(u1, u2);

➡️ Потоки объединяются — результат приходит как готов.

👀 Backpressure (контроль скорости)
Publisher шлёт данные → Subscriber сам говорит, сколько готов принять.
Нет перегрузки, система держится даже при большом потоке событий.



🗣️ Запомни: Не блокируйся на ожидании. Подписывайся, фильтруй и трансформируй.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤‍🔥43
ZGC 🆚 Shenandoah — битва без стоп-пауз

Java в 2025 уже умеет два монстра low-latency GC: ZGC и Shenandoah.
Оба обещают микросекундные паузы даже на кучах в сотни гигабайт.
Но у каждого свой характер. Давай разберёмся.

📦 Запуск

ZGC (JDK 15+):
java -XX:+UseZGC -Xmx16g -Xms16g -jar app.jar


Shenandoah (JDK 12+, лучше JDK 17+):
java -XX:+UseShenandoahGC -Xmx16g -Xms16g -jar app.jar


➡️ Оба вкл/выкл одним флагом.

🧠 Архитектура под капотом

🔵 ZGC:

🟢Использует colored pointers (цветные указатели в 64-битных адресах).
🟢Разносит паузы на микросекундные stop-the-world (STW).
🟢Очень простая логика: mark → relocate, всё конкурентно.


🔴 Shenandoah:

🟢Делает concurrent compacting: сжимает кучу без паузы.
🟢Использует brooks pointers (доп. уровень индирекции → каждый объект указывает на forwarder).
🟢Чуть медленнее из-за этой индирекции, но тоже держит паузы \~<10мс.


⚙️ Пример приложения для сравнения
import java.util.ArrayList;
import java.util.List;

public class StressTest {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
while (true) {
for (int i = 0; i < 100; i++) {
list.add(new byte[1024 * 1024]); // 100MB
}
if (list.size() > 2000) list.clear(); // держим heap живым
}
}
}


📊 Запускаем и смотрим логи

🔵 ZGC (-XX:+UseZGC -Xlog:gc*):
[2.345s][info][gc] GC(7) Pause Mark Start 0.2ms
[2.345s][info][gc] GC(7) Pause Mark End 0.1ms
[2.346s][info][gc] GC(7) Concurrent Relocate 45.6ms

➡️ Паузы \~0.1–0.3 мс. Основная работа уходит в «Concurrent Relocate», но это в фоне.

🔴 Shenandoah (-XX:+UseShenandoahGC -Xlog:gc*):
[2.456s][info][gc] Pause Init Mark 1.2ms
[2.457s][info][gc] Concurrent Mark 38.9ms
[2.496s][info][gc] Pause Final Evac 1.5ms

➡️ Паузы больше (1–2 мс), но всё ещё очень маленькие.


🧪 Реальные различия (TL;DR)

Характеристика        | ZGC 🟢                       | Shenandoah 🔴
-------------------- | ---------------------------- | ----------------------------
⏱️ Паузы (latency) | ~0.2–2 мс (супер-стабильно) | ~1–10 мс (чуть длиннее)
⚡️ Throughput | Чуть выше (нет индирекции) | Чуть ниже (Brooks ptr)
🗂 Heap size | До 16 ТБ (!) | Ограничен ~4–8 ТБ
🧩 Сложность | Простая модель | Более гибкий, но сложнее
🎯 Подходит для | Trading, real-time API | Крупные JVM-сервисы, где не критичны 2–3 мс



🎯 Выводы

👍 ZGC → если нужна ультра-низкая задержка (финтех, realtime). Работает предсказуемо.
👍 Shenandoah → если heap до пары терабайт и важнее гибкость (много опций тюнинга).

🗣️ Запомни: ZGC = хирург с лазером, точный и быстрый.Shenandoah = механик с инструментами, чуть дольше, но чинит даже сложные ситуации.В продакшене пробуй оба — профит зависит от нагрузки и архитектуры.
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍4🔥4
🧩 Sealed Classes — контроль наследования в Java

Java 17 принесла sealed classes — способ держать наследование под контролем. Больше никаких «кто угодно расширил класс и сделал фигню». Теперь ты сам решаешь, кто может быть наследником.

📦 Базовый sealed-класс

public sealed class Shape
permits Circle, Rectangle, Triangle {}

➡️ Только три класса могут расширять Shape.

🔑 Наследники обязаны выбрать модификатор

public final class Circle extends Shape {}
public non-sealed class Rectangle extends Shape {}
public sealed class Triangle extends Shape
permits Equilateral, Isosceles {}

🟢 final — тупик, дальше наследовать нельзя.
🟢 sealed — можно, но только по твоему списку.
🟢 non-sealed — снимает ограничения, разрешает наследование всем.

🧠 Идеально для switch

static String type(Shape s) {
return switch (s) {
case Circle c -> "Окружность";
case Rectangle r -> "Прямоугольник";
case Triangle t -> "Треугольник";
};
}

➡️ Компилятор знает все варианты и не требует default. Ты не забудешь обработать новый тип.

🛠 Контроль API

public sealed interface Request
permits Login, Register, Ping {}

public final class Login implements Request {}
public final class Register implements Request {}
public final class Ping implements Request {}

➡️ Никто не добавит лишний «левый» запрос. Архитектура закрыта и прозрачна.


📚 Отличие от abstract
🟢abstract оставляет свободу: наследников сколько угодно.
🟢sealed фиксирует список: никакой самодеятельности.

➡️ Это не про обязательные методы, а про ограничение иерархии.

🧱 Комбо с record

public sealed interface Shape permits Circle, Rectangle {}

public record Circle(double r) implements Shape {}
public record Rectangle(double w, double h) implements Shape {}

➡️ Получаешь компактные immutable-модели и полную предсказуемость.


🗣️ Запомни: Используй в API, моделях и ролях, где важна безопасность и прозрачность.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍115🔥4👏1
🧪 Records с Validation: immutable данные с проверкой на входе

В Java record — это идеальный инструмент для работы с неизменяемыми объектами. Но часто нам нужно не просто хранить данные, а проверять их корректность при создании. Хорошая новость: в record это встроено прямо в конструктор.

🔒 Простая неизменяемость
public record User(String name, int age) {}

➡️ Все поля final, сеттеров нет, объект полностью immutable. Но можно передать хоть отрицательный возраст — компилятор не остановит.


⚖️ Валидация в compact-конструкторе
public record User(String name, int age) {
public User {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("Имя не может быть пустым");
}
if (age < 0) {
throw new IllegalArgumentException("Возраст не может быть отрицательным");
}
}
}

➡️Такой конструктор автоматически вызывается при каждом new User(...), и данные сразу проверяются.

🛡 Гарантия на уровне модели
User u = new User("Иван", 25);   //  всё ок
User bad = new User("", -5); // IllegalArgumentException

➡️ Теперь ошибка «грязных данных» невозможна: объект даже не создастся.

🔄 Компактность vs. явный конструктор

Можно написать и полный конструктор:
public User(String name, int age) {
if (age < 18) throw new IllegalArgumentException("Только 18+");
this.name = name;
this.age = age;
}

➡️ Но компактная форма предпочтительнее — она короче и сразу показывает, что мы в record.

📦 Валидация коллекций внутри record
public record Order(List<String> items) {
public Order {
if (items == null || items.isEmpty()) {
throw new IllegalArgumentException("Заказ пуст");
}
items = List.copyOf(items); // защитная копия
}
}

➡️ Даже список внутри immutable — теперь никто не сможет изменить заказ после создания.

🚦 Использование с аннотациями Bean Validation
public record Customer(
@NotBlank String name,
@Email String email
) {}

➡️Если подключён Hibernate Validator или Jakarta Validation, поля проверятся автоматически.


🧩 Кейс: «валидный объект» как контракт
public record Email(String value) {
public Email {
if (!value.matches("^[\\w.-]+@[\\w.-]+\\.[a-z]{2,}$")) {
throw new IllegalArgumentException("Некорректный email");
}
}
}

➡️ Теперь в коде невозможно передать «плохую строку» туда, где нужен Email.


🗣️ Запомни: record — это не просто сокращённый POJO, а мощный способ гарантировать неизменяемость и корректность данных ещё при создании объекта.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1310🔥5
Media is too big
VIEW IN TELEGRAM
☕️Основные концепции ООП

В этом видео автор знакомит с фундаментальными принципами объектно-ориентированного программирования (ООП) на примере Java. Объясняется, для чего нужно ООП и каковы его ключевые концепции, без глубокого вникания в код.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
3
🔄 CompletableFuture + ExecutorService — сложные асинхронные цепочки

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

⚡️ Запуск асинхронной задачи
ExecutorService executor = Executors.newFixedThreadPool(4);

CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> {
sleep(500);
return "Результат";
}, executor);

System.out.println(future.join()); // блокируемся и получаем результат
executor.shutdown();

➡️ supplyAsync запускает задачу в отдельном потоке из пула.

🔗 Построение цепочек
CompletableFuture<Integer> chain =
CompletableFuture.supplyAsync(() -> 10)
.thenApply(x -> x * 2)
.thenApply(x -> x + 5);

System.out.println(chain.join()); // 25

➡️ Каждая операция обрабатывает результат предыдущей. Код становится декларативным.

🚦 Обработка ошибок
CompletableFuture<String> safe =
CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("Ошибка");
return "OK";
}).exceptionally(ex -> "Fallback");

System.out.println(safe.join()); // Fallback

➡️ exceptionally позволяет задать запасной путь при сбое.

⏱️ Таймауты
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> {
sleep(2000);
return "Долго";
});

String result = future.orTimeout(1, TimeUnit.SECONDS)
.exceptionally(ex -> "Таймаут")
.join();

System.out.println(result); // Таймаут

➡️ orTimeout прерывает выполнение, если задача не завершилась за указанное время.

🔀 Запуск нескольких задач
CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> 2);

CompletableFuture<Integer> sum =
a.thenCombine(b, Integer::sum);

System.out.println(sum.join()); // 3

➡️ thenCombine позволяет объединять результаты из разных потоков.

🎭 AnyOf и AllOf
CompletableFuture<Object> any =
CompletableFuture.anyOf(a, b);

CompletableFuture<Void> all =
CompletableFuture.allOf(a, b);

➡️ anyOf завершится, когда готов хотя бы один, allOf — когда готовы все.

🧩 ExecutorService как фундамент
Важно передавать свой ExecutorService в supplyAsync и runAsync. Иначе будет использоваться общий ForkJoinPool, который не всегда предсказуем.



🗣️ Запомни: CompletableFuture + ExecutorService дают асинхронность, цепочки и контроль ошибок прямо из коробки. Умение строить такие цепи позволяет писать быстрые и надёжные сервисы без Reactive или сторонних решений.
Please open Telegram to view this post
VIEW IN TELEGRAM
111👍9🔥1
🛠 Memory-mapped Files — работа с файлами через ByteBuffer

Обычно мы читаем и пишем файлы через стандартные IO-потоки. Но у этого подхода есть потолок скорости. Memory-mapped files позволяют работать с файлами напрямую через память, минуя часть «лишних движений».

📂 Простое чтение через FileChannel
try (FileChannel channel = FileChannel.open(Path.of("data.txt"), StandardOpenOption.READ)) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
}

➡️ Файл как будто целиком «подмонтирован» в память, а чтение идёт напрямую через ByteBuffer.

💾 Запись в файл
try (FileChannel channel = FileChannel.open(Path.of("out.txt"),
StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
buffer.put("Hello world!".getBytes());
}

➡️ Любые изменения в буфере автоматически попадают в файл.

Работа с очень большими файлами
long size = 10L * 1024 * 1024 * 1024; // 10 GB
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);

➡️ Даже огромные файлы можно обрабатывать кусками, без полного чтения в память.

🔄 Двусторонний доступ
buffer.put(0, (byte) 'X');
System.out.println((char) buffer.get(0));

➡️ Одно и то же отображение позволяет и читать, и писать данные на месте.

🧱 Ограничения
Memory-mapped файлы зависят от ОС и архитектуры. Если закончилась виртуальная память — программа рухнет.

➡️ Нужно аккуратно выбирать размер отображения и закрывать ресурсы.

🧵 Использование в многопоточке
// Несколько потоков читают один и тот же MappedByteBuffer

➡️ Общая память = быстрый обмен, но придётся синхронизировать доступ.


🗣️ Запомни:Memory-mapped files — это способ работать с файлами так, будто они часть оперативки. Он даёт скорость и гибкость, но требует аккуратного контроля памяти и синхронизации.
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍93
🏗 Java Module System (JPMS) — модульность приложения

С Java 9 появилась модульная система (JPMS), которая помогает разделять проект на независимые блоки и жёстко контролировать зависимости. Это шаг дальше от обычных JAR-файлов и классического classpath.

📦 Создание модуля через module-info.java
module com.example.app {
requires com.example.service;
exports com.example.app.api;
}

➡️ Файл module-info.java лежит в корне src/com.example.app и описывает, какие модули нужны (requires) и какие пакеты доступны наружу (exports). Всё остальное скрыто.

🔒 Инкапсуляция пакетов
module com.example.service {
exports com.example.service.api;
// внутренние классы не экспортируются
}

➡️Код внутри модуля по умолчанию недоступен извне. Это защита от случайного использования «внутренних» API.

🔗 Транзитивные зависимости
module com.example.web {
requires transitive com.example.service;
}

➡️ Если модуль A требует B как transitive, то любой, кто подключает A, автоматически получает доступ к B.

🧩 Пример: приложение из трёх модулей
com.example.model   → только данные
com.example.service → бизнес-логика
com.example.app → точка входа

➡️ Модули явно описывают связи. Код не сможет обратиться к тому, что не экспортировано.

⚠️ Ошибка при неправильной зависимости
module com.example.model {
// ничего не экспортирует
}

module com.example.app {
requires com.example.model;
}

➡️ Компилятор выдаст ошибку: package ... is not visible. Нужно явно exports.

🚦 Сравнение с классическим classpath
👍 classpath: всё видно всем, легко словить «spaghetti» зависимостей.
👍 modules: строгие границы, меньше сюрпризов при рефакторинге.


🔧 Совместимость и migration
🔴 Старые JAR можно подключать через automatic modules.
🔴 Постепенно можно переносить проект на полноценные модули.


🗣️ Запомни: JPMS — это не только про разбиение проекта, но и про контроль связей. Модули делают систему чище, безопаснее и помогают строить большие приложения без хаоса зависимостей.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍54
Media is too big
VIEW IN TELEGRAM
☕️Функции в языке Java

В этом видео автор подробно разбирает, как создавать и использовать функции (методы) в Java.
Пошагово объясняется, как объявлять методы, передавать параметры, возвращать значения и вызывать их из других частей программы.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
3
⌨️ Что такое default методы интрефейса?

Java 8 позволяет добавлять неабстрактные реализации методов в интерфейс, используя ключевое слово default:

interface Example {
int process(int a);
default void show() {
System.out.println("default show()");
}
}



✔️Если класс реализует интерфейс, он может, но не обязан, реализовать методы по-умолчанию, уже реализованные в интерфейсе. Класс наследует реализацию по умолчанию.

✔️Если некий класс реализует несколько интерфейсов, которые имеют одинаковый метод по умолчанию, то класс должен реализовать метод с совпадающей сигнатурой самостоятельно. Ситуация аналогична, если один интерфейс имеет метод по умолчанию, а в другом этот же метод является абстрактным - никакой реализации по умолчанию классом не наследуется.

✔️Метод по умолчанию не может переопределить метод класса java.lang.Object.

✔️Помогают реализовывать интерфейсы без страха нарушить работу других классов.

✔️Позволяют избежать создания служебных классов, так как все необходимые методы могут быть представлены в самих интерфейсах.

✔️Дают свободу классам выбрать метод, который нужно переопределить.

✔️Одной из основных причин внедрения методов по умолчанию является возможность коллекций в Java 8 использовать лямбда-выражения.

Вызывать default метод интерфейса в реализующем этот интерфейс классе можно используя ключевое слово super вместе с именем интерфейса:

interface Paper {
default void show() {
System.out.println("default show()");
}
}

class Licence implements Paper {
public void show() {
Paper.super.show();
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍6