Общий совет: выбирать поля, которые с большой долью вероятности будут различаться. Для этого необходимо использовать уникальные, лучше всего примитивные поля, например, такие как id, uuid. При этом нужно следовать правилу, если поля задействованы при вычислении
hashCode(), то они должны быть задействованы и при выполнении equals().#java #hashCode #equals
Please open Telegram to view this post
VIEW IN TELEGRAM
👨💻6❤2
В Java лямбда-выражения используются с функциональными интерфейсами, которые имеют ровно один абстрактный метод. Например, стандартный функциональный интерфейс
Function из пакета java.util.function представляет собой типичный пример.Для нашего примера используем встроенный функциональный интерфейс
IntUnaryOperator. Он выглядит вот так:
@FunctionalInterface
public interface IntUnaryOperator {
int applyAsInt(int operand);
}
Этот интерфейс представляет функцию, которая принимает один аргумент типа
int и возвращает значение типа int.
import java.util.function.IntUnaryOperator;
public class Main {
public static int applyOperation(int number, IntUnaryOperator operator) {
return operator.applyAsInt(number);
}
public static void main(String[] args) {
int number = 5;
// Лямбда-выражение для удвоения значения
IntUnaryOperator doubleOperation = x -> x * 2;
System.out.println("Double: " + applyOperation(number, doubleOperation)); // Double: 10
// Лямбда-выражение для увеличения значения на 10
IntUnaryOperator addTenOperation = x -> x + 10;
System.out.println("Add Ten: " + applyOperation(number, addTenOperation)); // Add Ten: 15
}
}
В нашем примере метод
applyOperation принимает два параметра: число и функциональный интерфейс IntUnaryOperator. Лямбда-выражение используется для определения конкретной функции.#java #lambda #IntUnaryOperator
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2
Этот метод предлагает удобный способ вычисления и вставки значений в
Map, если они отсутствуют.Пример кода с объяснением:
import java.util.HashMap;
import java.util.Map;
public class MapExample {
public static void main(String[] args) {
Map<String, Integer> wordLengths = new HashMap<>();
String word = "apple";
// Использование computeIfAbsent для вычисления и вставки значения в Map
wordLengths.computeIfAbsent(word, String::length);
// Вывод длины слова "apple", которая была вычислена и вставлена в Map
System.out.println("Длина слова apple: " + wordLengths.get(word));
}
}
computeIfAbsent() для wordLengths, чтобы вычислить и вставить длину слова "apple" в Map, если она отсутствует. Если значение уже присутствует, метод возвращает текущее значение.Этот метод предоставляет удобный и эффективный способ добавления значений в мапу, если они отсутствуют, что может помочь упростить код и избежать лишних проверок на наличие ключей в
Map.Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤3👨💻1
При компиляции все параметры обобщённых типов удаляются («стираются»), и на их место подставляется либо ограничивающий тип (
T extends Number → Number), либо Object.Это нужно для совместимости с «сырыми» классами до появления Generics в Java 5.
Последствия стирания типов:
1️⃣ Нельзя создавать массивы обобщённого типа:
// Ошибка компиляции!
List<String>[] array = new List<String>[10];
2️⃣ Нельзя проверить во время выполнения, является ли объект именно
List<String>:
List<String> list = List.of("a", "b");
if (list instanceof List<String>) { … } // компилятор не пропустит
3️⃣ Перегрузка методов с разными параметрами-дженериками конфликтует:
// Ошибка name clash: стираются оба в process(Collection)
void process(Collection<String> c) { … }
void process(Collection<Integer> c) { … }
Стиpaниe типов позволяет Java Generics не увеличивать размер байт-кода и оставаться совместимой с ранними версиями платформы, но налагает ограничения на проверку и создание обобщённых типов во время выполнения.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3
JUnit 5 — это не просто
@Test. Он принёс гибкость, читаемость и мощные возможности для модульного тестирования.Вот мини-шпаргалка по ключевым аннотациям, которые ты реально будешь использовать.
1.
@Test — запуск теста@Test
void shouldReturnTrueWhenInputIsValid() {
assertTrue(MyService.validate("test"));
}
2.
@BeforeEach и @AfterEach — подготовка и очистка@BeforeEach
void init() {
db.connect();
}
@AfterEach
void cleanup() {
db.disconnect();
}
3. 🏗
@BeforeAll и @AfterAll — один раз на весь класс@BeforeAll
static void globalSetup() {
System.out.println("Запускаем все тесты");
}
4.
@DisplayName — читаемое имя теста@DisplayName("Должен вернуть true при корректном email")
@Test
void validEmailTest() {
assertTrue(EmailValidator.isValid("test@example.com"));
}5.
@Nested — группировка тестов@Nested
class WhenUserIsAdmin {
@Test
void shouldHaveFullAccess() { ... }
}
6.
@ParameterizedTest + @ValueSource — параметризованные тесты@ParameterizedTest
@ValueSource(strings = {"admin", "user", "guest"})
void roleShouldNotBeNull(String role) {
assertNotNull(role);
}
7.
@Disabled — временно отключить тест@Disabled("Фича ещё не реализована")
@Test
void futureFeatureTest() {
fail("не должно запускаться");
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍6❤🔥1🔥1
stream().map().filter().collect() — читаемо и мощноJava Stream API — это как
grep | awk | sort в Linux, только в Java-стиле.Самая частая связка: map → filter → collect. И она может заменить кучу шаблонного кода.
Допустим, у нас есть список:
List<String> names = List.of("Alice", "bob", "CHARLIE", "dave");До Stream API:
List<String> result = new ArrayList<>();
for (String name : names) {
String upper = name.toUpperCase();
if (upper.length() > 3) {
result.add(upper);
}
}
С
stream():List<String> result = names.stream()
.map(String::toUpperCase)
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
1. `map()` — преобразование
.map(String::trim)
.map(String::toLowerCase)
2. `filter()` — отбор
.filter(s -> s.startsWith("a"))
3. `collect()` — сборка результата
.collect(Collectors.toSet())
.collect(Collectors.joining(", "))
🧺 Собирает результат обратно в список, множество, строку и т.д.
List<User> users = getUsers();
List<String> activeAdults = users.stream()
.filter(User::isActive)
.filter(u -> u.getAge() >= 18)
.map(User::getName)
.collect(Collectors.toList());
if, for и add() — код фокусируется на логике, а не на реализации.map().filter().collect() — это про читаемый и декларативный код.Не пиши как императивный робот, пиши как человек: что ты хочешь получить — а не как это сделать.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤3🔥2👨💻1
final в Java — стоп-сигнал для измененийВ Java
final — это как «не трогай»: запрет на изменение.Но что именно он запрещает? Всё зависит от того, где его поставить: переменная, метод, класс.
Разберёмся
1.
final переменные — нельзя переназначитьfinal int port = 8080;
port = 9090; // ❌ ошибка компиляции
final List<String> list = new ArrayList<>();
list.add("Hello"); // ✅ можно
list = new ArrayList<>(); // ❌ нельзя
final не делает объект неизменяемым. Только ссылку.2.
final методы — нельзя переопределить в наследникахclass Animal {
final void sleep() {
System.out.println("Zzz...");
}
}
class Dog extends Animal {
// ❌ Нельзя переопределить sleep()
}3.
final классы — нельзя наследовать вообщеfinal class Utility {
static void log(String msg) { ... }
}class MyUtil extends Utility {} // ❌ ошибкаMath, String)SecurityManager, System)final аргументы в методах → защищает от случайного переназначенияfinal поля + @Immutable → хорошая практика для DTOfinal классы → в record, enum, String, LocalDate и т.д.final = контроль. Это способ сказать «вот это трогать нельзя». Используй его осознанно — чтобы сделать поведение предсказуемым и безопасным.Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥3👨💻2❤1
До Java 13 работа с многострочными строками была болью. JSON? HTML? SQL? Только через
"\n" + и кучу экранирования.Но с Text Blocks всё стало проще, понятнее и читаемо.
Сравни:
String html = "<html>\n" +
" <body>Hello</body>\n" +
"</html>";
String html = """
<html>
<body>Hello</body>
</html>
""";
String, просто оформленная красиво.Text Blocks:1.
Код больше не превращается в кашу. Особенно полезно для:
SQL-запросов:
String query = """
SELECT id, name
FROM users
WHERE active = true
ORDER BY created_at DESC
""";
HTML/JSON шаблонов:
String json = """
{
"name": "Alice",
"role": "admin"
}
""";
2.
Забудь про
\", \\n, \\t — теперь можно писать почти как в блокноте.3.
Java сама уберёт начальные отступы на основе самой "узкой" строки.
Пример:
String msg = """
Line 1
Line 2
Line 3
""";
Line 1
Line 2
Line 3
4.
.formatted() Нельзя вставить переменные прямо в Text Block?
Используем .formatted() — коротко и читабельно:
String user = "Bob";
String template = """
{
"user": "%s",
"access": "granted"
}
""".formatted(user);
````
```java
String s = """
Hello""";
System.out.println(s.length()); // 6, а не 5 (есть \n)
3.
Нельзя писать так:
String name = "Eve";
String wrong = """
Hello, $name!
"""; // ❌ не сработает
Вместо этого:
String right = """
Hello, %s!
""".formatted(name);
+ "\n".Please open Telegram to view this post
VIEW IN TELEGRAM
❤18👍4👨💻2🔥1
Factory-паттерн — это способ создавать объекты, не раскрывая логику создания. Но в Java он часто обрастает шаблонами, абстракциями и XML'ем. Зачем? Можно проще.
Допустим, у нас есть интерфейс:
interface Notification {
void send(String message);
}И реализации:
class EmailNotification implements Notification {
public void send(String message) {
System.out.println("Email: " + message);
}
}
class SmsNotification implements Notification {
public void send(String message) {
System.out.println("SMS: " + message);
}
}class NotificationFactory {
public static Notification create(String type) {
return switch (type) {
case "email" -> new EmailNotification();
case "sms" -> new SmsNotification();
default -> throw new IllegalArgumentException("Unknown type");
};
}
}Использование:
Notification n = NotificationFactory.create("email");
n.send("Hello world!");1.
Меняем реализацию в одном месте.
2.
Можно возвращать любые классы — хоть анонимные, хоть лямбды.
3.
Никаких XML, DI-фреймворков и пяти уровней абстракции.
1.
Если
new работает — не трогай.2.
Часто
enum c фабрикой — читается лучше, чем строки.Please open Telegram to view this post
VIEW IN TELEGRAM
👍19❤4🔥3
@Cacheable — быстро и просто в SpringКогда метод тяжёлый (долго считает, лезет в БД или API) — зачем вызывать его 100 раз, если результат тот же?
Решение: кеш. В Spring это делается одной аннотацией.
@Cacheable?Она сохраняет результат метода в кеш.
При повторном вызове с теми же аргументами — возвращает значение из кеша, не запуская метод снова.
@Cacheable("users")
public User getUserById(Long id) {
System.out.println("Fetching from DB...");
return userRepository.findById(id).orElseThrow();
}Первый вызов: метод выполнится → результат кешируется.
Следующие вызовы с тем же
id: результат берётся из кеша.1. Добавь зависимость (если ещё нет):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2. Включи кеш в
@Configuration:@EnableCaching
@SpringBootApplication
public class MyApp { }
Spring по умолчанию использует простой in-memory кеш (ConcurrentHashMap), но можно подключить Caffeine, Redis, Ehcache и т.д.
🎯 Ключ кеша по полям:
@Cacheable(value = "products", key = "#category + ':' + #limit")
public List<Product> getTopProducts(String category, int limit) { ... }
@Cacheable(value = "data", unless = "#result == null")
public Data fetchData(String key) { ... }
@CachePut("users")
public User updateUser(User user) { ... }🧹 Очистка кеша:
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) { ... }
@Cacheable — это «копим и не дёргаем зря».С ним ты ускоряешь работу приложения без лишнего кода, особенно если есть дорогие вызовы (БД, сеть, парсинг).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤5👌3🔥2
Media is too big
VIEW IN TELEGRAM
Этот курс охватывает основы Java, включая установку JDK, работу с переменными, условными операторами, циклами и объектно-ориентированным программированием. Подходит для тех, кто только начинает знакомство с языком
Please open Telegram to view this post
VIEW IN TELEGRAM
👾2👍1
Object это базовый класс для всех остальных объектов в Java. Любой класс наследуется от Object и, соответственно, наследуют его методы:public boolean equals(Object obj) – служит для сравнения объектов по значению;int hashCode() – возвращает hash код для объекта;String toString() – возвращает строковое представление объекта;Class getClass() – возвращает класс объекта во время выполнения;protected Object clone() – создает и возвращает копию объекта;void notify() – возобновляет поток, ожидающий монитор;void notifyAll() – возобновляет все потоки, ожидающие монитор;void wait() – остановка вызвавшего метод потока до момента пока другой поток не вызовет метод notify() или notifyAll() для этого объекта;void wait(long timeout) – остановка вызвавшего метод потока на определённое время или пока другой поток не вызовет метод notify() или notifyAll() для этого объекта;void wait(long timeout, int nanos) – остановка вызвавшего метод потока на определённое время или пока другой поток не вызовет метод notify() или notifyAll() для этого объекта;protected void finalize() – может вызываться сборщиком мусора в момент удаления объекта при сборке мусора.#java #Object #methods
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤3
Нужен простой способ понять, какие строки кода вообще исполняются при тестировании?
JaCoCo делает это красиво: запускаешь тесты — получаешь отчёт в HTML с краской по строкам.
В pom.xml добавляешь:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
Теперь просто запускаешь:
mvn clean verify
После этого открой отчёт:
open target/site/jacoco/index.html
Есть метод:
public int divide(int a, int b) {
if (b == 0) throw new IllegalArgumentException("zero");
return a / b;
}Если тесты вызывают только divide(10, 2) — то строка с исключением будет красной (не покрыта).
Значит, ты пропустил случай деления на 0 — тест не полный.
Тесты без покрытия — это как замок на двери без ключа. Видимость есть, защиты нет.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👨💻1
Нужно, чтобы один объект реагировал на изменения другого? Без тайных фреймворков, просто на Java?
Решение — паттерн Наблюдатель (Observer). Он позволяет подписчикам автоматически узнавать об изменениях.
// Observable (Subject)
class NewsAgency {
private List<NewsListener> listeners = new ArrayList<>();
public void addListener(NewsListener l) {
listeners.add(l);
}
public void publish(String news) {
for (NewsListener l : listeners) {
l.update(news);
}
}
}
// Observer (Listener)
interface NewsListener {
void update(String news);
}
// Конкретный подписчик
class TelegramBot implements NewsListener {
public void update(String news) {
System.out.println("🔔 Telegram получил: " + news);
}
}
public class Main {
public static void main(String[] args) {
NewsAgency agency = new NewsAgency();
agency.addListener(new TelegramBot());
agency.publish("Java 21 уже вышел!");
agency.publish("🧵 Loom теперь в preview!");
}
}🔔 Telegram получил: Java 21 уже вышел!
🔔 Telegram получил: 🧵 Loom теперь в preview!
Добавь нескольких подписчиков:
class EmailNotifier implements NewsListener {
public void update(String news) {
System.out.println("📧 Email-рассылка: " + news);
}
}agency.addListener(new EmailNotifier());
Теперь все слушатели получают событие одновременно.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18👨💻4❤3🔥1
С 8-й версии Java предлагает мощные инструменты для работы с коллекциями — и пора ими воспользоваться. Забудь про
for и while, когда можно писать элегантно и декларативно.В статье ты найдёшь:
Stream API, которые экономят строки кодаfilter(), map(), collect() и других трюковPlease open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤2❤🔥2👍2
RestTemplate давно знаком, но теперь Spring рекомендует использовать WebClient. Это не просто новый способ сделать HTTP-запросы — это полноценный инструмент, который умеет работать асинхронно, поддерживает реактивность и даёт гораздо больше контроля над процессом.
Вот как выглядит простой GET-запрос:
WebClient client = WebClient.create();
String body = client.get()
.uri("https://api.github.com/users/octocat")
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println(body);
Здесь
bodyToMono(String.class) возвращает реактивный тип Mono, который содержит результат. Если вызвать .block(), запрос будет выполняться синхронно, так что можно легко внедрять WebClient даже в привычный код.Для POST-запроса с телом используем:
WebClient client = WebClient.create("https://httpbin.org");
String response = client.post()
.uri("/post")
.header("Content-Type", "application/json")
.bodyValue("{\"name\":\"PyLinux\"}")
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println(response);Ошибки обрабатываются через
onStatus, например, для 4xx:client.get()
.uri("https://api.github.com/404")
.retrieve()
.onStatus(status -> status.is4xxClientError(), resp -> {
System.out.println("Ошибка клиента: " + resp.statusCode());
return Mono.error(new RuntimeException("Client error"));
})
.bodyToMono(String.class)
.block();
Если нужно передать заголовки или параметры:
WebClient client = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader("Authorization", "Bearer your_token")
.build();
String data = client.get()
.uri(uriBuilder -> uriBuilder
.path("/data")
.queryParam("limit", 10)
.build())
.retrieve()
.bodyToMono(String.class)
.block();
Таким образом, WebClient позволяет легко делать запросы с любыми параметрами и заголовками, при этом можно управлять асинхронностью и реактивностью без лишних сложностей.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4❤🔥3💅2🔥1
Media is too big
VIEW IN TELEGRAM
Этот курс объясняет, что такое Project Loom в Java, включая виртуальные потоки, улучшение масштабируемости и новые возможности многозадачности. Подходит для разработчиков, интересующихся современными подходами к многозадачности в Java.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1🔥1
List.of() в Java — быстро, но не всегда безопасноС Java 9 появился удобный способ создать список:
List<String> names = List.of("Alice", "Bob", "Charlie");Выглядит лаконично, работает быстро — но есть нюанс.
List<String> list = List.of("a", "b");
list.add("c"); // 💥 UnsupportedOperationException
list.remove("a"); // 💥 тоже ошибкаЛюбая попытка изменить список — ошибка во время выполнения. Это не
ArrayList, а immutable List.List<String> list = List.of("hello", null); // 💥 NullPointerExceptionДаже один
null — и всё падает. Потому что List.of() не допускает null ни в одном элементе.System.out.println(List.of("a", "b").getClass());
// class java.util.ImmutableCollections$ListNЭто не
ArrayList, не LinkedList. Это внутренний тип, оптимизированный под неизменяемость.Если твой код ждёт
ArrayList и будет кастить — ловишь ClassCastException.List.of(DAYS))null нетList<String> modifiable = new ArrayList<>(List.of("a", "b", "c"));
modifiable.add("d"); // 👍 теперь можноList.of() — круто для коротких, неизменяемых данных.Но если планируешь менять список или есть шанс на
null — лучше явно используй `ArrayList`.Не пались на удобстве — знай, что лежит под капотом.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13❤5
🧵 Используй try-with-resources с потоками правильно (и продвинуто)
Если ты до сих пор закрываешь потоки руками или думаешь, что try-with-resources — это только для файлов, смотри:
🛠 Пример 1: чтение и закрытие автоматически
☑️ Поток закрывается автоматически — даже если внутри случится исключение.
🧪 Пример 2: кастомный ресурс (любой AutoCloseable)
🧠 Всё, что реализует AutoCloseable, можно оборачивать в try-with-resources. Это не только файлы, но и сетевые соединения, стримы, логи, сессии Hibernate и др.
🕸 Пример 3: несколько ресурсов
🔒 Все ресурсы закроются в обратном порядке.
🗣️ Запомни: try-with-resources — не просто синтаксический сахар. Это способ писать надёжный код без утечек ресурсов и без лишнего шаблонного кода. Особенно полезно при работе с сетью, файлами, базами данных и внешними API.
Если ты до сих пор закрываешь потоки руками или думаешь, что try-with-resources — это только для файлов, смотри:
try (BufferedReader reader = Files.newBufferedReader(Path.of("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}public class TempResource implements AutoCloseable {
public void doSomething() {
System.out.println("Работаем с ресурсом");
}
@Override
public void close() {
System.out.println("Ресурс закрыт");
}
}
try (TempResource res = new TempResource()) {
res.doSomething();
}try (
InputStream in = new FileInputStream("input.txt");
OutputStream out = new FileOutputStream("output.txt")
) {
in.transferTo(out);
}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍6🔥4
Считаешь Java не для AI? Серьёзно ошибаешься! LangChain, фреймворк для разработки с языковыми моделями, теперь уже доступен и для Java — через LangChain4J.
В статье ты узнаешь:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2🔥2👏1