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 без сюрпризов: контрактное тестирование между сервисами

Когда микросервисы растут, как грибы после дождя — баги начинают путешествовать между ними.
Контрактное тестирование ставит границы: продюсер не ломает клиента, а потребитель не ждёт несуществующие поля.
Schema-first и consumer-driven подходы делают интеграцию предсказуемой — без ручных костылей.


⚙️ 1. Schema-first — один источник истины
syntax = "proto3";

message Order {
string id = 1;
double amount = 2;
string status = 3;
}

Сначала схема (OpenAPI, Avro, Protobuf) — потом код. Никаких сюрпризов при интеграции.

🚀 2. Генерация классов из схемы
mvn protobuf:compile

➡️ Java-классы создаются из схемы автоматически — никто не пишет руками DTO.

🧠 3. Контракты между командами
interaction:
request:
method: GET
path: /orders/123
response:
status: 200
body:
id: "123"
amount: 50.0

Фиксируешь поведение API. Если кто-то меняет ответ — тесты падают.

🧩 4. Consumer-driven contracts через Pact
@Pact(consumer = "billing-service")
public RequestResponsePact pact(PactDslWithProvider builder) {
return builder
.given("Order exists")
.uponReceiving("Get order by ID")
.path("/orders/123").method("GET")
.willRespondWith()
.status(200)
.body("{\"id\":\"123\",\"amount\":50.0}")
.toPact();
}

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

🔍 5. Проверка продюсера
@Provider("order-service")
@PactFolder("pacts")
public class ProviderVerificationTest {
@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void verifyPacts(PactVerificationContext context) {
context.verifyInteraction();
}
}

Перед деплоем проверяется, не сломали ли API.

⚙️ 6. Pact Broker — как единая база контрактов
docker run -d -p 9292:9292 pactfoundation/pact-broker

➡️ Все контракты хранятся централизованно, CI проверяет совместимость.

🧩 7. Валидация схемы при CI
mvn verify -Pcontract-tests

Любая несовместимая правка схемы — стоп-релиз.

🧱 8. Миграция без боли
body:
amount: 50.0
currency: "USD" # новое поле, не ломает старый контракт

➡️ Добавляй поля безопасно: старые клиенты не страдают.


📦 9. Интеграция с Spring Cloud Contract
@SpringBootTest
@AutoConfigureStubRunner(ids = "com.demo:order-service:+:stubs:8080")
class BillingTest { ... }

Потребитель тестируется на реальных стабах продюсера.

🗣 Запомни: контрактное тестирование — это страховка от хаоса. Код может меняться, но правила общения — нет.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍3🔥1
👨‍💻 Java + WebAssembly: когда JVM становится WASM-машиной

WebAssembly давно уже не только браузер. Wasmtime дал Java прямой доступ к WASM-модулям.Пиши на Rust алгоритм, компилируй в .wasm, вызывай из Java за микросекунды.Никаких FFI, никаких JNI — просто бинарный протокол между JVM и WASM.

⚙️ 1. Устанавливаем Wasmtime
implementation("org.wasmtime:wasmtime:17.0.0")

➡️ Одна строка — и Java получает WASM-машину.

🚀 2. Загружаем и вызываем WASM-функцию
Engine engine = new Engine();
Store store = new Store(engine);
byte[] wasm = Files.readAllBytes(Paths.get("add.wasm"));
Module module = Module.fromBinary(engine, wasm);
Instance instance = new Instance(store, module, new Extern[]{});
Func add = instance.getExport(store, "add").func();
System.out.println(add.call(store, 5L, 3L));

Загрузил, вызвал функцию, получил результат.

🧠 3. Компилируем Rust в WASM
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}


rustup target add wasm32-unknown-unknown
cargo build --target wasm32-unknown-unknown --release

➡️ Чистый бинарь ~2КБ без зависимостей.

📡 4. WASI — I/O из WASM
WasiCtx wasiCtx = WasiCtx.builder()
.inheritStdout()
.inheritStderr()
.build();
wasiCtx.define(store);

Linker linker = new Linker(engine);
Instance instance = linker.instantiate(store, module);
Func main = instance.getExport(store, "_start").func();
main.call(store);

➡️ WASM-модуль получает доступ к файлам и stdout.

🔧 5. Передача памяти между JVM и WASM
Memory memory = instance.getExport(store, "memory").memory();
byte[] data = "Hello WASM".getBytes();
memory.write(store, 0, data);

Func process = instance.getExport(store, "process").func();
process.call(store, 0L, (long) data.length);

byte[] result = new byte[100];
memory.read(store, 100, result);
System.out.println(new String(result).trim());

Шарим память напрямую, никаких копирований.

💾 6. Оптимизированные вычисления в WASM
#[no_mangle]
pub extern "C" fn matrix_mul(a: *const f64, b: *const f64,
c: *mut f64, n: usize) {
unsafe {
for i in 0..n {
for j in 0..n {
let mut sum = 0.0;
for k in 0..n {
sum += (*a.add(i*n+k)) * (*b.add(k*n+j));
}
*c.add(i*n+j) = sum;
}
}
}
}

➡️ Матричное умножение в 2-3x быстрее, чем на Java.

⚡️ 7. GraalVM Polyglot + WASM
try (Context context = Context.create("wasm")) {
Source source = Source.newBuilder("wasm",
Files.readAllBytes(Paths.get("calc.wasm")),
"module").build();
Value wasm = context.eval(source);
System.out.println(wasm.getMember("add").execute(10, 20).asInt());
}

Динамический вызов без явных типов.

🎬 8. Система плагинов на WASM
Map<String, Instance> plugins = new HashMap<>();

void loadPlugin(String name, String path) throws Exception {
byte[] wasm = Files.readAllBytes(Paths.get(path));
Instance instance = new Instance(new Store(new Engine()),
Module.fromBinary(new Engine(), wasm), new Extern[]{});
plugins.put(name, instance);
}

String executePlugin(String name, String input) {
Func process = plugins.get(name)
.getExport(null, "process").func();
return process.call(null, input).toString();
}

➡️ Плагины как отдельные WASM-модули, полная изоляция.

📊 9. Fibonacci: Java vs WASM
// Java: 2000ms на fib(40)
public static long fib(long n) {
return n <= 1 ? n : fib(n-1) + fib(n-2);
}


// WASM: 380ms на fib(40)
#[no_mangle]
pub extern "C" fn fib(n: u64) -> u64 {
match n { 0|1 => n, _ => fib(n-1) + fib(n-2) }
}

➡️ WASM выигрывает в 5 раз на рекурсии.

🗣 Запомни: WASM — не браузер, а способ запустить оптимизированный код внутри Java. Rust + WASM = скорость железа, JVM = надёжность.
Please open Telegram to view this post
VIEW IN TELEGRAM
6🔥1
Media is too big
VIEW IN TELEGRAM
☕️ Вложенные и анонимные классы в Java

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

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


👉 Ссылка на первоисточник

🗣Запомни: вложенные и анонимные классы — инструменты для более гибкой и компактной архитектуры кода.

🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
☕️ Java без JVM: магия GraalVM Native Image

Когда время запуска и потребление памяти решают всё — классическая JVM становится роскошью.
GraalVM превращает Java-приложение в нативный бинарь: без JVM, без warm-up, без лагов.
Меньше 100 МБ памяти, запуск за миллисекунды — и это всё ещё Java.

🔥 1. Устанавливаем GraalVM

sdk install java 23-graal
gu install native-image

✔️ GraalVM — это и JVM, и компилятор в одном флаконе. native-image — его волшебная часть.

⚙️ 2. Собираем проект под GraalVM

mvn -Pnative native:compile

➡️ Плагин native-maven-plugin превращает .jar в бинарник.
Никаких зависимостей на JVM при запуске.

🧩 3. Пример минимального приложения

public class Main {
public static void main(String[] args) {
System.out.println("Hello, native world!");
}
}



native-image -jar app.jar
./app

✔️ Получаешь app — самостоятельный бинарь, мгновенно стартующий.

🚀 4. Время запуска и память
Java: ~1.5s, 150MB  
Native: 0.05s, 30MB

➡️ Разница ощущается не только в цифрах — контейнеры стартуют как Go или Rust-приложения.

🔍 5. Работа с Spring Boot

mvn -Pnative spring-boot:build-image

✔️ Spring Boot 3.x уже дружит с GraalVM: автоанализ конфигурации, JSON и reflection.

🧠 6. Reflection и динамика

GraalVM анализирует код заранее. Всё, что создаётся рефлексией — нужно описать:

[
{ "name": "com.demo.User", "allDeclaredConstructors": true }
]

➡️ Без этого компилятор просто выкинет “лишние” классы.

🧱 7. Совместимость с Micronaut и Quarkus

Эти фреймворки заточены под GraalVM:

mvn package -Dpackaging=native-image

✔️ DI, конфигурации, REST — всё работает из коробки, без плясок с reflection.

📦 8. Docker без JVM

FROM scratch
COPY app /
ENTRYPOINT ["/app"]

➡️ Никаких JDK в образе. Размер — меньше 50 МБ. Старт — мгновенный.

🧩 9. Debug и профилирование

native-image --verbose --diagnostics-mode ...

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

🗣️ Запомни: GraalVM не делает Java быстрее — он делает её ближе к железу.
Никаких виртуальных машин, просто чистый бинарь, который летает.
Please open Telegram to view this post
VIEW IN TELEGRAM
8🔥4👍1
⌨️ Полные и сокращённые версии AND и OR

&& и || называются сокращёнными логическими операторами AND и OR соответственно, или операторами короткой схемы вычислений. В спецификации Java их ещё зовут условными. Значения их операндов могут быть только булева типа.

В отличие от двойных, одиночные & и | называются операторами полной схемы вычислений. Значения их операндов могут быть как только булевыми, так и только целочисленными (вместе с оператором ^ они используются в побитовых операциях).

В чём разница?

В том, что для операторов & и | всегда вычисляются значения обоих операндов, а при работе операторов && и || второй операнд вычисляется только по необходимости.

То есть иногда результат выражения однозначно определён уже по первому операнду:

✔️ Если первый операнд && равен false, то второй не вычисляется, так как уже понятно, что результат всего выражения будет false.

✔️ Если первый операнд || равен true, то второй не вычисляется, так как уже понятно, что || вернёт true.

&& и || используют как операторы булевой логики. Они оперируют значениями только булева типа и применяются только в логических выражениях.
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍4
Media is too big
VIEW IN TELEGRAM
☕️ Пакеты, перегрузки и модификаторы в Java

В этом видео автор подробно разбирает, как организовывать код с помощью пакетов (packages) и зачем они нужны для структурирования проекта.

Также объясняется принцип перегрузки методов — когда один метод может иметь разные версии с разными параметрами. Разбирается практическое применение модификаторов static и final, которые меняют поведение классов и методов.


👉 Ссылка на первоисточник

🗣Запомни: пакеты, перегрузки и модификаторы — это основа для написания масштабируемого и понятного кода.

🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
2
☕️ Java в коробке: когда встроенное устройство ≠ десктоп

Java когда-то весила 500 МБ и требовала мощный железо. Теперь? GraalVM Native Image, Project Loom и TinyJVM трансформируют Java в лёгкого воина для IoT и edge. 50 МБ образ, микросекундный старт, батарея держит часы вместо минут — и это всё ещё Java.

⚡️ 1. Проблема: классическая JVM на ARM-микроконтроллере

// Классический Hello World на Raspberry Pi
java -Xmx512m -jar app.jar
// Результат: 15 секунд запуска, 300 МБ памяти, батарея мрёт

JVM для edge = убить муху кувалдой. Нужно легче.

🧠 2. GraalVM Native Image — первое спасение

native-image --no-server -H:+StaticExecutableWithDynamics -jar app.jar
./app
# Запуск: 0.05с, память: 30 МБ

➡️Статический бинарь без зависимостей. Прямо на ARM64, ARM32 — работает везде.

🔧 3. Минималистичное приложение для сенсора

public class TemperatureSensor {
public static void main(String[] args) throws Exception {
// Читаем температуру каждые 5 сек
while (true) {
double temp = readTemp(); // Из GPIO или UART
System.out.println("Temp: " + temp + "°C");
Thread.sleep(5000);
}
}

static double readTemp() {
return Math.random() * 50; // Симуляция
}
}

➡️Никаких фреймворков, никакого overhead'а.

📡 4. Связь по MQTT — стандарт IoT

import org.eclipse.paho.client.mqttv3.*;

public class IotDevice {
public static void main(String[] args) throws Exception {
MqttClient client = new MqttClient(
"tcp://broker.hivemq.com:1883",
"device-001"
);
client.connect();

String payload = "{\"temp\":25.5,\"humidity\":60}";
client.publish("sensors/room1", payload.getBytes(), 1, true);

client.disconnect();
}
}

✔️Отправляем данные на MQTT-broker — и остальной мир слушает. Просто и эффективно.

🎯 5. Quarkus для edge — фреймворк, заточенный под IoT

<!-- pom.xml -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-mqtt</artifactId>
</dependency>



mvn package -Dnative
# Результат: 80 МБ образ (вместо 500 МБ обычного Spring Boot)

➡️Quarkus + native image = полнофункциональная Java-фреймворк, который весит как Go-приложение.

🔌 6. Работа с GPIO на Raspberry Pi через Pi4j

import com.pi4j.io.gpio.*;

public class LedBlink {
public static void main(String[] args) throws Exception {
GpioController gpio = GpioFactory.getInstance();
GpioPinDigitalOutput pin = gpio.provisionDigitalOutputPin(
RaspiPin.GPIO_01,
"LED",
PinState.LOW
);

for (int i = 0; i < 10; i++) {
pin.toggle();
Thread.sleep(500);
}
gpio.shutdown();
}
}

➡️Мигаем светодиодом — классика IoT. Pi4j абстрагирует сложность GPIO'шных операций.

💾 7. Лёгкая БД для edge — SQLite или H2

import java.sql.*;

public class EdgeDb {
public static void main(String[] args) throws Exception {
Connection conn = DriverManager.getConnection(
"jdbc:sqlite:/tmp/device.db"
);

Statement stmt = conn.createStatement();
stmt.execute(
"CREATE TABLE IF NOT EXISTS readings " +
"(id INTEGER PRIMARY KEY, temp REAL, time LONG)"
);
stmt.execute(
"INSERT INTO readings (temp, time) VALUES (25.5, " +
System.currentTimeMillis() + ")"
);
conn.close();
}
}

➡️SQLite весит 500 КБ, не нужен отдельный сервер. Идеально для edge-узла.

🌐 8. REST API на встроенном устройстве через Micronaut

import io.micronaut.http.annotation.*;

@Controller("/api")
public class SensorController {

@Get("/temperature")
public Map<String, Object> getTemp() {
return Map.of(
"temp", 25.5,
"unit", "celsius",
"timestamp", System.currentTimeMillis()
);
}
}

➡️Микронаут stubs в 2-3 МБ. Запускаешь на Raspberry Pi — и уже есть REST API.

🗣️ Запомни:native image на edge = батарея в 5x дольше, код за день.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥43👍2
☕️ JVM-тюнинг: когда GC паузы решают всё

Приложение тормозит не из-за кода, а из-за GC пауз в самый неудачный момент.
Garbage Collector жрёт 30% CPU, heap фрагментируется, latency скачет от 10ms до 2 секунд. Правильные флаги меняют всё: ZGC держит паузы <10ms, G1 стабилен на больших heap'ах.Мониторинг в реальном времени показывает узкие места — без этого тюнишь вслепую.

⚙️ 1. Смотрим логи GC
java -XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+PrintGCTimeStamps \
-Xloggc:gc.log \
-jar app.jar

➡️Паузы, сборки, размеры поколений, время остановки JVM — всё видно в логах. Без этого ты тюнишь вслепую.

🚀 2. Выбираем правильный GC
# Для low-latency (микросервисы, финтех)
java -XX:+UseZGC \
-XX:ZUncommitDelay=300 \
-Xms8g -Xmx8g \
-jar app.jar

# Для throughput (batch, аналитика)
java -XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:InitiatingHeapOccupancyPercent=35 \
-Xms8g -Xmx8g \
-jar app.jar

✔️ZGC держит паузы <10ms даже на heap'е в 100GB. G1 более стабилен и предсказуем на средних объёмах.

3. Heap размер фиксированный
java -Xms8g -Xmx8g \
-XX:+AlwaysPreTouch \
-XX:+UseTransparentHugePages \
-jar app.jar

➡️Xms == Xmx избегает resize пауз при росте приложения. AlwaysPreTouch выделяет память сразу, а не при первом обращении — убирает пики latency на старте.

🔧 4. Young generation отдельно
java -XX:NewRatio=3 \
-XX:SurvivorRatio=8 \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=50 \
-jar app.jar

➡️NewRatio=3 означает молодое поколение = 1/4 heap'а. SurvivorRatio=8 — eden в 8 раз больше survivor space. Частые быстрые сборки Young Gen = редкие Full GC.

💾 5. Параллельные потоки GC
java -XX:ParallelGCThreads=8 \
-XX:ConcGCThreads=4 \
-XX:+ParallelRefProcEnabled \
-XX:+UseG1GC \
-jar app.jar

➡️ParallelGCThreads — для stop-the-world сборок (обычно = кол-во ядер / 2). ConcGCThreads — параллельная работа без паузы. На 16-ядре ставь 8 и 4.

⚡️ 6. Concurrent cleanup раньше
java -XX:InitiatingHeapOccupancyPercent=35 \
-XX:G1ReservePercent=10 \
-XX:+UseG1GC \
-jar app.jar

➡️GC начнёт cleanup в 35% occupancy, не дожидаясь переполнения. G1ReservePercent=10 резервирует 10% heap'а для evacuation — меньше Full GC.

🎬 7. String deduplication
java -XX:+UseG1GC \
-XX:+UseStringDeduplication \
-XX:StringDeduplicationAgeThreshold=3 \
-jar app.jar

➡️Если 100k одинаковых строк в памяти (JSON, SQL, логи), G1 их объединит. AgeThreshold=3 — склеивать строки старше 3 поколений.

🌐 8. Class Data Sharing
# Создаём архив классов
java -Xshare:dump \
-XX:SharedArchiveFile=./app-cds.jsa \
-cp app.jar

# Боевые запуски
java -Xshare:on \
-XX:SharedArchiveFile=./app-cds.jsa \
-jar app.jar

➡️Классы загружаются один раз и шарятся между процессами. Startup на 30-40% быстрее, GC pressure меньше.

📈 9. Мониторим в реальном времени
# Общая картина GC
jstat -gc -h10 <pid> 1000

# Детали по поколениям
jstat -gccapacity <pid> 2000

# Причины сборок
jstat -gccause <pid> 1000

➡️Каждую секунду видишь: S0C, S1C (survivor spaces), EC (eden), OC (old), время GC. Если >5% времени в GC — пора оптимизировать.

🗣 Запомни: GC — не враг, а нервная система JVM. Настроишь — летает.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥63👍2
⌨️ Java Records: Убийца бойлерплейта

Устали писать бесконечные геттеры, сеттеры, equals(), hashCode() и toString() для простых классов-хранилищ данных (DTO)?

Начиная с Java 14 (и как стандарт с Java 16), у нас есть Records.

💀 Как это было раньше:
Чтобы создать простой класс User, нужно было написать строк 50 кода или генерировать их через IDE/Lombok.

✔️ Как это выглядит сейчас:

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


Всё! Одна строка. Что происходит «под капотом»?

1️⃣ Неизменяемость: Все поля автоматически private final.

2️⃣ Конструктор: Генерируется канонический конструктор со всеми полями.

3️⃣ Методы доступа: Вместо getName(), методы называются так же, как поля — name() и age().

4️⃣ Object методы: equals(), hashCode() и toString() уже переопределены и работают корректно.

➡️ Где использовать:
Идеально подходят для DTO, ключей в Map или ответов API.

🗣️ Нюанс: Record не может наследоваться от другого класса (так как он уже неявно наследует java.lang.Record), но может имплементировать интерфейсы.

А вы уже перешли на рекорды или всё еще любите @Data из Lombok? 👇
Please open Telegram to view this post
VIEW IN TELEGRAM
👍143🤡2
⌨️ Java Pattern Matching для instanceof: Прощай, лишний каст!

Если вы пишете на Java давно, у вас наверняка выработался рефлекс: сначала проверить тип через instanceof, а потом тут же сделать явное приведение (cast).

Начиная с Java 16, мы можем забыть про эту рутину.

Как мы писали раньше:

Object obj = "Hello World";

if (obj instanceof String) {
String s = (String) obj; // 🤢 Лишняя строка и дублирование типа
System.out.println(s.toUpperCase());
}


Как писать теперь (Pattern Matching):

if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}


🧪 В чем магия?
Вы объявляете переменную (s) прямо в проверке. Если obj действительно является строкой, Java автоматически приводит тип и кладет результат в s. Эта переменная доступна внутри блока if.

🚀 Уровень PRO:
Вы можете использовать созданную переменную в том же условии!

if (obj instanceof String s && s.length() > 5) {
System.out.println("Длинная строка: " + s);
}


Это мелочь, но она убирает тонны визуального шума из кода, особенно в методах equals() или при обработке разнородных данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51🔥1
⌨️ Switch в Java: Забудьте про break!

Помните старый switch, который мы все тихо ненавидели? Громоздкий синтаксис, необходимость писать break в каждом кейсе и страх случайно провалиться в следующий блок (fall-through).

Начиная с Java 14, у нас есть Switch Expressions. Это не просто косметика, это превращение инструкции в выражение.

💀 Как было (скучно и опасно):

int days = 0;
switch (month) {
case FEBRUARY:
days = 28;
break; // Забыл break? Получи баг!
case APRIL:
case JUNE:
days = 30;
break;
default:
days = 31;
}


➡️ Как стало (элегантно):

int days = switch (month) {
case FEBRUARY -> 28;
case APRIL, JUNE -> 30;
default -> 31;
};


Что изменилось?

1️⃣ Стрелочный синтаксис (->): Код справа от стрелки выполняется только для этого кейса. Никаких break больше не нужно!

2️⃣ Возврат значения: Теперь switch может возвращать результат, который можно сразу присвоить переменной.

3️⃣ Несколько условий: Можно перечислять кейсы через запятую (case APRIL, JUNE).

4️⃣ Слово yield: Если логика сложная и нужен многострочный блок кода, для возврата значения используется yield (вместо return).


int result = switch (input) {
case "A" -> 1;
case "B" -> {
System.out.println("Вычисляем...");
yield 2;
}
default -> 0;
};


Код становится компактнее, безопаснее и понятнее.
Please open Telegram to view this post
VIEW IN TELEGRAM
8🔥5👍2
🚀 Spring Boot: проект за 1 час без боли и конфигураций

Spring Boot — это когда ты запускаешь продовый сервис быстрее, чем успеешь сделать себе чай.
Никаких XML, минимум настроек, встроенный сервер, автоконфигурация и готовые стартовые зависимости.
Вот как собрать рабочий backend за 1 час — от нуля до API.

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

build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'org.postgresql:postgresql'
}

➡️ Web, валидация, JPA и PostgreSQL — базовый набор для любого сервиса.

▶️ 2. Запускаем приложение

Main-класс:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

➡️ Встроенный Tomcat поднимется сам.
Никаких конфигов, никакого сервера руками.

🌐 3. Первый REST-контроллер
@RestController
@RequestMapping("/api/hello")
public class HelloController {

@GetMapping
public Map<String, String> hello() {
return Map.of("message", "Spring Boot работает!");
}
}

➡️ Уже есть API, уже можно дёргать в Postman.

🗄 4. Настрока БД

application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/demo
username: user
password: pass
jpa:
hibernate:
ddl-auto: update
show-sql: true

➡️ Spring Boot сам создаёт таблицы, коннектится и логирует SQL.

📦 5. Сущность + репозиторий
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String name;
}


public interface UserRepo extends JpaRepository<User, Long> {}

➡️ CRUD готов без единой строчки SQL.

🧩 6. Сервисный слой
@Service
@RequiredArgsConstructor
public class UserService {

private final UserRepo repo;

public User create(String name) {
var user = new User();
user.setName(name);
return repo.save(user);
}
}

➡️ Чистая логика, никаких репозиторных костылей в контроллере.

🎯 7. API для создания пользователя
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {

private final UserService service;

@PostMapping
public User create(@RequestParam String name) {
return service.create(name);
}
}

➡️ Post → сохраняем → возвращаем — 30 секунд работы.

🧪 8. Тестируем без сервера
@WebMvcTest(UserController.class)
class UserControllerTest {

@Autowired MockMvc mvc;

@Test
void testCreate() throws Exception {
mvc.perform(post("/api/users?name=Tom"))
.andExpect(status().isOk());
}
}

➡️ MockMvc поднимает только Web-слой, работает молниеносно.

📦 9. Создаём fat-jar и запускаем
./gradlew bootJar
java -jar build/libs/app.jar

➡️ Внутри — сервер, конфиги, код. Готово для деплоя.

☁️ 10. Dockerfile на 5 строк
FROM eclipse-temurin:21
COPY build/libs/app.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]

➡️ Легко уезжает в Kubernetes, AWS, GCP, куда хочешь.


🗣️ Запомни:Spring Boot — это не «фреймворк», а ускоритель: он убирает рутину, чтобы ты писал только логику, а не конфигурационный ад.
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍3🔥2👾1
☕️ Spring AI: LLM прямо внутри твоего Java-сервиса

Никаких отдельных пайплайнов, очередей и костылей.
Spring AI даёт готовый слой для работы с LLM: модели, промпты, memory, embeddings — всё как в Python, только нативно для Java.

⚙️ 1. Подключаем Spring AI

build.gradle
dependencies {
implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
}

➡️ Starter тащит автоконфигурацию, модельный клиент и готовые бинды.

🔑 2. Настройка доступа к модели

application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
model: gpt-4.1

➡️ Осталось только дернуть bean — Spring сам соберёт клиент.

💬 3. Генерация ответа из Java-кода
@RestController
@RequiredArgsConstructor
public class AiController {

private final ChatClient chatClient;

@GetMapping("/explain")
public String explain(@RequestParam String code) {
return chatClient
.prompt("Объясни этот код кратко:\n" + code)
.call()
.content();
}
}

➡️ ChatClient → prompt → call().
Минимум связующей логики.

🧩 4. Структурированный вывод (JSON → Java)
record Result(String summary, int score) {}

var response = chatClient
.prompt("""
Проанализируй текст.
Верни: summary, score (0-10).
""")
.responseAs(Result.class);

➡️ LLM сразу мапится в твой record-класс.
Никаких JSON-ручек.

📚 5. Prompts как ресурсы
@AiClient
public interface ReviewAi {

@SystemPrompt("prompts/system.txt")
@UserPrompt("prompts/review.txt")
String analyze(String text);
}

➡️ Промпты лежат в папке, модель вызывается как обычный Java-метод.

🔍 6. Embeddings для поиска
var embedding = embeddingClient.embed("Spring AI makes Java smart");

vectorStore.add("doc-1", embedding);

➡️ Vector store — часть Spring AI: Chroma, PGVector, Redis.
Подходит для RAG в микросервисах.

🧠 7. RAG: ответ с учётом своих данных
var answer = ragClient
.query("Как работает наш биллинг?")
.call()
.content();

➡️ ragClient сам:
1. берёт эмбеддинг вопроса,
2. ищет релевантные документы,
3. подаёт всё в LLM.


🪢 8. Streaming-ответы (chunk за chunk’ом)
chatClient
.prompt("Сгенерируй длинное объяснение")
.stream()
.forEach(chunk -> System.out.print(chunk.content()));

➡️ Нужен, если делаешь real-time UI или аналитический сервис.

🔌 9. Используем несколько моделей одновременно
spring:
ai:
clients:
local:
base-url: http://localhost:1234/v1
chat:
model: llama3
openai:
api-key: ${KEY}
chat:
model: gpt-4.1

➡️ Можно запрашивать локальную LLaMA и облачную OpenAI в одном приложении.

🧵 10. Memory (контекст диалога)
chatClient
.withConversation("session-42")
.prompt("Продолжи диалог")
.call();

➡️ Хранит состояние переписки без твоих ручных костылей.

🗣️ Запомни:Spring AI — это мост между классическим Java-стеком и LLM-миром: минимум клея, максимум интеграций, всё по-джавовски строго и предсказуемо.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥82👍1🥰1
⌨️ HashMap и TreeMap: Когда и как использовать

В Java коллекции Map предоставляют возможность хранить пары "ключ-значение". Два популярных варианта — HashMap и TreeMap. Давайте разберем их ключевые особенности и когда их лучше использовать.

📚 Kраткие определения:

- HashMap:
  - Неупорядоченная коллекция.
  - Основан на хэш-таблице.

- TreeMap:
  - Упорядоченная коллекция.
  - Основан на красно-черном дереве.
  - Поддерживает сортировку по ключам.

⚖️ Когда использовать:

- HashMap:
  - Если важна производительность и порядок хранения не имеет значения.
  - Когда нужно быстро получать значения по ключу

- TreeMap:
  - Если необходимо хранить элементы в отсортированном порядке.
  - Для использования функционала "примитивного" поиска (например, firstKey() или lastKey()).

📌 Подведение итогов:

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

#java #HashMap #TreeMap
Please open Telegram to view this post
VIEW IN TELEGRAM
👍91
Напоминаем про наш канал с тестами по Java ⌨️
Самое время проверить свои знания и понять, что стоит подтянуть.
➡️ Java | Tests
Please open Telegram to view this post
VIEW IN TELEGRAM
2
⌨️ Java Collection Factories: List.of и Map.of

Если вы пишете на Java дольше 5 лет, вы помните эту боль. Вам просто нужно создать Map с двумя значениями. Но Java требовала от вас целый ритуал.

🐢 Как это было раньше (Java 8 и старее):

// Для списка:
List<String> list = Arrays.asList("a", "b", "c");
// Вроде ок, но этот список можно менять (set), но нельзя менять размер (add/remove).

// Для Map — вообще ужас:
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map = Collections.unmodifiableMap(map); // Если хотим защитить от изменений


🚀 Как это делается с Java 9:

List<String> list = List.of("a", "b", "c");

Map<String, Integer> map = Map.of("one", 1, "two", 2);

Set<String> set = Set.of("a", "b", "c");


Преимущества:

1️⃣ Неизменяемость (Immutability): Эти коллекции нельзя менять. Попытка сделать .add() или .put() сразу выбросит UnsupportedOperationException. Это делает код безопаснее (особенно в многопоточности).

2️⃣ Лаконичность: Map создается в одну строку (до 10 пар ключ-значение можно писать через запятую, дальше — через Map.ofEntries).

3️⃣ Никаких null: Если вы попытаетесь положить null в List.of или Map.of, вы сразу получите ошибку. Java приучает нас не использовать null в коллекциях.

💡 Лайфхак для Java 16+:
Если вы работаете со стримами, забудьте про .collect(Collectors.toList()).
Теперь можно писать просто:

List<String> result = stream.filter(s -> s.length() > 3).toList();


Обратите внимание: toList() возвращает неизменяемый список, в отличие от коллектора.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥72👍2🤷‍♂1
⌨️ Sealed Classes: Фейсконтроль для наследования

В ООП всегда была дилемма.
Если вы делаете класс public, от него может наследоваться кто угодно.
Если делаете final, от него не может наследоваться никто.

А что, если я хочу, чтобы от моего класса Payment могли наследоваться только CashPayment и CardPayment, и больше никто?

До Java 17 приходилось придумывать костыли с пакетами. Теперь у нас есть Sealed Classes (Запечатанные классы).

✔️ Как это работает:
Вы используете ключевое слово sealed и через permits перечисляете, кому "можно".

public sealed interface Shape permits Circle, Square, Rectangle {
// Общие методы
}


Теперь Java (и компилятор) гарантирует: никаких других фигур, кроме этих трех, в программе существовать не может.

➡️ Правила игры:
Наследники (Circle, Square...) должны выбрать свою судьбу и указать один из модификаторов:

1️⃣ final — от меня наследовать нельзя (конец цепочки).

2️⃣ sealed — я тоже строгий, вот мой список наследников.

3️⃣ non-sealed — ладно, от меня можно наследовать всем (открываем шлюз).

🔥 Зачем это нужно? (Главная фишка)
Это идеально работает в связке с новым Switch.
Поскольку компилятор точно знает все возможные варианты наследников, он не потребует от вас ветку default!


String result = switch (shape) {
case Circle c -> "Это круг радиусом " + c.radius();
case Square s -> "Это квадрат";
case Rectangle r -> "Это прямоугольник";
// default не нужен! Java знает, что других фигур нет.
};


Это делает моделирование бизнес-логики (статусы заказов, типы ошибок) невероятно надежным. Если вы добавите новую фигуру, код перестанет компилироваться, пока вы не обработаете её в свитче.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍3❤‍🔥11😁1
⌨️ Sequenced Collections: Конец мучений с list.get(size - 1)

Признайтесь, сколько раз за свою карьеру вы писали этот уродливый код, чтобы достать последний элемент списка?

😫 Старая школа (до Java 21):

var list = List.of("A", "B", "C");

// Чтобы взять последний элемент:
String last = list.get(list.size() - 1);

// А если это Set? (LinkedHashSet)
// Приходилось использовать итератор или перегонять в список... 😱
String lastInSet = set.iterator()... // Ой, всё, лень писать.


Это было неудобно, нечитаемо и чревато ошибками (привет, -1).

🥳 Java 21 (Sequenced Collections):
В Java наконец-то добавили общий интерфейс для всех коллекций, у которых есть порядок элементов — SequencedCollection.

Теперь у List, Deque, SortedSet и LinkedHashSet появились единые методы:

list.getFirst(); // Взять первый
list.getLast(); // Взять последний (Наконец-то!)

list.addFirst("Z"); // Добавить в начало
list.addLast("X"); // Добавить в конец

list.reversed(); // Получить представление коллекции в обратном порядке


🔥 Почему это круто?

1️⃣ Единый стандарт. Раньше у Deque были методы getFirst, у Listget(0), у SortedSetfirst(). Теперь везде одинаково.

2️⃣ Работает с Set. Теперь можно легко взять первый или последний элемент из LinkedHashSet или TreeSet, не прибегая к итераторам.

3️⃣ Безопасность типов. Метод reversed() возвращает «живое» представление. Изменения в нем отразятся на оригинале (для мутабельных коллекций).

Вроде мелочь, а код становится намного чище.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍51
🔥 БЕСПЛАТНЫЙ КУРС ПО СОЗДАНИЮ НЕЙРО-СОТРУДНИКОВ НА GPT И ДРУГИХ LLM 🔥

Ищете практический и углубленный курс, чтобы освоить создание нейро-сотрудников? Мы создали курс из 5 объемных занятий. Это именно то, что нужно, чтобы прокачать свои навыки абсолютно бесплатно!

📌 Темы занятий:
1. Введение в мир нейро-сотрудников
2. Как работают LLM и их аналоги
3. Создание базы знаний для нейро-сотрудника (RAG)
4. Тестирование и отладка нейро-сотрудников
5. Интеграция нейро-сотрудников в Production

Вот 5 тем курса - он максимально простой и доступный, общеобразовательный, без какого-либо сложного программирования 📚Прохождение этого курса, скорее всего, займет у вас от 1 до 3 часов

🤖 Присоединяйтесь к нашему бесплатному курсу и разберитесь в этой увлекательной теме с нами!
1🔥1
⌨️ Java Unnamed Variables: Сила символа _

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

😒 Как мы выкручивались раньше:
Приходилось придумывать имя переменной, чтобы компилятор был доволен, а потом IDE ругалась: "Variable 'e' is never used".

try {
int number = Integer.parseInt(input);
} catch (NumberFormatException e) { // <-- Зачем нам 'e'?
// Мы и так знаем, что это не число, детали ошибки нам не важны
System.out.println("Это не число!");
}


Или в циклах:

for (var s : list) { // <-- Нам нужно просто посчитать количество, 's' не нужна
count++;
}


Как стало с Java 22 (Unnamed Variables):
Теперь можно использовать символ подчеркивания _. Это сигнал компилятору: "Здесь должна быть переменная, но я не собираюсь её использовать".

try {
int number = Integer.parseInt(input);
} catch (NumberFormatException _) { // Красота!
System.out.println("Это не число!");
}


Или в паттерн-матчинге (для instanceof и switch), если нам важен только тип, а не само значение:

if (obj instanceof String _) {
System.out.println("Да, это строка (но читать её я не буду)");
}


🔥 Почему это круто?

1️⃣ Чистота намерений: Читая код, другой разработчик сразу понимает: эта переменная игнорируется намеренно, а не по ошибке.

2️⃣ Спокойствие IDE: Анализаторы кода больше не спамят предупреждениями "Unused variable".

3️⃣ Меньше когнитивной нагрузки: Не нужно придумывать имена вроде ignored, unused или dummy.
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍2