Java
Самая актуальная информация по Java По всем вопросам- @haarrp @itchannels_telegram - 🔥лучшие каналы @pythonl - 🐍 @ai_machinelearning_big_data- ml @ArtificialIntelligencedl - AI @datascienceiot - ds @pythonlbooks 📚 РКН: clck.ru/3FmwKr #VRHSZ
Show more📈 Analytical overview of Telegram channel Java
Channel Java (@javatg) in the Russian language segment is an active participant. Currently, the community unites 16 773 subscribers, ranking 7 782 in the Technologies & Applications category and 39 820 in the Russia region.
📊 Audience metrics and dynamics
Since its creation on невідомо, the project has demonstrated rapid growth, gathering an audience of 16 773 subscribers.
According to the latest data from 08 July, 2026, the channel demonstrates stable activity. Although there has been a change in the number of participants by -86 over the last 30 days and by -2 over the last 24 hours, overall reach remains high.
- Verification status: Not verified
- Engagement rate (ER): The average audience engagement rate is 12.19%. Within the first 24 hours after publication, content typically collects 6.09% reactions from the total number of subscribers.
- Post reach: On average, each post receives 2 044 views. Within the first day, a publication typically gains 1 022 views.
- Reactions and interaction: The audience actively supports content: the average number of reactions per post is 11.
- Thematic interests: Content is focused on key topics such as github, void, api, kotlin, static.
📝 Description and content policy
The author describes the resource as a platform for expressing subjective opinions:
“Самая актуальная информация по Java
По всем вопросам- @haarrp
@itchannels_telegram - 🔥лучшие каналы
@pythonl - 🐍
@ai_machinelearning_big_data- ml
@ArtificialIntelligencedl - AI
@datascienceiot - ds
@pythonlbooks 📚
РКН: clck.ru/3FmwKr
#VR...”
Thanks to the high frequency of updates (latest data received on 09 July, 2026), the channel maintains relevance and a high level of publication reach. Analytics show that the audience actively interacts with content, making it an important point of influence in the Technologies & Applications category.
switch expression убирает старую боль со случайно забытым break.
В старом switch легко получить тихий баг: пропустил break, и выполнение провалилось в следующий case.
В новом варианте каждый case возвращает значение через ->, а break не нужен:
String label = switch (status) {
case NEW -> "queued";
case RUNNING -> "in progress";
case DONE -> "finished";
};
Плюс компилятор следит за полнотой. Добавили новый enum-статус, но не обработали его в switch? Получите compile error, а не странный баг в рантайме.
Мелочь, которая делает код короче и безопаснее.
@Entity
public class Account {
@Id
private Long id;
private BigDecimal balance;
@Version
private Long version;
}
Как это работает:
Когда Entity читается из базы, Hibernate забирает текущую версию записи. Например, version = 2.
При обновлении он делает update с проверкой этой версии. Если за это время другая транзакция уже изменила запись, версия в базе стала другой.
Тогда update не затронет ни одной строки, и Hibernate выбросит OptimisticLockException.
Подходит, когда чтений много, записей меньше, а конфликты случаются редко.
Плохо подходит, если несколько потоков постоянно меняют одну и ту же запись или нужна строгая сериализация операций. Там уже лучше смотреть в сторону pessimistic locking, очередей или переработки модели записи.
@Version особенно полезен для аккаунтов, заказов, настроек, профилей и любых сущностей, где нельзя молча перетереть чужое изменение.
User user = userRepository.findByEmail(email);
if (user != null) {
Address address = user.getAddress();
if (address != null) {
return address.getCity();
}
}
return "unknown";
Проблема не только в количестве строк.
В таких вложенных if легко забыть один уровень проверки и получить NPE в самом неожиданном месте.
С Optional это можно записать короче и безопаснее:
return userRepository.findByEmail(email)
.map(User::getAddress)
.map(Address::getCity)
.orElse("unknown");
Каждый map() выполняется только если предыдущее значение существует.
Если пользователя нет, адреса нет или город не задан - цепочка спокойно дойдёт до orElse().
Для таких случаев Optional хорошо работает как способ явно показать:
значение может отсутствовать, и это нормальная часть логики.
Главное не превращать Optional в новую религию.
Он особенно полезен на границах методов и в цепочках, где каждый следующий шаг может вернуть null.HashSet, а EnumSet.
EnumSet - это специальная реализация Set, заточенная именно под enum-значения.
Фишка в том, что внутри он хранит элементы не как обычные объекты в hash-таблице, а как битовую маску.
Каждое значение enum получает свой бит:
enum Permission {
READ,
WRITE,
DELETE
}
Если в наборе есть READ и WRITE, внутри это может быть просто несколько включённых битов.
Из-за этого операции вроде contains, add, remove, union, intersection работают очень быстро и занимают меньше памяти.
Пример:
EnumSet<Permission> admin = EnumSet.of(
Permission.READ,
Permission.WRITE,
Permission.DELETE
);
boolean canWrite = admin.contains(Permission.WRITE);
Для прав доступа, статусов, флагов, режимов и feature toggles это почти идеальный вариант.
HashSet<Enum> тоже будет работать, но чаще это лишняя тяжесть.
Если набор состоит только из enum-значений - сначала подумайте про EnumSet.java.time, а не старые Date и Calendar.
Почему?
Calendar легко использовать неправильно:
cal.set(2026, 5, 31);
Выглядит как май, но это июнь, потому что месяцы там считаются с нуля.
Ещё проблема: Date и Calendar изменяемые, а SimpleDateFormat не thread-safe.
Нормальный вариант:
LocalDate today = LocalDate.of(2026, 5, 31);
LocalDate deadline = today.plusDays(7);
Тут всё читается буквально: 5 — это май, дата immutable, форматирование безопаснее и понятнее.
Для времени с часовым поясом:
ZonedDateTime rome =
meeting.atZone(ZoneId.of("Europe/Rome"));
Главная мысль: java.time убирает кучу старых ловушек Java, которые раньше спокойно доезжали до продакшена.
List list = new ArrayList();
list.add("hello");
Integer n = (Integer) list.get(0);
Код компилируется, но потом может прилететь ClassCastException.
Правильнее так:
List<String> safe = new ArrayList<>();
safe.add("hello");
String s = safe.get(0);
Включить жёсткий режим:
javac -Xlint:all -Werror *.java
Для Maven:
<compilerArgs>
<arg>-Xlint:all</arg>
<arg>-Werror</arg>
</compilerArgs>
Главная мысль: пусть build падает сейчас, а не продакшен потом.java.time.
Старый подход легко ломается:
Calendar cal = Calendar.getInstance();
cal.set(2026, 5, 31);
Кажется, что это май?
Нет. В Calendar месяцы считаются с нуля, поэтому 5 — это июнь.
Плюс Date и Calendar изменяемые, неудобные и часто приводят к багам.
Даже SimpleDateFormat не thread-safe, поэтому в многопоточном коде может внезапно устроить хаос.
Нормальный современный вариант:
LocalDate today = LocalDate.of(2026, 5, 31);
LocalDate deadline = today.plusDays(7);
String text = deadline.format(DateTimeFormatter.ISO_LOCAL_DATE);
Тут всё читаемо:
5 — это май.
plusDays(7) — понятно.
DateTimeFormatter — безопаснее.
Для дат используйте LocalDate.
Для даты и времени — LocalDateTime.
Для часовых поясов — ZonedDateTime.
java.time — один из тех апдейтов Java, который реально убирает целый класс старых багов.
import java.util.*;
public class Main {
static class User {
int id;
User(int id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof User)) return false;
return id == ((User) obj).id;
}
@Override
public int hashCode() {
return id;
}
}
public static void main(String[] args) {
Map<User, String> map = new HashMap<>();
User user = new User(1);
map.put(user, "admin");
System.out.print(map.containsKey(user) + " ");
user.id = 2;
System.out.print(map.containsKey(user) + " ");
System.out.print(map.get(user) + " ");
System.out.print(map.size());
}
}
А — true true admin 1
Б — true false null 1
В — true false admin 1
Г — Error
Правильный ответ: Б — true false null 1
HashMap кладёт объект в корзину по hashCode.
Сначала user.id = 1, значит ключ лежит в корзине для хэша 1.
Потом мы меняем id на 2, и hashCode() тоже меняется.
Теперь HashMap ищет этот же объект уже в другой корзине и не находит его.
Объект всё ещё внутри map, поэтому size() остаётся 1.
Вывод: ключи в HashMap должны быть неизменяемыми. Иначе можно «потерять» объект внутри коллекции.instanceof, а потом вручную выбирать поведение.
Плохо:
if (animal instanceof Dog) return "Woof";
if (animal instanceof Cat) return "Meow";
if (animal instanceof Cow) return "Moo";
Такой код быстро разрастается. Добавили новое животное, снова идём менять общий метод.
Лучше дать каждому типу самому определить поведение:
interface Animal {
String sound();
}
class Dog implements Animal {
public String sound() { return "Woof"; }
}
class Cat implements Animal {
public String sound() { return "Meow"; }
}
А дальше код становится простым:
String describe(Animal animal) {
return animal.sound();
}
Смысл полиморфизма как раз в этом: не спрашивать объект “кто ты?”, а просто вызвать нужное действие.
Меньше if, меньше кастов, меньше хрупкой логики.BufferedReader, InputStream, OutputStream, FileReader, соединениями или другими ресурсами, которые нужно закрывать, используйте try-with-resources.
Плохо:
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
На первый взгляд всё нормально.
Но если внутри чтения файла вылетит исключение, reader.close() может не выполниться. В итоге останутся открытые file handles, stream’ы или соединения.
Лучше так:
try (BufferedReader reader =
new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
try-with-resources автоматически вызовет close() даже при исключении.
Почему это важно:
* не нужен ручной finally
* меньше boilerplate-кода
* ниже риск утечек ресурсов
* код проще читать
* безопаснее работать с файлами, сетью и БД
Правило простое:
если объект реализует AutoCloseable или Closeable, почти всегда стоит использовать try-with-resources.
Это одна из тех привычек, которые делают Java-код чище и надёжнее.
String sql = "SELECT * FROM users WHERE email = '" + email + "'";
Files.readString(Path.of("/data/" + filename));
int age = Integer.parseInt(request.getParameter("age"));
На вид обычный код, но в нём сразу несколько проблем:
• SQL-инъекции;
• path traversal через ../;
• некорректные типы и диапазоны;
• мусорные данные, которые ломают логику дальше по системе.
Правильный подход - валидировать данные на границе приложения и использовать безопасные API.
if (!EMAIL.matcher(email).matches()) {
throw new BadRequestException("invalid email");
}
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE email = ?"
);
ps.setString(1, email);
Для файлов - нормализовать путь и проверять, что пользователь не вышел за разрешённую директорию:
Path base = Path.of("/data").toAbsolutePath().normalize();
Path file = base.resolve(filename).normalize();
if (!file.startsWith(base)) {
throw new SecurityException("path escape blocked");
}
А для DTO лучше сразу описывать правила:
public record SignupRequest(
@NotBlank @Email String email,
@Min(18) @Max(120) int age
) {}
Чем раньше вы отсекаете плохие данные, тем меньше шансов, что они превратятся в уязвимость внутри системы.