Java Ready | Программирование
8.69K subscribers
1.29K photos
70 videos
1 file
668 links
Авторский канал по разработке на Java.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Автор: @energy_it

Реклама на бирже: https://telega.in/c/java_ready
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
💅 Awesome Java — большая база материалов для Java-разработчика!

В репозитории собраны лучшие библиотеки, фреймворки, инструменты, статьи и сервисы для разработки. Всё аккуратно разложено по категориям и собрано в одном месте, экономит огромное количество времени на поиске инструментов и материалов. Полезно тем, кто работает с backend на Java.

Оставляю ссылочку: GitHub 📱


👉 Java Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
👍143🔥2
Знаете, что для денег в Java лучше не использовать double?

На первый взгляд кажется, что цена это обычное число:
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);


Так код становится предсказуемым, а расчёты не ломаются из-за погрешностей.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥53
Иерархия Java Collections Framework!

На картинке собрана структура коллекций Java: интерфейсы, абстрактные классы и основные реализации.

Например, List, Set, Queue и Deque идут от Collection, а Map стоит отдельно, потому что хранит данные в формате ключ-значение.

По схеме удобно увидеть, чем связаны ArrayList, LinkedList, HashSet, TreeSet, HashMap, LinkedHashMap, PriorityQueue и другие классы.

Такая картинка помогает быстрее понять, что выбирать под задачу: список, множество, очередь или map.

Сохрани, чтобы не путаться в коллекциях Java!

👉 Java Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍136🔥4
Проблема примитивной одержимости в Java!

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, банковских системах и любых проектах, где ошибка в одном поле может стоить дорого.

👉 Java Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
11🔥4👍3
This media is not supported in your browser
VIEW IN TELEGRAM
✍️ Gizmo AI — нейросеть для обучения и запоминания информации!

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

📌 Оставляю ссылочку: gizmo.ai

👉 Java Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥103👍2
Знаете, что в Java можно сделать неизменяемую копию коллекции?

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

Например:
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 изменяемый.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍7🔥4
📂 Напоминалка по HTTP/2 и HTTP/3!

Например, HTTP/2 работает поверх TCP и мультиплексирует запросы через одно соединение, а HTTP/3 использует QUIC поверх UDP и избавляется от Head-of-Line Blocking между потоками.

На картинке — простое сравнение HTTP/2 и HTTP/3: как работают streams, почему TCP может тормозить все запросы сразу.

Сохрани, чтобы не потерять!

👉 Java Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥95👍4
Создание CSV-файла с данными!

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.

👉 Java Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍2🔥2
😎 Хочешь понять, как Java-сервис поведёт себя под нагрузкой ещё до релиза? Тогда эта статья тебе точно пригодится!

В ней ты узнаешь:
• Как использовать Virtual Threads для запуска большого количества задач
• Как локально имитировать нагрузку на сервис
• Как смотреть, где приложение упирается в память, CPU или базу данных
• Как собирать простые метрики во время теста


Продолжай читать на Хабре!


👉 Java Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
6🔥4👍3
📂 Шпаргалка по аннотациям Spring Boot!

Например, @RestController используется для создания REST API, @Autowired — для внедрения зависимостей, а @Transactional помогает управлять транзакциями.

На картинке — основные аннотации Spring Boot, Spring MVC, JPA, Security, Validation и тестирования, которые чаще всего используются в реальных проектах.

Сохрани, чтобы не потерять!

👉 Java Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
20👍11🔥6
Знаете, что в Java можно безопасно создать файл и не перезаписать старый?

Обычно при записи легко случайно затереть существующий файл:
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);
}


Так поведение становится предсказуемым: код либо создаёт новый файл, либо честно сообщает, что такой файл уже есть.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍5🔥2
📂 Иерархия исключений в Java!

На картинке собрана структура Java exceptions: от базового Throwable до Exception, RuntimeException, IOException, Error и популярных наследников.

Например, NullPointerException, IllegalArgumentException, IndexOutOfBoundsException и ClassCastException относятся к RuntimeException.

Это unchecked-исключения: Java не заставляет явно ловить их через try-catch или прописывать в throws.

Сохрани, чтобы не путаться в checked, unchecked и Error!

👉 Java Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍4🔥4
Проблема неправильного использования Optional в Java!

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, а валидировать данные раньше.

👉 Java Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
9👍5🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
🤔 RavesliJava — обучение и основы Java для начинающих!

На сайте собран курс, который помогает освоить язык с нуля. Здесь разбираются основы синтаксиса, переменные, циклы, методы, массивы, ООП, классы и другие темы, необходимые для старта в Java-разработке. Материал подаётся последовательно и сопровождается примерами кода, благодаря чему обучение проходит проще и понятнее.

📌 Оставляю ссылочку: ravesli.com

👉 Java Ready | #сайт
Please open Telegram to view this post
VIEW IN TELEGRAM
👍84🔥2
Проблема “анемичных” запросов без валидации в Java!

В 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.

👉 Java Ready | #практика
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 Learning Roadmap — структурированное изучение Java!

Этот репозиторий хорошо подойдёт тем, кто хочет изучать Java без поиска информации. Здесь собрано много полезного материала для обучения: книги, roadmap’ы, практика, полезные репозитории, YouTube-каналы и материалы для подготовки к собеседованиям. Всё удобно разложено по разделам.

Оставляю ссылочку: GitHub 📱


👉 Java Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
10🔥5👍2
Знаете, что в Java можно запускать много блокирующих задач через virtual threads?

Раньше для параллельной обработки часто создавали пул обычных потоков:
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 могут сильно упростить архитектуру.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍4🔥3
📂 Напоминалка по Hibernate и JPA!

Например, @OneToMany описывает связь между сущностями, а LAZY помогает не загружать лишние данные заранее.

На картинке — основные аннотации, стратегии выборки, кэширование и частые проблемы, которые важно помнить при работе с Hibernate и JPA.

Сохрани, чтобы не потерять!

👉 Java Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍112🔥2
Знаете, что в Java лучше работать с файлами через Path, а не собирать пути строками?

Иногда путь к файлу пишут так:
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");


Так код становится чище и меньше зависит от слэшей, ручной склейки строк и особенностей операционной системы.

👉 Java Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍94🔥3
📂 Жизненный цикл потока в Java!

На картинке показаны основные состояния Thread: New, Runnable, Running, Blocked, Waiting, Time Waiting и Terminated.

Например, когда поток только создан через new Thread(...), он находится в состоянии New.

После вызова start() поток переходит в активную зону и может быть выбран планировщиком для выполнения.

Если поток ждёт освобождения lock, он попадает в Blocked.

Если поток ждёт сигнал от другого потока, например через wait() или join(), он переходит в Waiting.

А если ожидание ограничено по времени, например через sleep() или wait(timeout), это уже Timed Waiting.

Сохрани, чтобы быстрее понимать многопоточность в Java!

👉 Java Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍2🔥2