Например,
@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
Например,
sealed class позволяет жёстко контролировать наследование, а permits явно указывает, какие классы могут расширять базовый тип.На картинке — краткая шпаргалка по sealed, non-sealed и final классам, а также правилам построения закрытой иерархии в Java 17.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥5👍3
Знаете, что в Java можно писать отрицательные проверки в Stream API без ! внутри lambda?
Часто код выглядит так:
Работает, но когда условий становится больше, такие отрицания начинают хуже читаться.
В Java есть Predicate.not():
Так сразу видно: оставить все строки, которые не blank.
Например:
Это удобно для фильтрации имён, email, тегов, параметров запроса, строк из файлов и любых пользовательских данных.
Особенно хорошо читается вместе с method reference:
Важно только не превращать это в головоломку: если условие сложное, лучше вынести его в отдельный метод с понятным названием.
Так код в stream-цепочках становится аккуратнее и легче читается.
👉 Java Ready | #совет
Часто код выглядит так:
.filter(name -> !name.isBlank())
Работает, но когда условий становится больше, такие отрицания начинают хуже читаться.
В Java есть Predicate.not():
.filter(Predicate.not(String::isBlank))
Так сразу видно: оставить все строки, которые не blank.
Например:
List<String> names = users.stream()
.map(User::name)
.filter(Objects::nonNull)
.filter(Predicate.not(String::isBlank))
.toList();
Это удобно для фильтрации имён, email, тегов, параметров запроса, строк из файлов и любых пользовательских данных.
Особенно хорошо читается вместе с method reference:
Predicate.not(String::isEmpty)
Predicate.not(Collection::isEmpty)
Predicate.not(Optional::isEmpty)
Важно только не превращать это в головоломку: если условие сложное, лучше вынести его в отдельный метод с понятным названием.
Так код в stream-цепочках становится аккуратнее и легче читается.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥4👍3
This media is not supported in your browser
VIEW IN TELEGRAM
Этот репозиторий представляет собой большой структурированный ресурс по Java, в котором собраны основные темы языка простым и понятным языком. Вместо просмотра десятков статей можно открыть один репозиторий и последовательно изучать материал.
Оставляю ссылочку: GitHub📱
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤4👍2
Фильтруем список задач через Stream API в Java!
Допустим, у нас есть список задач, и нужно получить только те, которые ещё не выполнены.
Создадим простой record:
Теперь подготовим список:
Можно пройтись циклом и вручную собрать новый список:
Но для такой задачи хорошо подходит Stream API:
filter оставляет только элементы, которые проходят условие.
В нашем случае это задачи, у которых done() возвращает false.
Выведем результат:
Получим только активные задачи:
Такой подход удобно использовать не только для задач, но и для заказов, пользователей, товаров, уведомлений или любых списков объектов.
Например, можно выбрать только оплаченные заказы:
filter это базовый, но очень полезный инструмент Stream API для отбора данных по условию.
👉 Java Ready | #практика
Допустим, у нас есть список задач, и нужно получить только те, которые ещё не выполнены.
Создадим простой record:
record Task(String title, boolean done) {}Теперь подготовим список:
List<Task> tasks = List.of(
new Task("Read docs", false),
new Task("Write tests", true),
new Task("Fix bug", false)
);
Можно пройтись циклом и вручную собрать новый список:
List<Task> active = new ArrayList<>();
for (Task task : tasks) {
if (!task.done()) {
active.add(task);
}
}
Но для такой задачи хорошо подходит Stream API:
List<Task> active = tasks.stream()
.filter(task -> !task.done())
.toList();
filter оставляет только элементы, которые проходят условие.
В нашем случае это задачи, у которых done() возвращает false.
Выведем результат:
active.forEach(System.out::println);
Получим только активные задачи:
Task[title=Read docs, done=false]
Task[title=Fix bug, done=false]
Такой подход удобно использовать не только для задач, но и для заказов, пользователей, товаров, уведомлений или любых списков объектов.
Например, можно выбрать только оплаченные заказы:
List<Order> paid = orders.stream()
.filter(Order::isPaid)
.toList();
filter это базовый, но очень полезный инструмент Stream API для отбора данных по условию.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍3🔥3👎1
Шпаргалка по Date and Time API в Java!
На картинке собраны основные вещи по java.time: LocalDate, LocalTime, LocalDateTime, Period, Duration и DateTimeFormatter.
Например, LocalDate используют для даты без времени, LocalTime — для времени без даты, а LocalDateTime — когда нужно хранить и дату, и время вместе.
Ещё полезно помнить, что классы из java.time неизменяемые. Если вызвать plusDays() или minusMonths(), старый объект не изменится, Java вернёт новый.
Это удобно для API, логов, расписаний, бронирований, отчётов и любых задач, где нужно аккуратно работать с датами.
Сохрани, чтобы не путаться между Date, Calendar и нормальным java.time!
👉 Java Ready | #ресурс
На картинке собраны основные вещи по java.time: LocalDate, LocalTime, LocalDateTime, Period, Duration и DateTimeFormatter.
Например, LocalDate используют для даты без времени, LocalTime — для времени без даты, а LocalDateTime — когда нужно хранить и дату, и время вместе.
Ещё полезно помнить, что классы из java.time неизменяемые. Если вызвать plusDays() или minusMonths(), старый объект не изменится, Java вернёт новый.
Это удобно для API, логов, расписаний, бронирований, отчётов и любых задач, где нужно аккуратно работать с датами.
Сохрани, чтобы не путаться между Date, Calendar и нормальным java.time!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥5👍4
Знаете, что в Java можно удобно форматировать многострочный текст через text blocks?
Если нужно собрать письмо, SQL-запрос, JSON или шаблон сообщения, обычные строки быстро становятся шумными:
В современной Java можно использовать text blocks:
Так текст выглядит почти так же, как итоговый результат.
Это удобно для SQL:
И для сообщений пользователю:
Главный плюс меньше \n, кавычек, конкатенации и случайных ошибок в форматировании.
Важно только не использовать text blocks как замену нормальной сериализации JSON или безопасных SQL-параметров.
Для данных из пользователя всё равно нужны ObjectMapper, prepared statements и экранирование там, где это требуется.
👉 Java Ready | #совет
Если нужно собрать письмо, SQL-запрос, JSON или шаблон сообщения, обычные строки быстро становятся шумными:
String json = "{\n" +
" \"name\": \"" + name + "\",\n" +
" \"active\": true\n" +
"}";В современной Java можно использовать text blocks:
String json = """
{
"name": "%s",
"active": true
}
""".formatted(name);
Так текст выглядит почти так же, как итоговый результат.
Это удобно для SQL:
String sql = """
SELECT id, email, created_at
FROM users
WHERE active = true
ORDER BY created_at DESC
""";
И для сообщений пользователю:
String message = """
Hello, %s!
Your order #%d is ready.
""".formatted(name, orderId);
Главный плюс меньше \n, кавычек, конкатенации и случайных ошибок в форматировании.
Важно только не использовать text blocks как замену нормальной сериализации JSON или безопасных SQL-параметров.
Для данных из пользователя всё равно нужны ObjectMapper, prepared statements и экранирование там, где это требуется.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3🔥3
В этом посте покажу, как с помощью
Files.walk и Stream API искать файлы по маске, обходить большие директории и даже считать общий размер — на чистой Java, без лишних зависимостей.Сегодня рассмотрим:
• Как рекурсивно искать файлы и папки.
• Как фильтровать по расширению или маске.
• И посчитаем общий размер всех файлов.
Этот способ в разы лучше и производительней чем старый
File.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2🔥2🤝2