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

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

✍️По всем вопросам: @Pascal4eg
Download Telegram
Stream

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

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

Промежуточные операции
📌 Промежуточные операции не выполняются до тех пор, пока не будет вызвана какая-либо терминальная операция.

Они составляют конвейер выполнения Stream. Промежуточную операцию можно добавить в конвейер Stream методами:
filter()
map()
flatMap()
distinct()
sorted()
peek()
limit()
skip()


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

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

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

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

Терминальные операции
Терминальные операции могут проходить через поток для получения результата или побочного эффекта.

📌 После выполнения терминальной операции, потоковый конвейер считается использованным и больше не может использоваться.

Терминальные операции:
forEach()
forEachOrdered()
toArray()
reduce()
collect()
min()
max()
count()
anyMatch()
allMatch()
noneMatch()
findFirst()
findAny()


Каждая из этих операций инициирует выполнение всех промежуточных операций.
👍821
⌨️ private методы в интерфейсах

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

private методы могут быть как экземплярными, так и static.

Пример:

public interface MyInterface {
default void showMessage() {
print("Default method calling private method");
}

static void showStaticMessage() {
print("Static method calling private method");
}

// Private method for reuse
private static void print(String message) {
System.out.println(message);
}
}


#java #interface #private
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍4🔥1
⌨️ PriorityQueue — это реализация структуры данных очередь с приоритетом, которая упорядочивает элементы по их естественному порядку или по заданному компаратору. Она представляет собой часть коллекции Java Collections Framework и находится в пакете java.util.

В отличие от Queue, элементы PriorityQueue не упорядочены по времени их добавления, а по приоритету.

Основные методы:

add(E e) - добавляет элемент в очередь.

remove() - удаляет и возвращает элемент с наивысшим приоритетом. Если очередь пуста, генерируется исключение NoSuchElementException.

poll() - удаляет и возвращает элемент с наивысшим приоритетом. Если очередь пуста, возвращает null.

peek() - возвращает, но не удаляет элемент с наивысшим приоритетом. Если очередь пуста, возвращает null.

element() - возвращает, но не удаляет элемент с наивысшим приоритетом. Если очередь пуста, генерируется исключение NoSuchElementException.

Пример:

class Task implements Comparable<Task> {
String name;
Integer priority;

public Task(String name, Integer priority) {
this.name = name;
this.priority = priority;
}

@Override
public int compareTo(@NotNull Task o) {
return priority - o.priority;
}
}

public class Test {
public static void main(String[] args) {
PriorityQueue<Task> pq = new PriorityQueue<>();

pq.add(new Task("Task1", 1));
pq.add(new Task("Task5", 5));
pq.add(new Task("Task2", 2));
pq.add(new Task("Task4", 4));
pq.add(new Task("Task3", 3));

while (!pq.isEmpty()) {
System.out.println(pq.poll().name);
// Выведет: Task1, Task2, Task3, Task4, Task5
}
}
}


#java #PriorityQueue
Please open Telegram to view this post
VIEW IN TELEGRAM
👍74
🧩 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