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

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

✍️По всем вопросам: @Pascal4eg
Download Telegram
⚡️ Static Initializer Hack в Java — выполняем код при загрузке класса

Java даёт способ выполнить код один раз, ещё до создания объекта. Это называется static initializer — и с ним можно делать настоящие фокусы 👇

🔁 Что это вообще такое

Когда JVM загружает класс — выполняются все static-блоки, даже если ты не создавал ни одного объекта.

Это можно использовать для:


🟢 регистрации классов
🟢 скрытой инициализации
🟢 запуска кода без main()
🟢 взлома "одиночек" или подмены поведения

📦 Простой пример static-блока
public class Hello {
static {
System.out.println("Загрузка Hello");
}

public static void main(String[] args) {
System.out.println("main()");
}
}


➡️ Вывод:
Загрузка Hello
main()

🟢 Сначала сработал static-блок, потом вызвался main.

💣 Выполнение кода без main()
public class RunOnce {
static {
System.out.println("Код запустился без main()");
System.exit(0); // JVM завершится
}
}

🟢 При запуске RunOnce.class — код выполнится, и main() даже не потребуется.

🧠 Применение: регистрация классов
interface Plugin {
void run();
}

class Registry {
static List<Plugin> plugins = new ArrayList<>();
static void register(Plugin p) {
plugins.add(p);
}
}

public class PluginA implements Plugin {
static {
Registry.register(new PluginA());
}

public void run() {
System.out.println("Плагин A запущен");
}
}


🟢 Просто загрузив PluginA, ты уже зарегистрировал его — без явного вызова конструктора или методов.

🧪 Хак с подменой поведения
class Evil {
static {
System.setSecurityManager(null); // отключаем защиту
}
}

🟢 Опасная штука — используется при обходе sandbox'а или во взломе.
Подгружаешь Evil.class, и твой код внезапно без ограничений.


💬 Хочешь пост про Unsafe, Objenesis или про bytecode-инъекции? Пиши тему — сделаем с практикой.

🗣️ Запомни:Если ты понимаешь когда и зачем срабатывает static-блок — ты уже на шаг ближе к JVM-магию ☄️
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥5😇32👏1
🧰 Маппинг Enum по String без if/switch: делаем Map

Классика: приходит строка "ACTIVE" — надо получить Status.ACTIVE. Но вместо кучи if или switch — строим быстрый и красивый Map-мэппинг, который работает за O(1) и не ломается при добавлении новых enum.

Вот как это сделать правильно 👇

🟢 Enum с кодом
public enum Status {
ACTIVE("active"),
BLOCKED("blocked"),
DELETED("deleted");

private final String code;
Status(String code) { this.code = code; }
public String getCode() { return code; }
}

➡️ Мы явно задали code, который будет использоваться для маппинга (например, из JSON, БД или UI).

🟢 Создаём Map один раз
public class StatusMapper {
private static final Map<String, Status> CODE_MAP = Arrays.stream(Status.values())
.collect(Collectors.toMap(Status::getCode, Function.identity()));

public static Status fromCode(String code) {
return CODE_MAP.get(code);
}
}

➡️ При первом запуске строим Map<String, Status>, где ключ — это status.getCode(), а значение — сам enum.

🧪 Использование:
Status status = StatusMapper.fromCode("active");
System.out.println(status); // ACTIVE

➡️ Работает мгновенно и безопасно. Без цепочек if, без null, без ошибок при добавлении новых значений.

🛡 Добавим защиту от невалидных значений:
public static Status fromCode(String code) {
Status result = CODE_MAP.get(code);
if (result == null) {
throw new IllegalArgumentException("Unknown status: " + code);
}
return result;
}

➡️ Код теперь fail-fast. Любое неожиданное значение сразу бросит ошибку — удобно в API и при валидации.

🗣️ Запомни: Enum.name() и Enum.toString() — не для бизнес-логики. Маппинг через Map — самый быстрый и читаемый способ.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍165🔥2👏1😇1
🧨 Unsafe в Java: прямой доступ к памяти (и да — это реально работает)

Java — безопасный язык, пока ты не подключаешь Unsafe. Это внутренний API из JDK, который даёт доступ к памяти как в C: ты можешь читать, писать, аллоцировать и даже обойти конструкторы. Без всяких JVM-защит.

Не использовать в проде без необходимости. Но как концепт — огонь.

📦 Получаем доступ к Unsafe (через костыль)
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);

➡️ Это приватное поле, поэтому нужен рефлекс. С Java 12 и выше понадобится --add-opens, но в старых версиях работает напрямую.

💥 Аллокация памяти вручную
long address = unsafe.allocateMemory(8); // 8 байт
unsafe.putLong(address, 123456789L); // запись
long value = unsafe.getLong(address); // чтение

System.out.println(value); // 123456789

unsafe.freeMemory(address); // ручной free

➡️ Как malloc/free. Память вне кучи (off-heap), GC её не видит.

🧬 Доступ к полям объекта по смещению
class Demo {
private int secret = 42;
}

Demo d = new Demo();
Field field = Demo.class.getDeclaredField("secret");
long offset = unsafe.objectFieldOffset(field);

unsafe.putInt(d, offset, 99); // меняем приватное поле

System.out.println(unsafe.getInt(d, offset)); // 99

➡️ Меняем приватные поля БЕЗ сеттеров и даже без setAccessible.

🚫 Обход конструктора: создаём объект без new
class NoConstructor {
private NoConstructor() {
throw new RuntimeException("Don't call me");
}
}

NoConstructor obj = (NoConstructor) unsafe.allocateInstance(NoConstructor.class);
System.out.println(obj);

➡️ JVM не вызывает конструктор вообще. Объект появляется из воздуха.

🧠 Прямое управление массивами
int[] arr = new int[10];
long base = unsafe.arrayBaseOffset(int[].class);
int scale = unsafe.arrayIndexScale(int[].class);

unsafe.putInt(arr, base + scale * 5, 99); // пишем в arr[5]
System.out.println(arr[5]); // 99

➡️ Как pointer arithmetic в C. У тебя есть offset и шаг (scale).

✔️ Используй Unsafe, чтобы:

🟢 делать off-heap кэш/пулы
🟢 изменять приватные данные в рантайме
🟢 писать кастомные структуры данных
🟢 встраивать C-подобные трюки в Java


🗣️ Запомни: Это выход за пределы JVM-санбокса. Но иногда он даёт то, что невозможно сделать иначе. Unsafe — это как ядро Java-хакерства. У него огромная сила, но и риск.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍74😈3😇1
☕️ JDK 24 ускоряет Java-бэкенд: от виртуальных потоков до операций с FFM

Нашёл одну свежую и реально интересную статью — обзор Performance Improvements in JDK 24 от Inside.java (март 2025). В ней рассказывается о конкретных улучшениях, которые уже влияют на скорость приложений.

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

📌 Оптимизация «bulk operations» в Foreign Function & Memory API — быстрее копирование и заполнение сегментов
📌 MergeStore в JIT — меньше операций записи, выше скорость работы с массивами
📌 Ускоренный startup через скрытые классы для конкатенации строк
📌 +27 % ускорения хеширования с SHA‑3
📌 Доработки виртуальных потоков: снятие pinning, лучшая синхронизация
📌 Что полезного для разработчиков: всё это работает «из коробки» — без переписывания кода
📌 Как это помогает писать код лучше: меньше задержек, быстрее прод, внимание разработчика уходит в логику, а не оптимизацию

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

🗣️ JDK‑24 — это не просто новая версия, а реальный прирост скорости для вашего продакшена.

🤩 Java Фишки и трюки || #Cтатья
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍7👏2
🧩 Marker + Default methods: интерфейсы как конфигурация, без аннотаций и свитчей

Ты можешь использовать интерфейсы не как API, а как декларативную конфигурацию — через маркер + дефолтные методы. Без if-else, без флагов, без switch.

📛 Маркер как флаг
public interface Debuggable {} // пустой интерфейс


➡️ Никакой логики, просто метка. Если класс его реализует — значит, он отлаживаемый.

public class Service implements Debuggable {}


💡 Теперь достаточно instanceof:
void log(Object o) {
if (o instanceof Debuggable) {
System.out.println("🐞 Включаем отладку");
}
}

➡️ Никаких .isDebug(), никаких аннотаций — только тип.

🛠 Дефолтные методы — поведение по умолчанию
public interface HasTimeout {
default int getTimeout() {
return 1000; // мс
}
}


Ты получаешь “конфигурацию по умолчанию”, которую можно переопределить:
public class FastRequest implements HasTimeout {
@Override
public int getTimeout() {
return 100;
}
}

public class NormalRequest implements HasTimeout {
// будет 1000 по умолчанию
}


void send(HasTimeout req) {
System.out.println("⌛️ Таймаут: " + req.getTimeout());
}

➡️ Без config-файлов, без if (instanceof) — всё в типе.

📦 Конфигурация как набор интерфейсов
public interface RequiresAuth {
default boolean isAuthRequired() {
return true;
}
}

public interface ReturnsJson {
default String contentType() {
return "application/json";
}
}


➡️ И теперь просто пишешь:
public class ApiCall implements RequiresAuth, ReturnsJson {}


void handle(Object call) {
if (call instanceof RequiresAuth auth && auth.isAuthRequired()) {
System.out.println("🔐 Проверка токена");
}
if (call instanceof ReturnsJson rj) {
System.out.println("📤 JSON-ответ: " + rj.contentType());
}
}

➡️ Тип сам диктует поведение. Без логики — просто наличие интерфейса.


📚 Даже enum можно конфигурировать
public interface Critical {}

public enum Action implements Critical {
DELETE_ALL, RESET_DB
}


void execute(Enum<?> action) {
if (action instanceof Critical) {
System.out.println("🚨 ВНИМАНИЕ: опасная операция");
} else {
System.out.println(" Безопасно");
}
}

➡️ Enum + интерфейс = мощная типовая декларация.


🗣️ Запомни: Marker + Default methods = тип как конфигурация. Ты пишешь код, который читает сам себя. Поведение не через данные, а через тип.Меньше if, меньше флагов, больше ясности.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥163👍3
🧪 Создание объекта без конструктора — это реально

В Java обычно new вызывает конструктор. Но можно создать объект, вообще не вызывая ни один конструктор. Даже private, даже если он кидает исключения.

Это работает через ☠️ Unsafe или ✔️ Objenesis.

☠️ Unsafe: объект без жизни
class User {
private User() {
throw new RuntimeException("нельзя вызывать");
}
}

Unsafe unsafe = ...;
User user = (User) unsafe.allocateInstance(User.class);

➡️ Объект создан. Конструктор не сработал.
➡️ Все поля — нули, даже final.

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

Objenesis: библиотека для этого
Objenesis objenesis = new ObjenesisStd();
User user = objenesis.newInstance(User.class);

➡️ Тоже создаёт объект без конструктора.
➡️ Работает на всех JVM, без хака Unsafe.
➡️ Поддерживается, используется в Spring, Mockito, Kryo.

📦 Maven-зависимость:
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>3.3</version>
</dependency>


📛 Где это используют

Hibernate, Jackson, Kryo, Mockito — создают объекты из JSON или прокси без вызова конструктора.
Ты можешь сделать то же:
class Secret {
private final int x = 42;
private Secret() {
throw new IllegalStateException("нельзя");
}
}

Secret s = (Secret) unsafe.allocateInstance(Secret.class);
System.out.println(s.x); // 0, даже final не работает

➡️ Конструктор не вызвался, даже final сброшен.

🗣 Запомни:Ты можешь создать любой объект — даже если конструктор кидает ошибку.
Просто
Unsafe.allocateInstance(Class) или objenesis.newInstance(Class).
Но инициализировать поля придётся самому.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍43👏1
Media is too big
VIEW IN TELEGRAM
☕️ Установка JDK и IDE.

В этом видео автор простым и понятным языком демонстрирует установку Java с нуля — от загрузки JDK до настройки среды разработки (IDE). Всё показано пошагово: выбор инструментария, установка, проверка работоспособности.Это отличный старт, если ты только начинаешь изучать Java и хочешь настроить всё правильно с самых основ.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
👍83
JDBC 🆚 Hibernate: как Java работает с базами — и чем это тебе грозит

Java умеет работать с БД как вручную (через JDBC), так и “по-умному” (через Hibernate/JPA).
Один путь — контроль и боль. Второй — магия и её последствия.
Разберём всё по фактам, на живых примерах 👇

🔌 JDBC — низкоуровневый доступ через SQL
Connection conn = DriverManager.getConnection(url, user, pass);
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
stmt.setInt(1, 42);
ResultSet rs = stmt.executeQuery();

while (rs.next()) {
System.out.println(rs.getString("name"));
}

🟢 Всё вручную: от подключения до маппинга.
🟢 Полный контроль, но каждый чих — в 5 строк.
🟢 Если ты решил добавить JOIN — добро пожаловать в SQL-приключения.

🏗 Hibernate / JPA — ORM и сущности
@Entity
public class User {
@Id
private Long id;
private String name;
}


User user = entityManager.find(User.class, 42L);

🟢 SQL скрыт под капотом, данные — это Java-объекты.
🟢 Быстро, декларативно, красиво.
🟢 Но ты больше не контролируешь, что именно улетает в базу.

💥 Пример боли: Lazy vs Eager
@Entity
public class Post {
@ManyToOne(fetch = FetchType.LAZY)
private User author;
}


Post post = postRepo.findById(1L).get();
System.out.println(post.getAuthor().getName()); // 💣 LazyInitializationException

🟢 Если сессия закрыта — Hibernate не подгрузит данные.
🟢Работает только внутри транзакции.
🟢 Или делай JOIN FETCH, или не забывай про открытые сессии.

🛠 Spring + JPARepository — удобный уровень
public interface UserRepo extends JpaRepository<User, Long> {
List<User> findByName(String name);
}

➡️ Никаких SQL, методы создаются из названия.
➡️ CRUD, пагинация, фильтры — всё из коробки.
➡️ Но если нужен ручной SQL — Spring Data позволяет это тоже:

@Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
List<User> search(@Param("name") String name);


⚙️ JdbcTemplate — между ручным и магическим
List<User> users = jdbcTemplate.query(
"SELECT * FROM users",
(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"))
);

➡️ Ты пишешь SQL, но не паришься с ResultSet.
🟢 Оптимально для кастомных запросов.

🧪 Тестирование — через @DataJpaTest и H2
@DataJpaTest
class RepoTest {
@Autowired UserRepo repo;

@Test
void testSave() {
repo.save(new User("Test"));
assertTrue(repo.findByName("Test").size() > 0);
}
}

➡️ H2 работает в памяти, быстро поднимается и убивается.
🟢 Подходит для unit+integration тестов одновременно.

🗣️ Запомни: JDBC — как нарезать салат вручную: аккуратно, но долго. Hibernate — как блендер: быстро, но иногда не туда. Выбирай по задаче, а не по вкусу архитектора.
Please open Telegram to view this post
VIEW IN TELEGRAM
10👍9🔥4😇1
☕️ JSON в Java: Jackson и Gson — кто быстрее сериализует твои данные

Java не умеет JSON "из коробки", но есть два монстра: Jackson и Gson.
Оба читают и пишут JSON. Оба работают с POJO. Но подходы — разные.
Покажу, как это работает в реальных проектах 👇

🦾 Jackson — промышленный стандарт
ObjectMapper mapper = new ObjectMapper();
User user = new User("Jon", 42);

String json = mapper.writeValueAsString(user);
User copy = mapper.readValue(json, User.class);

➡️ Всё просто: writeValue и readValue.
🟢 Поддерживает аннотации, вложенные объекты, коллекции, даты и даже Map.

📛 Кастомизация через аннотации:
public class User {
@JsonProperty("username")
private String name;

@JsonIgnore
private String password;
}

➡️ Переименовал поле и спрятал пароль — без лишних движений.

🟢 Jackson — это как Spring: сначала пугает, потом спасает.

🧼 Gson — проще, но со своими приколами
Gson gson = new Gson();
User user = new User("Jon", 42);

String json = gson.toJson(user);
User copy = gson.fromJson(json, User.class);

➡️ Минимум зависимостей, минимум магии.
🟢 Легко встраивается, быстро стартует.

📛 Переименование через @SerializedName:

public class User {
@SerializedName("username")
private String name;
}

🟢 Gson не умеет автоматически сериализовать дату — готовь адаптер.

🔄 Коллекции и вложенные структуры
List<User> users = List.of(new User("Jon", 1), new User("Doe", 2));
String json = mapper.writeValueAsString(users);

➡️ Jackson всё поймёт.

Gson тоже справится, но если читаешь обратно — будь осторожен с типами:
Type type = new TypeToken<List<User>>(){}.getType();
List<User> list = gson.fromJson(json, type);

🟢 Java и дженерики — не лучшие друзья Gson.

🧪 Кастомные сериализаторы — когда всё сложно
public class UserSerializer extends JsonSerializer<User> {
@Override
public void serialize(User u, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(u.getName().toUpperCase());
}
}


Используешь, когда стандартная сериализация не подходит:
SimpleModule module = new SimpleModule();
module.addSerializer(User.class, new UserSerializer());
mapper.registerModule(module);

➡️ Jackson выигрывает, если нужно что-то нестандартное.

📊 Jackson vs Gson — что выбрать?
| Фича           | Jackson    | Gson                     |
| -------------- | ---------- | ----------------------- |
| Скорость | 🟢 Быстрее | 🟡 Средняя |
| Поддержка даты | 🟢 Да | 🔴 Только через адаптер |
| Кастомизация | 🟢 Гибко | 🟡 Просто |
| Работа с Map | 🟢 Легко | 🟡 Можно, но осторожно |
| Вес | 🟡 Большой | 🟢 Маленький |

➡️ Jackson мощнее и гибче, но тяжелее.
➡️ Gson проще и легче, но ограничен.


🗣️ Запомни: Если ты в Spring Boot — бери Jackson. Он уже встроен. А Gson — как нож бабушки: лёгкий, но не режет экзотику.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍105🔥2👎1
🌐 Kubernetes для Java-разработчика — деплой без паники

Когда Java-приложение готово, остаётся один вопрос: а как его запустить в проде?
Если тебе нужен масштаб, отказоустойчивость и автоматизация — ответ один: Kubernetes.
Показываю, как он работает с Java-приложением — без воды 👇

📦 Сборка: сначала контейнер
FROM eclipse-temurin:17
COPY target/app.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

➡️ Сборка стандартная. Главное — получить рабочий Docker image
🟢 Один и тот же образ можно запускать локально и в кластере
🟢 Неважно Spring это, Micronaut или Quarkus — всё сводится к jar + Dockerfile

🟡 Kubernetes Deployment — всё начинается отсюда
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 2
selector:
matchLabels:
app: java-app
template:
metadata:
labels:
app: java-app
spec:
containers:
- name: app
image: myorg/java-app:1.0
ports:
- containerPort: 8080

➡️ Разворачивает несколько копий приложения
🟢 Контейнер запускается в поде, под управлением kubelet
🟢 Один deployment.yaml заменяет тонны bash-скриптов и ручного запуска

🌐 Сделать доступным: Service
apiVersion: v1
kind: Service
metadata:
name: java-service
spec:
selector:
app: java-app
ports:
- port: 80
targetPort: 8080
type: LoadBalancer

➡️ Прокидывает внешний трафик внутрь кластера
🟢 LoadBalancer в облаке, ClusterIP — для внутренней связи
🟢 Без Service приложение живёт, но недоступно

📡 Настройка входа — через Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: java-ingress
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: java-service
port:
number: 80


➡️ Публичный домен, HTTPS, маршрутизация — всё через Ingress
🟢 Можно добавить cert-manager для автоматических TLS
🟢 Ingress — как прокси, только встроенный и декларативный

🔁 Обновления без даунтайма
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1

➡️ Kubernetes обновляет поды по очереди
🟢 Пользователи не замечают релиза
🟢 Микросервисы обожают это


🛠 ConfigMap и Secrets — без пересборки образа

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
SPRING_PROFILES_ACTIVE: prod


envFrom:
- configMapRef:
name: app-config

➡️ Конфиги выносятся из jar-файла
🟢 Смена конфигурации = обновление без пересборки
🟢 Секреты тоже выносятся отдельно — не пиши пароли в YAML

🗣️ Запомни: Kubernetes — как космический корабль. Первый запуск трудный, но потом ты просто говоришь “🚀 в прод”, и всё летит.
Please open Telegram to view this post
VIEW IN TELEGRAM
101👍123🤯1
🐳 Docker Compose для Java: запускаем всё и сразу, в один файл

Когда у тебя Spring Boot, база, очередь и ещё 2 микросервиса — руками это не запустишь.
Docker Compose — способ задеплоить всё одним махом.
Без скриптов, без флагов, без “а где тут Redis?”. Просто up -d — и готово 👇


📦 Минимальный пример — Spring Boot + Postgres
version: '3.9'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/demo
- SPRING_DATASOURCE_USERNAME=java
- SPRING_DATASOURCE_PASSWORD=secret
depends_on:
- db

db:
image: postgres:16
environment:
- POSTGRES_DB=demo
- POSTGRES_USER=java
- POSTGRES_PASSWORD=secret

➡️ Всё в одном файле: и приложение, и база
🟢 depends_on гарантирует порядок запуска
🟢 Не нужен отдельный Postgres — он стартует сам
➡️ Ты просто клонируешь проект и запускаешь — как магия

🔄 Hot reload: монтируем код
volumes:
- .:/app

➡️ Приложение пересобирается при изменениях
🟢 Удобно для разработки, особенно с Spring DevTools
🟢 Работает как live reload, но без веб-сокетов

🧪 Добавляем ещё сервис: например, Redis
  redis:
image: redis:7
ports:
- "6379:6379"

➡️ В Java конфиге указываешь localhost:6379 — и всё
🟢 Redis появляется в сети как обычный контейнер
🟢 В Compose все сервисы в одной сети по умолчанию


🔐 .env для переменных окружения
SPRING_DATASOURCE_USERNAME=java
SPRING_DATASOURCE_PASSWORD=secret


env_file:
- .env

➡️ Хранишь секреты вне Compose-файла
🟢 Удобно для CI/CD
🟢 Никогда не коммить .env в репозиторий

🚫 Остановка и очистка
docker compose down -v

➡️ Останавливает и удаляет тома, сети, контейнеры
🟢 -v — чтобы не остался мусор от базы
🟢 Это не “stop” — это аннигиляция всего стенда

🗣️Запомни: Docker Compose — как мультиварка: закинул всё внутрь, нажал кнопку, через минуту обед.
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍8
⚔️ Kubernetes v🆚 Docker Compose — что выбрать для деплоя Java-приложения

Оба запускают контейнеры.
Оба читают YAML.
Но у одного — кнопка "всё просто", а у второго — масштаб и продакшен.

🐳 Docker Compose: для dev и staging
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
depends_on:
- db
db:
image: postgres:16

➡️ Всё в одном файле, разворачивается за 10 секунд
🟢 Подходит для локальной разработки и тестов
🟢 Никаких ingress, сервисов, namespaces
➡️ Отлично, пока всё помещается в голову

☸️ Kubernetes: для продакшена и масштабов
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-app
spec:
replicas: 2
template:
spec:
containers:
- name: java
image: myapp:1.0
ports:
- containerPort: 8080

➡️ Управление через pod'ы и deployment'ы
🟢 Поддерживает автоскейлинг, rolling updates, отказоустойчивость
➡️ Требует kubectl, ingress, сервисов, конфигов
🟢 Не подойдёт, если ты просто хочешь “запустить и посмотреть”

📡 Сеть: как доступен сервис

Compose:
ports:
- "8080:8080"

➡️ Пробрасывает порт хоста прямо в контейнер

Kubernetes:
kind: Service
spec:
type: LoadBalancer

➡️ Отдельный объект Service, управление через балансировщик

🟢 Kubernetes — это про маршрутизацию, а не про “форвард порт”

🔐 Переменные и секреты

Compose:
env_file:
- .env

➡️ Подключение переменных из файла

Kubernetes:
envFrom:
- configMapRef:
name: app-config

➡️ConfigMap и Secret — всё разделено по объектам
🟢 В K8s конфигурация живёт отдельно от кода и образа

🔁 Обновления

Compose:
docker compose down && docker compose up -d

➡️ Всё падает и поднимается заново

Kubernetes:
strategy:
type: RollingUpdate

➡️ Плавные обновления без даунтайма
🟢 K8s сам обновляет по одному поду


🗣️ Запомни: Docker Compose — это как запускать Java через main(). Kubernetes — как запускать через продакшеновый оркестр с логами, шинами и охраной.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍32
Media is too big
VIEW IN TELEGRAM
☕️ Переменные и типы данных. Как хранить информацию в приложении.

В этом видео автор объясняет, что такое переменные и типы данных в Java.
Разбирается, как в языке хранится информация, какие бывают типы (int, double, boolean и др.) и как правильно их использовать.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
👍84😇1
👑 API Gateway: единая точка входа для всех запросов

В Java API Gateway — это слой, который принимает все запросы к микросервисам, решает, куда их направить, и что с ними сделать по пути.
Он не выполняет бизнес-логику — он управляет потоком данных и правилами доступа.

📛 Пример: простейший роутинг
public class ApiGateway {
public void handleRequest(String path) {
if (path.startsWith("/users")) {
userService();
} else if (path.startsWith("/orders")) {
orderService();
} else {
notFound();
}
}
void userService() { System.out.println("👥 Обработка пользователей"); }
void orderService() { System.out.println("📦 Обработка заказов"); }
void notFound() { System.out.println(" Маршрут не найден"); }
}

➡️ Один вход — разные направления. Gateway решает, куда отправить запрос.

🧪 Добавляем фильтры и валидацию
public void handleRequest(String path, String token) {
if (!isValidToken(token)) {
System.out.println("⛔️ Доступ запрещён");
return;
}
if (path.startsWith("/admin")) {
adminService();
} else {
publicService();
}
}
boolean isValidToken(String token) {
return token != null && token.equals("secret");
}
void adminService() { System.out.println("🔐 Админ-панель"); }
void publicService() { System.out.println("🌍 Публичный API"); }

➡️ Логика авторизации в одном месте, а не разбросана по сервисам.

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

Смотри, нужно добавить кеширование без изменения кода сервисов:
import java.util.HashMap;
import java.util.Map;

public class CachingGateway {
private Map<String, String> cache = new HashMap<>();

public String handle(String path) {
if (cache.containsKey(path)) {
return "⚡️ Из кеша: " + cache.get(path);
}
String response = callService(path);
cache.put(path, response);
return response;
}

String callService(String path) {
return "Данные для " + path;
}
}

➡️ Сервисы ничего не знают о кешировании — всё делает Gateway.

💾 Другой кейс — агрегация данных
public class AggregationGateway {
public String aggregate() {
String users = getUsers();
String orders = getOrders();
return "👥 " + users + " | 📦 " + orders;
}
String getUsers() { return "5 пользователей"; }
String getOrders() { return "12 заказов"; }
}

➡️ Один запрос к Gateway — и клиент получает объединённые данные из разных сервисов.

📦 Комбинации: безопасность + логирование + роутинг
public class SecureLoggingGateway {
public void handle(String path, String token) {
log(path);
if (!isValidToken(token)) return;
route(path);
}
void log(String path) { System.out.println("📜 Лог: " + path); }
boolean isValidToken(String token) { return "secret".equals(token); }
void route(String path) { System.out.println("➡️ Роутинг к " + path); }
}

➡️ Весь контроль — в одном месте, а не в каждом сервисе.

📚 API Gateway и enum маршрутов
public enum Route {
USERS, ORDERS, STATS
}
public class EnumGateway {
public void dispatch(Route r) {
switch (r) {
case USERS -> System.out.println("👥 Пользователи");
case ORDERS -> System.out.println("📦 Заказы");
case STATS -> System.out.println("📊 Статистика");
}
}
}

➡️ Даже маршруты можно жёстко задать через enum для безопасности.

🗣️ Запомни: API Gateway — это точка, где решается всё: куда пойдёт запрос, кто его выполнит, и что будет по пути. Один слой — и порядок во всей системе.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍143
🧵 Structured Concurrency в Java — управление задачами в одном scope, без утечек и хаоса

Structured Concurrency — это модель, где все задачи живут в одном «контейнере».
Падает одна — падают все. Контейнер закрыт — задачи завершены.

📁 Шаг 1: проблема без структуры
Thread t1 = new Thread(() -> fetchUsers());
Thread t2 = new Thread(() -> fetchOrders());
t1.start(); t2.start();

➡️ Потоки живут сами по себе, ошибки не передаются, ресурсы не контролируются.

📁 Шаг 2: ждём задачи вручную
t1.join();
t2.join();

➡️ Мы можем подождать завершения, но всё ещё нет общей отмены при ошибке.

📁 Шаг 3: общий пул потоков
ExecutorService ex = Executors.newFixedThreadPool(2);
Future<String> f1 = ex.submit(() -> fetchUsers());
Future<String> f2 = ex.submit(() -> fetchOrders());

➡️ Теперь задачи в одном месте, можно управлять ими через Futures.

📁 Шаг 4: обработка ошибок централизованно
try {
String u = f1.get();
String o = f2.get();
} catch (Exception e) {
f1.cancel(true);
f2.cancel(true);
}

➡️ Если одна задача падает — отменяем все остальные.

📁 Шаг 5: таймауты для всех задач
List<Future<String>> results = ex.invokeAll(tasks, 1, TimeUnit.SECONDS);

➡️ Через 1 секунду все незавершённые задачи отменяются.

📁 Шаг 6: try-with-resources для пула
try (ExecutorService ex2 = Executors.newFixedThreadPool(2)) {
// запуск задач
}

➡️ При выходе из блока пул и все задачи закрываются автоматически.

📁 Шаг 7: объединение результатов
String combined = u + " | " + o;
System.out.println(combined);

➡️ Structured Concurrency упрощает сбор данных от разных задач.

📁 Шаг 8: CompletableFuture для асинхронщины
CompletableFuture<String> u = CompletableFuture.supplyAsync(() -> fetchUsers());
CompletableFuture<String> o = CompletableFuture.supplyAsync(() -> fetchOrders());

➡️ Задачи стартуют параллельно, и можно дождаться всех вместе.

📁 Шаг 9: join с обработкой ошибок
CompletableFuture.allOf(u, o).join();

➡️ Ждём обе задачи; если одна упадёт — получим исключение и сможем отменить вторую.

📁 Шаг 10: собственный scope-класс
class TaskScope implements AutoCloseable {
ExecutorService ex = Executors.newCachedThreadPool();
List<Future<?>> futures = new ArrayList<>();
<T> Future<T> fork(Callable<T> c) { var f = ex.submit(c); futures.add(f); return f; }
public void close() { futures.forEach(f -> f.cancel(true)); ex.shutdownNow(); }
}

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

📁 Шаг 11: запуск в scope
try (TaskScope scope = new TaskScope()) {
var f1 = scope.fork(() -> fetchUsers());
var f2 = scope.fork(() -> fetchOrders());
}

➡️ Выйдя из try — все задачи будут остановлены.

📁 Шаг 12: добавляем логику таймаута внутрь scope
Future<T> forkWithTimeout(Callable<T> c, long t, TimeUnit u) {
return ex.submit(() -> ex.submit(c).get(t, u));
}

➡️ Теперь каждая задача в scope может иметь свой лимит времени.

📁 Шаг 13: завершение
System.out.println(" Все задачи завершены в одном месте");

➡️ Structured Concurrency даёт полный контроль над жизнью задач — от запуска до завершения.

🗣️ Запомни: Structured Concurrency — это про контроль и предсказуемость.
Все задачи в одном scope → нет висящих потоков, нет утечек, нет забытых таймаутов.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥92
🚀 CI/CD: автоматизация сборки и доставки — и это работает

CI/CD позволяет запускать сборку, тесты и деплой без участия человека.
Но если ты подключаешь CI/CD к проекту — ты отмечаешь его как “готовый к автоматизации”.
Это и есть CI/CD — конвейер, который действует по событиям.

📛 Пример: сборка и тесты в Jenkins
pipeline {
agent any
stages {
stage('Build') { steps { sh 'mvn clean package' } }
stage('Test') { steps { sh 'mvn test' } }
}
}

push в main
➡️ Jenkins не ждёт команды — он сам собирает и проверяет проект.

🧪 Проверка — через GitHub Actions
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: '17'
- run: mvn clean verify

➡️ Вот где начинается магия: код проверяется сразу при пуше.

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

Смотри, тебе нужно различать коммиты с кодом и релизы.
Не хочешь вручную запускать тесты перед релизом? — решается одной конфигурацией.
on:
push:
tags:
- 'v*'

➡️ Любой тег с версией запустит сборку релиза.

💾 Другой кейс — автоматический деплой
- name: Deploy
run: ssh user@server 'cd /app && git pull && ./restart.sh'

➡️ Без ручного захода на сервер, без копирования файлов — просто push.

📦 Комбинации: проект — это набор этапов
- run: mvn test      # тесты
- run: docker build # сборка контейнера
- run: helm upgrade # деплой в Kubernetes

➡️ Всё описано в сценарии: от проверки до выката.

📚 даже для инфраструктуры
- uses: hashicorp/setup-terraform@v2
- run: terraform apply -auto-approve

➡️ CI/CD может управлять серверами и базами так же, как кодом.

🗣️ Запомни: CI/CD — это не костыль, а чистый, системный способ доставлять код.
Оно не думает, а действует. Всё, что нужно — событие в репозитории.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍103❤‍🔥2
📦 Массивы, строки и коллекции в Java: быстро, гибко, удобно

Java даёт три больших способа хранить данные: массивы (Array), строки (String) и коллекции (ArrayList, HashMap).
Каждый из них — под свой сценарий.

🧩 Массив — фиксированный размер и быстрый доступ
int[] numbers = {1, 2, 3};
System.out.println(numbers[0]); // 1
numbers[1] = 42; // меняем элемент

➡️ Индекс — ключ, размер фиксирован, добавлять новые элементы нельзя без создания нового массива.


📜 Строки — неизменяемые последовательности символов
String text = "Hello";
String upper = text.toUpperCase();
System.out.println(text); // Hello
System.out.println(upper); // HELLO

➡️ String в Java — immutable. Любое «изменение» создаёт новую строку. Для частых правок есть StringBuilder.

📋 ArrayList — динамический список
import java.util.ArrayList;

ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
System.out.println(list.get(1)); // Python
list.remove(0); // удалили "Java"

➡️ Масштабируется сам. Можно добавлять/удалять элементы без забот о размере.


🗺 HashMap — хранение по ключу
import java.util.HashMap;

HashMap<String, Integer> ages = new HashMap<>();
ages.put("Alice", 25);
ages.put("Bob", 30);

System.out.println(ages.get("Bob")); // 30

🟢 Идеально, если нужно быстро искать по ключу, а не по индексу.

🛠 Комбо: коллекции + массивы
ArrayList<int[]> matrix = new ArrayList<>();
matrix.add(new int[]{1, 2, 3});
matrix.add(new int[]{4, 5, 6});
System.out.println(matrix.get(1)[2]); // 6


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


🗣️ Запомни: Выбирай структуры данных по задаче — массивы для скорости и фиксированного размера, StringBuilder для частых изменений строк, ArrayList для гибких списков и HashMap для мгновенного поиска по ключу.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍115👏21
Media is too big
VIEW IN TELEGRAM
☕️Циклы и операторы в них (For, While, Do while)

Это видео посвящено циклам в языке Java — фундаментальной теме для любого разработчика. Ты научишься использовать циклы for, while и do-while, а также освоишь операторы управления потоком внутри циклов.


🤩 Java Фишки и трюки || #Видео
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍3
☕️ CI/CD с Jenkins Pipelines — как Java проект летит в прод

Сборка руками? Заливка через FTP?
⛔️ Это не девелопмент, а тетрис в продакшене.

Jenkins автоматизирует всё — от сборки до деплоя, пока ты пьёшь кофе ☕️
Вот как это работает на Java 👇

⚙️ Что такое Jenkins Pipeline

Jenkinsfile — это сценарий, который описывает, что и как делать с кодом.
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './gradlew build'
}
}
stage('Test') {
steps {
sh './gradlew test'
}
}
stage('Deploy') {
steps {
sh 'scp build/libs/app.jar user@server:/app/'
}
}
}
}

🟢 Всё по шагам: сначала билд, потом тесты, потом деплой
➡️ Каждый коммит триггерит этот сценарий

🔥 Плохо без CI/CD:
🟢Костыльные .bat скрипты и ручной scp
🟢Кто-то залил баг — тесты никто не запускал
🟢Ночью прод лег, никто не понял, почему


✔️ Хорошо с Jenkins:
🟢git push → Jenkins билдит и пушит сам
🟢Ошибся в тестах? Не пройдёшь дальше
🟢Всё логируется, откатывается и работает одинаково везде


👍 CI — это контроль. CD — это доставка без боли.

Сборка Java проекта в пайплайне
stage('Build') {
steps {
sh './mvnw package -DskipTests'
}
}

➡️ Jenkins билдит .jar или .war сам
🟢 Поддерживает Gradle, Maven, Ant — что угодно

🧪 Тесты — обязательный рубеж
stage('Test') {
steps {
sh './mvnw test'
}
}

➡️Никаких «ну оно у меня работало»
🟢 Прогон JUnit и Coverage на каждый коммит

🚀 Деплой на сервер — без доступа к ssh
stage('Deploy') {
steps {
sh 'scp target/app.jar user@host:/srv/'
}
}

➡️ Jenkins сам подключается, копирует, рестартит
🟢 Или делает docker build + push → K8s deploy

🗣️ Запомни: CI/CD нужен не когда всё плохо. А чтобы плохо не стало. Jenkins спасёт от багов, усталости и вечного "вручную".
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥65
☕️ ArrayList & HashMap: быстрые коллекции, которые решают всё

В Java массивы — фиксированные, а коллекции умеют расти, менять размер и работать с разными типами данных.
ArrayList и HashMap — два столпа, на которых держатся 80% задач.

🔢. Создание и добавление
import java.util.ArrayList;

ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");

👉 ArrayList хранит элементы в порядке добавления и умеет расти автоматически.

🔢. Доступ по индексу
String lang = list.get(0);
System.out.println(lang); // Java

👉 Как в массиве: быстрый доступ по номеру, но индекс должен быть в пределах размера.

🔢. Перебор элементов
for (String l : list) {
System.out.println(l);
}

👉 Удобный синтаксис for-each для чтения всех значений без возни с индексами.

🔢. HashMap: хранение по ключу
import java.util.HashMap;

HashMap<String, Integer> map = new HashMap<>();
map.put("Apples", 5);
map.put("Oranges", 3);

👉 HashMap — это ключ-значение. Ключи уникальны, значения можно повторять.

🔢. Получение по ключу
int count = map.get("Apples");
System.out.println(count); // 5

👉 Быстрое извлечение: доступ почти моментальный.

🔢. Проверка наличия
if (map.containsKey("Oranges")) {
System.out.println("Есть апельсины!");
}

👉 Нет ключа — вернёт false, а не ошибку.

🗝 🔢. Перебор пар
for (var entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}

👉 Легко пройтись сразу и по ключам, и по значениям.


🗣️ Запомни: массивы — это база, но ArrayList и HashMap дают гибкость. Используй массивы, когда важна скорость и фиксированный размер, а коллекции — когда нужно удобно, динамично и без лишней возни.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍115🔥4