This media is not supported in your browser
VIEW IN TELEGRAM
В репозитории собраны лучшие библиотеки, фреймворки, инструменты, статьи и сервисы для разработки. Всё аккуратно разложено по категориям и собрано в одном месте, экономит огромное количество времени на поиске инструментов и материалов. Полезно тем, кто работает с backend на Java.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤3🔥2
Знаете, что для денег в Java лучше не использовать double?
На первый взгляд кажется, что цена это обычное число:
Но результат может удивить:
Причина в том, что double хранит числа в бинарном формате, и многие десятичные дроби не представляются точно.
Для денег, налогов, скидок и балансов лучше использовать BigDecimal:
Важно создавать BigDecimal из строки, а не из double:
А вот так лучше не делать:
При делении обязательно указывай округление:
Так код становится предсказуемым, а расчёты не ломаются из-за погрешностей.
👉 Java Ready | #совет
На первый взгляд кажется, что цена это обычное число:
double price = 0.1;
double total = price + price + price;
System.out.println(total);
Но результат может удивить:
0.30000000000000004
Причина в том, что double хранит числа в бинарном формате, и многие десятичные дроби не представляются точно.
Для денег, налогов, скидок и балансов лучше использовать BigDecimal:
BigDecimal price = new BigDecimal("0.10");
BigDecimal total = price
.add(price)
.add(price);Важно создавать BigDecimal из строки, а не из double:
new BigDecimal("0.10")А вот так лучше не делать:
new BigDecimal(0.10)
При делении обязательно указывай округление:
amount.divide(count, 2, RoundingMode.HALF_UP);
Так код становится предсказуемым, а расчёты не ломаются из-за погрешностей.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥5❤3
Иерархия Java Collections Framework!
На картинке собрана структура коллекций Java: интерфейсы, абстрактные классы и основные реализации.
Например, List, Set, Queue и Deque идут от Collection, а Map стоит отдельно, потому что хранит данные в формате ключ-значение.
По схеме удобно увидеть, чем связаны ArrayList, LinkedList, HashSet, TreeSet, HashMap, LinkedHashMap, PriorityQueue и другие классы.
Такая картинка помогает быстрее понять, что выбирать под задачу: список, множество, очередь или map.
Сохрани, чтобы не путаться в коллекциях Java!
👉 Java Ready | #ресурс
На картинке собрана структура коллекций Java: интерфейсы, абстрактные классы и основные реализации.
Например, List, Set, Queue и Deque идут от Collection, а Map стоит отдельно, потому что хранит данные в формате ключ-значение.
По схеме удобно увидеть, чем связаны ArrayList, LinkedList, HashSet, TreeSet, HashMap, LinkedHashMap, PriorityQueue и другие классы.
Такая картинка помогает быстрее понять, что выбирать под задачу: список, множество, очередь или map.
Сохрани, чтобы не путаться в коллекциях Java!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤6🔥4
Проблема примитивной одержимости в Java!
Primitive Obsession - это ситуация, когда в коде слишком много важных бизнес-сущностей хранятся как обычные
Например:
На первый взгляд всё нормально. Но со временем такой код становится опасным.
Потому что BigDecimal сам по себе не говорит, что это именно деньги. String не говорит, что это валюта. А email можно случайно передать туда, где ожидался userId.
Пример ошибки:
Код компилируется, но логика уже сломана: email и currency перепутаны местами.
В реальных системах такое часто происходит с id, email, phone, currency, amount, status, role, countryCode и другими значимыми полями.
Корректный подход, выносить важные значения в отдельные value object:
Теперь метод становится понятнее:
Так сложнее случайно перепутать параметры, а валидация живёт рядом с самим значением.
Например, деньги тоже можно оформить отдельным типом:
Теперь отрицательная сумма не сможет незаметно пройти дальше по бизнес-логике.
Такой подход особенно полезен в доменной логике, платежах, заказах, авторизации, CRM, банковских системах и любых проектах, где ошибка в одном поле может стоить дорого.
👉 Java Ready | #практика
Primitive Obsession - это ситуация, когда в коде слишком много важных бизнес-сущностей хранятся как обычные
String, int, long или BigDecimal.Например:
public void pay(BigDecimal amount, String currency, String email) {
// код
}На первый взгляд всё нормально. Но со временем такой код становится опасным.
Потому что BigDecimal сам по себе не говорит, что это именно деньги. String не говорит, что это валюта. А email можно случайно передать туда, где ожидался userId.
Пример ошибки:
pay(
new BigDecimal("100.00"),
"user@mail.com",
"USD"
);
Код компилируется, но логика уже сломана: email и currency перепутаны местами.
В реальных системах такое часто происходит с id, email, phone, currency, amount, status, role, countryCode и другими значимыми полями.
Корректный подход, выносить важные значения в отдельные value object:
public record Email(String value) {
public Email {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("Email is blank");
}
}
}Теперь метод становится понятнее:
public void pay(Money amount, CurrencyCode currency, Email email) {
// код
}Так сложнее случайно перепутать параметры, а валидация живёт рядом с самим значением.
Например, деньги тоже можно оформить отдельным типом:
public record Money(BigDecimal amount) {
public Money {
if (amount == null || amount.signum() < 0) {
throw new IllegalArgumentException("Invalid amount");
}
}
}Теперь отрицательная сумма не сможет незаметно пройти дальше по бизнес-логике.
Такой подход особенно полезен в доменной логике, платежах, заказах, авторизации, CRM, банковских системах и любых проектах, где ошибка в одном поле может стоить дорого.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥4👍3
This media is not supported in your browser
VIEW IN TELEGRAM
AI-сервис, который помогает превращать заметки, PDF, статьи, видео и другие материалы в карточки для обучения и квизы. Нейросеть автоматически выделяет главное, генерирует вопросы и помогает быстрее запоминать информацию с помощью повторения и интерактивного формата обучения.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤3👍2
Знаете, что в Java можно сделать неизменяемую копию коллекции?
Иногда нужно передать список дальше по коду, но не хочется, чтобы его случайно изменили.
Например:
Если внутри process() кто-то вызовет clear() или add(), исходный список тоже изменится.
Для таких случаев удобно использовать copyOf():
Теперь safeIds нельзя изменить:
То же самое есть у Set и Map:
Это полезно для DTO, настроек, прав доступа, списков ID, результатов из базы и любых данных, которые после создания не должны меняться.
Важно: copyOf() делает коллекцию неизменяемой, но если внутри лежат mutable-объекты, сами эти объекты всё ещё можно менять.
Здесь нельзя добавить нового User в список, но можно изменить поля существующего объекта, если сам User изменяемый.
👉 Java Ready | #совет
Иногда нужно передать список дальше по коду, но не хочется, чтобы его случайно изменили.
Например:
List<Long> ids = new ArrayList<>();
ids.add(10L);
ids.add(20L);
process(ids);
Если внутри process() кто-то вызовет clear() или add(), исходный список тоже изменится.
Для таких случаев удобно использовать copyOf():
List<Long> safeIds = List.copyOf(ids);
Теперь safeIds нельзя изменить:
safeIds.add(30L); // UnsupportedOperationException
То же самое есть у Set и Map:
Set<Long> uniqueIds = Set.copyOf(ids);
Map<String, Integer> copy = Map.copyOf(stats);
Это полезно для DTO, настроек, прав доступа, списков ID, результатов из базы и любых данных, которые после создания не должны меняться.
Важно: copyOf() делает коллекцию неизменяемой, но если внутри лежат mutable-объекты, сами эти объекты всё ещё можно менять.
List<User> users = List.copyOf(originalUsers);
Здесь нельзя добавить нового User в список, но можно изменить поля существующего объекта, если сам User изменяемый.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍7🔥4
Например, HTTP/2 работает поверх TCP и мультиплексирует запросы через одно соединение, а HTTP/3 использует QUIC поверх UDP и избавляется от Head-of-Line Blocking между потоками.
На картинке — простое сравнение HTTP/2 и HTTP/3: как работают streams, почему TCP может тормозить все запросы сразу.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤5👍4
Создание CSV-файла с данными!
Готовим список строк, которые попадут в
Преобразуем строки в единый текст:
Сохраняем CSV-файл в проект:
🔥 В итоге получаем готовый файл
👉 Java Ready | #практика
CSV — самый простой формат хранения таблиц. Строки это записи, значения разделяются запятыми. Java позволяет создать такой файл без библиотек.Готовим список строк, которые попадут в
CSV:var rows = java.util.List.of(
"name,age,city",
"Alice,22,Paris",
"Bob,30,Berlin"
);
Преобразуем строки в единый текст:
var csv = String.join("\n", rows);Сохраняем CSV-файл в проект:
java.nio.file.Files.writeString(
java.nio.file.Path.of("users.csv"),
csv
);
🔥 В итоге получаем готовый файл
users.csv, который можно открыть в Excel, Numbers или Google Sheets.Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍2🔥2
В ней ты узнаешь:
• Как использовать Virtual Threads для запуска большого количества задач
• Как локально имитировать нагрузку на сервис
• Как смотреть, где приложение упирается в память, CPU или базу данных
• Как собирать простые метрики во время тестаПродолжай читать на Хабре!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥4👍3
Например,
@RestController используется для создания REST API, @Autowired — для внедрения зависимостей, а @Transactional помогает управлять транзакциями.На картинке — основные аннотации Spring Boot, Spring MVC, JPA, Security, Validation и тестирования, которые чаще всего используются в реальных проектах.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤20👍11🔥6
Знаете, что в Java можно безопасно создать файл и не перезаписать старый?
Обычно при записи легко случайно затереть существующий файл:
Если файл уже есть, его содержимое будет заменено.
Для логов, экспортов, отчётов, временных файлов и сохранения пользовательских данных это может быть опасно.
В таких случаях можно использовать
Теперь Java создаст файл только если его ещё нет.
Если файл уже существует, будет выброшено исключение:
Это удобно, когда нужно защититься от случайной перезаписи важных данных.
Например, можно явно обработать ситуацию:
Так поведение становится предсказуемым: код либо создаёт новый файл, либо честно сообщает, что такой файл уже есть.
👉 Java Ready | #совет
Обычно при записи легко случайно затереть существующий файл:
Files.writeString(path, json);
Если файл уже есть, его содержимое будет заменено.
Для логов, экспортов, отчётов, временных файлов и сохранения пользовательских данных это может быть опасно.
В таких случаях можно использовать
StandardOpenOption.CREATE_NEW:
Files.writeString(path, json, StandardOpenOption.CREATE_NEW);
Теперь Java создаст файл только если его ещё нет.
Если файл уже существует, будет выброшено исключение:
FileAlreadyExistsException
Это удобно, когда нужно защититься от случайной перезаписи важных данных.
Например, можно явно обработать ситуацию:
try {
Files.writeString(path, json, StandardOpenOption.CREATE_NEW);
} catch (FileAlreadyExistsException e) {
throw new IllegalStateException("Export already exists", e);
}Так поведение становится предсказуемым: код либо создаёт новый файл, либо честно сообщает, что такой файл уже есть.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍5🔥2
На картинке собрана структура Java exceptions: от базового Throwable до Exception, RuntimeException, IOException, Error и популярных наследников.
Например, NullPointerException, IllegalArgumentException, IndexOutOfBoundsException и ClassCastException относятся к RuntimeException.
Это unchecked-исключения: Java не заставляет явно ловить их через try-catch или прописывать в throws.
Сохрани, чтобы не путаться в checked, unchecked и Error!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍4🔥4
Проблема неправильного использования Optional в Java!
Optional часто используют как “защиту от null”, но если применять его везде подряд, код может стать сложнее, а не безопаснее.
Например, плохая практика - хранить Optional в поле класса:
На первый взгляд кажется удобно: email может быть, а может не быть.
Но Optional задумывался в первую очередь как возвращаемое значение метода, а не как тип поля.
Лучше хранить само значение:
А Optional возвращать из метода, где отсутствие результата является нормальным сценарием:
Ещё один частый антипример принимать Optional как параметр метода:
Такой код усложняет вызовы и заставляет вызывающий код оборачивать значение вручную.
Обычно понятнее сделать перегрузку метода или принять обычное nullable-значение с явной проверкой:
Также опасно вызывать get() без проверки:
Если пользователя нет, код упадёт с NoSuchElementException.
Безопаснее обработать отсутствие явно:
Или задать fallback:
Optional хорошо подходит там, где метод может ничего не найти:
Но если значение обязательно по бизнес-логике, лучше не прятать ошибку в Optional, а валидировать данные раньше.
👉 Java Ready | #практика
Optional часто используют как “защиту от null”, но если применять его везде подряд, код может стать сложнее, а не безопаснее.
Например, плохая практика - хранить Optional в поле класса:
public class User {
private Optional<String> email;
}
На первый взгляд кажется удобно: email может быть, а может не быть.
Но Optional задумывался в первую очередь как возвращаемое значение метода, а не как тип поля.
Лучше хранить само значение:
public class User {
private String email;
}
А Optional возвращать из метода, где отсутствие результата является нормальным сценарием:
public Optional<String> getEmail() {
return Optional.ofNullable(email);
}
Ещё один частый антипример принимать Optional как параметр метода:
public void updateEmail(Optional<String> email) {
}
Такой код усложняет вызовы и заставляет вызывающий код оборачивать значение вручную.
Обычно понятнее сделать перегрузку метода или принять обычное nullable-значение с явной проверкой:
public void updateEmail(String email) {
if (email == null || email.isBlank()) {
throw new IllegalArgumentException("Email is blank");
}
this.email = email;
}
Также опасно вызывать get() без проверки:
User user = findUser(id).get();
Если пользователя нет, код упадёт с NoSuchElementException.
Безопаснее обработать отсутствие явно:
User user = findUser(id)
.orElseThrow(() -> new UserNotFoundException(id));
Или задать fallback:
String name = user.getName()
.orElse("Guest");
Optional хорошо подходит там, где метод может ничего не найти:
public Optional<User> findUser(Long id) {
return userRepository.findById(id);
}
Но если значение обязательно по бизнес-логике, лучше не прятать ошибку в Optional, а валидировать данные раньше.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍5🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
На сайте собран курс, который помогает освоить язык с нуля. Здесь разбираются основы синтаксиса, переменные, циклы, методы, массивы, ООП, классы и другие темы, необходимые для старта в Java-разработке. Материал подаётся последовательно и сопровождается примерами кода, благодаря чему обучение проходит проще и понятнее.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4🔥2
Проблема “анемичных” запросов без валидации в Java!
В backend-коде часто создают DTO, которые просто переносят данные из API внутрь приложения.
Например:
На первый взгляд всё нормально: есть email, есть имя.
Но если такой объект проходит дальше без проверки, в бизнес-логику могут попасть пустые строки, null, мусорные email и другие некорректные значения.
Код компилируется, объект создаётся, а ошибка всплывает уже позже: в сервисе, базе данных или при отправке письма.
Почему это опасно в реальных системах:
Один из вариантов, это валидировать данные прямо на границе системы.
Например, через Bean Validation:
В Spring Boot такой объект можно проверить автоматически:
Теперь некорректный запрос не пройдёт дальше контроллера.
Но важно помнить: API-валидация не заменяет доменную модель.
Если email важная часть бизнес-логики, лучше вынести его в отдельный value object:
Тогда внутри приложения уже нельзя случайно создать пользователя с пустым email.
👉 Java Ready | #практика
В backend-коде часто создают DTO, которые просто переносят данные из API внутрь приложения.
Например:
public record CreateUserRequest(
String email,
String name
) {}
На первый взгляд всё нормально: есть email, есть имя.
Но если такой объект проходит дальше без проверки, в бизнес-логику могут попасть пустые строки, null, мусорные email и другие некорректные значения.
new CreateUserRequest("", " ");
Код компилируется, объект создаётся, а ошибка всплывает уже позже: в сервисе, базе данных или при отправке письма.
Почему это опасно в реальных системах:
• ошибки появляются далеко от места ввода данных
• сервисы начинают дублировать одни и те же проверки
• в базе могут оказаться некорректные значенияОдин из вариантов, это валидировать данные прямо на границе системы.
Например, через Bean Validation:
public record CreateUserRequest(
@Email String email,
@NotBlank String name
) {}
В Spring Boot такой объект можно проверить автоматически:
@PostMapping("/users")
public UserDto create(@Valid @RequestBody CreateUserRequest request) {
return userService.create(request);
}
Теперь некорректный запрос не пройдёт дальше контроллера.
Но важно помнить: API-валидация не заменяет доменную модель.
Если email важная часть бизнес-логики, лучше вынести его в отдельный value object:
public record Email(String value) {
public Email {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("Email is blank");
}
}
}
Тогда внутри приложения уже нельзя случайно создать пользователя с пустым email.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥4👍2
This media is not supported in your browser
VIEW IN TELEGRAM
Этот репозиторий хорошо подойдёт тем, кто хочет изучать Java без поиска информации. Здесь собрано много полезного материала для обучения: книги, roadmap’ы, практика, полезные репозитории, YouTube-каналы и материалы для подготовки к собеседованиям. Всё удобно разложено по разделам.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥5👍2
Знаете, что в Java можно запускать много блокирующих задач через virtual threads?
Раньше для параллельной обработки часто создавали пул обычных потоков:
Но если задачи в основном ждут сеть, базу данных, файлы или внешние API, обычные потоки могут быстро стать дорогими.
В Java 21 появились virtual threads:
Теперь можно запускать отдельный лёгкий поток под каждую задачу:
Это особенно удобно для I/O-heavy сценариев: HTTP-запросов, JDBC, чтения файлов, отправки уведомлений и интеграций с внешними сервисами.
Важно: virtual threads не делают CPU-heavy код быстрее сами по себе.
Если задача активно считает, шифрует, парсит огромные данные или грузит процессор, лучше думать про обычный пул по числу ядер.
А вот для большого количества блокирующих операций virtual threads могут сильно упростить архитектуру.
👉 Java Ready | #совет
Раньше для параллельной обработки часто создавали пул обычных потоков:
ExecutorService executor = Executors.newFixedThreadPool(10);
Но если задачи в основном ждут сеть, базу данных, файлы или внешние API, обычные потоки могут быстро стать дорогими.
В Java 21 появились virtual threads:
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
Теперь можно запускать отдельный лёгкий поток под каждую задачу:
executor.submit(() -> sendEmail(user));
executor.submit(() -> generateReport(user));
executor.submit(() -> syncAnalytics(user));
Это особенно удобно для I/O-heavy сценариев: HTTP-запросов, JDBC, чтения файлов, отправки уведомлений и интеграций с внешними сервисами.
Важно: virtual threads не делают CPU-heavy код быстрее сами по себе.
Если задача активно считает, шифрует, парсит огромные данные или грузит процессор, лучше думать про обычный пул по числу ядер.
А вот для большого количества блокирующих операций virtual threads могут сильно упростить архитектуру.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥3
Например,
@OneToMany описывает связь между сущностями, а LAZY помогает не загружать лишние данные заранее.На картинке — основные аннотации, стратегии выборки, кэширование и частые проблемы, которые важно помнить при работе с Hibernate и JPA.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤2🔥2
Знаете, что в Java лучше работать с файлами через Path, а не собирать пути строками?
Иногда путь к файлу пишут так:
На первый взгляд нормально, но такой код начинает ломаться, когда проект запускается на другой ОС или путь собирается из нескольких частей.
В Java для этого есть Path:
Java сама соберёт корректный путь для текущей системы.
С ним удобно работать через Files:
Или проверить, существует ли файл:
Можно безопасно получить вложенный путь:
Так код становится чище и меньше зависит от слэшей, ручной склейки строк и особенностей операционной системы.
👉 Java Ready | #совет
Иногда путь к файлу пишут так:
String path = "data/" + fileName;
На первый взгляд нормально, но такой код начинает ломаться, когда проект запускается на другой ОС или путь собирается из нескольких частей.
В Java для этого есть Path:
Path path = Path.of("data", "users.json");Java сама соберёт корректный путь для текущей системы.
С ним удобно работать через Files:
String json = Files.readString(path);
Или проверить, существует ли файл:
if (Files.exists(path)) {
// file exists
}Можно безопасно получить вложенный путь:
Path avatar = Path.of("uploads")
.resolve(userId + ".png");Так код становится чище и меньше зависит от слэшей, ручной склейки строк и особенностей операционной системы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤4🔥3
На картинке показаны основные состояния Thread: New, Runnable, Running, Blocked, Waiting, Time Waiting и Terminated.
Например, когда поток только создан через new Thread(...), он находится в состоянии New.
После вызова start() поток переходит в активную зону и может быть выбран планировщиком для выполнения.
Если поток ждёт освобождения lock, он попадает в Blocked.
Если поток ждёт сигнал от другого потока, например через wait() или join(), он переходит в Waiting.
А если ожидание ограничено по времени, например через sleep() или wait(timeout), это уже Timed Waiting.
Сохрани, чтобы быстрее понимать многопоточность в Java!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍2🔥2