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

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

✍️По всем вопросам: @Pascal4eg
Download Telegram
🛰 Embedded JavaScript в JVM — запускаем JS в Java без WebView

Хочешь встроить JavaScript в Java-приложение?
Без браузера, без WebView, просто для вычислений, шаблонов, скриптов или конфигов?

Это возможно — через встроенные скриптовые движки. Работает с JDK 8+, на проде, без зависимостей (или с GraalVM).

Как это вообще работает?

Java включает JS-движок (Nashorn до Java 14), а после — можно подключить GraalVM JavaScript.

Через javax.script.ScriptEngine ты можешь:
🟢 выполнять JS-код;
🟢 вызывать JS-функции из Java;
🟢 передавать переменные между языками;
🟢 использовать JS как DSL внутри проекта.

🔧 Простой запуск скрипта
import javax.script.*;

public class Demo {
public static void main(String[] args) throws Exception {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
engine.eval("print('Привет из JavaScript');");
}
}

✔️ Всё — ты только что исполнил JS прямо в JVM.

📥 Передаём переменные
engine.put("name", "Neo");
engine.eval("print('Hello, ' + name);"); // Hello, Neo

Ты можешь прокидывать данные из Java в JS — и наоборот.

📌 Вызываем JS-функцию из Java
engine.eval("function add(a, b) { return a + b; }");

Invocable inv = (Invocable) engine;
Object result = inv.invokeFunction("add", 5, 7);

System.out.println(result); // 12

➡️ Используй как простую встроенную логику.

🧠 Пример — вычисление формулы из строки
String expr = "price * qty + tax";
Bindings vars = engine.createBindings();
vars.put("price", 100);
vars.put("qty", 2);
vars.put("tax", 50);

Object result = engine.eval(expr, vars);
System.out.println(result); // 250

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

⚙️ Подключение GraalVM JS (если JDK >14)
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<version>23.1.1</version>
</dependency>

Context context = Context.create("js");
Value result = context.eval("js", "2 + 2");
System.out.println(result.asInt()); // 4

GraalVM даёт современный JS с поддержкой всего ES6+.


🗣️ Запомни:Embedded JavaScript — это про гибкость без внешних зависимостей.
Хочешь дать пользователю конфиги на JS? Или динамически описывать поведение? Или быстро собрать вычислитель формул?Ты можешь запускать JavaScript напрямую в JVM — просто, безопасно и эффективно.
Please open Telegram to view this post
VIEW IN TELEGRAM
8🔥4🗿1
Посты на какие темы вы хотели бы увидеть на нашем канале? Пишите в комметариях.
10
⛓️ Chain of Responsibility на enum-ах — антипаттерн, который работает

Chain of Responsibility — это когда у тебя есть цепочка проверок или действий, которые обрабатываются по очереди, пока не будет «готово» или «отказ».
В Java обычно это: интерфейс + куча обработчиков.
Но можно сделать проще: на `enum` с абстрактными методами.

🔧 Пример: валидация пользователя
enum UserValidator {
CHECK_NAME {
boolean validate(User u) {
return u.name != null && !u.name.trim().isEmpty();
}
},
CHECK_AGE {
boolean validate(User u) {
return u.age >= 18;
}
},
CHECK_EMAIL {
boolean validate(User u) {
return u.email != null && u.email.contains("@");
}
};

abstract boolean validate(User u);
}

Каждый enum — это как мини-обработчик.
Ты прямо внутри перечисления задаёшь поведение.
Код не размазан по 10 классам и не требует интерфейсов.

🔁 Проверка по цепочке
User user = new User("Neo", 30, "neo@matrix.io");

boolean valid = Arrays.stream(UserValidator.values())
.allMatch(v -> v.validate(user));

➡️ Если все валидаторы вернули true — пользователь валиден.

⚠️ Обработка ошибок поимённо
Arrays.stream(UserValidator.values())
.filter(v -> !v.validate(user))
.forEach(v -> System.out.println("⛔️ Ошибка в: " + v.name()));

📌Ты можешь узнать, что именно не прошло, без лишних if'ов.

🧪 Альтернатива: остановиться на первом ошибочном
Optional<UserValidator> failed = Arrays.stream(UserValidator.values())
.filter(v -> !v.validate(user))
.findFirst();

if (failed.isPresent()) {
System.out.println("Провал на шаге: " + failed.get().name());
}

➡️ Удобно, если хочешь быстро выйти из цепочки.

📦 Всё это работает с обычными классами
class User {
String name;
int age;
String email;

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


🗣️ Запомни: Иногда enum с методами — это рабочий компромисс, когда не хочется поднимать тяжёлый Chain of Responsibility.Просто, читаемо, удобно. Особенно для валидаций, фильтров, конвертаций и тестов.
Please open Telegram to view this post
VIEW IN TELEGRAM
117🤔5❤‍🔥1👨‍💻1
Media is too big
VIEW IN TELEGRAM
☕️Введение в Swing | Как создать графический калькулятор на Java?

Это видео показывает, как создать простой графический калькулятор на Java с использованием Swing. Подойдёт новичкам: шаг за шагом разбирается создание кнопок, полей ввода и обработка событий. Отличный старт для изучения GUI-разработки на Java.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32
👻 Ghost Interfaces: тип как флаг без логики — и это работает

Java позволяет создавать интерфейсы без методов. Они ничего не делают.
Но если ты дашь этот интерфейс классу — ты отмечаешь объект как "особенный".
Это и есть ghost interface — маркерный интерфейс, флаг через тип.

📛 Пример: метка для доступа
public interface AdminAccess {
// ничего не требует, просто существует
}


public class User {}

public class AdminUser extends User implements AdminAccess {}

➡️ AdminUser не обязан ничего переопределять.
Но теперь ты можешь понять, что это админ, просто глядя на тип.

🧪 Проверка — через instanceof
void check(User u) {
if (u instanceof AdminAccess) {
System.out.println(" Доступ разрешён");
} else {
System.out.println("⛔️ Нет прав");
}
}

➡️ Вот где начинается магия: поведение зависит от типа, а не от полей или флагов.

🛠 Применение на практике

Смотри, тебе нужно различать обычные запросы и те, что требуют валидации.
Не хочешь городить if (request.needsValidation())? — решается в один тип.
public interface RequiresValidation {}

public class LoginRequest implements RequiresValidation {}
public class PingRequest {}


void handle(Object req) {
if (req instanceof RequiresValidation) {
System.out.println("🔍 Проверка данных");
} else {
System.out.println("🚀 Без валидации");
}
}

➡️ Никаких полей. Просто интерфейс как ярлык.

💾 Другой кейс — запрет на сохранение
public interface ReadOnly {}

public class ConfigFile implements ReadOnly {}
public class LogFile {}


void save(Object file) {
if (file instanceof ReadOnly) {
throw new RuntimeException(" Это read-only");
}
System.out.println("💾 Сохраняем файл...");
}

➡️ Без boolean readonly, без .getPermissions()просто instanceof.

📦 Комбинации: объект — это набор флагов
public interface JsonSerializable {}
public interface RequiresAuth {}

public class ApiRequest implements JsonSerializable, RequiresAuth {}


if (req instanceof JsonSerializable) {
System.out.println("📤 Преобразуем в JSON");
}
if (req instanceof RequiresAuth) {
System.out.println("🔐 Проверяем токен");
}

➡️ Всё как в декларации типа: сам объект описывает себя.

📚 enum тоже работает
public interface Internal {}

public enum Command implements Internal {
REBOOT, SHUTDOWN
}

void dispatch(Enum<?> cmd) {
if (cmd instanceof Internal) {
System.out.println("🔐 Внутренняя команда");
} else {
System.out.println("🌍 Публичная");
}
}

➡️ Даже enum может быть "помечен" ghost-интерфейсом.


🗣 Запомни: Ghost Interfaces — это не костыль, а чистый, типовой способ описывать поведение.Они не делают, а говорят. Всё, что нужно — instanceof.Пиши логику по типу, не по данным.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍63👨‍💻3
🧪 Java Agents — как влезть в чужой .jar и следить за ним изнутри

Нужно посмотреть, какие классы грузятся в рантайме?
Или вставить лог прямо в чужой метод, не трогая исходник?

Java Agent запускается до `main()` и даёт доступ ко всему, что загружается в JVM.

🔧 Шаг 🔢: создаём агент
public class SpyAgent {
public static void premain(String args, Instrumentation inst) {
System.out.println("👁 Агент стартовал");
}
}

➡️ Метод premain() будет вызван раньше `main()` обычного приложения.
Параметры:

🟢 args — аргументы, переданные агенту
🟢 Instrumentation — интерфейс для перехвата и изменения классов

🧲 Шаг 🔢: перехватываем загрузку классов
inst.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain domain,
byte[] classfileBuffer) {
if (className.contains("Service")) {
System.out.println("🎯 Загружается: " + className);
}
return null;
}
});

➡️ Каждый раз, когда JVM грузит класс — агент получает байты.
Если хочешь, можешь их изменить и отдать «новую» версию класса.

🗂 Шаг🔢: манифест — как JVM узнает, что это агент

Создаём MANIFEST.MF:
Premain-Class: SpyAgent
Can-Redefine-Classes: true


Собираем .jar с этим манифестом:
jar cmf MANIFEST.MF spy-agent.jar SpyAgent.class


🚀 Шаг 4: подключаем агент к приложению
java -javaagent:spy-agent.jar -jar victim.jar

➡️ JVM загрузит твой агент, вызовет premain(), и только потом запустит main() из victim.jar.

🔁 Пример: вставляем поведение с ByteBuddy
new AgentBuilder.Default()
.type(named("com.example.Target"))
.transform((builder, type, cl, module) ->
builder.method(named("process"))
.intercept(MethodDelegation.to(LoggerInterceptor.class))
).installOn(inst);


public class LoggerInterceptor {
@RuntimeType
public static void intercept() {
System.out.println("📦 Метод process() вызван");
}
}

➡️ Агент находит нужный класс, перехватывает метод и вставляет лог.
Ты не меняешь исходник — всё происходит на лету, при загрузке.

🧱 Структура проекта:
spy-agent/
├── SpyAgent.java
├── LoggerInterceptor.java
├── MANIFEST.MF


🗣️ Запомни: Java Agent — это способ влезть в чужое приложение до его запуска.Ты можешь видеть всё, что грузится, менять методы, добавлять поведение — не касаясь исходников и не пересобирая .jar.
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍6👨‍💻5
⚙️ "Bytecode Engineering" с ASM и Byte Buddy: правим классы на лету

Хочешь подменить метод в чужом классе, не трогая исходники? Или генерировать новый класс динамически? В Java это делается через байткод-инжиниринг — на уровне JVM-инструкций.

ASM: низкоуровневый контроль
public class AddPrintAdapter extends MethodVisitor {
public AddPrintAdapter(MethodVisitor mv) {
super(Opcodes.ASM9, mv);
}

@Override
public void visitCode() {
super.visitCode();
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("🔥 Метод вызван");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
"println", "(Ljava/lang/String;)V", false);
}
}

➡️ Этот адаптер добавит System.out.println() в начало любого метода. Работа с visitCode() — прямой доступ к байткоду.

ClassReader reader = new ClassReader("com.example.Foo");
ClassWriter writer = new ClassWriter(reader, 0);
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9, writer) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if (name.equals("run")) {
return new AddPrintAdapter(mv);
}
return mv;
}
};
reader.accept(visitor, 0);
byte[] modifiedClass = writer.toByteArray();

➡️ Прочитали класс, нашли метод run, встроили println() — и получили новый .class.

🧠 Byte Buddy: высокоуровневая альтернатива
new ByteBuddy()
.subclass(Object.class)
.name("com.example.DynamicClass")
.defineMethod("hello", void.class, Modifier.PUBLIC)
.intercept(MethodDelegation.to(HelloInterceptor.class))
.make()
.load(ClassLoader.getSystemClassLoader());

➡️ Создаём класс с методом hello() прямо во время выполнения. Без .java-файлов, всё в памяти.

public class HelloInterceptor {
@RuntimeType
public static void intercept() {
System.out.println("👋 Hello from dynamic method");
}
}

➡️ При вызове hello() будет выполнен этот перехватчик. Никаких аннотаций, никакой магии — только Java.

🗣️ Запомни:Bytecode-инжиниринг даёт тебе контроль над классами после компиляции. ASM — это scalpel. Byte Buddy — это лего. Если нужно вставить поведение в рантайме — это твой инструмент.
Please open Telegram to view this post
VIEW IN TELEGRAM
👨‍💻8👍4
⚙️ Java Core — что реально важно понимать

Java Core — это не просто "синтаксис", это то, на чём держится весь язык: классы, объекты, память, потоки, исключения. Ни Spring, ни фреймворки не помогут, если ты не знаешь базу.

📦 Классы и объекты
class User {
String name;
int age;

void sayHello() {
System.out.println("Привет, меня зовут " + name);
}
}

User u = new User();
u.name = "Иван";
u.sayHello();

➡️ Всё в Java — объект. Класс — это чертёж. Объект — это реальность.

👷 Конструкторы и перегрузка
class Point {
int x, y;

Point() {
this(0, 0);
}

Point(int x, int y) {
this.x = x;
this.y = y;
}
}

➡️ this(...) вызывает другой конструктор — удобно задавать значения по умолчанию.

🧱 Наследование и super
class Animal {
void speak() {
System.out.println("звук");
}
}

class Dog extends Animal {
void speak() {
super.speak();
System.out.println("гав");
}
}

➡️ super нужен, когда хочешь использовать поведение родителя, а не полностью переопределять.

🧠 Интерфейсы и абстракции
interface Printable {
void print();
}

class Book implements Printable {
public void print() {
System.out.println("Печать книги");
}
}

➡️ Интерфейс задаёт что умеет объект, но не как именно.

🔁 Память: stack vs heap
void run() {
int x = 10; // stack
String name = "Java"; // heap (объект)
}

➡️ Локальные переменные — в стеке. Объекты и массивы — в куче (heap).

🪓 Final, static, this
class Example {
static int count = 0;
final int id;

Example() {
this.id = ++count;
}
}

🟢 final — нельзя переопределить/изменить
🟢 static — общая переменная на все объекты
🟢 this — текущий объект

🔄 Циклы, итераторы, foreach
List<String> names = List.of("Alice", "Bob");

for (String name : names) {
System.out.println(name);
}

➡️ Работает с коллекциями, массивами, любым, что реализует Iterable.

🧰 try-catch-finally
try {
int x = 1 / 0;
} catch (ArithmeticException e) {
System.out.println("Ошибка: деление на 0");
} finally {
System.out.println("Завершение");
}

➡️ finally выполнится всегда. Можно использовать для закрытия ресурсов.

⚡️ Вложенные классы и лямбды
Runnable r = () -> System.out.println("Привет из потока");
new Thread(r).start();

➡️ Лямбды — краткий способ описать анонимный метод.

⛓️equals() 🆚 ==
String a = "test";
String b = new String("test");

System.out.println(a == b); // false
System.out.println(a.equals(b)); // true

🟢 == сравнивает ссылки
🟢 .equals() сравнивает содержимое

🧵 Потоки (Thread) и синхронизация
class Task extends Thread {
public void run() {
System.out.println("Поток: " + Thread.currentThread().getName());
}
}
new Task().start();

➡️ start() запускает поток асинхронно. run() — просто метод, вызовется как обычный.

🔑 Ключевые коллекции
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
Map<String, Integer> map = new HashMap<>();

🟢List — упорядоченный список
🟢Set — уникальные элементы
🟢 Map — пары ключ-значение

📚 Generic'и
class Box<T> {
T value;

void set(T value) { this.value = value; }
T get() { return value; }
}

➡️ Box<String>, Box<Integer>типизируешь класс под себя.


🗣️ Запомни:
Java Core — это то, что ты используешь каждый день, даже если не замечаешь. Понимание классов, памяти, коллекций, исключений и потоков — это не “для экзамена”, это основа всей архитектуры твоего кода.
Please open Telegram to view this post
VIEW IN TELEGRAM
17👍5🔥4❤‍🔥1👨‍💻1
🌐 Java:Что выбрать — виртуальные потоки или реактивный подход?

В 2025 году Java предлагает два мощных и актуальных подхода к масштабируемому коду: виртуальные потоки (Project Loom) и реактивное программирование (Project Reactor, RxJava). Какой из них выбрать, зависит от задач, но важно понять различия.

В статье ты узнаешь:

📌 Что такое виртуальные потоки и как они упрощают многозадачность, устраняя сложности с потоками ОС.
📌 Когда классический реактивный подход через Flux/Mono оправдан — и где он будет эффективнее.
📌 Конкретные примеры — REST‑сервисы, WebFlux и виртуальные потоки одновременно.
📌 Почему виртуальные потоки могут заменить реактив на большинстве задач, но не всегда.

➡️ Читайте и наслаждайтесь

🗣️ Вывод: Java - это мир гибкого выбора: хочешь простоту и читаемость — бери Loom, нужна экстремальная отказоустойчивость — Reactivity остаётся в деле.

🤩 Java Фишки и трюки || #Cтатья #ProjectLoom #Reactive
Please open Telegram to view this post
VIEW IN TELEGRAM
5🔥4👍3
☕️ ThreadPool в Java: управляем потоками по-взрослому

Если тебе нужно запускать задачи параллельно — не создавай 1000 new Thread() руками.
ThreadPool даст тебе контроль, переиспользование потоков и экономию ресурсов.

🚀 Простой пул через Executors
ExecutorService pool = Executors.newFixedThreadPool(4);

for (int i = 0; i < 10; i++) {
int taskId = i;
pool.submit(() -> {
System.out.println("🔧 Задача " + taskId + " в потоке " + Thread.currentThread().getName());
});
}

pool.shutdown();

➡️ Пул из 4 потоков. Все 10 задач выполнятся по очереди, переиспользуя потоки.
➡️ shutdown() завершает работу пула после выполнения задач.

⚖️ Типы пулов
Executors.newFixedThreadPool(4);       // фиксированное количество потоков
Executors.newCachedThreadPool(); // динамически растёт при нагрузке
Executors.newSingleThreadExecutor(); // один поток на всё

🟢 Fixed — стабильный.
🟢 Cached — вспышки нагрузки.
🟢 Single — последовательные задачи.

🧠 Почему не new Thread()?
new Thread(() -> doStuff()).start();  // плохо для большого числа задач

👍 Каждый Thread — это новый объект + ресурсы ОС.
👍 Пулы переиспользуют потоки, управляют ими и не перегружают систему.

🧰 Завершение работы пула
pool.shutdown();                    // мягкое завершение
pool.awaitTermination(10, TimeUnit.SECONDS);

pool.shutdownNow(); // жёсткая остановка

➡️ Сначала shutdown(), потом awaitTermination() — ждёшь окончания.
➡️ shutdownNow() — кидает InterruptedException в задачи.

⛓️ Callable и Future
ExecutorService pool = Executors.newSingleThreadExecutor();

Callable<String> task = () -> {
Thread.sleep(1000);
return "Готово!";
};

Future<String> result = pool.submit(task);

System.out.println(result.get()); // блокирует до результата
pool.shutdown();

➡️ Callable возвращает значение, в отличие от Runnable.
➡️ Future.get() — ждёт выполнения.

🔍 Настройка вручную через ThreadPoolExecutor
ExecutorService pool = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>()
);

➡️ Полный контроль: минимальное/максимальное число потоков, очередь, поведение.

🗣️ Запомни:
ThreadPool — это не про скорость, а про контроль и стабильность. Не плодишь потоки — а держишь их в узде. Подходит для бэкенда, ботов, парсеров, фоновых задач и всего, что живёт в многопоточке.
Please open Telegram to view this post
VIEW IN TELEGRAM
12🔥6👍1
🌿 Фишки и тонкости Spring — которые реально нужны в проектах

Spring — не только @Autowired и @Controller. Он умеет кэшировать, обрабатывать события, внедрять бин по условию, управлять конфигурацией, проксировать методы и даже сам себя обновлять.

Вот то, что ты точно начнёшь использовать в реальных проектах 👇

🔄 @PostConstruct — инициализация после DI
@Component
public class MyService {
@PostConstruct
public void init() {
System.out.println(" Всё сконфигурировано, запускаем...");
}
}

➡️ Метод вызовется сразу после того, как все зависимости внедрены. Не путать с конструктором — в нём ещё может не быть других бинов.

💥 @EventListener — реакция на внутренние события
@Component
public class UserEventHandler {

@EventListener
public void onRegistered(UserRegisteredEvent event) {
System.out.println("🎉 Новый пользователь: " + event.getEmail());
}
}


// где-то в коде:
applicationEventPublisher.publishEvent(new UserRegisteredEvent(email));

➡️ Это удобнее, чем коллбэки и наблюдатели — разнесённая логика, ничего не жёстко связано.

🧪 Условная загрузка бинов
@Bean
@ConditionalOnProperty(name = "feature.cool", havingValue = "true")
public CoolFeature coolFeature() {
return new CoolFeature();
}

➡️ Бин создастся только если в `application.properties` прописано `feature.cool=true`

⚙️ @ConfigurationProperties — структурированная конфигурация
@ConfigurationProperties(prefix = "mail")
@Component
public class MailSettings {
private String host;
private int port;
// getters/setters
}

➡️ В application.yml:
mail:
host: smtp.example.com
port: 587


📌 И всё это уже лежит в MailSettings бин — без @Value, без хардкода.

🧠 Внедрение значений через @Value
@Value("${app.name:DefaultApp}")
private String appName;

➡️ Подтянет app.name из пропертей. Если не указано — будет "DefaultApp".

📍 Логика до и после вызова метода — через AOP
@Aspect
@Component
public class LogAspect {
@Around("@annotation(Loggable)")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("▶️ Вызов: " + pjp.getSignature());
Object result = pjp.proceed();
System.out.println("⏹️ Результат: " + result);
return result;
}
}

➡️ Работает на любой метод с аннотацией @Loggable. Реальный способ вклиниваться в методы без изменения кода.

⚡️ Кеширование результатов методов
@Cacheable("users")
public User getById(Long id) {
return userRepository.findById(id).orElseThrow();
}

➡️ При повторных вызовах с тем же id результат будет из кеша. Настраивается в пару строк через @EnableCaching.

🧵 Асинхронные методы без Thread
@Async
public void sendMailAsync(String email) {
mailSender.send(email);
}

➡️ Метод вызовется в фоне, результат (если Future) можно отследить. Главное — не забыть @EnableAsync.

📦 Создание прототипов: новый бин каждый раз
@Bean
@Scope("prototype")
public Connection tempConnection() {
return new Connection();
}

➡️ Каждый вызов getBean(Connection.class) даст новый объект, а не singleton.

🔁 Рефреш контекста без перезапуска
@Autowired
private ConfigurableApplicationContext context;

public void reload() {
context.refresh();
}

➡️ Можно подгрузить новые проперти, обновить бины, не трогая процесс.


🗣️ Запомни:
Spring — не фреймворк, это платформа.
Настоящая сила — в деталях: события, прокси, AOP, конфигурации, кеш, асинхронность.
Чем лучше ты понимаешь эти фишки — тем меньше шаблонного кода и больше магии под контролем.
Please open Telegram to view this post
VIEW IN TELEGRAM
13👍6🔥4
🔐 Java и Безопасность: как защищают приложения в 2025 году

Безопасность давно перестала быть опцией — это must-have. В 2025 Java предлагает разработчику целый арсенал встроенных решений и новых подходов для защиты своих сервисов.

В статье ты узнаешь:

📌 Какие уязвимости до сих пор типичны для Java-приложений и как их избежать.
📌 Как работают современные security-фреймворки: Spring Security, OPA, Keycloak.
📌 Что такое Zero Trust архитектура и как реализовать её на Java.
📌 Почему OAuth 2.1, JWT и mTLS — это уже не “просто мода”, а стандарт де-факто.

➡️ Читайте и наслаждайтесь

🗣️ Вывод: в мире Java безопасность — это уже не боль, а гибкий и удобный инструментарий. Главное — начать использовать правильно.

🤩 Java Фишки и трюки || #Cтатья #JavaSecurity #SpringSecurity #ZeroTrust
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32🔥2👨‍💻1
☕️ Hibernate: ORM без боли

Hibernate — это Object-Relational Mapping.
Он превращает твои Java-классы в таблицы и наоборот.
Без JDBC, без ручных SQL-запросов — читаешь и сохраняешь объекты как есть.

📦 Сущность = таблица
@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue
private Long id;

private String name;

@Column(name = "email_address")
private String email;

// геттеры/сеттеры
}

🟢 @Entity превращает класс в таблицу
🟢 @Id — это первичный ключ
🟢 @Column — имя колонки, если отличается от поля

🔁 Чтение и запись без SQL
User u = new User();
u.setName("Alice");
u.setEmail("a@example.com");

session.save(u);

🟢 Hibernate сам делает INSERT INTO users ... под капотом.
🟢 Объекты = строки в таблице. Не нужно писать SQL — только работать с Java.

🔍 Поиск через HQL (Hibernate Query Language)
List<User> users = session
.createQuery("from User where email like :e", User.class)
.setParameter("e", "%@gmail.com")
.getResultList();

➡️ from User — это не SQL, а HQL. Работает на уровне классов, не таблиц.

🤝 Связи между таблицами
@Entity
public class Order {

@Id @GeneratedValue
private Long id;

@ManyToOne
private User user;

private String product;
}

➡️ Один User — много Order. Hibernate сам создаст user_id в таблице заказов.

User u = session.get(User.class, 1L);
List<Order> orders = u.getOrders(); // автоматический join

➡️ Хочешь lazy, eager — всё регулируется аннотациями.

📋 Обновление и удаление
User u = session.get(User.class, 2L);
u.setName("Bob");
session.update(u);


session.delete(u);

➡️ Hibernate сам решает, какие SQL-команды вызвать — ты просто работаешь с объектами.

🔄 Автоматическое создание таблиц
spring.jpa.hibernate.ddl-auto=update

➡️ Таблицы создаются автоматически по твоим сущностям.

🗣️ Запомни:
Hibernate — это способ писать код, а не SQL. Он мапит Java-объекты на реляционные таблицы, сам управляет транзакциями, связями, сессиями.
Но главное — он не магия, и работает только если ты понимаешь, что под капотом.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍85🔥2
☕️ ООП в Java: как писать поддерживаемый код, а не “инкапсуляция + наследование”

Ты не обязан учить ООП по книжке. Но обязан понимать, как структурировать код, чтобы его можно было:

🟢 расширять без боли,
🟢 не дублировать,
🟢 легко тестировать.

Вот как это делается в боевом коде 👇

📦 1. Интерфейсы = API, классы = реализация
public interface Storage {
void save(String data);
}

public class FileStorage implements Storage {
public void save(String data) {
// записать в файл
}
}

public class DBStorage implements Storage {
public void save(String data) {
// сохранить в БД
}
}

➡️ Теперь всё приложение работает с Storage, а не с FileStorage или DBStorage.
public class App {
private final Storage storage;

public App(Storage storage) {
this.storage = storage;
}

public void run() {
storage.save("some data");
}
}


👍 Меняешь реализацию — ничего не ломается. Архитектура устойчива.


🔄 2. Поведение через композицию, не через наследование
public class Logger {
public void log(String message) {
System.out.println("[LOG] " + message);
}
}

public class UserService {
private final Logger logger = new Logger();

public void register(String name) {
logger.log("Регистрация пользователя " + name);
}
}

➡️ Вместо extends Logger — просто вставили поведение. Гибче, проще, безопаснее.

🧩 3. Объекты = поведение, а не мешок полей
public class Order {
private final List<Product> products;

public Order(List<Product> products) {
this.products = products;
}

public BigDecimal total() {
return products.stream()
.map(Product::price)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}

➡️ Объект умеет что-то делать, а не просто хранить данные. Это и есть "настоящее ООП".

⚙️ 4. Варианты поведения — через делегаты, не if/else
public interface DiscountPolicy {
BigDecimal apply(BigDecimal price);
}

public class NoDiscount implements DiscountPolicy {
public BigDecimal apply(BigDecimal price) {
return price;
}
}

public class BlackFridayDiscount implements DiscountPolicy {
public BigDecimal apply(BigDecimal price) {
return price.multiply(new BigDecimal("0.7"));
}
}


public class Checkout {
private final DiscountPolicy discount;

public Checkout(DiscountPolicy discount) {
this.discount = discount;
}

public BigDecimal finalPrice(BigDecimal basePrice) {
return discount.apply(basePrice);
}
}

➡️ Новый тип скидки? Просто добавь класс. Никаких switch/case/if.

🧠 Реальное ООП — это:

👍 контейнеры поведения с чётким контрактом,
👍 код, который расширяется, а не переписывается,
👍 минимум зависимостей, максимум гибкости,
👍 отделённые обязанности (SRP без декламации).


🗣️ Запомни:
ООП в Java — это не “инкапсуляция — наследование — полиморфизм”, а грамотно разложенные по объектам ответственности и поведение.
Если твой код не надо переписывать, чтобы добавить новый сценарий — это оно.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1710👨‍💻3
⌨️ Методы hashCode() и equals()

Методы hashCode() и equals() в Java тесно связаны и играют ключевую роль в работе хеш-структур, таких как HashMap, HashSet, Hashtable.

🔹 Метод equals(Object obj)

Этот метод определяет логическое равенство объектов. По умолчанию (Object.equals) сравниваются ссылки (то есть ==), но обычно его переопределяют, чтобы сравнивать содержимое объектов.

📜 Контракт equals:

1️⃣ Рефлексивностьx.equals(x) должно быть true.
2️⃣ Симметричностьx.equals(y)y.equals(x).
3️⃣ Транзитивностьx.equals(y) и y.equals(z)x.equals(z).
4️⃣ Согласованность — многократные вызовы x.equals(y) возвращают один и тот же результат, если объекты не меняются.
5️⃣ Сравнение с nullx.equals(null) должно быть false.


🔹 Метод hashCode()

Возвращает целое число, которое используется, например, для определения "корзины" (bucket) в хеш-таблицах.

📜 Контракт hashCode:

👍Если x.equals(y) — обязательно x.hashCode() == y.hashCode().
👍Обратное не обязательно: если x.hashCode() == y.hashCode(), это не значит, что x.equals(y).

🔄 Связь между equals и hashCode:
Если вы переопределяете equals(), вы обязательно должны переопределить и hashCode(), иначе, например, объект не будет корректно работать в HashSet или HashMap.

⛔️ Пример ошибки:

Set<Person> people = new HashSet<>();
people.add(new Person("Alice"));
System.out.println(people.contains(new Person("Alice"))); // false, если hashCode не переопределен


Правильный шаблон:

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍7🔥2👏1
☕️ eBPF + JVM: мониторинг глубже, чем Prometheus

Хотите узнать, что реально происходит в Java‑приложении? eBPF открывает доступ к ядру Linux и даёт метрики и трассировку без переписывания кода или агентов.

В статье вы найдёте:

📌 Как eBPF и bpftrace помогают следить за GC, аллокациями и CPU
📌 Чем это глубже и точнее, чем обычный мониторинг
📌 Подводные камни и требования Linux 4.4+
📌 Почему это must‑have для high‑load Java

➡️ Читайте и наслаждайтесь

🗣️JVM больше не чёрный ящик: теперь вы реально видите, что происходит под капотом.

🤩 Java Фишки и трюки || #Cтатья
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍2🔥2
🔥 Почему String в Java — immutable, и как это может неожиданно сломать тебе код?

Строки в Java неизменяемы — это значит, что любой метод replace(), substring(), toUpperCase() и даже += не изменяет строку, а создаёт новую.
Это кажется удобным... пока ты не врежешься в реальные проблемы.

🟢 Что значит immutable?
String s = "hello";
s.toUpperCase();
System.out.println(s); // hello

➡️ Ожидал "HELLO"? Не тут-то было. s.toUpperCase() вернул новую строку, но ты её никуда не сохранил.
Оригинальная строка осталась неизменной.

🟢 Почему так сделано?

1️⃣. Безопасность
Строки используются в ClassLoader, Map, URL, Reflection.
Если бы кто-то мог изменить строку "com.mysql.Driver" в рантайме — ты бы загрузил не тот класс.


2️⃣. Хеширование
String.hashCode() кешируется. Если строка изменилась бы после попадания в HashMap,
ты больше не смог бы её достать по ключу.


3️⃣. Thread Safety
Несколько потоков могут использовать одну строку одновременно — без блокировок и синхронизации.
Иммутабельность = потокобезопасность.


🧨 Где это реально ломает приложения?

📍 Ошибка 1: сравнение по == вместо equals()
String a = "test";
String b = new String("test");

System.out.println(a == b); // false
System.out.println(a.equals(b)); // true

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

📍 Ошибка 2: изменение строки в цикле
String s = "";
for (int i = 0; i < 1000; i++) {
s += i;
}

➡️ Каждое += создаёт новую строку. 1000 операций = 1000 объектов = тормоза.
Правильно так:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result = sb.toString();


📍 Ошибка 3: утечка памяти через substring()
String huge = "..." // большой текст
String small = huge.substring(0, 5);

➡️ small держал ссылку на huge — из-за чего вся большая строка не могла быть удалена.
Java это исправили, но в старых версиях боль реальна.

🗣️ Запомни: String — это надёжный, безопасный и вечно новый объект.
Хочешь модифицировать — используй StringBuilder или StringBuffer.
Хочешь сравнить — equals(), не ==.
Please open Telegram to view this post
VIEW IN TELEGRAM
10👍6🔥3
🌐 Как сделать HTTP-запрос в Java

Хочешь забирать данные из API, отправлять JSON, работать с HTTP в Java — и без боли? С Java 11 всё проще, потому что появился HttpClient, который умеет GET, POST, JSON, async — всё, что надо.

Вот разбор — пошагово и с примерами 👇

👍Шаг 1: Импорт нужных классов
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

➡️ Всё встроено в java.net.http, ничего не нужно скачивать.

👍 Шаг 2: Создаём клиента
HttpClient client = HttpClient.newHttpClient();

➡️ Это "браузер" внутри Java — он отправляет запросы, ждёт ответы и управляет соединениями.

👍 Шаг 3: Создаём GET-запрос
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com"))
.build();

➡️ uri() — это URL запроса. Обязательно через URI.create(...).

👍 Шаг 4: Отправляем и получаем ответ
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());

➡️ send() — делает синхронный запрос.
➡️ BodyHandlers.ofString() — тело ответа как строка.

👍 Шаг 5: Отправка POST с JSON
String json = """
{
"name": "Alice",
"role": "admin"
}
""";

HttpRequest postRequest = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/api/users"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();

HttpResponse<String> postResponse = client.send(postRequest, HttpResponse.BodyHandlers.ofString());
System.out.println(postResponse.body());

➡️ Используем .POST() + BodyPublishers.ofString(...) для тела
➡️ Указываем Content-Type, чтобы сервер знал, что это JSON

👍 Шаг 6: Асинхронные запросы
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);

➡️ sendAsync() — не блокирует поток
➡️ Работает с CompletableFuture — можно делать цепочки

👍 Шаг 7: Обработка ошибок
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println(" Успех");
} else {
System.out.println(" Ошибка: " + response.statusCode());
}
} catch (Exception e) {
System.out.println("⚠️ Ошибка при запросе: " + e.getMessage());
}

➡️ Не забудь try/catch — HTTP может быть непредсказуемым


🗣️ Запомни: С HttpClient можно делать любые HTTP-запросы прямо из Java-кода.
Больше не нужно использовать громоздкий HttpURLConnection или сторонние библиотеки. Всё компактно и читаемо.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍162
☕️ Что такое Lombok и зачем он нужен

Если ты устал писать getters, setters, equals, toString, конструкторы и билдеры вручную — библиотека Lombok создана для тебя. Это мощный инструмент, который избавляет от шаблонного кода и делает Java-классы чище.

Вот как он работает и почему это стоит подключить уже сегодня 👇

🟢 Шаг 1: Что такое Lombok?

Lombokэто Java-библиотека, которая с помощью аннотаций генерирует код во время компиляции. Ты не видишь этих методов в коде, но они есть в .class-файле.

➡️ Экономит сотни строк кода и улучшает читаемость.

🟢 Шаг 2: Как подключить?

Для Maven:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>

Для Gradle:
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'

📌 Также убедись, что у тебя установлен Lombok plugin в IntelliJ IDEA / Eclipse!

🟢 Шаг 3: Убираем шаблонный код

Без Lombok:
public class User {
private String name;
private int age;

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public int getAge() { return age; }
public void setAge(int age) { this.age = age; }

@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}

С Lombok:
import lombok.Data;

@Data
public class User {
private String name;
private int age;
}

➡️ Всё то же самое, но в 3 строки! @Data создаёт:
get/set, toString(), equals(), hashCode() и конструктор по умолчанию.

Часто используемые аннотации Lombok
@Getter @Setter         // Только геттеры и сеттеры
@NoArgsConstructor // Пустой конструктор
@AllArgsConstructor // Конструктор со всеми полями
@RequiredArgsConstructor // Только для final / @NonNull полей
@Builder // Шаблон Builder
@ToString // Авто toString()
@EqualsAndHashCode // equals() + hashCode()
@Value // Immutable класс (все final)

➡️ Всё это убирает десятки строк шаблонного кода.

🟢 Пример с @Builder
import lombok.Builder;
import lombok.ToString;

@Builder
@ToString
public class Product {
private String name;
private double price;
private boolean available;
}

Product p = Product.builder()
.name("MacBook")
.price(2499.99)
.available(true)
.build();

System.out.println(p);

➡️ Идеально для создания объектов с множеством параметров.


🗣️ Запомни: Lombok — это не магия, а компиляция. Он просто пишет за тебя шаблонный код.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍142😇2
☕️ Как работает Lombok под капотом.

Lombok кажется магией: пишешь @Getter, @Builder, @Slf4j — и бац, всё работает. Но на самом деле он не добавляет методы во время выполнения. Всё происходит на этапе компиляции, и ты даже можешь это увидеть.

🟢 Что реально происходит с @Getter
@Getter
public class User {
private String name;
}

➡️ Во время компиляции Lombok встраивает в AST (абстрактное синтаксическое дерево) метод:
public String getName() {
return this.name;
}

➡️ То есть ты этого в коде не видишь, но javac уже получил готовый getName(), как будто ты сам его написал.


🟢 Где именно это происходит
Lombok использует Javac-плагины и работает как annotation processor. Он подключается в javac через SPI (Service Provider Interface) и внедряется в процесс разбора исходного кода — до того, как он станет байткодом.

➡️ Это не runtime, а compile-time магия.

🟢 Почему это работает в IDE
IntelliJ IDEA и Eclipse поддерживают Lombok через плагины. Они умеют подхватывать изменения AST от Lombok, и даже подсвечивать методы, которые физически не написаны.

➡️ Без плагина IDEA не увидит методов, и всё будет красным.


🟢 Пример: что генерирует @Builder
@Builder
public class Book {
private String title;
private int pages;
}

➡️ Под капотом Lombok добавляет

public static class BookBuilder {
private String title;
private int pages;

public BookBuilder title(String title) {
this.title = title;
return this;
}

public BookBuilder pages(int pages) {
this.pages = pages;
return this;
}

public Book build() {
return new Book(title, pages);
}
}

➡️ И плюс метод Book.builder() — точка входа.


🟢 Хочешь увидеть это сам?

Скомпилируй Lombok-класс и деобфусцируй .class:
javac Book.java
javap -p Book.class

➡️ Ты увидишь, что методы реально есть, просто их не было в твоём .java-файле.

🗣 Запомни: Lombok — это макросы на этапе компиляции, а не runtime-библиотека.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍97🔥6