12073
Присоединяйтесь к нашему каналу и погрузитесь в мир для Java-разработчика Связь: @devmangx РКН: https://clck.ru/3H4WUg
Пакетная обработка JDBC через StatelessSession в Hibernate
Статья объясняет, как использовать StatelessSession в Hibernate 6 для быстрой пакетной вставки, обновления и удаления данных с помощью JDBC Batching — без лишнего кеша и с высокой производительностью.
⏩Читать подробнее
👉 Java Portal | #cтатья
Разные подходы к структуре REST API:
1) Чистый CRUD-стиль
GET /users
POST /users
PUT /users/{id}
DELETE /users/{id}
POST /users/{id}/deactivate
POST /orders/{id}/cancelGET /users/{id}/orders
POST /users/{id}/addressesGET /orders?userId=123
GET /products?category=books&page=2
GET /v1/users
Accept: application/vnd.myapp.v2+json
Наблюдаемость Java-бэкенда с помощью OpenTelemetry
⏩Читать подробнее
👉 Java Portal | #cтатья
Проектирование Backend-системы. Система уведомлений
Цель —> Разработать систему для доставки уведомлений пользователям через:
➣ Встроенные уведомления
➣ Push-уведомления (мобильные/веб)
➣ Email
С поддержкой:
✓Масштабируемости (миллионы пользователей)
✓Настраиваемых пользовательских предпочтений
✓Надежности, повторных попыток и дедупликации
✓Реального времени и пакетных оповещений
Пошаговый сквозной процесс
Шаг 1: Срабатывание события
✓ Приложение инициирует событие:
➣ Например, пользователь A лайкнул ваш пост
➣ Отправляется запрос POST на API /notify:
{
"event_type": "like",
"actor": "UserA",
"receiver": "UserB",
"object": "Post123"
}"UserA лайкнул ваш пост 'How to scale systems'"[
{ "text": "UserA лайкнул ваш пост", "read": false, ... }
]
Добавление логирования HTTP-запросов в Spring Boot API
⏩AlexanderObregon/adding-request-logging-to-spring-boot-apis-3cdf1ee860c3">Читать подробнее
👉 Java Portal | #cтатья
Вот хороший пример очень плохого использования метки в Java:
void main() {
do onlyOnce: {
IO.println("Один раз?");
break onlyOnce;
} while (true);
}Один раз?
Один раз?
Один раз?
Один раз?
Один раз?
...
Канал с курсами и книгами для айтишников
Программирование 24/7 — настоящий портал для тех кто хочет стать айтишником. Там есть всё необходимое — топовые курсы и книги по всем языкам программирования(Frontend, Python, Java, C#, PHP и др.)
Все бесплатно, доступно и удобно — @edprogramming
Курс по конкурентному программированию от Ильи Сергеева
Если хочешь разобраться в параллельности, потоках, акторной модели и распределёнке — лови топовый курс от препода Yale-NUS
— Scala для конкурентного кода
— Мьютексы, блокировки, атомики
— Futures и async
— Акторы и обмен сообщениями
— Распределённый консенсус (в т.ч. BFT)
Можешь поблагодарить лукасом 😍
👉 Java Portal
Имбовая библиотека цветов для Kotlin Multiplatform
Если ты пилишь на Kotlin и хочешь красивые цвета под любую платформу — зацени compose-pipette
Поддерживает:
> JVM
> Android (от API 21)
> Kotlin/JS
> Kotlin/WASM
> macOS (пока не тестили)
> iOS (тоже не тестили)
Поддержи пост лайком ❤️
👉 Java Portal
Шпаргалка по Map-реализациям
Если путаешься в HashMap, TreeMap, ConcurrentHashMap — лови таблицу сравнения
> HashMap — быстрая, но не потокобезопасная. Подходит для общего использования
> LinkedHashMap — сохраняет порядок вставки
> TreeMap — сортирует по ключам.
> Hashtable — потокобезопасна, но старая и медленная
> ConcurrentHashMap — оптимальна для многопоточности
> EnumMap — супербыстрая для enum-ключей
> IdentityHashMap — использует == вместо .equals()
> WeakHashMap — ключи могут быть удалены GC. Полезно для кэшей
Не забывай про null-ключи, производительность, порядок —> всё это влияет на выбор структуры данных 💖
👉 Java Portal
Балансировка производительности и потокобезопасности
🔜 Читать: клик
👉 Java Portal | #cтатья
Дорожная карта Spring Boot для Java-разработчиков
💪💪💪
👉 Java Portal
Pattern Matching в switch — Java
🔜 Читать: клик
👉 Java Portal | #cтатья
Как синхронизировать двунаправленные ассоциации сущностей с помощью JPA и Hibernate
🔜 Читать: клик
👉 Java Portal | #cтатья
Модификаторы и пакеты доступа Java
🔜 Читать: клик
👉 Java Portal | #cтатья
Плохие практики в коде, которые кажутся безобидными (но это не так)
1) Чрезмерное использование private-методов, скрывающее сложность
Когда private-методов слишком много, это ломает читаемость и разносит логику по коду. Визуально всё выглядит «чисто», но важные логические шаги оказываются скрытыми.
Когда это плохо:
Вы выносите код в private-методы просто ради самого факта выноса, а не потому что они переиспользуемы или несут отдельный смысл.
Как лучше:
Выделяйте разные зоны ответственности в отдельные классы или сервисы, которые сотрудничают между собой.
2) Автоматическое добавление private + геттеров/сеттеров по умолчанию
Бездумное добавление геттеров и сеттеров превращает ООП в просто «структуры с методами». Это нарушает инкапсуляцию, а не сохраняет её.
Вместо этого:
● Открывайте только те геттеры/сеттеры, которые реально нужны для бизнес-логики.
● Вместо сеттеров лучше использовать методы, которые выполняют осмысленные действия с валидацией.
3) Чрезмерное использование static-методов и переменных
static-методы кажутся удобными, но убивают тестируемость и гибкость. Они привязывают код к жёсткому глобальному состоянию.
Как лучше:
Используйте внедрение зависимостей или передавайте нужную конфигурацию явно туда, где она требуется.
4) Слишком большой класс (“God Object”)
Огромный класс, который делает всё подряд, превращается в чёрный ящик — его сложно тестировать, менять и понимать.
Как лучше:
● Применяйте принцип единственной ответственности (Single Responsibility Principle): у класса должна быть только одна причина для изменения.
● Разделяйте обязанности между меньшими по размеру классами или модулями.
5) Скрытие семантики null / Optional
Возврат null или скрытое использование Optional приводит к неожиданным ошибкам во время выполнения. Отсутствие или наличие значения должно быть выражено явно.
Как лучше:
● Используйте Optional<T> или другой задокументированный способ, чтобы показать, что результат может отсутствовать.
● Если это действительно ошибка — выбрасывайте чётко определённое исключение.
👉 Java Portal
Некоторые редкие, но ценные паттерны в Java
1. Null Object Pattern (Объект-Заглушка вместо null)
❯ Позволяет избежать проверок на null, возвращая объект с "пустым" поведением, реализующий ожидаемый интерфейс.
❯ Помогает держать код чистым, без множества if (obj != null) перед каждым вызовом метода.
❯ Используй, когда возврат null приводит к захламлению условными конструкциями.
2. Parameter Object Pattern (Объект параметров)
❯ Объединяет логически связанные параметры в один объект.
❯ Такой объект удобно прокидывать через разные уровни приложения, обеспечивая согласованность.
❯ Используй, когда у метода слишком много связанных аргументов.
3. Fluent Interface / Method Chaining (Флюент-интерфейс / Цепочка вызовов)
❯ Позволяет вызывать методы цепочкой, улучшая читаемость и выражая намерение кода (особенно в билдерах и конфигурациях).
❯ Используй в билдерах или для декларативной настройки.
4. Execute Around Method Pattern (Шаблон обёртки вокруг действия)
❯ Инкапсулирует логику подготовки и завершения вокруг основного действия.
❯ Гарантирует корректное освобождение ресурсов.
❯ Используй для управления ресурсами (файлы, БД и т.п.).
5. Initialization-on-Demand Holder (ленивый Singleton без synchronized)
❯ Безопасный, потокобезопасный и производительный способ ленивой инициализации Singleton'а без явной синхронизации.
❯ Используй, если нужен быстрый и потокобезопасный синглтон.
👉 Java Portal
Хаки Java-кодинга, которые стоит знать разработчикам
✓ Используйте Map.ofEntries() для создания неизменяемых мап с несколькими парами
➣ Почему: короче, безопаснее, неизменяемо — снижает риск случайной мутации.
✓ Используйте Optional.map().orElse() вместо ручной обработки null
➣ Почему: устраняет проверки на null, читается лучше, легко комбинируется с другой логикой.
✓ Используйте Enum.valueOf() + name() для безопасного преобразования enum
➣ Почему: чище и удобнее для сериализации/десериализации enum'ов в API, БД или файлах.
✓ Используйте Comparator.comparing().thenComparing() вместо кастомной логики
➣ Почему: читаемо, понятно, без ошибок в условных сравнениях.
✓ Используйте Optional.ifPresentOrElse() вместо if-else с isPresent()
➣ Почему: выразительнее и чище — больше никаких ручных get() и нагромождений if-else
👉 Java Portal
Базовые приёмы экономии памяти в Java
1. Ленивая инициализация "тяжёлых" полей
– Используйте lazy initialization, чтобы откладывать создание объекта до момента, когда он действительно понадобится.
2. Избегайте анонимных внутренних классов в циклах
– Каждое такое использование создаёт объект внутреннего класса — это скрытая нагрузка на память.
3. Используйте статические фабричные методы
– Они позволяют экономить память за счёт повторного использования уже созданных экземпляров, вместо создания новых при каждом вызове.
4. Применяйте метод intern()
– Метод intern() класса String возвращает каноническое представление строки.
Используя intern(), можно гарантировать, что одна и та же строка будет представлена в памяти в виде единственного объекта, даже если она создаётся многократно.
String s1 = "hello";
String s2 = "hello".intern();
Integer.valueOf(), Boolean.TRUE и т.п.).
Вопросы для собеседования по микросервисам с ответами
1. Каковы ключевые характеристики микросервисов?
Ответ:
> Децентрализованное управление данными
> Сервисы развёртываются независимо друг от друга
> Проектирование на основе предметной области (DDD)
> Лёгкое взаимодействие (например, REST, gRPC)
> Изоляция сбоев
> Удобны для непрерывной доставки
2. Чем микросервисы отличаются от монолитной архитектуры?
Ответ:
> Монолит: единая кодовая база, жёстко связанные компоненты, сложно масштабировать.
> Микросервисы: множество сервисов, слабо связанные, развёртываются и масштабируются независимо.
3. Каковы основные преимущества использования микросервисов?
Ответ:
> Лучшая масштабируемость
> Более быстрое выведение продукта на рынок
> Независимые развёртывания
> Лучшая устойчивость к сбоям
> Возможность использовать разные языки и технологии
4. Какие есть сложности при работе с микросервисами?
Ответ:
> Сложность управления распределёнными системами
> Задержки в сети и накладные расходы на коммуникацию
> Согласованность данных
> Отладка и мониторинг
> Развёртывание и оркестрация
5. Как микросервисы обмениваются данными?
Ответ:
> Синхронно: через REST, gRPC
> Асинхронно: через очереди сообщений (RabbitMQ, Kafka)
6. Что такое service discovery в микросервисах?
Ответ:
> Это механизм, с помощью которого сервисы находят друг друга в сети.
> Применяются инструменты вроде Consul, Eureka, DNS Kubernetes.
7. Что такое API Gateway и зачем он нужен?
Ответ:
> API Gateway — это единая точка входа в систему. Он отвечает за маршрутизацию, безопасность, ограничение частоты запросов и агрегацию ответов от разных сервисов.
> Примеры: Kong, Zuul, NGINX, Spring Cloud Gateway.
8. Как в микросервисах управляют данными?
Ответ:
> Каждый сервис использует свою отдельную базу данных (подход “одна база на сервис”).
> Для согласованности применяются событийная архитектура или паттерн саги.
9. Что такое паттерн Saga?
Ответ:
> Saga — это последовательность локальных транзакций.
> Если одна из них завершается с ошибкой, запускаются компенсирующие действия для отката изменений.
10. Какие инструменты используют для разработки микросервисов?
Ответ:
> Языки: Java (Spring Boot), Node.js, Go, Python
> Сборка: Maven, Gradle
> Контейнеризация: Docker
> Оркестрация: Kubernetes
Разработчики, вот вам шпаргалка по техникам синхронизации потоков в Java 🧑💻
synchronized блок/метод -> использует встроенную блокировку объекта для обеспечения взаимного исключения.
ReentrantLock -> явная блокировка с более гибкими возможностями по сравнению с synchronized.
volatile- > гарантирует видимость изменений переменной между потоками.
Атомарные переменные (AtomicInteger и др.) -> обеспечивают атомарные операции без блокировок.
Semaphore -> управляет доступом к ресурсу с ограниченным числом разрешений.
CountDownLatch -> позволяет одному или нескольким потокам ожидать завершения операций в других потоках.
CyclicBarrier -> позволяет группе потоков дождаться друг друга до продолжения выполнения.
ReadWriteLock -> разрешает множественные параллельные чтения, но только одну запись.
👉 Java Portal
Начиная с JDK 12, появилась новая фича — java.text.CompactNumberFormat. Она позволяет компактно отображать большие числа
Пример:
Locale.setDefault(Locale.US);
NumberFormat compact = NumberFormat.getCompactNumberInstance();
System.out.println(compact.format(1));
System.out.println(compact.format(999));
System.out.println(compact.format(2_000));
System.out.println(compact.format(55_555));
System.out.println(compact.format(3_777_999));
System.out.println(compact.format(Integer.MAX_VALUE));
1
999
2K
56K
4M
2B
NumberFormat compact;
compact = NumberFormat.getCompactNumberInstance(Locale.US, Style.LONG);
1
999
2 thousand
56 thousand
4 million
2 billion
В Java лямбда-выражение создаёт экземпляр функционального интерфейса. Во время выполнения этот экземпляр создаётся с помощью класса LambdaMetaFactory
Следующий код на Java создаёт объект Function<String, String>
Function<String, String> f = s -> s.toUpperCase();
@SuppressWarnings("unchecked")
void main() throws Throwable {
MethodHandles.Lookup lookup;
lookup = MethodHandles.lookup();
CallSite callSite;
callSite = LambdaMetafactory.metafactory(
lookup,
"apply",
MethodType.methodType(Function.class),
MethodType.methodType(Object.class, Object.class),
lookup.findStatic(
getClass(),
"lambda",
MethodType.methodType(String.class, String.class)
),
MethodType.methodType(String.class, String.class)
);
MethodHandle target;
target = callSite.getTarget();
Function<String, String> f;
f = (Function<String, String>) target.invokeExact();
String msg;
msg = f.apply("Hello, World!");
IO.println(msg);
}
private static String lambda(String s) {
return s.toUpperCase();
}
HELLO, WORLD! ❤️
Strategy Pattern без занудства
Ситуация: вы пишете платёжную систему
У вас есть PaymentService, поддерживающий оплату кредитной картой.
Вы пишете:
public void pay(String method) {
if (method.equals("creditcard")) {
// логика оплаты кредиткой
}
}if (method.equals("creditcard")) {
// логика кредитки
} else if (method.equals("paypal")) {
// логика PayPal
} else if ...if-else. Каждый раз при добавлении нового способа оплаты:PaymentService теперь отвечает за слишком многоеCreditCardPayment, PayPalPayment, UPIPaymentPaymentService просто вызывает .pay() на нужной стратегииif-elseinterface PaymentStrategy {
void pay(int amount);
}
class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Плачу " + amount + " с помощью кредитки");
}
}
class PayPalPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Плачу " + amount + " через PayPal");
}
}
Хочешь, чтобы Swagger UI был доступен без авторизации в Spring Boot?
Просто добавь его в белый список:
.antMatchers("/swagger-ui/", "/v3/api-docs/").permitAll()
Хотите получить строковое представление объекта так, как если бы методы toString и hashCode не были переопределены? Тогда метод Objects::toIdentityString — это то, что вам нужно:
void main() {
IO.println(this);
IO.println(Objects.toIdentityString(this));
}
@Override
public String toString() {
return "Hello, World!";
}Hello, World!
HelloWorld@275710fc
Это может быть полезно в логировании, например, когда нужно отслеживать операции, выполненные над каким-то конкретным объектом, например, перед сбоем. Идентичный хэшкод — полезный способ сделать это
В Java можно столкнуться с ситуацией, когда поле final кажется имеющим два разных значения. Рассмотрим такой пример:
void main() {
var after = new Final(this::before);
IO.println("After value=" + after.value);
}
private void before(Final before){
IO.println("Before value=" + before.value);
}
class Final {
final int value;
Final(Consumer<? super Final> listener) {
listener.accept(this);
value = 123;
}
}Before value=0
After value=123
this. Код в начале конструктора не должен использовать this, кроме как для инициализации полей, у которых нет своих инициализаторов.
this, пока объект не готов
В Java тело лямбды прозрачно для this. Например, следующий код выведет Hello, World!:
private Runnable runnable = () -> {
System.out.println(this);
};
@Override
public String toString() {
return "Hello, World!";
}
void main() {
runnable.run();
}InnerClassThis$1@568bf312:private Runnable runnable = new Runnable() {
@Override public void run() {
System.out.println(this);
}
};
@Override
public String toString() {
return "Hello, World!";
}
void main(String[] args) {
runnable.run();
}this — она использует this из внешнего контекста. Это чётко описано в спецификации Java:Значение this в теле лямбды — то же самое, что и this во внешнем контексте.
Аннотации @OneToMany и @ManyToOne помогают определить связи между сущностями в базе данных
Пример:
Пользователь может иметь много заказов →
@OneToMany(mappedBy = "user")
private List<Order> orders;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
Многопоточность в Java
Есть основной метод, а вместе с ним — несколько потоков
Каждый поток выполняет свою задачу одновременно с другими
Это даёт прирост в скорости и производительности.
Полезно, когда нужно делать несколько вещей сразу — например, загружать файлы или обслуживать пользователей
👉 Java Portal