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

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

✍️По всем вопросам: @Pascal4eg
Download Telegram
Паттерн Bridge (Мост)

Паттерн Bridge позволяет разделить абстракцию и её реализацию, чтобы они могли развиваться независимо друг от друга. Это достигается через создание интерфейса (абстракции) и реализаций этого интерфейса. Такой подход позволяет избежать жесткой привязки между системой абстракций и ее реализациями.

Давайте посмотрим на пример, который иллюстрирует паттерн Bridge в контексте рисования фигур:

// Реализация интерфейса для рисования
interface DrawingAPI {
void drawCircle(double x, double y, double radius);
}

// Конкретная реализация для рисования на графическом окне
class DrawingAPI1 implements DrawingAPI {
public void drawCircle(double x, double y, double radius) {
System.out.println("API1.circle at {" + x + ", " + y + "} radius " + radius);
}
}

// Конкретная реализация для рисования в ASCII
class DrawingAPI2 implements DrawingAPI {
public void drawCircle(double x, double y, double radius) {
System.out.println("API2.circle at {" + x + ", " + y + "} radius " + radius);
}
}

// Абстракция
abstract class Circle {
protected DrawingAPI drawingAPI; // Ссылка на реализацию

protected Circle(DrawingAPI drawingAPI) {
this.drawingAPI = drawingAPI;
}

public abstract void draw(); // Метод для рисования
public abstract void resizeByPercentage(double pct); // Метод для изменения размера
}

// Конкретная абстракция
class CircleWithABorder extends Circle {
private double x, y, radius; // Параметры круга

public CircleWithABorder(double x, double y, double radius, DrawingAPI drawingAPI) {
super(drawingAPI); // Передаем реализацию в абстракцию
this.x = x;
this.y = y;
this.radius = radius;
}

public void draw() {
drawingAPI.drawCircle(x, y, radius); // Используем реализацию для рисования
}

public void resizeByPercentage(double pct) {
radius *= (1 + pct / 100); // Меняем размер круга
}
}


#java #bridge #pattern
👍83
⌨️ Java Concurrency API: Основные классы

Современные приложения все чаще требуют выполнения нескольких задач одновременно, и для этого Java предоставляет мощный инструмент — Concurrency API (набор классов и интерфейсов). Этот API позволяет разработчикам легко реализовывать многопоточность, управлять потоками и синхронизировать действия между ними, что значительно увеличивает производительность и отзывчивость приложений.

Основные классы:

1. Thread
- Класс для создания и управления потоками. Вы можете создать новый поток, реализовав интерфейс Runnable или расширив класс Thread.

2. Executor
- Интерфейс для управления потоками и выполнения задач. Позволяет абстрагироваться от управления потоками, сосредоточившись на логике приложения.
- ExecutorService: расширение Executor, управляющее жизненным циклом потоков.

3. Future
- Позволяет получать результаты из асинхронных задач. Используется в связке с ExecutorService для выполнения задач в фоновом режиме.

4. CountDownLatch
- Синхронизирует потоки, позволяя одному или нескольким потокам ждать завершения других потоков перед продолжением работы.

5. CyclicBarrier
- Используется для синхронизации группы потоков. Позволяет потоку ждать, пока все другие не достигнут определенной точки.

6. Semaphore
- Контролирует доступ к ресурсу, предоставляя определенное количество разрешений для потоков.

7. BlockingQueue
- Интерфейс, предоставляющий безопасный способ обмена данными между потоками при помощи очередей. Реализации включают ArrayBlockingQueue, LinkedBlockingQueue и другие.

#java #ConcurrencyAPI #Thread
Please open Telegram to view this post
VIEW IN TELEGRAM
👍142🔥2
⌨️ Пример реализации CountDownLatch

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

Пример кода:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {

public static void main(String[] args) throws InterruptedException {
// Создаем CountDownLatch для трех потоков
CountDownLatch latch = new CountDownLatch(3);

// Запускаем три потокa
for (int i = 0; i < 3; i++) {
new Thread(new Task(latch)).start();
}

// Основной поток ждет, пока все потоки завершатся
latch.await();
System.out.println("Все задачи завершены, продолжение работы основного потока.");
}
}

class Task implements Runnable {
private final CountDownLatch latch;

public Task(CountDownLatch latch) {
this.latch = latch;
}

@Override
public void run() {
try {
// Симуляция работы
Thread.sleep((long) (Math.random() * 1000));
System.out.println("Задача завершена: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// Уменьшаем счетчик
latch.countDown();
}
}
}


Описание кода:

1. Создание CountDownLatch: Мы создаем объект CountDownLatch с начальным значением 3, что означает, что основной поток будет ожидать завершения трех потоков.

2. Запуск потоков: В цикле запускаем три потока, каждый из которых выполняет задачу.

3. Ожидание завершения: В основном потоке вызываем latch.await(), который блокирует его выполнение до тех пор, пока счетчик не достигает нуля.

4. Задача: Каждый поток выполняет свою задачу (симуляция работы) и вызывает latch.countDown() в блоке finally, уменьшая счетчик.

Когда все три потока завершатся, основной поток продолжит выполнение и выведет сообщение.

#java #CountDownLatch
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍2
⌨️ HashMap и TreeMap: Когда и как использовать

В Java коллекции Map предоставляют возможность хранить пары "ключ-значение". Два популярных варианта — HashMap и TreeMap. Давайте разберем их ключевые особенности и когда их лучше использовать.

📚 Kраткие определения:

- HashMap:
  - Неупорядоченная коллекция.
  - Основан на хэш-таблице.

- TreeMap:
  - Упорядоченная коллекция.
  - Основан на красно-черном дереве.
  - Поддерживает сортировку по ключам.

⚖️ Когда использовать:

- HashMap:
  - Если важна производительность и порядок хранения не имеет значения.
  - Когда нужно быстро получать значения по ключу

- TreeMap:
  - Если необходимо хранить элементы в отсортированном порядке.
  - Для использования функционала "примитивного" поиска (например, firstKey() или lastKey()).

📌 Подведение итогов:

Выбор между HashMap и TreeMap зависит от ваших требований к производительности и порядку элементов. Помните, что HashMap лучше подходит для большинства случаев, когда необходим быстрый доступ, а TreeMap — для упорядоченного хранения данных.

#java #HashMap #TreeMap
Please open Telegram to view this post
VIEW IN TELEGRAM
👍132
public class Quest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3);
List<Integer> subList = list.subList(1, 2);
subList.set(0, 5);
System.out.println(list.get(1));
}
}


#java #quest
2👎1
⌨️ Сравнение объектов с помощью Comparator и Comparable

В Java для сортировки объектов используются два ключевых интерфейса: Comparable и Comparator. Понимание их отличий и применения поможет вам эффективно управлять сортировкой данных в коллекциях.

📚 Краткие определения:

- Comparable:
- Используется, когда класс должен иметь естественный порядок. Например, мы можем сортировать людей по возрасту.
- Сигнатура метода: int compareTo(T o).

Пример:

class Person implements Comparable<Person> {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public int getAge() {
return age;
}

@Override
public int compareTo(Person other) {
return Integer.compare(this.age, other.age); // Сравнение по возрасту
}
}


- Comparator:
- Используется для создания внешних стратегий сравнения, позволяя определять несколько способов сортировки. Подходит для сортировки объектов разных классов.
- Сигнатура метода: int compare(T o1, T o2).

Пример:

import java.util.Comparator;

class NameComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name); // Сравнение по имени
}
}


⚖️ Когда использовать:

- Comparable:
- Используйте, если у класса есть естественный порядок. Например, чтобы сортировать людей по возрасту.
- Сортировка с помощью Collections.sort() или Arrays.sort() будет проще.

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

List<Person> people = Arrays.asList(new Person("Alice", 30), new Person("Bob", 25));
Collections.sort(people); // Сортировка по возрасту с использованием Comparable


- Comparator:
- Идеален для ситуаций, когда необходимо изменить порядок сортировки или сравнивать объекты разных классов.

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

List<Person> people = Arrays.asList(new Person("Alice", 30), new Person("Bob", 25));
Collections.sort(people, new NameComparator()); // Сортировка по имени с использованием Comparator


#java #Comparator #Comparable
Please open Telegram to view this post
VIEW IN TELEGRAM
👍113
public class Quest {
public static void main(String[] args) {
int b = 500;
int c = inc(b);
System.out.println(b==c);
}
private static int inc(int num) {
return num++;
}
}


#java #quest
2👎2
Что выведет код?
Anonymous Quiz
35%
true
50%
false
15%
<Ошибка компиляции>
🎉2
🌐 REST и RESTful API

REST (Representational State Transfer) — это архитектурный стиль для проектирования сетевых приложений. Он использует стандартные методы HTTP и акцентирует внимание на взаимодействии между клиентом и сервером с помощью ресурсов.

Основные принципы REST:

1️⃣ Клиент-серверная архитектура:
- Четкое разделение между клиентом и сервером. Клиент отвечает за пользовательский интерфейс, а сервер — за обработку данных и бизнес-логику.

2️⃣ Статус и состояние:
- Каждый запрос от клиента к серверу должен содержать всю необходимую информацию для обработки запроса. Сервер не хранит состояние сеанса (stateless).

3️⃣ Кэшируемость:
- Ответы должны быть явно обнародованы как кэшируемые или не кэшируемые. Это позволяет уменьшить количество запросов к серверу и улучшает производительность.

4️⃣ Единообразие интерфейса:
- Все взаимодействия между клиентом и сервером осуществляются через четкий и единообразный интерфейс, что упрощает интеграцию.

5️⃣ Многоуровневость:
- Система может быть структурирована на несколько уровней, где каждый уровень может вызывать другой, обеспечивая гибкость и упрощая управление.


RESTful API — это API, который следует принципам REST. Это интерфейс, который использует стандартные HTTP методы для выполнения операций с ресурсами, представленными в виде URI.

Основные HTTP методы в RESTful API:

1️⃣ GET:
- Используется для получения информации о ресурсе.
- Пример: GET /users — получить список всех пользователей.

2️⃣ POST:
- Используется для создания нового ресурса.
- Пример: POST /users — создать нового пользователя.

3️⃣ PUT:
- Используется для обновления существующего ресурса (полное обновление).
- Пример: PUT /users/1 — обновить информацию о пользователе с ID=1.

4️⃣ PATCH:
- Используется для частичного обновления ресурса.
- Пример: PATCH /users/1 — обновить определенные поля у пользователя с ID=1.

5️⃣ DELETE:
- Используется для удаления ресурса.
- Пример: DELETE /users/1 — удалить пользователя с ID=1.

RESTful API обычно возвращают данные в форматах:

- JSON (JavaScript Object Notation): легковесный формат, который легко читаем и записывается как людьми, так и машинами. Это самый распространенный формат для передачи данных в RESTful API.

- XML (eXtensible Markup Language): более старый формат, который также используется, но менее популярен в новых приложениях.

Применение RESTful API:

✔️ Веб-приложения: RESTful API часто используются в веб-приложениях для взаимодействия с серверами и базами данных.

✔️ Мобильные приложения: Многие мобильные приложения используют RESTful API для получения данных.

✔️ Интеграция систем: RESTful API позволяют различным системам взаимодействовать друг с другом с минимальными усилиями.

#REST #RESTfulAPI #API
👍165🔥2❤‍🔥1
⌨️ Создание RESTful API на Spring Boot

1️⃣ Настройка проекта

Создайте новый проект Spring Boot, добавив зависимости Spring Web и Lombok.

2️⃣ Создание модели User


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private String email;
}

Модель данных для пользователя с свойствами ID, именем и email.

3️⃣ Создание контроллера UserController


import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {
private final List<User> users = new ArrayList<>();

@GetMapping
public List<User> getAllUsers() {
return users; // Получение всех пользователей
}

@PostMapping
public User createUser(@RequestBody User user) {
users.add(user); // Создание нового пользователя
return user;
}
}

Контроллер, обрабатывающий запросы для получения и создания пользователей.

4️⃣ Примеры запросов

✔️ Получение всех пользователей:

Запрос:

GET /api/users

Возвращает список всех пользователей.

✔️ Создание нового пользователя:

Запрос:

POST /api/users
Content-Type: application/json

{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com"
}

Создаёт нового пользователя и возвращает его данные.

5️⃣ Тестирование API с Postman

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

#java #RESTfulAPI #SpringBoot
Please open Telegram to view this post
VIEW IN TELEGRAM
👍132🔥1🎉1
⌨️ Что такое Пул Потоков (Thread Pool)?

Пул потоков (Thread Pool) — это группа заранее созданных потоков, готовых к выполнению задач. Вместо создания нового потока для каждой задачи, пул повторно использует уже существующие потоки, что снижает нагрузку на систему и повышает производительность приложения.

📌 Основные виды пулов потоков:

1️⃣ Фиксированный Пул (Fixed Thread Pool):
- Создает заданное количество потоков. Если все потоки заняты, новые задачи ждут в очереди.


ExecutorService executor = Executors.newFixedThreadPool(5);


2️⃣ Пул с переменным количеством потоков (Cached Thread Pool):
- Создает новые потоки по мере необходимости и уничтожает неактивные потоки после определенного времени.


ExecutorService executor = Executors.newCachedThreadPool();


3️⃣ Планировщик (Scheduled Thread Pool):
- Позволяет выполнять задачи с задержкой или периодически.


ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);


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

#java #ThreadPool
Please open Telegram to view this post
VIEW IN TELEGRAM
👍61
⌨️ Использование логов: Log4j и SLF4J

Логирование — важная часть программирования, позволяющая отслеживать поведение приложения, находить и устранять ошибки, а также анализировать производительность. В мире Java два наиболее популярных инструмента для логирования — Log4j и SLF4J. Разберем их, выясним, в чем их отличия, и как их эффективно использовать!

🛠 Log4j — это мощная библиотека логирования, создающая гибкий и настраиваемый механизм для записи отчетов о различных событиях.

Плюсы:
- Гибкая настройка: Вы можете четко настраивать уровни логирования, выводить логи в различные форматы и хранить их в файлах или базах данных.
- Аспектная ориентация: Легко создавать сложные правила логирования.

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

1. Добавление зависимостейpom.xml):


<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>


2. Создание конфигурации (log4j.properties):


log4j.rootLogger=DEBUG, FILE

log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=application.log
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n


3. Пример кода:


import org.apache.log4j.Logger;

public class Log4jExample {
static Logger logger = Logger.getLogger(Log4jExample.class);

public static void main(String[] args) {
logger.info("Информационное сообщение");
logger.error("Ошибка!", new Exception("Пример исключения"));
}
}


📖 SLF4J (Simple Logging Facade for Java) — это абстракция для разных библиотек логирования, которая позволяет сменить библиотеку логирования без изменения кода.

Плюсы:
- Универсальность: Поддерживает различные реализации логирования, что позволяет быть гибким в выборе инструментов.
- Чистота кода: Позволяет более лаконично управлять логами.

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

1. Добавление зависимостейpom.xml):


<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>


2. Пример кода:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jExample {
private static final Logger logger = LoggerFactory.getLogger(Slf4jExample.class);

public static void main(String[] args) {
logger.info("Информационное сообщение с SLF4J");
logger.error("Ошибка!", new Exception("Пример исключения"));
}
}


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

- Log4j — когда вам нужен детальный контроль и настройка логирования. Хорошо подходит для сложных приложений, требующих многофункционального логирования.

- SLF4J — когда нужна гибкость и возможность переключаться между различными библиотеками логирования. Идеален для разработчиков, работающих с несколькими проектами.

💡 Итог: Используйте SLF4J для создания абстрактного логирования и Log4j в качестве конкретной реализации, чтобы не только создать гибкую, но и мощную систему логирования для вашего Java-приложения!

#java #Logging #Log4j #SLF4J
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍102
😁22🤣42👍1
⌨️ Использование Fixed Thread Pool

1️⃣ Создание пула потоков

Для начала нужно создать экземпляр ExecutorService, который будет представлять ваш пул потоков. В этом примере мы создадим фиксированный пул из 5 потоков:
ExecutorService executor = Executors.newFixedThreadPool(5);


2️⃣ Отправка задач для выполнения

После создания пула, вы можете отправлять ему задачи с помощью метода submit(). В качестве примера, давайте создадим несколько задач, которые будут выполняться параллельно:
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Задача " + taskId + " выполняется в: " + Thread.currentThread().getName());
// Имитация длительной задачи
try {
Thread.sleep(1000); // Задержка на 1 секунду
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Задача " + taskId + " завершена в: " + Thread.currentThread().getName());
});
}


3️⃣ Завершение работы пула

После того как все задачи были отправлены, важно завершить работу пула, чтобы освободить ресурсы:
executor.shutdown(); // Ждем завершения всех задач


В этом примере создается пул из 5 потоков, и на него отправляется 10 задач. Поскольку пул состоит из 5 потоков, одновременно будут выполняться не более 5 задач. Остальные задачи будут ожидать своей очереди. Это демонстрирует, как Thread Pool управляет ресурсами и оптимизирует обработку задач.

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

#java #FixedThreadPool
Please open Telegram to view this post
VIEW IN TELEGRAM
👍71
public class Quest {
public static void main(String[] args) {
System.out.print(op1() + op2() * op3());
}

private static int op1() {
System.out.print(1);
return 1;
}
private static int op2() {
System.out.print(2);
return 2;
}
private static int op3() {
System.out.print(3);
return 3;
}
}


#java #quest
👍3👎21
Что выведет код?
Anonymous Quiz
49%
1237
30%
2317
15%
1239
5%
2319
🤯15👍8🎉1
⌨️Метапрограммирование: Использование Reflection для динамического изменения кода

Reflection — это механизм в Java, который позволяет программам анализировать и манипулировать объектами во время выполнения. С помощью рефлексии вы можете:

✔️ Получать информацию о классах, методах и полях. Это позволяет динамически исследовать структуры данных вашего приложения.

✔️ Создавать экземпляры классов: Вы можете создавать объекты, не зная их тип на этапе компиляции.

✔️ Вызывать методы динамически: Это дает возможность запускать методы объектов, основываясь на их именах, переданных в виде строк.

✔️ Изменять значения полей: Вы можете динамически изменять значения полей объектов, что может быть особенно полезно в контексте тестирования или сериализации.

Преимущества использования рефлексии:

✔️ Потенциал для создания более абстрактного кода: Вы можете реализовать паттерны проектирования, такие как Dependency Injection.

✔️ Упрощение работы с библиотеками и фреймворками: Многие популярные библиотеки, такие как Spring и Hibernate, активно используют рефлексию для создания и управления объектами.

В следующем посте мы рассмотрим конкретные примеры использования рефлексии на практике. Оставайтесь с нами! 🚀

#Java #Метапрограммирование #Reflection
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
⌨️ Пример реализации Dependency Injection с использованием Reflection

Продолжаем тему метапрограммирования и поговорим о том, как реализовать паттерн проектирования Dependency Injection (DI) с использованием рефлексии. DI позволяет нам разделить зависимости и упростить управление ими, что делает код более чистым и тестируемым.

Пример: Простой контейнер для Dependency Injection

В этом примере мы создадим простой контейнер, который будет автоматически связывать зависимости на основе аннотаций. Для этого нам понадобятся:

1. Аннотация @Inject для пометки полей, которые мы хотим внедрить.
2. Контейнер для обработки аннотаций и создания объектов.

Шаг 1: Создаем аннотацию


import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface Inject {}


Шаг 2: Создаем классы с зависимостями


class Service {
public void serve() {
System.out.println("Service is working!");
}
}

class Client {
@Inject
private Service service;

public void doSomething() {
service.serve();
}
}


Шаг 3: Реализуем контейнер для Dependency Injection


import java.lang.reflect.Field;

class DIContainer {
public static void inject(Object obj) {
Class<?> clazz = obj.getClass();

for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
try {
field.setAccessible(true); // Делаем поле доступным
Object dependency = field.getType().getDeclaredConstructor().newInstance(); // Создаем экземпляр зависимости
field.set(obj, dependency); // Внедряем зависимость
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}


Шаг 4: Используем наш контейнер


public class Main {
public static void main(String[] args) {
Client client = new Client();
DIContainer.inject(client); // Внедряем зависимости

client.doSomething(); // Вывод: Service is working!
}
}


Как это работает?

1️⃣ Мы создаем аннотацию @Inject, чтобы пометить поля, которые должны быть внедрены.

2️⃣ В классе Client поле service помечено этой аннотацией.

3️⃣ Контейнер DIContainer проходит по полям класса, ищет аннотацию @Inject, и, если находит, создает объект зависимости и устанавливает его в поле.

4️⃣ Когда мы вызываем метод doSomething, объект Service успешно внедрен, и мы можем использовать его методы.

#Java #Метапрограммирование #DependencyInjection
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥31
public class Quest {
public static void main(String[] args) {
System.out.println(4_32l + 4_32l);
}
}
👍4👎2