Когда микросервисы растут, как грибы после дождя — баги начинают путешествовать между ними.
Контрактное тестирование ставит границы: продюсер не ломает клиента, а потребитель не ждёт несуществующие поля.
Schema-first и consumer-driven подходы делают интеграцию предсказуемой — без ручных костылей.
syntax = "proto3";
message Order {
string id = 1;
double amount = 2;
string status = 3;
}
🚀 2. Генерация классов из схемы
mvn protobuf:compile
interaction:
request:
method: GET
path: /orders/123
response:
status: 200
body:
id: "123"
amount: 50.0
🧩 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();
}
@Provider("order-service")
@PactFolder("pacts")
public class ProviderVerificationTest {
@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void verifyPacts(PactVerificationContext context) {
context.verifyInteraction();
}
}docker run -d -p 9292:9292 pactfoundation/pact-broker
mvn verify -Pcontract-tests
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
WebAssembly давно уже не только браузер. Wasmtime дал Java прямой доступ к WASM-модулям.Пиши на Rust алгоритм, компилируй в .wasm, вызывай из Java за микросекунды.Никаких FFI, никаких JNI — просто бинарный протокол между JVM и WASM.
implementation("org.wasmtime:wasmtime:17.0.0")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));
#[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
📡 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);
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());
#[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;
}
}
}
}
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());
}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();
}
// 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) }
}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥1
Media is too big
VIEW IN TELEGRAM
В этом видео автор подробно объясняет, что такое вложенные классы и зачем они нужны — как описать дополнительный объект, который принадлежит к основному классу.
Разбирается также работа с анонимными классами, которые позволяют быстро добавить новый функционал к создаваемому объекту прямо на месте, без создания отдельного файла.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
Когда время запуска и потребление памяти решают всё — классическая JVM становится роскошью.
GraalVM превращает Java-приложение в нативный бинарь: без JVM, без warm-up, без лагов.
Меньше 100 МБ памяти, запуск за миллисекунды — и это всё ещё Java.
sdk install java 23-graal
gu install native-image
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 — самостоятельный бинарь, мгновенно стартующий.Java: ~1.5s, 150MB
Native: 0.05s, 30MB
mvn -Pnative spring-boot:build-image
GraalVM анализирует код заранее. Всё, что создаётся рефлексией — нужно описать:
[
{ "name": "com.demo.User", "allDeclaredConstructors": true }
]
Эти фреймворки заточены под GraalVM:
mvn package -Dpackaging=native-image
FROM scratch
COPY app /
ENTRYPOINT ["/app"]
🧩 9. Debug и профилирование
native-image --verbose --diagnostics-mode ...
Никаких виртуальных машин, просто чистый бинарь, который летает.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥4👍1
&& и || называются сокращёнными логическими операторами 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
В этом видео автор подробно разбирает, как организовывать код с помощью пакетов (packages) и зачем они нужны для структурирования проекта.
Также объясняется принцип перегрузки методов — когда один метод может иметь разные версии с разными параметрами. Разбирается практическое применение модификаторов static и final, которые меняют поведение классов и методов.
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-микроконтроллере
JVM для edge = убить муху кувалдой. Нужно легче.
🧠 2. GraalVM Native Image — первое спасение
➡️ Статический бинарь без зависимостей. Прямо на ARM64, ARM32 — работает везде.
🔧 3. Минималистичное приложение для сенсора
➡️ Никаких фреймворков, никакого overhead'а.
📡 4. Связь по MQTT — стандарт IoT
✔️ Отправляем данные на MQTT-broker — и остальной мир слушает. Просто и эффективно.
🎯 5. Quarkus для edge — фреймворк, заточенный под IoT
➡️ Quarkus + native image = полнофункциональная Java-фреймворк, который весит как Go-приложение.
🔌 6. Работа с GPIO на Raspberry Pi через Pi4j
➡️ Мигаем светодиодом — классика IoT. Pi4j абстрагирует сложность GPIO'шных операций.
💾 7. Лёгкая БД для edge — SQLite или H2
➡️ SQLite весит 500 КБ, не нужен отдельный сервер. Идеально для edge-узла.
🌐 8. REST API на встроенном устройстве через Micronaut
➡️ Микронаут stubs в 2-3 МБ. Запускаешь на Raspberry Pi — и уже есть REST API.
🗣️ Запомни:native image на edge = батарея в 5x дольше, код за день.
Java когда-то весила 500 МБ и требовала мощный железо. Теперь? GraalVM Native Image, Project Loom и TinyJVM трансформируют Java в лёгкого воина для IoT и edge. 50 МБ образ, микросекундный старт, батарея держит часы вместо минут — и это всё ещё Java.
// Классический 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 МБ
🔧 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; // Симуляция
}
}
📡 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();
}
}
<!-- pom.xml -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-mqtt</artifactId>
</dependency>
mvn package -Dnative
# Результат: 80 МБ образ (вместо 500 МБ обычного Spring Boot)
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();
}
}
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();
}
}
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()
);
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4❤3👍2
Приложение тормозит не из-за кода, а из-за GC пауз в самый неудачный момент.
Garbage Collector жрёт 30% CPU, heap фрагментируется, latency скачет от 10ms до 2 секунд. Правильные флаги меняют всё: ZGC держит паузы <10ms, G1 стабилен на больших heap'ах.Мониторинг в реальном времени показывает узкие места — без этого тюнишь вслепую.
java -XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+PrintGCTimeStamps \
-Xloggc:gc.log \
-jar app.jar
# Для 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
3. Heap размер фиксированный
java -Xms8g -Xmx8g \
-XX:+AlwaysPreTouch \
-XX:+UseTransparentHugePages \
-jar app.jar
java -XX:NewRatio=3 \
-XX:SurvivorRatio=8 \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=50 \
-jar app.jar
java -XX:ParallelGCThreads=8 \
-XX:ConcGCThreads=4 \
-XX:+ParallelRefProcEnabled \
-XX:+UseG1GC \
-jar app.jar
java -XX:InitiatingHeapOccupancyPercent=35 \
-XX:G1ReservePercent=10 \
-XX:+UseG1GC \
-jar app.jar
java -XX:+UseG1GC \
-XX:+UseStringDeduplication \
-XX:StringDeduplicationAgeThreshold=3 \
-jar app.jar
# Создаём архив классов
java -Xshare:dump \
-XX:SharedArchiveFile=./app-cds.jsa \
-cp app.jar
# Боевые запуски
java -Xshare:on \
-XX:SharedArchiveFile=./app-cds.jsa \
-jar app.jar
# Общая картина GC
jstat -gc -h10 <pid> 1000
# Детали по поколениям
jstat -gccapacity <pid> 2000
# Причины сборок
jstat -gccause <pid> 1000
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤3👍2
Устали писать бесконечные геттеры, сеттеры,
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
👍14❤3🤡2
Если вы пишете на 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
👍5❤1🔥1
Помните старый
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
➡️ Web, валидация, JPA и PostgreSQL — базовый набор для любого сервиса.
▶️ 2. Запускаем приложение
Main-класс:
➡️ Встроенный Tomcat поднимется сам.
Никаких конфигов, никакого сервера руками.
🌐 3. Первый REST-контроллер
➡️ Уже есть API, уже можно дёргать в Postman.
🗄 4. Настрока БД
application.yml
➡️ Spring Boot сам создаёт таблицы, коннектится и логирует SQL.
📦 5. Сущность + репозиторий
➡️ CRUD готов без единой строчки SQL.
🧩 6. Сервисный слой
➡️ Чистая логика, никаких репозиторных костылей в контроллере.
🎯 7. API для создания пользователя
➡️ Post → сохраняем → возвращаем — 30 секунд работы.
🧪 8. Тестируем без сервера
➡️ MockMvc поднимает только Web-слой, работает молниеносно.
📦 9. Создаём fat-jar и запускаем
➡️ Внутри — сервер, конфиги, код. Готово для деплоя.
☁️ 10. Dockerfile на 5 строк
➡️ Легко уезжает в Kubernetes, AWS, GCP, куда хочешь.
🗣️ Запомни:Spring Boot — это не «фреймворк», а ускоритель: он убирает рутину, чтобы ты писал только логику, а не конфигурационный ад.
Spring Boot — это когда ты запускаешь продовый сервис быстрее, чем успеешь сделать себе чай.
Никаких XML, минимум настроек, встроенный сервер, автоконфигурация и готовые стартовые зависимости.
Вот как собрать рабочий backend за 1 час — от нуля до API.
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'
}Main-класс:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Никаких конфигов, никакого сервера руками.
@RestController
@RequestMapping("/api/hello")
public class HelloController {
@GetMapping
public Map<String, String> hello() {
return Map.of("message", "Spring Boot работает!");
}
}
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> {}🧩 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);
}
}
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired MockMvc mvc;
@Test
void testCreate() throws Exception {
mvc.perform(post("/api/users?name=Tom"))
.andExpect(status().isOk());
}
}
📦 9. Создаём fat-jar и запускаем
./gradlew bootJar
java -jar build/libs/app.jar
FROM eclipse-temurin:21
COPY build/libs/app.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍3🔥2👾1
Никаких отдельных пайплайнов, очередей и костылей.
Spring AI даёт готовый слой для работы с LLM: модели, промпты, memory, embeddings — всё как в Python, только нативно для Java.
build.gradle
dependencies {
implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
}🔑 2. Настройка доступа к модели
application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
model: gpt-4.1
💬 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();
}
}
Минимум связующей логики.
🧩 4. Структурированный вывод (JSON → Java)
record Result(String summary, int score) {}
var response = chatClient
.prompt("""
Проанализируй текст.
Верни: summary, score (0-10).
""")
.responseAs(Result.class);Никаких JSON-ручек.
📚 5. Prompts как ресурсы
@AiClient
public interface ReviewAi {
@SystemPrompt("prompts/system.txt")
@UserPrompt("prompts/review.txt")
String analyze(String text);
}
var embedding = embeddingClient.embed("Spring AI makes Java smart");
vectorStore.add("doc-1", embedding);Подходит для RAG в микросервисах.
var answer = ragClient
.query("Как работает наш биллинг?")
.call()
.content();
1. берёт эмбеддинг вопроса,
2. ищет релевантные документы,
3. подаёт всё в LLM.
🪢 8. Streaming-ответы (chunk за chunk’ом)
chatClient
.prompt("Сгенерируй длинное объяснение")
.stream()
.forEach(chunk -> System.out.print(chunk.content()));
🔌 9. Используем несколько моделей одновременно
spring:
ai:
clients:
local:
base-url: http://localhost:1234/v1
chat:
model: llama3
openai:
api-key: ${KEY}
chat:
model: gpt-4.1
🧵 10. Memory (контекст диалога)
chatClient
.withConversation("session-42")
.prompt("Продолжи диалог")
.call();
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤2👍1🥰1
В 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
👍9❤1
Напоминаем про наш канал с тестами по Java ⌨️
Самое время проверить свои знания и понять, что стоит подтянуть.
➡️ Java | Tests
Самое время проверить свои знания и понять, что стоит подтянуть.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Если вы пишете на 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
🔥7❤2👍2🤷♂1
В ООП всегда была дилемма.
Если вы делаете класс
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❤🔥1❤1😁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, у List — get(0), у SortedSet — first(). Теперь везде одинаково.2️⃣ Работает с Set. Теперь можно легко взять первый или последний элемент из
LinkedHashSet или TreeSet, не прибегая к итераторам.3️⃣ Безопасность типов. Метод
reversed() возвращает «живое» представление. Изменения в нем отразятся на оригинале (для мутабельных коллекций).Вроде мелочь, а код становится намного чище.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍5❤1
🔥 БЕСПЛАТНЫЙ КУРС ПО СОЗДАНИЮ НЕЙРО-СОТРУДНИКОВ НА GPT И ДРУГИХ LLM 🔥
Ищете практический и углубленный курс, чтобы освоить создание нейро-сотрудников? Мы создали курс из 5 объемных занятий. Это именно то, что нужно, чтобы прокачать свои навыки абсолютно бесплатно!
📌 Темы занятий:
1. Введение в мир нейро-сотрудников
2. Как работают LLM и их аналоги
3. Создание базы знаний для нейро-сотрудника (RAG)
4. Тестирование и отладка нейро-сотрудников
5. Интеграция нейро-сотрудников в Production
Вот 5 тем курса - он максимально простой и доступный, общеобразовательный, без какого-либо сложного программирования 📚Прохождение этого курса, скорее всего, займет у вас от 1 до 3 часов
🤖 Присоединяйтесь к нашему бесплатному курсу и разберитесь в этой увлекательной теме с нами!
Ищете практический и углубленный курс, чтобы освоить создание нейро-сотрудников? Мы создали курс из 5 объемных занятий. Это именно то, что нужно, чтобы прокачать свои навыки абсолютно бесплатно!
📌 Темы занятий:
1. Введение в мир нейро-сотрудников
2. Как работают LLM и их аналоги
3. Создание базы знаний для нейро-сотрудника (RAG)
4. Тестирование и отладка нейро-сотрудников
5. Интеграция нейро-сотрудников в Production
Вот 5 тем курса - он максимально простой и доступный, общеобразовательный, без какого-либо сложного программирования 📚Прохождение этого курса, скорее всего, займет у вас от 1 до 3 часов
🤖 Присоединяйтесь к нашему бесплатному курсу и разберитесь в этой увлекательной теме с нами!
❤1🔥1
Все мы сталкивались с ситуацией, когда синтаксис требует объявить переменную, но она нам совершенно не нужна.
😒 Как мы выкручивались раньше:
Приходилось придумывать имя переменной, чтобы компилятор был доволен, а потом 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