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


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

РКН clck.ru/3KoGeP
Download Telegram
Понимание разницы между Error и Exception в Java

Что такое Error в Java?
- Error (ошибка) в Java представляет собой серьёзные проблемы, которые программа не должна пытаться обрабатывать. Такие ошибки, как правило, генерируются виртуальной машиной Java (JVM) и указывают на сбои, не зависящие от кода программы — например, утечки памяти или переполнение стека.

Что такое Exception в Java?
- Exception (исключение) — это ситуации, которые программа может предсказать и обработать. Это ошибки, от которых можно восстановиться во время выполнения с помощью механизмов обработки исключений, таких как конструкции try-catch.

📲 Мы в MAX

👉@BookJava
4👍2👎1🔥1
🎯 Как Java находит классы? Под капотом ClassLoader'ов

Сечас покажу вам одну из самых недооценённых тем в мире Java — механизм загрузки классов.

Когда ты запускаешь Java-приложение, оно не просто «видит» все классы. За кулисами работает цепочка загрузчиков:

1. Bootstrap ClassLoader
Самый базовый. Загружает классы из JDK (rt.jar, java.base, и так далее). На него даже нельзя получить ссылку в коде.

2. Platform ClassLoader (ранее Extension)
Загружает модули платформы (jmods), доступные из JDK, но не из java.base.

3. Application ClassLoader
Твой лучший друг. Он отвечает за загрузку классов из classpath (например, target/classes и lib/*.jar).

Но вот где начинается магия — ты можешь создать собственный ClassLoader и загружать классы в рантайме из файлов, БД или даже сети. Например:


ClassLoader customLoader = new URLClassLoader(new URL[]{new File("plugins/").toURI().toURL()});
Class<?> pluginClass = customLoader.loadClass("com.example.PluginImpl");


💡 Это используется в плагинных системах (например, IntelliJ, Jenkins, Minecraft).
Но будь осторожен — неправильная работа с загрузчиками может привести к ClassCastException, даже если классы выглядят одинаково.

📲 Мы в MAX

👉@BookJava
👍61
🚀 Подборка полезных IT каналов в Max


Системное администрирование, DevOps 📌

https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин

Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак

Английский с нуля 🇬🇧
https://max.ru/UchuEnglish

1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С

Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика

Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика

Программирование React📌
https://max.ru/react_lib React

Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика

Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика

GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub

Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных

Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков

Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов

Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻

Шутки программистов 📌
https://max.ru/itumor Шутки программистов

Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free

Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров

Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике

Вакансии 📌
https://max.ru/progjob Вакансии в IT

Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных

Бонус 📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
🤮4👍1
🧠 Как Java хранит boolean в памяти?

Сегодня я покажу вам, почему boolean в Java — это не просто true или false. А за этим простым типом скрывается интересный нюанс, особенно если ты задумываешься об экономии памяти.

В Java нет отдельного типа, который занимает всего 1 бит. Хотя логично было бы ожидать, что boolean — это один бит (true/false), на самом деле в памяти он занимает 1 байт (а иногда и больше, в зависимости от структуры объекта).

Пример:

public class Flags {
boolean flag1;
boolean flag2;
boolean flag3;
}

Ты думаешь — три бита. Но JVM выравнивает поля, и из-за этого объект может занимать 16 байт или больше, в зависимости от архитектуры. Почему так?

📌 Причина:
JVM упрощает модель памяти ради производительности — доступ к байтам быстрее, чем к битам. Нет битовых сдвигов, масок и лишней логики.

💡 Что делать, если хочется сэкономить память?

Используй BitSet:

BitSet flags = new BitSet(3);
flags.set(0, true);


Это уже реальная битовая структура. Отличный выбор, если у тебя десятки или сотни логических флагов.

А ты знал об этом нюансе хранения boolean? Пиши в комментариях, сталкивался ли с перерасходом памяти из-за простых типов.

📲 Мы в MAX

👉@BookJava
👍102🔥2👎1🤝1
👩‍💻 Как работает @Transactional в Spring: границы транзакций и типовые ошибки

Приглашаем на открытый урок.

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

На занятии мы разберем:
✔️Что реально делает @Transactional в Spring
✔️Почему важны proxy и вызов метода через Spring Bean
✔️Как работают propagation-режимы на примере REQUIRED и REQUIRES_NEW
✔️Когда происходит rollback и почему checked exceptions не всегда откатывают транзакцию
✔️Типовые ошибки при работе с транзакциями в сервисном слое

Урок будет полезен Java/Kotlin-разработчикам, которые уже пишут приложения на Spring или начинают использовать Spring в реальных backend-проектах и хотят лучше понимать поведение транзакций.

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

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🔍 Почему Optional — это не замена null везде и всегда

Привет! Сегодня хочу поделиться одной из часто встречающихся ошибок при использовании Optional в Java.

Многие разработчики, особенно начинающие, начинают использовать Optional везде, где может быть null, думая, что это автоматически делает код "безопасным". Но так ли это?

📌 Ключевая идея Optionalсигнализировать о возможном отсутствии значения в результате вызова метода.
А не заменять все поля и параметры на Optional.

Примеры плохой практики:


public class User {
private Optional<String> name; // Не нужно так делать
}


Почему это плохо:

- Увеличивается сложность сериализации (особенно с Jackson, GSON).
- Не соответствует архитектурной задумке: Optional — это не контейнер для полей.
- Проблемы с JPA (Hibernate не дружит с Optional-полями).
- Понижается читаемость кода.

💡 Лучше использовать Optional вот так:


public Optional<User> findUserById(Long id) {
// Возвращаем Optional, потому что пользователь может не существовать
}


То есть Optional — это про контракт на метод, а не про хранение данных.

Если кратко:
- Используй Optional в сигнатурах методов, когда результат может отсутствовать.
- Не используй Optional в полях и параметрах конструктора.

А ты как используешь Optional в проектах? Был ли опыт с его неправильным применением? Пиши в комментах👇

📲 Мы в MAX

👉@BookJava
👍6👎3
🧠 @Value в Spring — это ловушка, если вы используете списки или map'ы

Многие знают, что можно заинжектить список строк из application.yml вот так:


app:
langs:
- en
- fr
- de



@Value("${app.langs}")
private List<String> langs;


Но знаете, что вы получите?

⚠️ ОШИБКУ. @Value не умеет парсить YAML-массивы. Он ожидает строку, и даже с CSV-строкой (en,fr,de) — всё не так очевидно: Spring не применяет ConversionService для списков.

📌 Решение — использовать @ConfigurationProperties:


app:
langs:
- en
- fr
- de



@ConfigurationProperties(prefix = "app")
@Component
public class AppProps {
private List<String> langs;
// геттеры/сеттеры
}


💡 Профит:
- работает с List, Map, вложенными объектами;
- валидация через @Validated и @NotEmpty;
- легко покрыть тестами;
- меньше магии.

⚠️ @Value хорош для простых скаляров. Всё остальное — через @ConfigurationProperties.

📲 Мы в MAX

👉@BookJava
👍81🔥1
🧠 @Value vs @ConfigurationProperties — кого выбрать?

Часто вижу, как даже опытные разработчики по привычке используют @Value для инъекции конфигурации:


@Value("${app.timeout}")
private Duration timeout;


⚠️ Но с ростом приложения @Value становится хрупким и неудобным.

📌 Лучший подход — использовать @ConfigurationProperties:


@ConfigurationProperties(prefix = "app")
public record AppProperties(Duration timeout, String apiKey) {}



@Bean
@ConfigurationPropertiesBinding
public AppProperties appProperties() {
return new AppProperties();
}


Преимущества @ConfigurationProperties:
- 💡 Группирует настройки логически
- 🔍 Работает с валидацией (@Validated, @NotNull, и т.д.)
- 📚 Отлично поддерживается IDE (автокомплит, рефакторинг)
- 🔧 Удобно тестировать и мокать

🆕 Начиная с Spring Boot 2.2+, можно использовать record-классы и просто зарегистрировать бин через @EnableConfigurationProperties:


@Configuration
@EnableConfigurationProperties(AppProperties.class)
public class AppConfig {}


Так что, если у вас в проекте до сих пор десятки @Value — самое время навести порядок.

📲 Мы в MAX

👉@BookJava
👍1
👩‍💻 Стоит ли учить Java в 2026: куда движется язык и где работают джависты

Приглашаем на открытый урок.

🗓 16 июля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java-разработчик».

Программа урока:
✔️ Развенчиваем главный миф новичка: правда ли, что «Java умерла» и всем надо идти в Python или Go
✔️ Где Java реально работает в 2026: банки, финтех, маркетплейсы, стриминги с миллионами пользователей, логистика
✔️ Big Data на JVM: почему Kafka, Spark и Hadoop написаны на Java и зачем это знать даже питонисту
✔️ Куда движется язык: релизы каждые полгода, борьба с многословностью, нативная компиляция (GraalVM), связка с AI
✔️ Карьера джависта: зарплаты, стабильность и почему этот язык — фундамент для долгого пути в IT

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

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
🧠 Частая ловушка при работе с @Transactional в Spring

Сейчас покажу вам один распространённый анти-паттерн, который легко пропустить — вызов транзакционного метода внутри того же класса.

📌 Пример:


@Service
public class UserService {

@Transactional
public void registerUser(UserDto dto) {
saveUser(dto);
}

@Transactional
public void saveUser(UserDto dto) {
// сохранение пользователя
}
}


💥 Проблема:
Spring не применит транзакцию к saveUser(), потому что вызов происходит внутри одного и того же бина — минуя прокси.

Spring AOP работает через прокси, и @Transactional "срабатывает", только если метод вызывается извне, через прокси-объект.

⚠️ Это может привести к очень странным багам: вы думаете, что транзакция есть, а её нет.

💡 Как исправить:

1. Вынести метод в отдельный бин:


@Service
public class UserSaver {
@Transactional
public void save(UserDto dto) {
// сохраняем
}
}

@Service
public class UserService {
private final UserSaver saver;

public UserService(UserSaver saver) {
this.saver = saver;
}

public void registerUser(UserDto dto) {
saver.save(dto);
}
}


2. Или использовать TransactionTemplate вручную.

Всегда проверяйте, как вызываются методы с @Transactional. Особенно при рефакторинге!

📲 Мы в MAX

👉@BookJava
👍2👎2🔥1🤡1💊1
🧠 Как ускорить cold start Spring Boot приложения

Как можно сократить время старта Spring Boot 3+ приложения — без GraalVM и без магии.

📌 Используем флаг:

-Dspring.context.cache.applicationContext=true


💡 Что это такое?
Это встроенный механизм кэширования ApplicationContext, появившийся в Spring Boot 3.2.

Он сохраняет результат построения контекста и позволяет повторно использовать его между запусками, особенно в тестах и development-сценариях.

⚙️ Как работает:
- При первом запуске контекст билдится как обычно.
- Затем сериализуется и сохраняется на диск.
- При следующем запуске он подгружается из кеша (если не изменился), что даёт ускорение в 2-3 раза и больше.

🚀 Отлично подходит для:
- Тестов (@SpringBootTest);
- Dev tools и локального запуска;
- Разработки больших монолитов.

⚠️ Не влияет на продакшн (там кеш не используется по умолчанию)
⚠️ Не поддерживает все конфигурации (например, динамические настройки могут инвалидировать кеш)

Простой флаг — а экономит кучу времени каждый день.

📲 Мы в MAX

👉@BookJava
👍2🔥1
🧠 ThreadLocal — скрытая угроза утечек памяти

ThreadLocal — удобный способ хранить данные, привязанные к потоку. Например, для SimpleDateFormat или текущего пользователя в рамках запроса. Но с ним легко получить утечку памяти, особенно в thread pool'ах.

📌 Почему?
ThreadLocal-хранилище (Thread.threadLocals) живёт столько же, сколько поток. А потоки из пулов живут долго. Если ты забыл вызвать remove() — данные останутся в памяти навсегда.

Пример:


private static final ThreadLocal<UserContext> context = ThreadLocal.withInitial(UserContext::new);

public void handleRequest() {
try {
context.set(new UserContext("user123"));
// работа с контекстом
} finally {
context.remove(); // ОБЯЗАТЕЛЬНО!
}
}


⚠️ Что пойдёт не так без remove()?

- Поток из пула закончит обрабатывать запрос, но UserContext останется висеть в ThreadLocalMap этого потока.
- Если UserContext содержит ссылки на другие объекты (например, HttpSession, EntityManager и т.д.) — вся эта цепочка не будет GC-шиться.
- И так накапливается утечка.

💡 Советы:

- Всегда вызывай remove() в finally.
- Для Spring можно использовать RequestScope или @ControllerAdvice вместо ThreadLocal.
- Проверяй код сторонних библиотек, если они используют ThreadLocal, особенно в фильтрах и интерсепторах.

📲 Мы в MAX

👉@BookJava
👍2🔥1
👩‍💻 DAO на Spring JDBC

Приглашаем на открытый урок.

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

На занятии мы разберем:
✔️Преимущества нативного SQL при разработке DAO.
✔️ Основные возможности Spring JDBC для работы с запросами.
✔️ Подходы к обеспечению безопасности и тестируемости DAO.

Кому будет интересно:
Урок будет полезен Java-разработчикам, архитекторам ПО и специалистам по базам данных, стремящимся оптимизировать доступ к данным.

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

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
💡 Ленивая инициализация бинов в Spring Boot — мощный инструмент ускорения старта

По умолчанию Spring инициализирует все singleton-бины при запуске приложения. Это может быть проблемой в больших проектах: старт медленный, а половина бинов не нужна сразу.

📌 Как ускорить старт и снизить потребление памяти? — Lazy Init!

Глобально:


spring:
main:
lazy-initialization: true


Все бины станут ленивыми — создадутся только при первом обращении. Это может сократить старт приложения на 30-60%!

Локально (избирательно):


@Component
@Lazy
public class HeavyBean {
public HeavyBean() {
System.out.println("HeavyBean init...");
}
}


Или через @Lazy на зависимостях:


@Service
public class MyService {
public MyService(@Lazy HeavyBean heavyBean) {
this.heavyBean = heavyBean;
}
}


🧠 Когда использовать:

- В dev-окружении — чтобы ускорить локальный dev cycle.
- В CLI/Batch-приложениях, где используется 1-2 бина.
- Когда есть тяжёлые бины, не нужные на старте (например, интеграции, большие клиенты и т.п.).

⚠️ Осторожно:

- Если забыть @Lazy на зависимостях, Spring всё равно создаст бин.
- Некоторые бины должны быть загружены сразу (например, @Scheduled, @EventListener), иначе они не сработают.

📲 Мы в MAX

👉@BookJava
👍4
🧠 Как ускорить загрузку контекста Spring Boot — простой приём

Если у вас тяжёлый Spring Boot-приложение с кучей конфигураций и бинов, время старта может легко вырасти до 20–30 секунд и больше. Сегодня покажу приём, который помогает ускорить cold start за счёт отключения ненужных автоматических конфигураций.

📌 Spring Boot автоконфигурация — это палка о двух концах.
Она упрощает старт, но часто тянет за собой кучу лишнего. Особенно если вы используете @SpringBootApplication, которая включает в себя @EnableAutoConfiguration.

💡 Решение — использовать spring.autoconfigure.exclude, чтобы явно выключить ненужное. Например:


# application.yaml
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration


❗️Это особенно актуально:
- в микросервисах без UI (отключаем MVC)
- если datasource создаётся вручную
- в тестах и lightweight-режимах

👀 Как понять, что мешает?
1. Запусти приложение с флагом --debug или логгером org.springframework.boot.autoconfigure уровня DEBUG.
2. Посмотри, какие автоконфигурации "matched", но тебе не нужны.
3. Добавь их в exclude.

🧰 Альтернатива — использовать аннотацию @ImportAutoConfiguration с явным списком нужных автоконфигураций. Это тонкая настройка, идеально для библиотек и SDK.

⚠️ Не переусердствуй: отключение нужной конфигурации может привести к тихим багам. Лучше вырезать по одной и смотреть на эффект.

👉 Это один из способов сделать Spring Boot предсказуемым и быстрым, особенно в CI/CD или serverless-окружениях.

📲 Мы в MAX

👉@BookJava
👍3
👩‍💻 Основы многопоточности в Java

Приглашаем на открытый урок.

🗓 23 июля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса «Java-разработчик».

На уроке разберем:
✔️ Что такое поток выполнения и почему даже обычная Java-программа работает в главном потоке main.
✔️ Как создавать и запускать потоки через Thread и Runnable.
✔️ Зачем нужен join и как дождаться завершения запущенных потоков.
✔️ Почему многопоточность может ускорить программу, а может добавить лишние накладные расходы.
✔️ Какие типовые проблемы возникают в многопоточном коде: гонки, starvation и deadlock.

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

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🧠 Простое ускорение @Transactional методов в Spring Boot

Знаете, что @Transactional по умолчанию оборачивает метод в прокси?

Это значит:
- Внутренние вызовы в том же классе не проходят через транзакцию;
- Каждый такой прокси — это AOP-магия, которую можно обойти ради производительности.

📌 Если вы точно знаете, что метод будет вызываться только извне, и вам не нужна прокси-обёртка — используйте @Transactional на уровне интерфейса и включите interface-based proxy.


@Configuration
@EnableTransactionManagement(proxyTargetClass = false) // JDK proxy
public class TransactionConfig {
}



public interface UserService {
@Transactional
void createUser(User user);
}


📉 Это немного снижает overhead, особенно в высоконагруженных сервисах, где сотни тысяч вызовов @Transactional-методов.

💡 Подходит, если:
- У вас слоистая архитектура;
- Транзакции нужны только снаружи;
- Вы не используете вызовы this.someMethod() внутри сервиса.

⚠️ Не забывайте:
- JDK Proxy работает только с интерфейсами;
- Если вызываете методы внутри того же класса — прокси не сработает (и транзакция не начнётся).

📊 Профильте. Иногда замена proxyTargetClass = true на false даёт +3-5% к throughput.

📲 Мы в MAX

👉@BookJava
👍3
🇷🇺 Разбираешься в радиочипах, оптике и связи?
Забери до 1 000 000 рублей за свои инженерные навыки на турнире «Дронкон» 🇷🇺

«Сталинские Соколы» открывают регистрацию на 4-й Всероссийский турнир «Дронкон», который пройдет с 22 по 26 августа.

Турнир пройдет по направлению:
- Инженерное дело: навыки программирования, сборка электронного оборудования, беспроводная связь, оптические системы + стратегия «Битва Дронов»;

Призовой фонд для победителей:
🥇место – 1 000 000 рублей
🥈место – 700 000 рублей
🥉место – 500 000 рублей
Награда за 4-8 места - 100 000 рублей

Пройди заочный онлайн-этап и получи путевку на очный этап турнира в Республику Татарстан!
Перелет, питание, проживание - за счет организаторов.

🇷🇺 Подать заявку и узнать подробности 🇷🇺
🤡7👍2💩2🤮1
🧠 Сейчас покажу баг, который легко пропустить при работе с @Transactional в Spring Boot 3+.

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

Рассмотрим пример:


@Service
public class UserService {

@Transactional
public void createUser(User user) {
saveUser(user);
sendWelcomeEmail(user); // бросает исключение
}

public void saveUser(User user) {
userRepository.save(user);
}
}


💥 Если sendWelcomeEmail выбросит исключение — транзакция не откатится, потому что @Transactional работает через прокси. Вызов createUser() должен идти извне, чтобы Spring "знал", что нужно обернуть вызов в транзакцию.


Решения:

1. Вынести transactional-метод в отдельный бин:

@Service
public class UserCreationService {
@Transactional
public void createUser(User user) {
// ...
}
}


2. Внедрить self-прокси:

@Autowired
private UserService self;

public void externalCaller(User user) {
self.createUser(user);
}


3. Использовать AopContext:

((UserService) AopContext.currentProxy()).createUser(user);

⚠️ Не забудь включить exposeProxy = true в @EnableAspectJAutoProxy.


💡 Современный подход — разделение ответственности: transactional-методы живут в отдельных сервисах, их проще тестировать и не возникает подобных ловушек.

📲 Мы в MAX

👉@BookJava
👎4👍311
🧠 Lazy Initialization по-взрослому: не создавай проблемы на ровном месте

В Spring Boot часто можно встретить вот такую конструкцию:


@Service
public class EmailService {
private final SmtpClient client = new SmtpClient(); // дорогая инициализация
}


⚠️ Проблема: SmtpClient создаётся сразу при старте приложения. Даже если EmailService ни разу не вызовется. Это не только waste of resources, но и может сломать запуск, если SmtpClient требует специфического окружения.

📌 Решение — ленивая инициализация, но не через старый добрый null-check, а красиво, безопасно и читаемо:

💡 Способ #1: Lazy<T> wrapper


@Component
public class EmailService {
private final Supplier<SmtpClient> client = Suppliers.memoize(SmtpClient::new);

public void sendEmail(...) {
client.get().send(...);
}
}


Можно и без Guava:


public class Lazy<T> {
private Supplier<T> supplier;

public Lazy(Supplier<T> supplier) {
this.supplier = () -> {
T value = supplier.get();
this.supplier = () -> value;
return value;
};
}

public T get() {
return supplier.get();
}
}


💡 Способ #2: через Spring


@Service
public class EmailService {
private final ObjectProvider<SmtpClient> client;

public EmailService(ObjectProvider<SmtpClient> client) {
this.client = client;
}

public void sendEmail(...) {
client.getObject().send(...);
}
}



🧵 Итог:
- Не инициализируй тяжёлые объекты зря.
- Используй Supplier, ObjectProvider или Lazy<T>.
- Это особенно критично для тестов, лямбд и кэширования.

📲 Мы в MAX

👉@BookJava
👍2👎1