Библиотека Java разработчика
10.6K subscribers
1.16K photos
583 videos
58 files
1.48K links
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.


По всем вопросам @evgenycarter

РКН clck.ru/3KoGeP
Download Telegram
Многопоточность

Как можно создать потоки в Java?
Как устроить Deadlock, а затем уйти от взаимной блокировки навсегда
Два самых полезных слова в многопоточности join() и Future

источник

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
👩‍💻 Ваши Java-приложения под нагрузкой едва дышат? Профилирование и оптимизация кода остаются за скобками?

🎯 Курс «Java Developer. Advanced» от OTUS — это глубокое погружение в устройство JVM, байткод, сборщики мусора, профилирование и оптимизацию в облаке.
Наши эксперты-практики покажут, как выбрать GC, снять дамп памяти и обнаружить утечки, как профилировать “горячие” места, настраивать интеграцию с Prometheus & Grafana и деплоить сервисы в Kubernetes.

✔️ Вы научитесь обосновывать выбор JVM-имплементации, анализировать heap-дампы, синхронизировать потоки, использовать Reactor и неблокирующие I/O, проектировать метрики и трассировку.

➡️ Пройдите короткий тест и присоединяйтесь к группе: https://vk.cc/cRBJWK

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
Совет по Java 💡

Использование try-with-resources для автоматического управления ресурсами
Оператор try-with-resources гарантирует, что каждый ресурс будет закрыт в конце оператора.


try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}


📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Что такое «ссылка на метод»?

Если существующий в классе метод уже делает все, что необходимо, то можно воспользоваться механизмом method reference (ссылка на метод) для непосредственной передачи этого метода. Такая ссылка передается в виде:

⚫️имя_класса::имя_статического_метода для статического метода;
⚫️объект_класса::имя_метода для метода экземпляра;
⚫️название_класса::new для конструктора.
Результат будет в точности таким же, как в случае определения лямбда-выражения, которое вызывает этот метод.


private interface Measurable {
public int length(String string);
}

public static void main(String[] args) {
Measurable a = String::length;
System.out.println(a.length("abc"));
}


Ссылки на методы потенциально более эффективны, чем использование лямбда-выражений. Кроме того, они предоставляют компилятору более качественную информацию о типе и при возможности выбора между использованием ссылки на существующий метод и использованием лямбда-выражения, следует всегда предпочитать использование ссылки на метод.

📲 Мы в MAX

👉@BookJava
👍41
Использование Optional для избежания NullPointerException:
Optional — это контейнер, который может содержать или не содержать ненулевое значение.


Optional<String> optional = Optional.ofNullable(getValue());
optional.ifPresentOrElse(
value -> System.out.println("Value is present: " + value),
() -> System.out.println("Value is absent")
);


📲 Мы в MAX

👉@BookJava
👍6
⚡️ Микро-оптимизация: Прощай, AtomicInteger, привет, VarHandle!

👋 Сегодня поговорим о том, как можно немного ускорить ваш код, работающий с атомарными операциями, и сделать его более "современным" с помощью VarHandle, который появился в Java 9.

🧐 Проблема с AtomicInteger

AtomicInteger - это классический способ обеспечить атомарные операции (например, инкремент) над целым числом без блокировок, используя механизм Compare-And-Swap (CAS).

Однако, у него есть небольшой недостаток: он добавляет слой косвенности. Методы типа getAndIncrement() в AtomicInteger обычно вызывают внутренние статические методы из класса sun.misc.Unsafe (или его аналогов в более новых версиях), передавая в них ссылку на объект, смещение поля и новое значение.


// Примерно так это выглядит внутри AtomicInteger
// В реальном коде это, конечно, оптимизировано, но суть та же.
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}


Встречайте VarHandle!

VarHandle - это новый, более гибкий и, что самое главное, более производительный низкоуровневый API для работы с полями переменных (как экземпляров, так и статических) с заданными барьерами памяти и атомарностью.

Главное преимущество? JIT-компилятор (C2) лучше оптимизирует доступ через VarHandle, чем через старые обертки типа AtomicInteger или прямые вызовы Unsafe.

🛠 Как использовать VarHandle вместо AtomicInteger

Вместо того, чтобы хранить значение в отдельном объекте AtomicInteger, мы просто объявляем поле volatile в нашем классе и получаем VarHandle для этого поля.

1. Объявляем поле:


public class Counter {
private volatile int count = 0; // Поле должно быть volatile
// ...
}


2. Создаем VarHandle:

VarHandle нужно инициализировать один раз (обычно в статическом блоке) для доступа к полю.


private static final VarHandle COUNT_HANDLE;

static {
try {
// Получаем VarHandle для поля 'count' класса 'Counter' с типом int
COUNT_HANDLE = MethodHandles.lookup().findVarHandle(
Counter.class,
"count",
int.class
);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}


3. Выполняем атомарную операцию:

Метод getAndAdd VarHandle работает точно так же, как getAndAdd в AtomicInteger.


public int increment() {
// В отличие от AtomicInteger, где первый аргумент неявен,
// VarHandle требует первым аргументом *объект*,
// к полю которого мы обращаемся.
return (int) COUNT_HANDLE.getAndAdd(this, 1);
}


🚀 Результат микро-оптимизации

В бенчмарках (например, с использованием JMH) можно увидеть, что операции getAndAdd() через VarHandle могут быть незначительно быстрее (зачастую на 5-10%), чем те же операции через AtomicInteger, особенно под высокой нагрузкой, благодаря более эффективной генерации кода JIT-компилятором.

🛑 Важно: Это микро-оптимизация. В большинстве приложений вы не заметите разницы. Но если вы пишете критически важные фреймворки, высоконагруженные коллекции или библиотеки, где каждая наносекунда на счету, переход на VarHandle может быть оправдан.



VarHandle - это не только способ микро-оптимизации, но и стандартный, гибкий API для атомарного доступа, который заменил устаревший и менее безопасный Unsafe. Для нового кода, где требуется низкоуровневый атомарный доступ, стоит отдавать предпочтение именно ему.

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5👎5🔥1
Совет

По умолчанию при обновлении сущности Hibernate обновляет все ее поля. Вы можете использовать аннотацию @DynamicUpdate на объекте
@Entity и Hibernate будет обновлять только те столбцы, которые изменились.



📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Совет SpringBoot

Вы можете запустить метод в Spring @Service сразу после запуска приложения, аннотируя его с помощью
@EventListener (ApplicationReadyEvent.class). Метод не может иметь параметров. Иногда я неправильно использую его, чтобы быстро протестировать определенный метод Spring Service.

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🏆1
Использование лямбда-выражений и Streams:
Лямбда-выражения и streams делают код более лаконичным и читаемым.


List<String> names = Arrays.asList("John", "Jane", "Jack", "Doe");
names.stream()
.filter(name -> name.startsWith("J"))
.map(String::toUpperCase)
.forEach(System.out::println);


📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👎4👍3
☕️ Spring Core: Зачем нужна аннотация @Bean?

Если вы работаете со Spring, вы видите эту аннотацию постоянно. Но чем она отличается от простого навешивания @Component над классом? Давайте разберем.

💡 Что это такое?

Аннотация @Bean используется в методах конфигурационных классов (помеченных @Configuration). Она говорит Spring-контейнеру:

"Эй, Spring! Выполни этот метод, возьми то, что он вернет, и сохрани этот объект у себя в контексте (ApplicationContext). Управляй им как бином".


🛠 Как это выглядит?


@Configuration
public class AppConfig {

// Мы явно создаем объект и отдаем его Спрингу
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper(); // Например, библиотека Jackson
}
}




🔥 Когда использовать @Bean, а когда @Component?

Это самый частый вопрос на собеседованиях.

1. Используйте @Component@Service, @Repository), когда:

- Это ваш класс. Вы имеете доступ к исходному коду.
- Вам нужна магия автоматического сканирования (component scanning). Вы просто ставите аннотацию над классом, и Spring сам его находит.

2. Используйте @Bean, когда:

- Сторонние библиотеки. Вы не можете зайти в класс ObjectMapper (из Jackson) или AmazonS3Client и написать там @Component, потому что это чужой код (read-only). Чтобы добавить такой объект в контекст Spring, вы создаете для него метод с @Bean.
- Сложная логика создания. Если создание объекта требует условий (if/else) или сложной конфигурации, проще описать это в методе явно.

⚙️ Фишки @Bean

- Имена: По умолчанию имя бина совпадает с именем метода. Можно изменить: @Bean("myCoolBean").
- Init/Destroy: Можно указать методы, которые сработают при создании или удалении бина: @Bean(initMethod = "init", destroyMethod = "cleanup").
- Зависимости: Если методу с @Bean нужны аргументы, Spring автоматически найдет и подставит их из контекста.

Итог: @Component - для автоматизации своих классов, @Bean - для ручного контроля и чужих библиотек.


#Java #Spring #SpringBoot #Coding #Education

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥3
🔍 Завтра тестовое собеседование с Java-разработчиком

17 декабря(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир →
@shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
Совет Spring Framework💡

Вы можете инжектировать (autowire) бины, которые могут отсутствовать, обернув их в java.util.Optional. Таким образом вы сообщаете, что этот бин является необязательным, избегаете исключения, если он не существует, и можете аккуратно обработать его отсутствие с помощью Optional API.

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51
⌨️ Открытый урок «Тестирование Spring приложений. Интеграционные тесты с контекстом. Тестирование слоя репозиториев и сервисов».

🗓 25 декабря в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Разработчик на Spring Framework».

Интеграционные тесты в Spring: тестовый контекст, репозитории (JDBC/JPA), сервисы и транзакции.

На вебинаре:
✔️Особенности интеграционных тестов с поднятием контекста Spring.
✔️Тестирование слоя репозиториев: JDBC и JPA.
✔️Работа с транзакциями на сервисном слое и подходы к тестированию.

Кому будет полезно:
Backend-разработчикам на Java, начинающим осваивать тестирование Spring-приложений.

Что вы получите:
Разберётесь в базе интеграционного тестирования со Spring-контекстом и сможете написать простые тесты для репозиториев и сервисов.

🔗 Ссылка на регистрацию: https://vk.cc/cSrDoD

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🧵 String.join(): Склеиваем строки без боли

Помните те времена, когда для объединения списка строк через запятую приходилось писать циклы, использовать StringBuilder, а потом еще и аккуратно удалять последний разделитель? 🤯

Начиная с Java 8, у нас есть элегантный статический метод String.join, который делает код чистым и читаемым.

🛠 Как это работает?

Метод принимает разделитель (delimiter) и элементы, которые нужно склеить. Элементами могут быть как просто перечисление строк (varargs), так и любая коллекция (Iterable).

1️⃣ Пример с перечислением строк:


String result = String.join(" -> ", "Wake up", "Code", "Sleep");

System.out.println(result);
// Вывод: Wake up -> Code -> Sleep


2️⃣ Пример с коллекцией (List, Set):


List<String> langs = Arrays.asList("Java", "Kotlin", "Groovy");

// Больше никаких циклов!
String output = String.join(" | ", langs);

System.out.println(output);
// Вывод: Java | Kotlin | Groovy


🧐 Важные нюансы:

- Null-safe (частично): Если сам список или массив равен null, вы получите NullPointerException. Но если null является одним из элементов списка, метод просто преобразует его в строку "null".
- Под капотом: Метод использует StringJoiner (еще один класс из Java 8), что обеспечивает неплохую производительность по сравнению с обычной конкатенацией через +.

🚀 Когда использовать?

Используйте String.join, когда у вас уже есть коллекция или массив строк, и вам нужно быстро собрать их в одну строку.

Если же вы работаете со Stream API, то лучше подойдет коллектор:
.collect(Collectors.joining(", "))


#Java #Core #Tips #CleanCode

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Использование метода String.join

Метод String.join можно использовать для объединения строк с заданным разделителем.


List<String> items = Arrays.asList("Apple", "Banana", "Cherry");
String result = String.join(", ", items);
System.out.println(result); // Output: Apple, Banana, Cherry


📲 Мы в MAX

👉@BookJava
👍21
Функциональные интерфейсы: Магия за кулисами Лямбд

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

🎯 Что это такое?

Функциональный интерфейс - это интерфейс, который содержит ровно один абстрактный метод.

Именно это ограничение позволяет компилятору превращать лямбда-выражение в экземпляр этого интерфейса.

💙 Примечание: В интерфейсе может быть сколько угодно default или static методов. Главное - только один абстрактный.

📝 Аннотация @FunctionalInterface

Ставить её над интерфейсом не обязательно, но хорошим тоном считается ставить.
Зачем? Она работает как защита от дурака: если вы или ваш коллега случайно добавите второй абстрактный метод в интерфейс, компилятор сразу выдаст ошибку, не дожидаясь падения кода в местах использования лямбд.



🧰 Шпаргалка: "Великолепная четверка"

В пакете java.util.function уже есть готовые интерфейсы на 99% случаев жизни. Не пишите свои велосипеды, пока не выучите эти:

1. Predicate <T>
💙Что делает: Проверяет условие.
💙 Метод: boolean test(T t)
💙 Где нужен: Фильтрация стримов (`filter`), проверки.


2. Consumer <T>
💙 Что делает: "Потребляет" объект, ничего не возвращая.
💙 Метод: void accept(T t)
💙 Где нужен: Вывод на экран, запись в БД, forEach.


3. Supplier <T>
💙 Что делает: "Поставляет" объект (из ниоткуда), ничего не принимая.
💙 Метод: T get()
💙 Где нужен: Ленивая инициализация, генерация значений, orElseGet.


4. Function <T, R>
💙 Что делает: Превращает объект типа T в объект типа R.
💙 Метод: R apply(T t)
💙 Где нужен: Преобразование данных, map в стримах.


💻 Пример в коде


@FunctionalInterface
interface Converter {
int stringToInt(String s);
}

public class Main {
public static void main(String[] args) {
// 1. Создаем реализацию через лямбду
Converter converter = str -> Integer.parseInt(str);

// 2. Использование стандартного Consumer
java.util.function.Consumer<Integer> print = x -> System.out.println("Result: " + x);

int result = converter.stringToInt("123");
print.accept(result); // Вывод: Result: 123
}
}



🔥 Итог

Функциональные интерфейсы - это контракт для лямбда-выражений. Используйте стандартные из java.util.function, чтобы ваш код был понятен другим разработчикам без лишних документаций.

#Java #Core #Lambda #FunctionalProgramming

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍1
🚀 Функциональные интерфейсы: Level Up

В первой части мы разобрали «Великолепную четверку» (Predicate, Consumer, Supplier, Function). Но что делать, если нужно принять два аргумента? Или если тип входа и выхода совпадает, и лень писать лишний код?

Для этого в Java есть Bi-версии и Операторы.

👯 Семейство «Bi» (Два аргумента)

Стандартные интерфейсы принимают только один параметр. Если вам нужно обработать пару значений (например, ключ и значение из Map), используйте приставку Bi.

1. BiPredicate <T, U>
💙 Метод: boolean test(T t, U u)
💙 Пример: Проверить, что длина строки T больше числа U.


2. BiConsumer <T, U>
💙 Метод: void accept(T t, U u)
💙 Пример: map.forEach((k, v) -> ...) - классический пример использования.


3. BiFunction <T, U, R>
💙 Метод: R apply(T t, U u)
💙 Пример: Сложить число T и число U, получить результат R.



🔄 Семейство «Operator» (Один и тот же тип)

Часто бывает, что вы преобразуете объект, не меняя его тип (String -> String, int -> int). Писать Function<String, String> - слишком длинно.

1. UnaryOperator <T>
💙 Наследник Function<T, T>.
💙 Пример: str -> str.toUpperCase() (принимает строку, возвращает строку).


2. BinaryOperator <T>
💙 Наследник BiFunction<T, T, T>.
💙 Пример: (a, b) -> a + b (два числа на вход, одно число на выход). Именно он используется в Stream.reduce.


Осторожно с боксингом!

Дженерики (<T>) работают только с объектами. Если вы используете Function<Integer, Integer> для математики, Java будет постоянно распаковывать и запаковывать int в Integer, что бьет по производительности.

Для примитивов есть свои спецназовцы:

💙 IntPredicate, LongConsumer, DoubleFunction и т.д.
💙 Правило: Если работаете с числами - всегда ищите примитивный аналог интерфейса.

💻 Пример в коде


import java.util.function.*;

public class AdvancedLambdas {
public static void main(String[] args) {
// 1. BinaryOperator: объединяем две строки
BinaryOperator<String> concat = (s1, s2) -> s1 + " " + s2;
System.out.println(concat.apply("Hello", "Java"));

// 2. BiPredicate: проверяем, начинается ли строка с префикса
BiPredicate<String, String> startsWith = (str, prefix) -> str.startsWith(prefix);
System.out.println(startsWith.test("Telegram", "Tele")); // true

// 3. IntUnaryOperator: работаем с примитивами без лишних объектов
IntUnaryOperator square = x -> x * x;
System.out.println(square.applyAsInt(5)); // 25
}
}



🔥 Итог

💙 Нужно 2 аргумента? Ищите Bi.
💙 Тип входа и выхода совпадает? Ищите Operator.
💙 Работаете с int/long/double? Ищите интерфейсы с префиксом типа.

#Java #AdvancedJava #FunctionalProgramming

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
6
🍒 Магия двойного двоеточия: Method References

Мы уже научились писать лямбды. Но иногда даже лямбда кажется слишком громоздкой. Если ваша лямбда не делает ничего, кроме вызова одного уже существующего метода, Java позволяет использовать Method Reference (ссылку на метод).

Синтаксис простой: Класс::метод (без скобок!).

🛠 4 ситуации, когда это работает

Есть 4 основных способа использовать оператор ::. Важно понимать разницу, чтобы не путаться.

1. Ссылка на статический метод
💙 Лямбда: s -> Integer.parseInt(s)
💙 Reference: Integer::parseInt
💙 Суть: Просто перенаправляем входящий параметр в статический метод.


2. Ссылка на метод конкретного объекта
💙 Лямбда: obj -> System.out.println(obj)
💙 Reference: System.out::println
💙 Суть: У нас уже есть готовый объект (System.out), и мы вызываем его метод для каждого элемента.


3. Ссылка на метод произвольного объекта определенного типа (Самый хитрый пункт! 🤯)
💙 Лямбда: s -> s.toLowerCase()
💙 Reference: String::toLowerCase
💙 Суть: Здесь метод вызывается у самого объекта, который пришел в лямбду. Хотя синтаксис похож на статический вызов, это вызов инстанс-метода.


4. Ссылка на конструктор
💙 Лямбда: () -> new ArrayList<>()
💙 Reference: ArrayList::new
💙 Суть: Используется для создания новых объектов (часто в Stream API: Collectors.toCollection(ArrayList::new)).


💻 Пример: Было vs Стало

Смотрите, как очищается код. Допустим, у нас есть список имен:


List<String> names = Arrays.asList("Alex", "Bob", "Anna");

// Уровень 1: Анонимный класс (Олдскул)
names.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});

// ⚠️ Уровень 2: Обычная лямбда
names.forEach(s -> System.out.println(s));

// Уровень 3: Method Reference (Красота)
names.forEach(System.out::println);



🧠 Как понять, когда использовать?

Если ваша лямбда выглядит так:
x -> Class.method(x)
или
x -> x.method()
...смело меняйте её на ::.

Но! Если вам нужно изменить аргумент перед передачей (например, x -> System.out.println("Name: " + x)), то Method Reference уже не подойдет, оставайтесь на лямбде.

#Java #CleanCode #MethodReference

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍52