Java | Фишки и трюки
7.21K subscribers
182 photos
29 videos
6 files
40 links
Java: примеры кода, интересные фишки и полезные трюки

Купить рекламу: https://telega.in/c/java_tips_and_tricks

✍️По всем вопросам: @Pascal4eg
Download Telegram
public class Quest {
public static void greet() {
System.out.println("Hello world!");
}

public static void main(String[] args) {
Quest q = null;
q.greet();
}
}
👍3
👍3🎉3😁21
⌨️ EnumMap — это реализация Map, которая использует в качестве ключей исключительно Enum.

Внутренне EnumMap использует массив для хранения значений, где индекс массива соответствует порядковому номеру (ordinal()) элемента перечисления. Это делает его быстрее, чем хэш-таблицы (HashMap) и более экономным по памяти.

EnumMap хранит ключи в порядке их объявления в перечислении, что отличает его от большинства других Map (кроме LinkedHashMap).

Так как ключи строго ограничены перечислением, это снижает вероятность ошибок при разработке.

🔍 Пример:

import java.util.EnumMap;

enum Action {
START, STOP, PAUSE
}

public class EnumMapExample {
public static void main(String[] args) {
EnumMap<Action, Runnable> actionMap = new EnumMap<>(Action.class);

// Определяем поведение для каждого значения Enum
actionMap.put(Action.START, () -> System.out.println("Starting the process..."));
actionMap.put(Action.STOP, () -> System.out.println("Stopping the process..."));
actionMap.put(Action.PAUSE, () -> System.out.println("Pausing the process..."));

// Пример вызова
Action currentAction = Action.START;
actionMap.get(currentAction).run();
}
}


#Java #EnumMap
Please open Telegram to view this post
VIEW IN TELEGRAM
👍195
⌨️ Как работает IdentityHashMap

IdentityHashMap — это необычная реализация Map, которая использует сравнение по ссылкам (==) вместо стандартного метода equals().

В IdentityHashMap два ключа считаются равными только если это один и тот же объект (сравнение по ссылкам).

📌 Пример:

java
import java.util.IdentityHashMap;
import java.util.Map;

public class IdentityHashMapExample {
public static void main(String[] args) {
Map<String, String> map = new IdentityHashMap<>();

String key1 = new String("key");
String key2 = new String("key");

map.put(key1, "Value 1");
map.put(key2, "Value 2");

System.out.println(map); // Выведет обе пары: {key=Value 1, key=Value 2}
}
}


key1 и key2 — это разные объекты, даже если их строки одинаковы.

Когда использовать?

1️⃣ Кэширование: если нужно различать объекты с одинаковыми данными.

2️⃣ Оптимизация: для низкоуровневой работы с объектами, где важна ссылка, а не значение.

3️⃣ Специфические задачи: при разработке компиляторов, интерпретаторов или для отслеживания уникальных объектов.

💡 Ограничение:
IdentityHashMap не гарантирует порядок элементов и редко используется в стандартных задачах. Это инструмент для узкоспециализированных случаев!

#Java #IdentityHashMap
Please open Telegram to view this post
VIEW IN TELEGRAM
👍101
⌨️ WeakHashMap: когда удаление происходит само собой

WeakHashMap — это коллекция, которая позволяет ключам удаляться из неё автоматически, если на них больше нет сильных ссылок.

Если ключ в WeakHashMap становится недостижимым (нет сильных ссылок), он удаляется сборщиком мусора.

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

📌 Пример:

java
import java.util.WeakHashMap;

public class WeakHashMapExample {
public static void main(String[] args) {
WeakHashMap<Object, String> map = new WeakHashMap<>();

Object key = new Object();
map.put(key, "Value");

System.out.println("Before GC: " + map); // Ключ и значение есть

key = null; // Убираем сильную ссылку на объект
System.gc(); // Вызываем сборку мусора

System.out.println("After GC: " + map); // Ключ и значение исчезли
}
}


Пока объект-ключ доступен, пара ключ-значение остаётся в WeakHashMap.
Как только ключ становится недоступным, он автоматически удаляется сборщиком мусора.

Когда использовать?

1️⃣ Кэширование: для хранения данных, которые можно восстановить, если они удалены.

2️⃣ Маппинг вспомогательных данных: например, привязка метаданных к объектам, где их удаление важно.

💡 Совет:
Не используйте WeakHashMap для данных, которые критически важны — сборщик мусора может удалить их в любой момент!

#Java #WeakHashMap
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🤷‍♂21👎1🤩1
⌨️ Как управлять порядком элементов в LinkedHashMap?

LinkedHashMap — это реализация Map, которая сохраняет порядок добавления элементов. Но что, если нужен порядок по использованию (LRU-кэш)? LinkedHashMap справится и с этим!

Порядок добавления (по умолчанию):

java
import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapExample {
public static void main(String[] args) {
Map<Integer, String> map = new LinkedHashMap<>();

map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");

System.out.println(map); // {1=One, 2=Two, 3=Three}
}
}


Порядок использования:
Передайте accessOrder = true в конструктор.

java
import java.util.LinkedHashMap;
import java.util.Map;

public class LRUExample {
public static void main(String[] args) {
Map<Integer, String> map = new LinkedHashMap<>(16, 0.75f, true);

map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");

map.get(2); // Используем ключ 2
System.out.println(map); // {1=One, 3=Three, 2=Two}
}
}


Можно переопределить метод removeEldestEntry() для автоматического удаления старых записей.


@Override
protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) {
return size() > 3; // Удалять записи, если их больше 3
}


#Java #LinkedHashMap #LRU
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥2
import java.util.*;

public class Quest {
private static Random rnd = new Random();
public static void main(String[] args) {
StringBuffer word = null;
switch(rnd.nextInt(2)) {
case 1: word = new StringBuffer('P');
case 2: word = new StringBuffer('G');
default: word = new StringBuffer('M');
}
word.append('a');
word.append('i');
word.append('n');
System.out.println(word);
}
}
👍3
Что выведет код?
Anonymous Quiz
27%
Main
32%
Pain или Main
23%
Pain или Gain
18%
ain
😭15🎉3
😁10🤣6👎1💯1
⌨️ Параллельное программирование и CompletableFuture

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

Стандартный метод работы с потоками может быть сложным и запутанным. CompletableFuture — более удобный и мощный инструмент, который упрощает асинхронное программирование.

✍️ Пример: Выполнение задач параллельно с использованием CompletableFuture

import java.util.concurrent.CompletableFuture;

public class AsyncExample {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();

CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
sleep(2000);
return "Результат задачи 1";
});

CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
sleep(3000);
return "Результат задачи 2";
});

CompletableFuture<String> combined = task1.thenCombine(task2, (result1, result2) -> result1 + " & " + result2);

// Ожидаем завершения всех задач и получаем общий результат
combined.thenAccept(result -> {
long endTime = System.currentTimeMillis();
System.out.println("Общий результат: " + result);
System.out.println("Время выполнения: " + (endTime - startTime) + " мс");
});

combined.join(); // Дождёмся завершения наших задач
}

private static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


🔧 Что происходит в коде?
1️⃣ Мы запускаем две задачи, каждая из которых выполняется асинхронно.
2️⃣ Используем метод thenCombine, чтобы объединить результаты двух задач.
3️⃣ Когда обе задачи завершены, выводим общий результат и время выполнения.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍112
⌨️ Files.walkFileTree(): мощный инструмент для работы с файловой системой

Files.walkFileTree() из пакета java.nio.file позволяет рекурсивно обходить дерево каталогов и выполнять сложные операции. Это гибкий и удобный способ работать с файловой системой, включая чтение, обработку, копирование или удаление файлов.

🔍 Как это работает?
1️⃣ Реализуется интерфейс FileVisitor, который определяет действия для каждой операции.
2️⃣ Можно обрабатывать файлы и директории на каждом этапе обхода.

📌 Пример: удаление дерева каталогов


import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class WalkFileTreeExample {
public static void main(String[] args) throws IOException {
Path startPath = Paths.get("example_directory");

Files.walkFileTree(startPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("Deleting file: " + file);
Files.delete(file); // Удаляем файл
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
System.out.println("Deleting directory: " + dir);
Files.delete(dir); // Удаляем директорию после удаления её содержимого
return FileVisitResult.CONTINUE;
}
});
}
}


🔗 Ключевые методы FileVisitor:
visitFile() — вызывается для каждого файла.
postVisitDirectory() — вызывается после обхода содержимого директории.
preVisitDirectory() — вызывается перед входом в директорию.
visitFileFailed() — обрабатывает ошибки доступа.

Когда использовать?
1️⃣ Для удаления, копирования или архивирования файлов/папок.
2️⃣ Для поиска файлов по сложным критериям.
3️⃣ Для выполнения массовых операций с файлами (изменение атрибутов, анализ).

💡 Совет: Используйте SimpleFileVisitor, чтобы переопределять только нужные методы, а не реализовать весь интерфейс FileVisitor.

#Java #Files #FileVisitor
Please open Telegram to view this post
VIEW IN TELEGRAM
👍61
⌨️ Collectors.teeing(): две операции над одним потоком

С появлением Java 12 Collectors.teeing() стал удобным инструментом для объединения двух независимых операций над потоком. Этот коллектор позволяет выполнять две разные операции над данными и объединять их результат с помощью функции-объединителя.

🔍 Как это работает?
1️⃣ Указываем два коллектора для операций.
2️⃣ Задаём функцию, которая объединит результаты этих операций.

📌 Пример: Найдём сумму и среднее списка чисел.


import java.util.List;
import java.util.stream.Collectors;

public class TeeingExample {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5);

var result = numbers.stream().collect(
Collectors.teeing(
Collectors.summingInt(Integer::intValue), // Первая операция: сумма
Collectors.averagingInt(Integer::intValue), // Вторая операция: среднее
(sum, avg) -> "Sum: " + sum + ", Average: " + avg // Объединение результатов
)
);

System.out.println(result); // Вывод: Sum: 15, Average: 3.0
}
}


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

Когда использовать?
1️⃣ Для выполнения нескольких независимых операций над данными потока.
2️⃣ Если нужно объединить результаты этих операций.

💡 Совет:
Collectors.teeing() особенно полезен, когда результаты независимых операций логически связаны. Например, для расчёта статистики или агрегации данных.

#Java #Streams #Collectors #teeing
Please open Telegram to view this post
VIEW IN TELEGRAM
👍112
⌨️ Base64: стандартные методы кодирования и декодирования

Base64 — это способ представления данных в текстовом формате, который можно безопасно передавать через сеть и использовать в текстовых форматах. В Java для работы с Base64 используется встроенный класс java.util.Base64.

🔍 Кодирование строки в Base64:

import java.util.Base64;

public class Base64Example {
public static void main(String[] args) {
String original = "Hello, Java!";
String encoded = Base64.getEncoder().encodeToString(original.getBytes());

System.out.println("Encoded: " + encoded); // Вывод: SGVsbG8sIEphdmEh
}
}


🔗 Декодирование строки из Base64:

String encoded = "SGVsbG8sIEphdmEh";
String decoded = new String(Base64.getDecoder().decode(encoded));
System.out.println("Decoded: " + decoded); // Вывод: Hello, Java!


Варианты кодировщиков:
1️⃣ Base64.getEncoder() — стандартный кодировщик.
2️⃣ Base64.getUrlEncoder() — для URL и имён файлов (без символов + и /).
3️⃣ Base64.getMimeEncoder() — для MIME-форматов (разбивает строки на блоки по 76 символов).

💡 Когда использовать?
1️⃣ При передаче данных в текстовом виде (например, JSON или бинарные файлы).
2️⃣ Для работы с авторизацией (например, токены в Basic Auth).
3️⃣ Взаимодействие с API, использующими кодировку Base64.

#Java #Base64
Please open Telegram to view this post
VIEW IN TELEGRAM
👍121
⌨️ Optional.orElse() и Optional.orElseGet()

Optional.orElseGet() — это метод, который позволяет задать значение по умолчанию, вызывая поставщик (Supplier) только тогда, когда Optional пуст.

Если значение присутствует, возвращается оно.
Если значение отсутствует, вызывается Supplier, который возвращает значение по умолчанию.

📌 Пример:

import java.util.Optional;

public class OptionalOrElseGetExample {
public static void main(String[] args) {
String defaultValue = "Default Value";

// Пример с orElse
System.out.println("Using orElse:");
System.out.println(getValue().orElse(expensiveOperation(defaultValue)));

// Пример с orElseGet
System.out.println("Using orElseGet:");
System.out.println(getValue().orElseGet(() -> expensiveOperation(defaultValue)));
}

private static Optional<String> getValue() {
return Optional.empty(); // Эмулируем пустой Optional
}

private static String expensiveOperation(String input) {
System.out.println("Executing expensive operation...");
return input;
}
}


🔗 Ключевая разница между orElse и orElseGet:
✔️orElse() всегда вычисляет значение по умолчанию, даже если оно не нужно.
✔️orElseGet() вызывает логику только при необходимости, что экономит ресурсы.

💡 Совет:
Используйте orElseGet, если значение по умолчанию требует сложных вычислений или вызовов. Это сделает ваш код эффективнее.

#Java #Optional #orElse #orElseGet
Please open Telegram to view this post
VIEW IN TELEGRAM
👍71🔥1👏1
public class Quest {
public static void main(String[] args) {
System.out.println("a\".length() + \"b".length());
}
}
👍1
Что выведет код?
Anonymous Quiz
11%
2
27%
16
13%
18
48%
<Ошибка компиляции>
👍71🎉1
⌨️ StackTraceElement

StackTraceElement — это класс, который предоставляет информацию о стеке вызовов во время исключения. Понимание его возможностей помогает лучше отлаживать код и искать причины ошибок.

🔍 Что можно узнать с помощью StackTraceElement?
1️⃣ Имя класса, где возникло исключение.
2️⃣ Имя метода, вызвавшего ошибку.
3️⃣ Имя файла и номер строки, где произошла ошибка.

📌 Пример использования:

public class StackTraceExample {
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
for (StackTraceElement element : e.getStackTrace()) {
System.out.println("Class: " + element.getClassName());
System.out.println("Method: " + element.getMethodName());
System.out.println("Line: " + element.getLineNumber());
System.out.println("File: " + element.getFileName());
System.out.println("---");
}
}
}

static void methodA() {
methodB();
}

static void methodB() {
throw new RuntimeException("Test exception");
}
}


🔗 Что будет на выходе?
При возникновении исключения вы получите информацию о каждом уровне стека вызовов:

Class: StackTraceExample
Method: methodB
Line: 23
File: StackTraceExample.java
---
Class: StackTraceExample
Method: methodA
Line: 19
File: StackTraceExample.java
---
Class: StackTraceExample
Method: main
Line: 6
File: StackTraceExample.java
---


Зачем это изучать?
1️⃣ Быстрое выявление проблем в коде.
2️⃣ Удобная диагностика ошибок в логах.
3️⃣ Возможность точечно логировать критичные места.

#Java #StackTraceElement
Please open Telegram to view this post
VIEW IN TELEGRAM
👍71
😁27💯2🤣2👍1🤡1
⌨️ Suppressed Exceptions в Java появились с введением механизма try-with-resources в Java 7. Они используются для того, чтобы не терять исключения, которые могут возникнуть при автоматическом закрытии ресурсов.

Когда внутри блока try происходит исключение, а при закрытии ресурса (например, в методе close() интерфейса AutoCloseable) возникает другое исключение, это второе исключение считается подавленным и сохраняется в основном исключении. Таким образом, вы не теряете информацию о проблемах, возникших при закрытии ресурсов.

Пример:

import java.io.*;

public class SuppressedExceptionsExample {
public static void main(String[] args) {
try (MyResource resource = new MyResource()) {
throw new RuntimeException("Исключение в блоке try");
} catch (Exception e) {
System.out.println("Основное исключение: " + e.getMessage());
for (Throwable suppressed : e.getSuppressed()) {
System.out.println("Подавленное исключение: " + suppressed.getMessage());
}
}
}
}

class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
throw new Exception("Исключение при закрытии ресурса");
}
}


Объяснение:
1️⃣ В блоке try выбрасывается исключение RuntimeException с сообщением "Исключение в блоке try".
2️⃣ Во время закрытия ресурса (метод close()), генерируется другое исключение с сообщением "Исключение при закрытии ресурса".
3️⃣ Исключение из close() добавляется в список подавленных исключений основного исключения.

Вывод:

Основное исключение: Исключение в блоке try
Подавленное исключение: Исключение при закрытии ресурса


Этот механизм позволяет сохранять информацию о всех проблемах, которые возникли, включая те, что произошли при управлении ресурсами.

#java #Suppressed
Please open Telegram to view this post
VIEW IN TELEGRAM
👍82🔥2
⌨️ Создание собственных Unchecked Exceptions

В Java вы можете создавать свои собственные исключения, чтобы сделать код более читаемым и выразительным. Если ошибка относится к категории Unchecked Exceptions, ваш класс должен наследоваться от RuntimeException.

🔍 Зачем нужны Unchecked Exceptions?
- Они используются для ошибок, которые не требуют явной обработки с помощью try-catch.
- Примеры: NullPointerException, IllegalArgumentException.

📌 Пример создания Unchecked Exception:

// Определяем исключение
class InvalidAgeException extends RuntimeException {
public InvalidAgeException(String message) {
super(message);
}
}

// Используем в коде
public class CustomUncheckedExample {
public static void main(String[] args) {
int age = -5;

if (age < 0) {
throw new InvalidAgeException("Возраст не может быть отрицательным: " + age);
}
}
}


🔗 Ключевые моменты:
1️⃣ Наследуйтесь от RuntimeException для создания Unchecked Exceptions.
2️⃣ Добавляйте понятное сообщение, чтобы упростить отладку.
3️⃣ Используйте, когда ошибка связана с некорректным использованием API или данных.

Когда использовать?
- Если ошибка относится к программной логике.
- Когда обработка исключения должна быть необязательной.

💡 Совет: Используйте Unchecked Exceptions для сигнализации о проблемах, которые можно избежать с помощью правильного использования API.

#Java #Exceptions #UncheckedExceptions
Please open Telegram to view this post
VIEW IN TELEGRAM
👍125
⌨️ Класс Phaser (из пакета java.util.concurrent) используется для координации потоков, которые выполняют этапы работы (phases). Он позволяет потокам синхронизироваться на каждом этапе, продолжая выполнение только тогда, когда все участвующие потоки завершили текущий этап.

Рассмотрим пример, где несколько потоков выполняют 3 этапа работы:

import java.util.concurrent.Phaser;

public class PhaserExample {
public static void main(String[] args) {
// Создаем Phaser для 3 потоков (регистрация)
Phaser phaser = new Phaser(3);

// Запускаем 3 потока
for (int i = 0; i < 3; i++) {
new Thread(new Worker(phaser), "Поток-" + (i + 1)).start();
}
}
}

class Worker implements Runnable {
private final Phaser phaser;

public Worker(Phaser phaser) {
this.phaser = phaser;
}

@Override
public void run() {
try {
for (int phase = 1; phase <= 3; phase++) {
System.out.println(Thread.currentThread().getName() + " выполняет этап " + phase);

// Имитация работы
Thread.sleep(1000);

// Сообщаем о завершении этапа
phaser.arriveAndAwaitAdvance();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}


Объяснение:
1️⃣ Создание Phaser: Phaser phaser = new Phaser(3); — регистрируем 3 участника.
2️⃣ Работа потоков: Каждый поток выполняет работу, моделируемую с помощью Thread.sleep(1000), и вызывает phaser.arriveAndAwaitAdvance() для синхронизации.
3️⃣ Синхронизация фаз: arriveAndAwaitAdvance() — сообщает о завершении текущей фазы и блокируется, пока другие участники не завершат фазу.
4️⃣ Количество фаз: Здесь 3 фазы, и каждый поток выполняет все три, прежде чем завершить выполнение.

Пример вывода:

Поток-1 выполняет этап 1
Поток-2 выполняет этап 1
Поток-3 выполняет этап 1
Поток-1 выполняет этап 2
Поток-2 выполняет этап 2
Поток-3 выполняет этап 2
Поток-1 выполняет этап 3
Поток-2 выполняет этап 3
Поток-3 выполняет этап 3


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

#java #Phaser
Please open Telegram to view this post
VIEW IN TELEGRAM
👍113