Многопоточность в Java: Deadlock
Deadlock (взаимная блокировка) — это состояние, при котором два или более потоков блокируют друг друга, ожидая освобождения ресурсов, занятых друг другом. Это приводит к тому, что ни один из потоков не может продолжать выполнение.
В этом примере возникает deadlock, потому что thread1 захватывает lock1 и ждет lock2, в то время как thread2 захватывает lock2 и ждет lock1. Таким образом, оба потока оказываются в состоянии взаимной блокировки.
Способы предотвращения Deadlock
Иерархия блокировок: Всегда захватывайте ресурсы в определенном порядке.
Тайм-ауты: Используйте методы с тайм-аутами для захвата блокировок, такие как tryLock из ReentrantLock.
Избегайте вложенных блокировок: Старайтесь минимизировать количество вложенных блокировок.
Пример с использованием tryLock
В этом примере tryLock используется для попытки захвата блокировок с тайм-аутом, что предотвращает возникновение deadlock.
#Java #Training #Multithreading #Medium #Deadlock
Deadlock (взаимная блокировка) — это состояние, при котором два или более потоков блокируют друг друга, ожидая освобождения ресурсов, занятых друг другом. Это приводит к тому, что ни один из потоков не может продолжать выполнение.
public class DeadlockExample {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
public void method2() {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) {
System.out.println("Thread 2: Holding lock 2 & 1...");
}
}
}
public static void main(String[] args) {
DeadlockExample example = new DeadlockExample();
Thread thread1 = new Thread(example::method1);
Thread thread2 = new Thread(example::method2);
thread1.start();
thread2.start();
}
}
В этом примере возникает deadlock, потому что thread1 захватывает lock1 и ждет lock2, в то время как thread2 захватывает lock2 и ждет lock1. Таким образом, оба потока оказываются в состоянии взаимной блокировки.
Способы предотвращения Deadlock
Иерархия блокировок: Всегда захватывайте ресурсы в определенном порядке.
Тайм-ауты: Используйте методы с тайм-аутами для захвата блокировок, такие как tryLock из ReentrantLock.
Избегайте вложенных блокировок: Старайтесь минимизировать количество вложенных блокировок.
Пример с использованием tryLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class DeadlockAvoidanceExample {
private final Lock lock1 = new ReentrantLock();
private final Lock lock2 = new ReentrantLock();
public void method1() {
try {
if (lock1.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 1: Holding lock 1...");
Thread.sleep(50);
if (lock2.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 1: Holding lock 1 & 2...");
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void method2() {
try {
if (lock2.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 2: Holding lock 2...");
Thread.sleep(50);
if (lock1.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 2: Holding lock 2 & 1...");
} finally {
lock1.unlock();
}
}
} finally {
lock2.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
DeadlockAvoidanceExample example = new DeadlockAvoidanceExample();
Thread thread1 = new Thread(example::method1);
Thread thread2 = new Thread(example::method2);
thread1.start();
thread2.start();
}
}
В этом примере tryLock используется для попытки захвата блокировок с тайм-аутом, что предотвращает возникновение deadlock.
#Java #Training #Multithreading #Medium #Deadlock
Многопоточность в Java: Пул потоков
Пул потоков (Thread Pool) — это механизм управления коллекцией потоков, предназначенный для выполнения задач. Потоки в пуле повторно используются для выполнения нескольких задач, что позволяет сократить накладные расходы на создание и уничтожение потоков и повысить производительность многопоточных приложений.
Зачем нужен пул потоков?
Улучшение производительности: Создание нового потока — дорогостоящая операция. Пул потоков позволяет избежать этих накладных расходов, повторно используя уже существующие потоки.
Контроль над количеством потоков: Пул потоков позволяет ограничить максимальное количество одновременно работающих потоков, что предотвращает исчерпание ресурсов системы.
Упрощение управления задачами: Пул потоков предоставляет удобные методы для отправки задач на выполнение, что упрощает управление многопоточными задачами.
Создание пула потоков
В Java для работы с пулом потоков используется класс ExecutorService, который предоставляет методы для управления потоками и выполнения задач. Воспользоваться готовыми реализациями можно с помощью класса Executors.
Пример создания пула потоков с фиксированным числом потоков
В этом примере создается пул из трех потоков, и пять задач отправляются на выполнение. Задачи будут выполняться параллельно, но не более трех одновременно.
Виды пулов потоков
FixedThreadPool: Пул с фиксированным числом потоков. Новый поток не создается, если все потоки заняты, до тех пор, пока один из них не освободится.
CachedThreadPool: Пул, который создает потоки по мере необходимости, но переиспользует старые потоки, если они доступны. Подходит для выполнения большого количества коротких задач.
SingleThreadExecutor: Пул с единственным потоком, выполняющий задачи последовательно.
ScheduledThreadPool: Пул, который позволяет выполнять задачи с задержкой или периодически.
#Java #Training #Multithreading #Medium
Пул потоков (Thread Pool) — это механизм управления коллекцией потоков, предназначенный для выполнения задач. Потоки в пуле повторно используются для выполнения нескольких задач, что позволяет сократить накладные расходы на создание и уничтожение потоков и повысить производительность многопоточных приложений.
Зачем нужен пул потоков?
Улучшение производительности: Создание нового потока — дорогостоящая операция. Пул потоков позволяет избежать этих накладных расходов, повторно используя уже существующие потоки.
Контроль над количеством потоков: Пул потоков позволяет ограничить максимальное количество одновременно работающих потоков, что предотвращает исчерпание ресурсов системы.
Упрощение управления задачами: Пул потоков предоставляет удобные методы для отправки задач на выполнение, что упрощает управление многопоточными задачами.
Создание пула потоков
В Java для работы с пулом потоков используется класс ExecutorService, который предоставляет методы для управления потоками и выполнения задач. Воспользоваться готовыми реализациями можно с помощью класса Executors.
Пример создания пула потоков с фиксированным числом потоков
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is running in thread " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // Симуляция долгой задачи
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " completed");
});
}
executorService.shutdown();
}
}
В этом примере создается пул из трех потоков, и пять задач отправляются на выполнение. Задачи будут выполняться параллельно, но не более трех одновременно.
Виды пулов потоков
FixedThreadPool: Пул с фиксированным числом потоков. Новый поток не создается, если все потоки заняты, до тех пор, пока один из них не освободится.
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
CachedThreadPool: Пул, который создает потоки по мере необходимости, но переиспользует старые потоки, если они доступны. Подходит для выполнения большого количества коротких задач.
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
SingleThreadExecutor: Пул с единственным потоком, выполняющий задачи последовательно.
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ScheduledThreadPool: Пул, который позволяет выполнять задачи с задержкой или периодически.
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
#Java #Training #Multithreading #Medium
Что выведет код?
#Tasks
public class StringChallenge {
public static void main(String[] args) {
String str1 = "Java";
String str2 = new String("Java");
String str3 = "Ja" + "va";
String str4 = str2.intern();
boolean result1 = (str1 == str2);
boolean result2 = (str1 == str3);
boolean result3 = (str1 == str4);
boolean result4 = str1.equals(str2);
int sum = (result1 ? 1 : 0) + (result2 ? 1 : 0) + (result3 ? 1 : 0) + (result4 ? 1 : 0);
System.out.println(sum);
}
}
#Tasks
Многопоточность в Java: ExecutorService
ExecutorService — это интерфейс, который предоставляет методы для управления жизненным циклом потоков и для выполнения задач. Он является частью фреймворка java.util.concurrent и предоставляет гибкие способы работы с потоками.
Основные методы ExecutorService
submit(Runnable task): Отправляет задачу на выполнение и возвращает объект Future, представляющий эту задачу.
submit(Callable<T> task): Отправляет задачу, возвращающую результат, на выполнение и возвращает объект Future.
invokeAll(Collection<? extends Callable<T>> tasks): Выполняет все задачи из коллекции и возвращает список объектов Future.
invokeAny(Collection<? extends Callable<T>> tasks): Выполняет все задачи из коллекции и возвращает результат одной из успешно завершенных задач.
shutdown(): Начинает плавное завершение работы пула, позволяя выполнению текущих задач завершиться.
shutdownNow(): Пытается немедленно завершить выполнение всех задач.
Пример использования submit
Пример использования invokeAll и invokeAny
Управление завершением ExecutorService
Правильное завершение ExecutorService важно для предотвращения утечек ресурсов и корректного завершения программы.
shutdown(): Начинает плавное завершение. Новые задачи не принимаются, но уже отправленные задачи продолжают выполнение.
shutdownNow(): Пытается немедленно завершить все задачи, возвращает список задач, которые не были начаты.
awaitTermination(long timeout, TimeUnit unit): Ожидает завершения всех задач в течение указанного времени.
#Java #Training #Multithreading #Medium #ExecutorService
ExecutorService — это интерфейс, который предоставляет методы для управления жизненным циклом потоков и для выполнения задач. Он является частью фреймворка java.util.concurrent и предоставляет гибкие способы работы с потоками.
Основные методы ExecutorService
submit(Runnable task): Отправляет задачу на выполнение и возвращает объект Future, представляющий эту задачу.
submit(Callable<T> task): Отправляет задачу, возвращающую результат, на выполнение и возвращает объект Future.
invokeAll(Collection<? extends Callable<T>> tasks): Выполняет все задачи из коллекции и возвращает список объектов Future.
invokeAny(Collection<? extends Callable<T>> tasks): Выполняет все задачи из коллекции и возвращает результат одной из успешно завершенных задач.
shutdown(): Начинает плавное завершение работы пула, позволяя выполнению текущих задач завершиться.
shutdownNow(): Пытается немедленно завершить выполнение всех задач.
Пример использования submit
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class SubmitExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Callable<Integer> task1 = () -> {
Thread.sleep(1000);
return 10;
};
Callable<Integer> task2 = () -> {
Thread.sleep(2000);
return 20;
};
Future<Integer> future1 = executorService.submit(task1);
Future<Integer> future2 = executorService.submit(task2);
try {
System.out.println("Result of task1: " + future1.get());
System.out.println("Result of task2: " + future2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
Пример использования invokeAll и invokeAny
public class InvokeExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
List<Callable<Integer>> tasks = Arrays.asList(
() -> {
Thread.sleep(1000);
return 1;
},
() -> {
Thread.sleep(2000);
return 2;
},
() -> {
Thread.sleep(3000);
return 3;
}
);
try {
List<Future<Integer>> results = executorService.invokeAll(tasks);
for (Future<Integer> result : results) {
System.out.println("Result: " + result.get());
}
Integer anyResult = executorService.invokeAny(tasks);
System.out.println("First completed task result: " + anyResult);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
Управление завершением ExecutorService
Правильное завершение ExecutorService важно для предотвращения утечек ресурсов и корректного завершения программы.
shutdown(): Начинает плавное завершение. Новые задачи не принимаются, но уже отправленные задачи продолжают выполнение.
executorService.shutdown();
shutdownNow(): Пытается немедленно завершить все задачи, возвращает список задач, которые не были начаты.
List<Runnable> notExecutedTasks = executorService.shutdownNow();
awaitTermination(long timeout, TimeUnit unit): Ожидает завершения всех задач в течение указанного времени.
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
#Java #Training #Multithreading #Medium #ExecutorService
Concurrency
Concurrency (параллельность) — это способность программы выполнять несколько задач одновременно. В контексте Java, это включает использование многопоточности и синхронизации для обеспечения корректного выполнения параллельных операций.
Коллекции и многопоточность
Стандартные коллекции, такие как ArrayList, HashMap, HashSet и другие, не являются потокобезопасными. Это означает, что если несколько потоков одновременно выполняют операции чтения и записи с этими коллекциями, могут возникнуть проблемы, такие как:
Несогласованность данных: Данные могут быть повреждены или потеряны.
Исключения времени выполнения: Например, ConcurrentModificationException.
Для решения этих проблем в Java были введены потокобезопасные коллекции из пакета java.util.concurrent.
ConcurrentHashMap — это потокобезопасная реализация HashMap, которая позволяет нескольким потокам одновременно выполнять операции чтения и записи. В отличие от синхронизированного HashMap, ConcurrentHashMap использует механизм сегментации (локов), что позволяет повысить производительность за счет уменьшения количества блокировок.
Пример использования ConcurrentHashMap
В этом примере ConcurrentHashMap используется для параллельных операций чтения и записи. Поток writerThread добавляет элементы в карту, в то время как поток readerThread считывает значения из карты. Благодаря использованию ConcurrentHashMap данные остаются согласованными, и исключения времени выполнения не возникают.
Основные методы ConcurrentHashMap
put(K key, V value): Добавляет пару ключ-значение в карту.
get(Object key): Возвращает значение, связанное с указанным ключом.
remove(Object key): Удаляет пару ключ-значение из карты.
replace(K key, V value): Заменяет значение, связанное с указанным ключом, на новое значение.
computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction): Если ключ отсутствует в карте, вычисляет значение и добавляет его в карту.
#Java #Training #Multithreading #Medium
Concurrency (параллельность) — это способность программы выполнять несколько задач одновременно. В контексте Java, это включает использование многопоточности и синхронизации для обеспечения корректного выполнения параллельных операций.
Коллекции и многопоточность
Стандартные коллекции, такие как ArrayList, HashMap, HashSet и другие, не являются потокобезопасными. Это означает, что если несколько потоков одновременно выполняют операции чтения и записи с этими коллекциями, могут возникнуть проблемы, такие как:
Несогласованность данных: Данные могут быть повреждены или потеряны.
Исключения времени выполнения: Например, ConcurrentModificationException.
Для решения этих проблем в Java были введены потокобезопасные коллекции из пакета java.util.concurrent.
ConcurrentHashMap — это потокобезопасная реализация HashMap, которая позволяет нескольким потокам одновременно выполнять операции чтения и записи. В отличие от синхронизированного HashMap, ConcurrentHashMap использует механизм сегментации (локов), что позволяет повысить производительность за счет уменьшения количества блокировок.
Пример использования ConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// Добавление элементов в карту
concurrentMap.put("One", 1);
concurrentMap.put("Two", 2);
concurrentMap.put("Three", 3);
// Создание потоков для параллельного чтения и записи
Thread writerThread = new Thread(() -> {
for (int i = 4; i <= 6; i++) {
concurrentMap.put("Number " + i, i);
System.out.println("Added: Number " + i);
try {
Thread.sleep(100); // Симуляция задержки
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread readerThread = new Thread(() -> {
for (int i = 1; i <= 6; i++) {
System.out.println("Value for Number " + i + ": " + concurrentMap.get("Number " + i));
try {
Thread.sleep(150); // Симуляция задержки
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
writerThread.start();
readerThread.start();
try {
writerThread.join();
readerThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
В этом примере ConcurrentHashMap используется для параллельных операций чтения и записи. Поток writerThread добавляет элементы в карту, в то время как поток readerThread считывает значения из карты. Благодаря использованию ConcurrentHashMap данные остаются согласованными, и исключения времени выполнения не возникают.
Основные методы ConcurrentHashMap
put(K key, V value): Добавляет пару ключ-значение в карту.
get(Object key): Возвращает значение, связанное с указанным ключом.
remove(Object key): Удаляет пару ключ-значение из карты.
replace(K key, V value): Заменяет значение, связанное с указанным ключом, на новое значение.
computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction): Если ключ отсутствует в карте, вычисляет значение и добавляет его в карту.
#Java #Training #Multithreading #Medium
Что выведет код?
#Tasks
public class AlgorithmChallenge {
public static void main(String[] args) {
int[] arr = {5, 3, 8, 6, 2};
int result = mysteriousFunction(arr);
System.out.println(result);
}
public static int mysteriousFunction(int[] arr) {
int n = arr.length;
int sum = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (arr[i] > arr[j]) {
sum += arr[i] - arr[j];
}
}
}
return sum;
}
}
#Tasks
Продвинутые методы ConcurrentHashMap
ConcurrentHashMap предоставляет дополнительные методы для более тонкого управления данными и выполнения атомарных операций.
Пример использования computeIfAbsent
Использование forEach и reduce
ConcurrentHashMap поддерживает методы для параллельной обработки данных, такие как forEach и reduce.
Пример использования forEach
Пример использования reduce
Примеры использования ConcurrentHashMap в реальных задачах
Пример 1: Подсчет количества слов
Пример 2: Кэширование вычислений
#Java #Training #Multithreading #Medium
ConcurrentHashMap предоставляет дополнительные методы для более тонкого управления данными и выполнения атомарных операций.
Пример использования computeIfAbsent
import java.util.concurrent.ConcurrentHashMap;
public class ComputeIfAbsentExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// Добавление элементов в карту
concurrentMap.put("One", 1);
concurrentMap.put("Two", 2);
// Использование computeIfAbsent для добавления значения, если ключ отсутствует
concurrentMap.computeIfAbsent("Three", key -> 3);
concurrentMap.computeIfAbsent("One", key -> 10); // Это значение не будет добавлено, так как ключ уже существует
System.out.println(concurrentMap); // {One=1, Two=2, Three=3}
}
}
Использование forEach и reduce
ConcurrentHashMap поддерживает методы для параллельной обработки данных, такие как forEach и reduce.
Пример использования forEach
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
public class ForEachExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("One", 1);
concurrentMap.put("Two", 2);
concurrentMap.put("Three", 3);
// Параллельная обработка элементов карты
concurrentMap.forEach(1, (key, value) -> {
System.out.println("Key: " + key + ", Value: " + value);
});
}
}
Пример использования reduce
import java.util.concurrent.ConcurrentHashMap;
public class ReduceExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("One", 1);
concurrentMap.put("Two", 2);
concurrentMap.put("Three", 3);
// Параллельное суммирование значений карты
int sum = concurrentMap.reduceValues(1, Integer::sum);
System.out.println("Sum of values: " + sum); // Output: Sum of values: 6
}
}
Примеры использования ConcurrentHashMap в реальных задачах
Пример 1: Подсчет количества слов
import java.util.concurrent.ConcurrentHashMap;
public class WordCountExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> wordCounts = new ConcurrentHashMap<>();
String[] words = {"apple", "banana", "apple", "orange", "banana", "apple"};
for (String word : words) {
wordCounts.merge(word, 1, Integer::sum);
}
System.out.println(wordCounts); // {apple=3, banana=2, orange=1}
}
}
Пример 2: Кэширование вычислений
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
public class ComputationCacheExample {
private final ConcurrentHashMap<Integer, String> cache = new ConcurrentHashMap<>();
public String compute(Integer key, Function<Integer, String> computation) {
return cache.computeIfAbsent(key, computation);
}
public static void main(String[] args) {
ComputationCacheExample example = new ComputationCacheExample();
Function<Integer, String> computation = key -> {
try {
Thread.sleep(1000); // Симуляция долгого вычисления
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result for " + key;
};
System.out.println(example.compute(1, computation)); // Вычисление и кэширование
System.out.println(example.compute(1, computation)); // Использование закэшированного значения
}
}
#Java #Training #Multithreading #Medium
Аннотации в Java
Аннотации в Java — это метаданные, которые добавляются к коду для предоставления дополнительной информации. Они могут быть использованы компилятором, инструментами разработки и во время выполнения (через рефлексию). Аннотации не изменяют логики программы, но могут влиять на её поведение.
Основные встроенные аннотации
@ Override: Указывает, что метод переопределяет метод суперкласса.
@ Deprecated: Обозначает, что элемент (метод, класс, поле) устарел и не рекомендуется к использованию.
@ SuppressWarnings: Подавляет указанные предупреждения компилятора.
@ SafeVarargs: Обозначает, что метод с переменным числом аргументов не выполняет потенциально небезопасных операций с varargs.
@ FunctionalInterface: Указывает, что интерфейс является функциональным и содержит только один абстрактный метод.
Примеры использования встроенных аннотаций
Методы аннотаций
Аннотации могут содержать методы. Методы аннотаций могут иметь значения по умолчанию.
Аннотацию можно использовать с параметрами:
Аннотации и рефлексия
Аннотации можно считывать во время выполнения с помощью рефлексии. Это позволяет динамически изменять поведение программы в зависимости от аннотированных элементов.
Пример использования рефлексии для чтения аннотаций
#Java #Training #Annotation #Medium
Аннотации в Java — это метаданные, которые добавляются к коду для предоставления дополнительной информации. Они могут быть использованы компилятором, инструментами разработки и во время выполнения (через рефлексию). Аннотации не изменяют логики программы, но могут влиять на её поведение.
Основные встроенные аннотации
@ Override: Указывает, что метод переопределяет метод суперкласса.
@ Deprecated: Обозначает, что элемент (метод, класс, поле) устарел и не рекомендуется к использованию.
@ SuppressWarnings: Подавляет указанные предупреждения компилятора.
@ SafeVarargs: Обозначает, что метод с переменным числом аргументов не выполняет потенциально небезопасных операций с varargs.
@ FunctionalInterface: Указывает, что интерфейс является функциональным и содержит только один абстрактный метод.
Примеры использования встроенных аннотаций
public class AnnotationExample {
@Override
public String toString() {
return "AnnotationExample";
}
@Deprecated
public void oldMethod() {
System.out.println("This method is deprecated");
}
@SuppressWarnings("unchecked")
public void uncheckedOperation() {
List rawList = new ArrayList();
rawList.add("String");
}
@SafeVarargs
public final void safeVarargsMethod(List<String>... lists) {
for (List<String> list : lists) {
System.out.println(list);
}
}
@FunctionalInterface
interface MyFunctionalInterface {
void execute();
}
}
Методы аннотаций
Аннотации могут содержать методы. Методы аннотаций могут иметь значения по умолчанию.
@interface MyAnnotation {
String value();
int number() default 42;
}
Аннотацию можно использовать с параметрами:
@MyAnnotation(value = "Example", number = 10)
public class AnnotatedClass {
}
Аннотации и рефлексия
Аннотации можно считывать во время выполнения с помощью рефлексии. Это позволяет динамически изменять поведение программы в зависимости от аннотированных элементов.
Пример использования рефлексии для чтения аннотаций
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class ReflectionExample {
@MyAnnotation(value = "Test", number = 5)
public void annotatedMethod() {
}
public static void main(String[] args) throws Exception {
Method method = ReflectionExample.class.getMethod("annotatedMethod");
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println("Value: " + annotation.value());
System.out.println("Number: " + annotation.number());
}
}
}
#Java #Training #Annotation #Medium
Что выведет код?
#Tasks
public class ConcatChallenge {
public static void main(String[] args) {
String[] arr = {"Java", "is", "fun"};
String result = mysteriousConcat(arr);
System.out.println(result);
}
public static String mysteriousConcat(String[] arr) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i < arr.length - 1) {
sb.append("-");
}
}
return sb.toString();
}
}
#Tasks
Создание собственных аннотаций в Java
Создание собственных аннотаций позволяет добавлять метаданные к вашему коду, которые могут быть использованы компилятором, инструментами разработки или во время выполнения. Аннотации можно использовать для валидации данных, конфигурации и других задач.
Для создания аннотации используется ключевое слово @ interface.
@ Retention: Указывает, насколько долго аннотация сохраняется (например, только в исходном коде, в классе или во время выполнения).
RetentionPolicy.SOURCE: Аннотация сохраняется только в исходном коде и отбрасывается компилятором.
RetentionPolicy.CLASS: Аннотация сохраняется в файле класса, но не доступна во время выполнения.
RetentionPolicy.RUNTIME: Аннотация сохраняется во время выполнения и может быть прочитана с помощью рефлексии.
Применение собственной аннотации
Чтение аннотаций с помощью рефлексии
Методы аннотаций
Аннотации могут содержать методы, которые определяют параметры аннотации. Методы могут иметь значения по умолчанию.
Пример использования:
Ограничения и мета-аннотации
Мета-аннотации — это аннотации, которые применяются к другим аннотациям. В Java существует несколько мета-аннотаций:
@ Retention: Указывает, насколько долго аннотация сохраняется.
@ Target: Указывает, где аннотация может быть применена (например, к методам, полям, классам).
@ Inherited: Указывает, что аннотация может быть унаследована подклассами.
@ Documented: Указывает, что аннотация должна быть задокументирована с помощью javadoc.
Пример использования мета-аннотаций:
Пример практического использования собственных аннотаций
Валидация с помощью аннотаций
Создадим аннотацию для проверки полей класса:
#Java #Training #Annotation #Medium
Создание собственных аннотаций позволяет добавлять метаданные к вашему коду, которые могут быть использованы компилятором, инструментами разработки или во время выполнения. Аннотации можно использовать для валидации данных, конфигурации и других задач.
Для создания аннотации используется ключевое слово @ interface.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation {
String value();
int number() default 42;
}
@ Retention: Указывает, насколько долго аннотация сохраняется (например, только в исходном коде, в классе или во время выполнения).
RetentionPolicy.SOURCE: Аннотация сохраняется только в исходном коде и отбрасывается компилятором.
RetentionPolicy.CLASS: Аннотация сохраняется в файле класса, но не доступна во время выполнения.
RetentionPolicy.RUNTIME: Аннотация сохраняется во время выполнения и может быть прочитана с помощью рефлексии.
Применение собственной аннотации
@MyCustomAnnotation(value = "Example", number = 10)
public class CustomAnnotatedClass {
}
Чтение аннотаций с помощью рефлексии
import java.lang.annotation.Annotation;
public class CustomAnnotationReflection {
public static void main(String[] args) {
Class<CustomAnnotatedClass> clazz = CustomAnnotatedClass.class;
if (clazz.isAnnotationPresent(MyCustomAnnotation.class)) {
MyCustomAnnotation annotation = clazz.getAnnotation(MyCustomAnnotation.class);
System.out.println("Value: " + annotation.value());
System.out.println("Number: " + annotation.number());
}
}
}
Методы аннотаций
Аннотации могут содержать методы, которые определяют параметры аннотации. Методы могут иметь значения по умолчанию.
public @interface DetailedAnnotation {
String description();
boolean enabled() default true;
}
Пример использования:
@DetailedAnnotation(description = "This is a detailed annotation", enabled = false)
public class DetailedAnnotatedClass {
}
Ограничения и мета-аннотации
Мета-аннотации — это аннотации, которые применяются к другим аннотациям. В Java существует несколько мета-аннотаций:
@ Retention: Указывает, насколько долго аннотация сохраняется.
@ Target: Указывает, где аннотация может быть применена (например, к методам, полям, классам).
@ Inherited: Указывает, что аннотация может быть унаследована подклассами.
@ Documented: Указывает, что аннотация должна быть задокументирована с помощью javadoc.
Пример использования мета-аннотаций:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MultiTargetAnnotation {
String value();
}
Пример практического использования собственных аннотаций
Валидация с помощью аннотаций
Создадим аннотацию для проверки полей класса:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NotNull {
}
Применим аннотацию к классу:
java
Копировать код
public class User {
@NotNull
private String name;
public User(String name) {
this.name = name;
}
}
#Java #Training #Annotation #Medium
Создадим метод для проверки полей:
Этот пример показывает, как создавать и использовать собственные аннотации для выполнения валидации данных. Аннотации делают код более читаемым и структурированным, позволяя легко добавлять новые проверки и конфигурации.
#Java #Training #Annotation #Medium
import java.lang.reflect.Field;
public class Validator {
public static void validate(Object object) throws IllegalAccessException {
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(NotNull.class)) {
field.setAccessible(true);
Object value = field.get(object);
if (value == null) {
throw new IllegalArgumentException(field.getName() + " cannot be null");
}
}
}
}
public static void main(String[] args) {
User user = new User(null);
try {
validate(user);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
Этот пример показывает, как создавать и использовать собственные аннотации для выполнения валидации данных. Аннотации делают код более читаемым и структурированным, позволяя легко добавлять новые проверки и конфигурации.
#Java #Training #Annotation #Medium
Всем доброго субботнего утра! ☀️
Как ваше настроение, какие планы на выходные?
Делитесь с нами в чате - https://t.me/Java_Beginner_chat
Напоминаю, что завтра в 16:00 по МСК запланирована онлайн встреча, где мы наконец-то закончим наше консольное TODO приложение!)))
Как ваше настроение, какие планы на выходные?
Делитесь с нами в чате - https://t.me/Java_Beginner_chat
Напоминаю, что завтра в 16:00 по МСК запланирована онлайн встреча, где мы наконец-то закончим наше консольное TODO приложение!)))
Утро программиста.
#Mems
объекты: Жена, Будильник, Автомобиль;
переменные: совесть, желание_спать, сытость, злость
если Будильник = 1
{
перевести(5min);
перевести(5min);
выключить();
встать();
}
если Будильник = 0
{
пока (совесть < желание_спать)
{
спать();
}
встать();
}
умыться();
если Жена > 0
{
приготовить_завтрак(жена);
}
если Жена = 0
{
приготовить_завтрак(содержимое холодильника[0]);
}
сытость = сытость + 1;
если автомобиль !=1
{
добраться_до_работы(спать());
}
если автомобиль =1
{
добраться_до_работы(злость);
}
#Mems
Напоминаю про канал моего товарища 3D-шника) - https://t.me/model3ddd/3511
Если вас интересует данная тема, есть желание изучить ее подробнее, ознакомиться с примерами работ - залетайте не думая!
Если вас интересует данная тема, есть желание изучить ее подробнее, ознакомиться с примерами работ - залетайте не думая!
Telegram
3Dshnik
📱 Все: мы будем использовать ChatGPT для революций в науке и технике
Мы используем ChatGPT:
3Dshnik - Подписаться
Мы используем ChatGPT:
3Dshnik - Подписаться