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

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

✍️По всем вопросам: @Pascal4eg
Download Telegram
⌨️ Приведение типов. Понижение и повышение типа

Java является строго типизированным языком программирования, а это означает, то что каждое выражение и каждая переменная имеет строго определенный тип уже на момент компиляции. Однако определен механизм приведения типов (casting) - способ преобразования значения переменной одного типа в значение другого типа.

В Java существуют несколько разновидностей приведения:

✔️ Тождественное (identity). Преобразование выражения любого типа к точно такому же типу всегда допустимо и происходит автоматически.

✔️ Расширение (повышение, upcasting) примитивного типа (widening primitive). Означает, что осуществляется переход от менее емкого типа к более ёмкому. Например, от типа byte (длина 1 байт) к типу int (длина 4 байта). Такие преобразование безопасны в том смысле, что новый тип всегда гарантировано вмещает в себя все данные, которые хранились в старом типе и таким образом не происходит потери данных. Этот тип приведения всегда допустим и происходит автоматически.

✔️ Сужение (понижение, downcasting) примитивного типа (narrowing primitive). Означает, что переход осуществляется от более емкого типа к менее емкому. При таком преобразовании есть риск потерять данные. Например, если число типа int было больше 127, то при приведении его к byte значения битов старше восьмого будут потеряны. В Java такое преобразование должно совершаться явным образом, при этом все старшие биты, не умещающиеся в новом типе, просто отбрасываются - никакого округления или других действий для получения более корректного результата не производится.

✔️ Расширение объектного типа (widening reference). Означает неявное восходящее приведение типов или переход от более конкретного типа к менее конкретному, т.е. переход от потомка к предку. Разрешено всегда и происходит автоматически.

✔️ Сужение объектного типа (narrowing reference). Означает нисходящее приведение, то есть приведение от предка к потомку (подтипу). Возможно только если исходная переменная является подтипом приводимого типа. При несоответствии типов в момент выполнения выбрасывается исключение ClassCastException. Требует явного указания типа.

✔️ Преобразование к строке (to String). Любой тип может быть приведен к строке, т.е. к экземпляру класса String.

✔️ Запрещенные преобразования (forbidden). Не все приведения между произвольными типами допустимы. Например, к запрещенным преобразованиям относятся приведения от любого ссылочного типа к примитивному и наоборот (кроме преобразования к строке). Кроме того, невозможно привести друг к другу классы, находящиеся на разных ветвях дерева наследования и т.п.

При приведении ссылочных типов с самим объектом ничего не происходит, - меняется лишь тип ссылки, через которую происходит обращение к объекту.

Для проверки возможности приведения нужно воспользоваться оператором instanceof:

Parent parent = new Child();
if (parent instanceof Child) {
Child child = (Child) parent;
}


#java #casting #upcasting #downcasting
Please open Telegram to view this post
VIEW IN TELEGRAM
👍103🔥2
🚀 Project Loom: виртуальные потоки вместо тяжёлых тредов

Java всегда славилась многопоточкой, но классические треды прожорливы: на каждый создаётся отдельный системный поток. Loom это меняет — теперь у нас есть виртуальные потоки (virtual threads).

🧵 Старый способ — тяжёлые треды

Thread thread = new Thread(() -> {
System.out.println("Hello from thread!");
});
thread.start();

➡️ Потоки живут в ОС. Их немного, и каждый ест память и ресурсы.

⚡️ Новый способ — виртуальные потоки

Thread vThread = Thread.ofVirtual().start(() -> {
System.out.println("Hello from virtual thread!");
});

➡️ Здесь поток лёгкий. Java сама маппит тысячи виртуальных потоков на несколько системных.

🔄 Масштаб без боли

for (int i = 0; i < 1_000_000; i++) {
Thread.ofVirtual().start(() -> work());
}

➡️ Миллион потоков без OutOfMemoryError. На обычных тредах — краш.

⏸️ Блокировка без страха

Thread.ofVirtual().start(() -> {
Thread.sleep(1000); // блокируемся
System.out.println("Done!");
});

➡️ Виртуальные треды спокойно блокируются. JVM паркует их, освобождая ресурсы.

🌐 Асинхронщина без Future/CompletableFuture

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> fetchFromApi());
executor.submit(() -> readFromDb());
}

➡️ Каждая задача — в своём виртуальном треде. Асинхронность пишется синхронно.

🔧 Работа с I/O

Files.lines(Path.of("data.txt"))
.forEach(line -> System.out.println(line));

➡️ Даже блокирующее I/O больше не страшно. В Loom оно «кажется» блокирующим, но реально не тормозит.

📊 Где использовать

🟢 сетевые сервисы (много коннектов);
🟢 микросервисы;
🟢 парсеры/скрейперы;
🟢 всё, где тысячи одновременных задач.


🗣️ Запомни: Про «тредпулы и колбэки» скоро можно будет забыть.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍105🔥1
⌨️ Почему 1==1 это true, а 128==128 это false?


Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false

Integer x = 1;
Integer y = 1;
System.out.println(x == y); // true


Это не просто волшебство! Это связано с целочисленным кэшированием в Java.

Для значений в диапазоне от -128 до 127 объекты типа Integer кэшируются, и ссылки на них одинаковы, а для значений за пределами этого диапазона создаются новые объекты, и ссылки на них будут разными. Так как используется оператор ==, то сравниваются ссылки на объекты.

#java #magic
Please open Telegram to view this post
VIEW IN TELEGRAM
👍182
Media is too big
VIEW IN TELEGRAM
☕️Конструкторы класса

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


🗣️Запомни: конструкторы упрощают инициализацию объектов и делают код более удобным и читаемым.

🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍2🔥2
🚀 Spring Boot 3 + AOT компиляция — реальная производительность

Spring Boot 3 принёс AOT (Ahead-Of-Time) компиляцию. Теперь можно собирать приложение в нативный бинарь через GraalVM и запускать его почти мгновенно. Это не просто оптимизация — это новый уровень для микросервисов.

Разберём, что это даёт в реальных проектах 👇

⚡️ Запуск в миллисекунды
./mvnw -Pnative native:compile
./target/demo

➡️ Обычный Spring Boot на JVM стартует 2–5 секунд. AOT-бинарь поднимается за 30–50 мс. Это критично для serverless и микросервисов, где холодный старт решает.

📦 Минимальные Docker-образы
FROM ghcr.io/graalvm/native-image:latest as builder
WORKDIR /app
COPY . .
RUN ./mvnw -Pnative native:compile

FROM ubuntu:22.04
COPY --from=builder /app/target/demo /demo
CMD ["/demo"]

➡️ В Docker-контейнере не нужна JVM. Итоговый образ весит 30–50 MB вместо 300–400 MB.

🧠 Экономия памяти
JVM-приложение:     150–200 MB RAM  
AOT-приложение: 20–40 MB RAM

➡️ GraalVM вырезает неиспользуемые классы, рефлексию и динамику. Сервер держит десятки сервисов вместо пары.

🔗 Поддержка Spring Boot Native
@SpringBootApplication
public class DemoApp {
public static void main(String[] args) {
SpringApplication.run(DemoApp.class, args);
}
}

➡️ Код не меняется. Но при сборке Spring генерирует hint'ы (reflect-config.json), чтобы GraalVM понял, какие классы реально нужны.


🧩 Работа с рефлексией и прокси
@TypeHint(types = {MyEntity.class}, access = AccessBits.ALL)
public class MyHints implements NativeConfiguration {}

➡️ В AOT режиме всё статично. Если библиотека лезет через Class.forName(), нужно явно подсказать GraalVM. Иначе приложение упадёт.

⚙️ Async, WebFlux, Security — всё работает
@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("Привет из AOT!");
}

➡️ Reactive-приложения и Spring Security совместимы. Но стоит проверить на совместимость сторонние библиотеки — они не всегда поддерживают AOT.


📊 JIT vs AOT: производительность под нагрузкой
🟢 JIT (обычная JVM) быстрее на долгих задачах, потому что оптимизирует байткод во время работы.
🟢 AOT быстрее стартует и экономнее в памяти, но чуть медленнее на горячем коде.

➡️ Для микросервисов и API важнее быстрый старт и лёгкость. Для больших вычислений — JVM.

🔁 CI/CD и DevOps
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: ./mvnw -Pnative native:compile
- uses: actions/upload-artifact@v3
with:
name: demo-binary
path: target/demo

➡️ AOT встраивается в pipeline. Сборка дольше (5–10 минут), но итоговый бинарь быстрый и лёгкий.

🚨 Ограничения AOT
👍 Долгая компиляция (в разы медленнее, чем обычный mvn package).
👍 Динамические фреймворки (Hibernate proxies, некоторые reflection API) требуют ручных hint'ов.
👍 Не всегда легко дебажить — стек-трейсы короче.


➡️ Но в продакшене эти минусы окупаются.

🗣️ Запомни: AOT в Spring Boot 3 — это не замена JVM, а выбор под задачу. Если важен холодный старт, размер образа и память — используй AOT. Если нужен гибкий код с динамикой и быстрые билды — оставляй JVM. Лучшие проекты совмещают оба подхода.
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍5🔥3
🌊 JEP 458: Stream Gatherers в Java 22

Java 22 добавила Gatherers — расширение Stream API, которое позволяет собирать элементы не только привычными map/filter/reduce, а своими кастомными способами. Теперь поток можно «собирать» прямо в процессе, без костылей.

📦 Простая идея Gatherer

Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.windowFixed(2))
.forEach(System.out::println);


➡️ Элементы пакуются по 2 штуки.

Вывод:

[1, 2]
[3, 4]
[5]


🔄 Sliding Window — скользящее окно

Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.windowSliding(3))
.forEach(System.out::println);

➡️ Формируются окна по 3 элемента с шагом в 1.

Вывод:

[1, 2, 3]
[2, 3, 4]
[3, 4, 5]


🗂 Группировка по условию

Stream.of("a", "b", "x", "y", "c", "d")
.gather(Gatherers.groupUntil(s -> s.equals("x")))
.forEach(System.out::println);

➡️ Группа закрывается, когда встречается "x".

Вывод:

[a, b, x]
[y, c, d]


⚙️ Свой Gatherer

Gatherer<Integer, ?, Integer> sumPairs =
Gatherer.of(
() -> new int[1], // state
(state, e, down) -> {
state[0] += e;
if (state[0] >= 10) {
down.push(state[0]);
state[0] = 0;
}
return true;
},
(state, down) -> {}
);

Stream.of(3, 4, 5, 6, 7)
.gather(sumPairs)
.forEach(System.out::println);

➡️ Числа суммируются, пока не превысят 10, потом сбрасываются.


12
13


📊 Gatherers.fold — промежуточная агрегация

Stream.of(1, 2, 3, 4, 5)
.gather(Gatherers.fold(0, Integer::sum))
.forEach(System.out::println);

➡️ Видим все промежуточные суммы.

Вывод:

1
3
6
10
15



⚖️ Gatherers 🆚 Collectors
Collectors работают в конце стрима (collect(...)).
Gatherers же действуют прямо в процессе, шаг за шагом.


➡️ Это позволяет писать stateful-преобразования, которые раньше выглядели громоздко.


🚀 Реальные применения
1. Буферизация при работе с большими потоками.
2. Batch-загрузки в БД.
3. Скользящие средние и метрики.
4. Stateful-пайплайны без ручного кода.

➡️ Всё это теперь можно писать нативно в Stream API.

🔗 Комбинации

Stream.of(1, 2, 3, 4, 5, 6)
.gather(Gatherers.windowFixed(2))
.map(list -> list.stream().mapToInt(i -> i).sum())
.forEach(System.out::println);

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

Вывод:

3
7
11


🗣️ Запомни: Gatherers в Java 22 выводят стримы на новый уровень. Они позволяют обрабатывать данные «на лету» и строить stateful-пайплайны, которые раньше были невозможны без костылей.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍129🔥62
Media is too big
VIEW IN TELEGRAM
☕️ Создание классов и объектов

В этом видео автор показывает, как в Java создавать собственные классы и объекты.
Разбирается структура класса, определение полей и методов, а также процесс создания экземпляров через ключевое слово new. Это практическая основа объектно-ориентированного программирования, без которой невозможно двигаться дальше.


🗣️Запомни: умение создавать классы и объекты — фундамент для понимания всей работы с ООП в Java.

🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5💊53🔥1
⚡️ Panama API + MemorySegment — работа с off-heap памятью без Unsafe

Когда нужен прямой доступ к памяти без Unsafe и JNI — помогает Panama API.
MemorySegment и MemorySession позволяют работать с off-heap памятью безопасно, быстро и управляемо.

📦 Выделение памяти вне heap
try (var session = MemorySession.openConfined()) {
MemorySegment segment = MemorySegment.allocateNative(100, session);
System.out.println("Размер: " + segment.byteSize());
}

➡️ Память выделяется за пределами JVM heap, но управляется сессией.

🔗 Запись и чтение примитивов
try (var session = MemorySession.openConfined()) {
MemorySegment seg = MemorySegment.allocateNative(8, session);
seg.set(ValueLayout.JAVA_INT, 0, 42);
int x = seg.get(ValueLayout.JAVA_INT, 0);
System.out.println(x);
}

➡️ Читаем и пишем напрямую, как в массив, только в off-heap памяти.

⚡️ Работа с массивами
try (var session = MemorySession.openConfined()) {
MemorySegment seg = MemorySegment.allocateNative(4 * 5, session);
for (int i = 0; i < 5; i++) {
seg.setAtIndex(ValueLayout.JAVA_INT, i, i * 10);
}
System.out.println(seg.getAtIndex(ValueLayout.JAVA_INT, 3));
}

➡️ setAtIndex и getAtIndex делают сегмент похожим на массив.

🌀 MemorySession = контроль жизни

MemorySegment seg;
try (var session = MemorySession.openConfined()) {
seg = MemorySegment.allocateNative(16, session);
}
System.out.println(seg.isAlive()); // false

➡️ После закрытия сессии память освобождается, сегмент становится "мертвым".

📑 Slice и view памяти
try (var session = MemorySession.openConfined()) {
MemorySegment seg = MemorySegment.allocateNative(16, session);
MemorySegment part = seg.asSlice(4, 8);
System.out.println("Slice: " + part.byteSize());
}

➡️ Можно создавать "представления" (slice) на часть памяти.

🚀 Обмен с native-кодом
try (var session = MemorySession.openConfined()) {
MemorySegment seg = MemorySegment.allocateNative(4, session);
seg.set(ValueLayout.JAVA_INT, 0, 123);
long addr = seg.address();
System.out.println("Адрес: " + addr);
}

➡️ Сегмент даёт доступ к "сырому" адресу для интеграции с нативными API.

🗣️ Запомни: Off-heap ускоряет работу с большими данными и нативным кодом без GC overhead.
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍54🤔1
🛡 Java Flight Recorder + JFR Events API — профилирование прямо в коде

Обычно JFR включают флагами JVM, но начиная с Java 14 появился Events API. Теперь можно описывать и логировать свои события прямо из кода, без тяжёлых логгеров.

📦 Определение кастомного события
import jdk.jfr.Event;
import jdk.jfr.Label;

class LoginEvent extends Event {
@Label("User")
String username;

@Label("Success")
boolean success;
}

➡️ Наследуем Event и описываем поля — это как лог-сообщение, но встроенное в JVM.

🚀 Публикация события
LoginEvent e = new LoginEvent();
e.username = "ivan";
e.success = true;
e.commit();

➡️ commit() записывает событие в JFR-файл, который потом анализируется в Mission Control.

🖥 Запуск записи профиля
java -XX:StartFlightRecording=filename=recording.jfr,duration=20s -jar app.jar

➡️ Даже без кода можно собрать JFR-трассу, но с Events API появляются свои бизнес-события.

📊 Использование категорий
import jdk.jfr.Category;

@Category("Auth")
class LoginEvent extends Event {
String username;
}

➡️События группируются в категории, и в Mission Control они будут выделены как отдельный блок.

⚡️ События с таймингом
class CalcEvent extends Event {}

CalcEvent e = new CalcEvent();
e.begin();
// тяжёлая операция
e.end();
e.commit();

➡️ begin()/end() автоматически меряют длительность и пишут её в метрику.

🔗 Комбинация JFR с логами
LoginEvent e = new LoginEvent();
e.username = "alex";
e.success = false;
e.commit();

System.out.println("Login attempt failed: alex");

➡️ Лог для человека, JFR-событие для анализа и графиков — два уровня контроля.

🧩 Интеграция с потоками
class TaskEvent extends Event {
String thread;
}

TaskEvent e = new TaskEvent();
e.thread = Thread.currentThread().getName();
e.commit();

➡️Можно писать, какие потоки нагружают систему и когда они стартуют.

📑 Анализ в Mission Control
Открываешь recording.jfr — и видишь всё: GC, потоки, задержки и свои кастомные события.

🗣️ Запомни: JFR Events API — это быстрый встроенный профайлинг. Свои события почти не грузят систему, пишутся дешевле логов и помогают находить проблемы прямо в продакшене.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41
📦 Records 🆚 Sealed Classes — компактность и контроль

Record убирает шаблонный код и делает объект иммутабельным.
Sealed Class закрывает иерархию и контролирует наследование.
Разные задачи, но вместе решают боль старой Java.


🧩 Record вместо POJO

public record User(String name, int age) {}

➡️ Всё сгенерировано: геттеры, equals, hashCode, toString.
Лаконичность, минимум шаблонов.
Изменять поля нельзя.

📜 Класс вручную vs record

class User {
private final String name;
private final int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

public String name() { return name; }
public int age() { return age; }
}

➡️ Один record = десяток строк кода.

⚙️ Record с валидацией

public record Product(String name, int price) {
public Product {
if (price < 0) throw new IllegalArgumentException("price < 0");
}
}

➡️ Можно добавлять логику в compact-конструкторе.

🔒 Sealed Class: ограниченная иерархия

public sealed class Shape permits Circle, Rectangle {}

public final class Circle extends Shape {}
public final class Rectangle extends Shape {}

➡️ Никто не добавит новый Shape без изменения sealed-класса.

🎯 Record внутри sealed

public sealed interface Payment
permits CardPayment, CashPayment {}

public record CardPayment(String number) implements Payment {}
public record CashPayment(double amount) implements Payment {}

➡️ Удобное комбо: варианты — records, интерфейс — sealed.

🔎 Pattern Matching по sealed

Payment p = new CardPayment("1234");

switch (p) {
case CardPayment c -> System.out.println("💳 " + c.number());
case CashPayment c -> System.out.println("💵 " + c.amount());
}

➡️ Компилятор гарантирует, что обработаны все варианты.

📐 Sealed vs enum

sealed interface Command permits Reboot, Shutdown {}

record Reboot() implements Command {}
record Shutdown() implements Command {}

➡️ Как enum, но гибче: каждая ветка со своей структурой.

🧮 Комбинации records + sealed

sealed interface Event permits Login, Logout {}

record Login(String user) implements Event {}
record Logout(String user) implements Event {}


➡️ Модель событий простая, неизменяемая и закрытая.

🗣️Запомни:Record = компактный неизменяемый объект вместо шаблонного класса. Sealed Class = закрытая иерархия и контроль наследников.Вместе дают лаконичность и безопасность, снимая боль старой Java.
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍5🔥3
⌨️ Что такое текстовые блоки (Text Blocks)?

Text Blocks (""") позволяют удобно работать с многострочными строками без необходимости экранировать кавычки.

Улучшают читаемость кода.
Поддерживают форматирование и перенос строк.
Упрощают работу с JSON, SQL и HTML.

✔️ Пример:


String json = """
{
"name": "Alice",
"age": 30
}
""";

System.out.println(json);


💡 Совет: Используйте Text Blocks для удобного написания больших строковых данных.

#java #textblocks #java15
Please open Telegram to view this post
VIEW IN TELEGRAM
👍103🔥3
⚡️ Scoped Values в Java — альтернатива ThreadLocal для виртуальных потоков

С появлением виртуальных потоков (Project Loom) старый ThreadLocal стал проблемой. Он держит данные на весь поток, что для миллионов lightweight-тредов дорого. Решение — ScopedValue.

📦 Объявление значения
import java.lang.ScopedValue;

public class Demo {
static final ScopedValue<String> USER = ScopedValue.newInstance();
}

➡️ Создаём "контейнер" для данных, которые будут жить только в заданной области.

🧩 Передача значения в scope
ScopedValue.where(USER, "Alice").run(() -> {
System.out.println("Hello, " + USER.get());
});

➡️ Значение доступно только внутри run(). Вышел из scope — данных нет.

⚡️ Виртуальные потоки + ScopedValue
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() ->
ScopedValue.where(USER, "Bob").run(() -> {
System.out.println("User in virtual thread: " + USER.get());
})
);
}

➡️ Локальные данные для каждого виртуального потока без утечек.

🔍 Сравнение с ThreadLocal
ThreadLocal<String> local = new ThreadLocal<>();
local.set("Alice");

➡️ Удобно, но значение живёт, пока жив поток. Виртуальные потоки → миллионы копий → утечки памяти.

🛡ScopedValue — иммутабельность
ScopedValue.where(USER, "Charlie").run(() -> {
// USER.set("Dave"); нельзя
System.out.println(USER.get());
});

➡️ Нельзя изменить внутри scope. Значение задаётся один раз, это делает код безопаснее.

🧠 Наследование scope
ScopedValue.where(USER, "Admin").run(() -> {
new Thread(() -> System.out.println(USER.get())).start();
});

➡️ Новый поток не видит значение. Данные строго локальны. Нет "магического" наследования, как у InheritableThreadLocal.


📊 Где применять
🟢 Контекст запроса (user id, trace id).
🟢 Логирование.
🟢 Передача настроек без проброса аргументов.


Минусы ScopedValue
🔴 Только read-only.
🔴 Живёт только в рамках scope.
🔴 Работает начиная с Java 20+.



🗣 Запомни: ThreadLocal опасен для виртуальных потоков — утечки и лишняя память. ScopedValue проще: задаёшь значение в scope, читаешь только там. Без наследования, без мутабельности, идеально для миллионов lightweight-тредов.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍63
🧩 Java без JNI: как FFM API ломает старые правила

Раньше, чтобы вызвать C-код из Java, приходилось страдать с JNI — заголовки, javah, System.loadLibrary(), краши и боль.Теперь всё проще: Foreign Function & Memory API (FFM) — новый стандарт для прямого доступа к нативным функциям и памяти.

⚙️ Импорт C-функций напрямую
FFM API позволяет вызывать функции из libc без JNI.
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;

try (Arena arena = Arena.openConfined()) {
Linker linker = Linker.nativeLinker();
SymbolLookup libc = linker.defaultLookup();
MethodHandle printf = linker.downcallHandle(
libc.find("printf").get(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)
);
MemorySegment msg = arena.allocateUtf8String("Hello from C!\n");
printf.invoke(msg);
}

➡️ Всё чисто на Java: без генерации C-кода, без jni.h, с полным контролем памяти.

🧠 Работа с нативной памятью
try (Arena arena = Arena.openConfined()) {
MemorySegment segment = arena.allocate(4);
segment.set(ValueLayout.JAVA_INT, 0, 1337);
System.out.println(segment.get(ValueLayout.JAVA_INT, 0));
}

➡️ Создаёшь участок памяти, читаешь и пишешь — без Unsafe и без GC-хаоса.

🧬 Interop с существующими C-библиотеками

Хочешь вызвать strlen или qsort? Пожалуйста.
MethodHandle strlen = linker.downcallHandle(
libc.find("strlen").get(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);
long len = (long) strlen.invoke(arena.allocateUtf8String("PyLinux"));
System.out.println(len);

➡️ Вызываешь функции C напрямую, как будто они твои.

🧰 Безопасность и контроль
FFM API чётко ограничивает область памяти (через Arena), предотвращая утечки и use-after-free.
Можно использовать “scoped memory” — живёт ровно столько, сколько нужно.


🚀 Производительность
JNI требует перехода между Java и C стэками → дорого.
FFM API компилируется JIT’ом и оптимизируется HotSpot → почти нативная скорость.


🔬 Статический биндинг с jextract

jextract генерирует Java-обёртки по .h-файлам.
jextract -t com.libc /usr/include/stdio.h

➡️ Автоматически создаёт Java-интерфейсы для вызова C-функций — без ручного JNI.

🧱 Будущее без JNI
С FFM API Java получает настоящий доступ к низкоуровневому миру:
🟢 без C-кода,
🟢 без падений JVM,
🟢 без плясок с System.load().


🗣️ Запомни:FFM API — это чистый Java-интерфейс к нативному миру. JNI уходит в прошлое.
Скорость и контроль — без риска.
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍7🔥6👎1🥰1
🔥 «Когда поток — это архитектура»
Kafka Streams и ksqlDB + Java: живые системы на событиях


Сервисы больше не должны “ждать запрос”. Современные системы реагируют на события — в реальном времени. Kafka Streams и ksqlDB превращают поток данных в ядро приложения, а не просто транспорт сообщений.

⚡️ 1. Архитектура event-driven

Kafka — это не просто очередь. Это commit-log всех событий в компании:
🟠пользователь оплатил заказ,
🟠склад обновил остатки,
🟠ML-модель прислала прогноз.

➡️Java-сервис подписывается на эти события и сам становится реактивным узлом.


🧩 2. Kafka Streams — логика прямо в потоке

Пример: считаем количество заказов в минуту по пользователю 👇
StreamsBuilder builder = new StreamsBuilder();

KStream<String, String> orders = builder.stream("orders");

orders.groupByKey()
.windowedBy(TimeWindows.of(Duration.ofMinutes(1)))
.count()
.toStream()
.to("orders_per_minute");

KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();

➡️ Данные не хранятся — они *текут*. Поток сам агрегирует и публикует результат в новый топик.

📊 3. KTable и состояние

Kafka Streams позволяет хранить состояние между событиями:
KTable<String, Long> orderCounts = orders
.groupByKey()
.count(Materialized.as("orders-store"));

➡️ KTable — как материализованная view в БД, но в оперативной памяти и постоянно обновляется.

🧠 4. ksqlDB — SQL для стримов

Если не хочется писать Java-код, можно просто запросить поток SQL-ом:
CREATE STREAM orders_stream (user_id VARCHAR, price DOUBLE)
WITH (KAFKA_TOPIC='orders', VALUE_FORMAT='JSON');

CREATE TABLE user_revenue AS
SELECT user_id, SUM(price) AS total
FROM orders_stream
WINDOW TUMBLING (SIZE 1 HOUR)
GROUP BY user_id;

➡️ Результат — новый поток данных. Kafka сама всё поддерживает в актуальном состоянии.

🔄 5. Реактивные микросервисы

Сервисы больше не ходят в БД, чтобы “узнать новое”.
Kafka — это их канал событий.

@KafkaListener(topics = "user_revenue")
public void handleRevenue(String msg) {
System.out.println("💰 " + msg);
}

➡️ Микросервисы реагируют на новые сообщения, а не крутят cron.

🧰 6. Пример конвейера событий
[orders] → [Kafka Streams: агрегация] → [ksqlDB: фильтрация] → [billing-service]

➡️ Каждый шаг — отдельная логика. Данные текут без REST-запросов.

🔗 7. Масштабирование и отказоустойчивость
Kafka Streams хранит state в RocksDB и реплицирует его между инстансами.
Перезапуск — и поток сам продолжает с последнего offset.

➡️ Никакой ручной синхронизации или блокировок.

⚙️ 8. Интеграция с экосистемой Spring
@EnableKafkaStreams
@Configuration
public class StreamConfig {
@Bean
public KStream<String, String> kStream(StreamsBuilder builder) {
return builder.stream("orders");
}
}

➡️ Spring Boot 3 поддерживает Kafka Streams из коробки — никаких костылей.

🚀 9. Kafka как data backbone
Kafka связывает микросервисы, ML, ETL и аналитические пайплайны.
Хранилище, транспорт и очередь — всё в одном.


🧩 10. Когда ksqlDB против Streams
| Сценарий                      | Что выбрать      |
| ----------------------------- | ---------------- |
| Быстрый прототип, SQL-запросы | ksqlDB |
| Сложная логика, типизация | Kafka Streams |
| Обработка JSON и Avro | оба поддерживают |
| Глубокая интеграция с Java | Streams |


🗣️ Запомни:Event-driven — не мода, а способ думать в реальном времени. Kafka Streams — это код, который живёт в потоке данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43🔥3👏1
🛰 «Смотри глубже: Observability в действии»
Мониторинг, трассировка и метрики, которые спасают прод

Observability — это не просто графики и алерты. Это способ понять, что реально происходит внутри системы.
В отличие от обычного мониторинга, observability показывает почему всё пошло не так.


Разберём, как инженеры держат прод под контролем 👇

📡 Metrics — сердце наблюдаемости
Prometheus + Grafana — золотой стандарт.
# prometheus.yml
scrape_configs:
- job_name: 'app'
static_configs:
- targets: ['app:8080']

➡️ Метрики — это числа: latency, CPU, RPS, ошибки.
Grafana визуализирует, Prometheus хранит и алертит.

🧭 Logs — что реально произошло
Elastic Stack или Loki от Grafana.
docker run -d -p 9200:9200 elasticsearch
docker run -d -p 5601:5601 kibana

➡️ Логи нужны, когда метрика показала «плохо».
Хочешь знать, почему? Иди в логи.

🚦 Traces — кто виноват в задержках
OpenTelemetry + Jaeger показывают путь запроса через микросервисы.
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("db_query"):
query_database()

➡️ Trace — это маршрут.
С его помощью видно, где реально теряется время.


🧩 OpenTelemetry — единый язык наблюдаемости
Собирает метрики, логи и трассировки из любого кода.
pip install opentelemetry-sdk opentelemetry-exporter-otlp

➡️ Универсальный стандарт. Работает и с Python, и с Java, и с Go.

💡 Alerting — чтобы не узнавать о сбоях из Telegram
Prometheus + Alertmanager → уведомления при SLA-фейле.
- alert: HighErrorRate
expr: rate(http_requests_total{status="500"}[5m]) > 0.05
for: 2m
labels:
severity: critical

➡️ Хороший alert не просто орёт. Он помогает понять причину.


🧱 Distributed Context — связать всё вместе
Код → метрики → логи → трассы — одно пространство событий.
Zipkin, Tempo, Jaeger — строят полную цепочку от запроса до ответа.

➡️ Это основа SRE-практик: найти проблему за минуты, а не часы.

🔍 Instrumentation — чтобы код умел говорить
Библиотеки сами шлют метрики: Spring Boot Actuator, FastAPI middleware, Django signals.

➡️ Observability — не внешний тул, а часть приложения.

⚙️ Cloud-native observability
AWS CloudWatch, GCP Operations Suite, Datadog, New Relic.
Для Kubernetes — Lens, K9s, Grafana Tempo.


➡️ Современные пайплайны включают observability как часть CI/CD.

🗣 Запомни: Observability ≠ просто мониторинг — это понимание поведения системы.
Please open Telegram to view this post
VIEW IN TELEGRAM
3🔥3👍2
🤝 «Контракты не лгут: как сервисы общаются без боли»
Contract Testing в Java — контроль реальности между продами

Когда микросервисы множатся, интеграционные тесты превращаются в хаос.
Сервис А ждёт одно, сервис B отдаёт другое — и привет, баг в проде.
Contract Testing решает это через договор: что именно API обещает и возвращает.


📄 Суть контракта
Потребитель (Consumer) описывает, что он ждёт от поставщика (Provider).
Provider подтверждает: «Да, я действительно так отвечаю».

➡️ Это проверка не по коду, а по взаимному соглашению.


⚙️ Pact — главный игрок в Java
Pact позволяет Consumer’у описывать ожидаемые запросы и ответы:
@Pact(consumer = "OrderService")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("User exists")
.uponReceiving("a request for user details")
.path("/users/123")
.method("GET")
.willRespondWith()
.status(200)
.body("{\"id\":123,\"name\":\"Alex\"}")
.toPact();
}

➡️ Это «контракт»: JSON с ожиданиями клиента.

🧪 Consumer Test — проверка ожиданий
@ExtendWith(PactConsumerTestExt.class)
@PactTestFor(providerName = "UserService", port = "8080")
public void testUserService(MockServer server) {
Response response = get(server.getUrl() + "/users/123");
assertEquals(200, response.getStatusCode());
}

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

🏭 Provider Test — подтверждение контракта
Поставщик поднимает mock и проверяет:
«Я реально возвращаю то, что обещал в контракте».
@Provider("UserService")
@PactFolder("pacts")
class UserServicePactTest {
@TestTarget
final Target target = new HttpTarget(8080);

@State("User exists")
public void userExists() {
// фикстуры для теста
}
}

➡️ Если что-то не совпадает — билд падает.

🔗 Pact Broker — центр доверия
Все контракты хранятся в брокере.
Каждый сервис знает, с кем и что согласовано.
docker run -d -p 9292:9292 pactfoundation/pact-broker

➡️ Это реестр соглашений между микросервисами.

🧩 Contract Testing ≠ интеграция
Интеграционные тесты требуют запущенные сервисы.
Контрактные тесты — только спецификации.

➡️ Меньше зависимостей, быстрее CI, больше стабильности.

🚀 Spring Cloud Contract — альтернатива Pact
Работает прямо в Spring Boot.
contract {
request {
method 'GET'
url '/users/123'
}
response {
status 200
body(id: 123, name: "Alex")
}
}

➡️ Контракт → автогенерированный тест для Provider’а.

Плюсы:
Нет неожиданностей при релизах.
CI ломается до продакшена, если кто-то нарушил контракт.
Каждый сервис тестируется изолированно.


Минусы:minu
Нужно поддерживать актуальность контрактов.
При множестве сервисов — много файлов.
Не ловит ошибки логики (только интерфейсов).


🗣 Запомни: Contract Testing — это «юридическая проверка» между сервисами.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍64🔥3
Media is too big
VIEW IN TELEGRAM
☕️Наследование в классах

В этом видео автор подробно объясняет концепцию наследования в Java: как один класс может наследовать свойства и методы другого, когда применять `extends`, как переопределять методы и использовать ключевое слово `super`.


🗣️Запомни: наследование — мощный механизм для повторного использования кода и организации иерархий классов.

🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
4🔥3
Media is too big
VIEW IN TELEGRAM
☕️Полиморфизм в ООП

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


🗣️Запомни: полиморфизм даёт гибкость в архитектуре и делает код расширяемым и масштабируемым.

🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
3❤‍🔥2
☕️ Java в потоке данных: Flink, Beam и Spark без магии

Когда данные бегут нескончаемым потоком, а твой монолит не успевает считать, Java превращается в машину для real-time-аналитики.
Flink, Beam и Spark — три пути к масштабным потоковым системам. Ни теории — только код.


💀 Обычный подход — всё тормозит
List<String> lines = Files.readAllLines(Paths.get("data.txt"));
Map<String, Long> counts = lines.stream()
.flatMap(line -> Arrays.stream(line.split(" ")))
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(counts);

➡️ Всё в память, всё синхронно. Умер на первом гигабайте.

⚙️ Flink — потоковые вычисления без задержек
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<String> stream = env.socketTextStream("localhost", 9999);

stream
.flatMap((String line, Collector<String> out) -> {
for (String word : line.split(" ")) out.collect(word);
})
.keyBy(word -> word)
.sum(1)
.print();

env.execute("Flink WordCount");

Реактивная обработка, fault tolerance и state — всё из коробки.

🧩 Spark Structured Streaming — SQL на лету

SparkSession spark = SparkSession.builder().appName("StreamSQL").getOrCreate();

Dataset<Row> lines = spark.readStream()
.format("socket")
.option("host", "localhost")
.option("port", 9999)
.load();

Dataset<Row> words = lines.as(Encoders.STRING())
.flatMap((FlatMapFunction<String, String>) x -> Arrays.asList(x.split(" ")).iterator(), Encoders.STRING());

words.groupBy("value").count()
.writeStream()
.outputMode("complete")
.format("console")
.start()
.awaitTermination();

DataFrame API + реальное время. SQL-запросы к стримам — это норма.

🧠 Beam — пиши один раз, запускай где угодно
Pipeline p = Pipeline.create();
p.apply(TextIO.read().from("input.txt"))
.apply(ParDo.of(new DoFn<String, String>() {
@ProcessElement
public void processElement(ProcessContext c) {
for (String word : c.element().split(" ")) c.output(word);
}
}))
.apply(Count.perElement())
.apply(TextIO.write().to("output"));
p.run().waitUntilFinish();

Beam абстрагирует всё: один pipeline может жить на Flink, Spark, Dataflow — без правок кода.

🚀 Kafka + Flink = real-time пайплайн
FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>("logs", new SimpleStringSchema(), props);
env.addSource(consumer)
.map(line -> line.toUpperCase())
.addSink(new FlinkKafkaProducer<>("processed", new SimpleStringSchema(), props));

Поток заходит из Kafka, проходит трансформацию и возвращается обратно.

📊 Stateful обработка
stream.keyBy(value -> value)
.flatMap(new RichFlatMapFunction<String, Tuple2<String, Integer>>() {
private transient ValueState<Integer> count;
public void open(Configuration parameters) {
count = getRuntimeContext().getState(new ValueStateDescriptor<>("count", Integer.class));
}
public void flatMap(String value, Collector<Tuple2<String, Integer>> out) throws Exception {
Integer current = count.value() == null ? 0 : count.value();
count.update(current + 1);
out.collect(Tuple2.of(value, current + 1));
}
});

Flink хранит состояние между событиями — perfect для realtime аналитики.

🧩 Beam с Cloud Dataflow
PipelineOptions options = PipelineOptionsFactory.create();
options.as(DataflowPipelineOptions.class).setRunner(DataflowRunner.class);

Запускаешь тот же код в Google Cloud — нативно масштабируется.

💡 Spark + Kafka integration
Dataset<Row> df = spark.readStream()
.format("kafka")
.option("kafka.bootstrap.servers", "localhost:9092")
.option("subscribe", "topic")
.load();

Spark сам читает Kafka topics и обрабатывает потоки.

🗣 Запомни: Flink — мозг real-time систем, Spark — мотор аналитики, Beam — мост между ними. Главное — не выбирай фреймворк по моде, выбирай под поток.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥53❤‍🔥2👍1
⌨️ Виды классов

1. Обычные классы (Regular Classes)
Это наиболее распространенные классы, которые вы создаете для определения объектов. Они могут содержать поля, методы, конструкторы и вложенные классы.

public class MyClass {
private int field;

public MyClass(int field) {
this.field = field;
}

public void method() {
// some code
}
}


2. Абстрактные классы (Abstract Classes)
Абстрактные классы не могут быть созданы как объекты напрямую. Они предназначены для предоставления общей функциональности, которую подклассы должны реализовать или дополнить.

public abstract class AbstractClass {
public abstract void abstractMethod();

public void concreteMethod() {
// some code
}
}


3. Вложенные классы (Nested Classes)
Классы, объявленные внутри другого класса. Они могут быть статическими или нестатическими.

🔹 Статические вложенные классы (Static Nested Classes):
Эти классы могут быть созданы без экземпляра внешнего класса.

public class OuterClass {
static class StaticNestedClass {
// some code
}
}


🔹 Внутренние классы (Inner Classes):
Эти классы имеют доступ ко всем членам внешнего класса и создаются в контексте экземпляра внешнего класса.

public class OuterClass {
class InnerClass {
// some code
}
}


4. Локальные классы (Local Classes)
Классы, объявленные внутри метода, конструктора или блока. Они имеют доступ к финальным переменным из охватывающего метода.

public class OuterClass {
public void method() {
class LocalClass {
// some code
}
LocalClass local = new LocalClass();
}
}


5. Анонимные классы (Anonymous Classes)
Классы без имени, создаваемые на месте для реализации интерфейса или наследования от класса. Часто используются для создания экземпляров интерфейсов или абстрактных классов.

public class OuterClass {
public void method() {
Runnable runnable = new Runnable() {
@Override
public void run() {
// some code
}
};
}
}


6. Перечисления (Enums)
Специальные классы, представляющие набор констант. Они могут содержать поля, методы и конструкторы.

public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}


7. Интерфейсы (Interfaces)
Технически не классы, но важная часть объектно-ориентированного программирования в Java. Интерфейсы определяют контракты, которые должны быть реализованы классами.

public interface MyInterface {
void myMethod();
}


8. Записи (Records)
Нововведение в Java 14 (в предварительном виде) и официально в Java 16. Они предоставляют компактный способ создания неизменяемых классов с полями и автоматически сгенерированными методами, такими как equals, hashCode и toString.

public record Point(int x, int y) {}


#java #classes
Please open Telegram to view this post
VIEW IN TELEGRAM
👍167🔥3
🤖 JavaBot: Telegram без магии и фреймворков

Хочешь быстро поднять Telegram-бота на Java без плясок вокруг Spring? Берём библиотеку TelegramBots, одну точку входа — и поехали.

⚙️ Подключаем зависимость

Gradle:
implementation 'org.telegram:telegrambots:6.9.7'


🧩 Создаём скелет бота
public class EchoBot extends TelegramLongPollingBot {
public String getBotUsername() { return "MyJavaBot"; }
public String getBotToken() { return "TOKEN"; }

public void onUpdateReceived(Update u) {
var chat = u.getMessage().getChatId().toString();
var text = u.getMessage().getText();
send(chat, "Ты написал: " + text);
}

void send(String chatId, String msg) {
try { execute(new SendMessage(chatId, msg)); }
catch (Exception e) { e.printStackTrace(); }
}
}


🚀 Запускаем бота
var bots = new TelegramBotsApi(DefaultBotSession.class);
bots.registerBot(new EchoBot());
System.out.println("Бот в деле 🚀");


💬 Тест
Ты: ping  
Бот: Ты написал: ping


🧠 Добавим немного логики
if (text.equals("/start"))
send(chat, "Привет! Я Java-бот 💪");
else if (text.equalsIgnoreCase("время"))
send(chat, LocalTime.now().toString());
else
send(chat, "Команда не понята 🤖");


⚡️ Inline-кнопки
var markup = new InlineKeyboardMarkup();
markup.setKeyboard(List.of(List.of(
new InlineKeyboardButton("Сказать hi").callbackData("hi")
)));
msg.setReplyMarkup(markup);


🧱 Структура проекта
src/
├── Main.java
└── EchoBot.java
build.gradle


🗣 Запомни: в Java всё строго — но именно поэтому Telegram-боты тут живут годами, не падают и не висят в “reconnecting…”
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥42