Quartz - это мощная библиотека с открытым исходным кодом для планирования и выполнения задач по расписанию в Java-приложениях. Она предоставляет гибкие возможности для определения сложных расписаний выполнения задач и поддерживает кластеризацию.
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.time.LocalDateTime;
public class BackupSchedulerExample {
public static void main(String[] args) throws SchedulerException {
// Создаем планировщик
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// Определяем задачу
JobDetail job = JobBuilder.newJob(DatabaseBackupJob.class)
.withIdentity("databaseBackupJob", "backupGroup")
.build();
// Создаем триггер для запуска задачи каждый день в 2:00
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("dailyBackupTrigger", "backupGroup")
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(2, 0))
.build();
// Планируем задачу с триггером
scheduler.scheduleJob(job, trigger);
// Запускаем планировщик
scheduler.start();
System.out.println("Планировщик резервного копирования запущен.");
System.out.println("Следующее резервное копирование запланировано на: "
+ trigger.getNextFireTime());
// Оставляем планировщик работать
try {
Thread.sleep(60000); // Ждем 1 минуту для демонстрации
} catch (InterruptedException e) {
e.printStackTrace();
}
// Останавливаем планировщик
scheduler.shutdown();
}
// Класс, представляющий задачу резервного копирования
public static class DatabaseBackupJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println("Выполняется резервное копирование базы данных: "
+ LocalDateTime.now());
// Здесь был бы реальный код для резервного копирования
}
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍6🔥1🗿1
Начиная с Java 16, проверка типов с помощью
instanceof стала гораздо удобнее благодаря Pattern Matching. Раньше, после проверки объекта на принадлежность к определённому классу, нужно было выполнять явное приведение типа. Теперь это можно сделать в одной строке.Пример до Java 16:
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str.length());
}
Пример с Pattern Matching:
if (obj instanceof String str) {
System.out.println(str.length());
}
Теперь после
instanceof можно сразу же использовать объект нужного типа в коде, что делает программу более читабельной и компактной.#java #PatternMatching #instanceof
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥3❤1
import org.mindrot.jbcrypt.BCrypt;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class AuthenticationSystem {
private static Map users = new HashMap<>();
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("1. Регистрация");
System.out.println("2. Вход");
System.out.println("3. Выход");
System.out.print("Выберите действие: ");
int choice = scanner.nextInt();
scanner.nextLine(); // Очистка буфера
switch (choice) {
case 1:
register(scanner);
break;
case 2:
login(scanner);
break;
case 3:
System.out.println("До свидания!");
return;
default:
System.out.println("Неверный выбор. Попробуйте снова.");
}
}
}
private static void register(Scanner scanner) {
System.out.print("Введите имя пользователя: ");
String username = scanner.nextLine();
System.out.print("Введите пароль: ");
String password = scanner.nextLine();
String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
users.put(username, hashedPassword);
System.out.println("Регистрация успешна!");
}
private static void login(Scanner scanner) {
System.out.print("Введите имя пользователя: ");
String username = scanner.nextLine();
System.out.print("Введите пароль: ");
String password = scanner.nextLine();
if (users.containsKey(username) && BCrypt.checkpw(password, users.get(username))) {
System.out.println("Вход выполнен успешно!");
} else {
System.out.println("Неверное имя пользователя или пароль.");
}
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤2🥰1
Для того что бы определить лямбда-выражение, нам нужен функциональный интерфейс. Изобретём свой:
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
Функциональный интерфейс
MathOperation содержит один абстрактный метод operate, который принимает два значения типа int и возвращает int. Аннотация @FunctionalInterface указывает на то, что интерфейс предназначен для использования в функциональном программировании и должен содержать только один абстрактный метод. Но аннотация не обязательна.Использование лямбда-выражения:
public class LambdaExample {
public static void main(String[] args) {
// Реализация интерфейса с помощью лямбда-выражения
MathOperation addition = (a, b) -> a + b;
MathOperation subtraction = (a, b) -> a - b;
MathOperation multiplication = (a, b) -> a * b;
MathOperation division = (a, b) -> a / b;
int x = 10;
int y = 5;
System.out.println("Addition: " + operate(x, y, addition)); // 15
System.out.println("Subtraction: " + operate(x, y, subtraction)); // 5
System.out.println("Multiplication: " + operate(x, y, multiplication)); // 50
System.out.println("Division: " + operate(x, y, division)); // 2
}
// Метод, принимающий MathOperation и применяющий его к данным
private static int operate(int a, int b, MathOperation operation) {
return operation.operate(a, b);
}
}
В нашем примере лямбда-выражения используются для создания экземпляров интерфейса
MathOperation для выполнения различных математических операций (сложение, вычитание и т.д.).Метод
operate принимает два целых числа и функциональный интерфейс MathOperation, затем выполняет переданную операцию. В метод в третьем параметре можно передать непосредственно лямбда-выражение, не используя промежуточную переменную.#java #lambda #FunctionalInterface
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤3
Автоупаковка - это механизм неявной инициализации объектов классов-оберток (
Byte, Short, Integer, Long, Float, Double, Character, Boolean) значениями соответствующих им исходных примитивных типов (byte, short, int...), без явного использования конструктора класса.Автоупаковка происходит при прямом присваивании примитива классу-обертке (с помощью оператора
=), либо при передаче примитива в параметры метода (типа класса-обертки).Автоупаковке в классы-обертки могут быть подвергнуты как переменные примитивных типов, так и константы времени компиляции (литералы и
final-примитивы). При этом литералы должны быть синтаксически корректными для инициализации переменной исходного примитивного типа.Автоупаковка переменных примитивных типов требует точного соответствия типа исходного примитива типу класса-обертки. Например, попытка упаковать переменную типа
byte в Short, без предварительного явного приведения byte в short вызовет ошибку компиляции.Автоупаковка констант примитивных типов допускает более широкие границы соответствия. В этом случае компилятор способен предварительно осуществлять неявное расширение/сужение типа примитивов:
✔️ неявное расширение/сужение исходного типа примитива до типа примитива, соответствующего классу-обертке (для преобразования
int в Byte, сначала компилятор самостоятельно неявно сужает int к byte)✔️ автоупаковку примитива в соответствующий класс-обертку. Однако, в этом случае существуют два дополнительных ограничения:
a) присвоение примитива обертке может производится только оператором
= (нельзя передать такой примитив в параметры метода без явного приведения типов) b) тип левого операнда не должен быть старше чем
Character, тип правого не должен старше, чем int: допустимо расширение/сужение byte в/из short, byte в/из char, short в/из char и только сужение byte из int, short из int, char из int. Все остальные варианты требуют явного приведения типов).Дополнительной особенностью целочисленных классов-оберток, созданных автоупаковкой констант в диапазоне -128 ... +127 является то, что они кэшируются JVM. Поэтому такие обертки с одинаковыми значениями будут являться ссылками на один объект.
#java #autoboxing
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1🔥1
До Java 9 весь Java-код (и твой, и JDK) хранился в виде «монолитных» JAR-файлов, и зависимости между ними не контролировались явно. Project Jigsaw решил эту проблему, введя модули, которые:
• Четко определяют, что экспортируется наружу.
• Указывают, от чего зависят.
• Позволяют уменьшить размер приложения (особенно полезно для Java SE Embedded).
• Улучшают безопасность и инкапсуляцию.
Каждый модуль содержит файл
module-info.java, в котором описываются зависимости и экспортируемые пакеты.
module com.example.app {
requires java.sql;
exports com.example.app.api;
}
✅ Преимущества модульной системы:
• Четкая структура зависимостей.
• Улучшенная читаемость и поддерживаемость.
• Компиляция и запуск только нужных модулей.
• Инкапсуляция внутренних пакетов (по умолчанию они недоступны извне).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤1
Блоки инициализации позволяют гибко управлять созданием объектов. Вот как их правильно использовать:
Выполняются перед конструктором:
public class Logger {
private String prefix;
{ // Блок инициализации экземпляра (выполняется перед конструктором)
prefix = "[LOG] " + LocalDateTime.now(); // Инициализация префикса с текущей датой/временем
System.out.println("Логгер готов!"); // Сообщение о готовности логгера
}
public Logger() {
System.out.println(prefix + " | Новый объект"); // Вывод информации о создании объекта
}
}▸ Для чего: предварительная настройка полей, валидация, логирование.
Срабатывают один раз при загрузке класса:
public class ConfigLoader {
static {
System.out.println("Загружаем конфиги...");
// Здесь можно читать файлы, подключать БД и т.д.
}
}▸ Для чего: инициализация кэшей, регистрация драйверов, загрузка ресурсов.
- Нестатические блоки → простая инициализация полей
- Статические блоки → настройка системных ресурсов
Для улучшения читаемости кода используйте блоки инициализации для простых операций. Избегайте сложной логики — это может затруднить отладку и понимание приложения.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤2
Enum в Java — это не просто перечисление констант. Это полноценные классы с конструкторами, методами и поддержкой ООП.
public enum FileFormat {
CSV {
public String parse(String data) {
return data.replace(",", ";");
}
},
JSON {
public String parse(String data) {
return "{" + data + "}";
}
};
public abstract String parse(String data);
}
public interface Formatter {
String format(String text);
}
public enum TextFormat implements Formatter {
UPPERCASE {
public String format(String text) {
return text.toUpperCase();
}
},
LOWERCASE {
public String format(String text) {
return text.toLowerCase();
}
};
}
// Пример вызова
String result = TextFormat.UPPERCASE.format("Hello"); // "HELLO"
public enum OrderStatus {
NEW(1) {
public OrderStatus next() {
return PROCESSING;
}
},
PROCESSING(2) {
public OrderStatus next() {
return SHIPPED;
}
},
SHIPPED(3) {
public OrderStatus next() {
return COMPLETED;
}
};
private final int code;
OrderStatus(int code) {
this.code = code;
}
public abstract OrderStatus next();
}
@Entity
public class Order {
@Enumerated(EnumType.STRING)
private OrderStatus status;
}
// Сохраняется как строка "NEW", "PROCESSING" в БД
public static OrderStatus findByCode(int code) {
return Arrays.stream(OrderStatus.values())
.filter(status -> status.code == code)
.findFirst()
.orElseThrow();
}
⚠️ Опасности:
▸ Перегруженность логикой нарушает принцип SRP
▸ Сложность тестирования из-за статической природы
▸ Ограниченная расширяемость после компиляции
Практические кейсы:
▸ Фабрики объектов с предопределёнными настройками
▸ State Machine для управления состоянием приложения
▸ Конфигурация параметров системы
public enum MathOperation {
PLUS("+", (a, b) -> a + b),
MINUS("-", (a, b) -> a - b);
private final String symbol;
private final IntBinaryOperator operation;
MathOperation(String symbol, IntBinaryOperator operation) {
this.symbol = symbol;
this.operation = operation;
}
public int apply(int a, int b) {
return operation.applyAsInt(a, b);
}
}
// Использование
int result = MathOperation.PLUS.apply(5, 3); // 8
String description = switch (status) {
case NEW -> "Новый заказ";
case PROCESSING -> "В обработке";
case SHIPPED -> "Отправлен";
};
Используйте
EnumSet для эффективной работы с группами констант:
EnumSet activeStatuses = EnumSet.of(NEW, PROCESSING);
if (activeStatuses.contains(currentStatus)) {
// Логика для активных статусов
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥2❤🔥1❤1
1.
implementation 'com.google.code.gson:gson:2.12.1'
2.
3.
4.
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
Gson gson = new Gson();
User user = new User("John", 25);
String json = gson.toJson(user);
System.out.println(json);
{"name":"John","age":25}
String json = "{\"name\":\"Jane\",\"age\":30}";
User user = gson.fromJson(json, User.class);
System.out.println("Имя: " + user.getName() + ", Возраст: " + user.getAge());
Имя: Jane, Возраст: 30
String jsonArray = "[{\"name\":\"John\",\"age\":25},{\"name\":\"Jane\",\"age\":30}]";
Type userListType = new TypeToken<List<User>>() {}.getType();
List<User> users = gson.fromJson(jsonArray, userListType);
for (User user : users) {
System.out.println("Имя: " + user.getName() + ", Возраст: " + user.getAge());
}
Имя: John, Возраст: 25
Имя: Jane, Возраст: 30
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation()
.create();
String json = gson.toJson(user);
System.out.println(json);
{
"name": "John",
"age": 25
}
try {
User user = gson.fromJson("{invalid_json}", User.class);
} catch (JsonSyntaxException e) {
e.printStackTrace();
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤2👾1
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
public interface ApiService {
@GET("users")
Call<List<User>> getUsers();
}
ApiService apiService = retrofit.create(ApiService.class);
apiService.getUsers().enqueue(new Callback>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.isSuccessful()) {
List<User> users = response.body();
// Обработка данных
} else {
// Обработка ошибки
}
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
// Обработка ошибки сети
}
});
public class User {
private int id;
private String name;
public int getId() {
return id;
}
public String getName() {
return name;
}
}
public interface ApiService {
@GET("users/{id}")
Call<User> getUser(@Path("id") int userId);
}
apiService.getUser(1).enqueue(new Callback() {
@Override
public void onResponse(Call<User> call, Response response) {
if (response.isSuccessful()) {
User user = response.body();
// Обработка данных пользователя
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
// Обработка ошибки сети
}
});
public interface ApiService {
@GET("users")
Call<List<User>> getUsers(@Header("Authorization") String token);
}
public interface ApiService {
@POST("users")
Call<User> createUser(@Body User user);
@GET("users")
Call<List<User>> getUsers(@Query("page") int page);
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤1
Java является строго типизированным языком программирования, а это означает, то что каждое выражение и каждая переменная имеет строго определенный тип уже на момент компиляции. Однако определен механизм приведения типов (casting) - способ преобразования значения переменной одного типа в значение другого типа.
В Java существуют несколько разновидностей приведения:
✔️ Тождественное (identity). Преобразование выражения любого типа к точно такому же типу всегда допустимо и происходит автоматически.
✔️ Расширение (повышение, upcasting) примитивного типа (widening primitive). Означает, что осуществляется переход от менее емкого типа к более ёмкому. Например, от типа
byte (длина 1 байт) к типу int (длина 4 байта). Такие преобразование безопасны в том смысле, что новый тип всегда гарантировано вмещает в себя все данные, которые хранились в старом типе и таким образом не происходит потери данных. Этот тип приведения всегда допустим и происходит автоматически.✔️ Сужение (понижение, downcasting) примитивного типа (narrowing primitive). Означает, что переход осуществляется от более емкого типа к менее емкому. При таком преобразовании есть риск потерять данные. Например, если число типа
int было больше 127, то при приведении его к byte значения битов старше восьмого будут потеряны. В Java такое преобразование должно совершаться явным образом, при этом все старшие биты, не умещающиеся в новом типе, просто отбрасываются - никакого округления или других действий для получения более корректного результата не производится.✔️ Расширение объектного типа (widening reference). Означает неявное восходящее приведение типов или переход от более конкретного типа к менее конкретному, т.е. переход от потомка к предку. Разрешено всегда и происходит автоматически.
✔️ Сужение объектного типа (narrowing reference). Означает нисходящее приведение, то есть приведение от предка к потомку (подтипу). Возможно только если исходная переменная является подтипом приводимого типа. При несоответствии типов в момент выполнения выбрасывается исключение
ClassCastException. Требует явного указания типа.✔️ Преобразование к строке (to String). Любой тип может быть приведен к строке, т.е. к экземпляру класса
String.✔️ Запрещенные преобразования (forbidden). Не все приведения между произвольными типами допустимы. Например, к запрещенным преобразованиям относятся приведения от любого ссылочного типа к примитивному и наоборот (кроме преобразования к строке). Кроме того, невозможно привести друг к другу классы, находящиеся на разных ветвях дерева наследования и т.п.
При приведении ссылочных типов с самим объектом ничего не происходит, - меняется лишь тип ссылки, через которую происходит обращение к объекту.
Для проверки возможности приведения нужно воспользоваться оператором
instanceof:
Parent parent = new Child();
if (parent instanceof Child) {
Child child = (Child) parent;
}
#java #casting #upcasting #downcasting
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤2
Использование "сырых" типов коллекций (вроде
List вместо List<String>) опасно! Generics (<>) обеспечивают безопасность типов на этапе компиляции.
// Опасно - можно добавить что угодно, ошибка будет при извлечении
List unsafeList = new ArrayList();
unsafeList.add("Привет");
unsafeList.add(123); // Компилятор не ругается!
// String s = (String) unsafeList.get(1); // ClassCastException во время выполнения!
// Безопасно - только строки!
List<String> safeList = new ArrayList<>();
safeList.add("Привет");
// safeList.add(123); // Ошибка компиляции! Нельзя добавить int в List<String>
String s = safeList.get(0); // Никакого приведения типов не нужно!
#java #generics #typesafety #bestpractice
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤1
Старый API даты/времени был неудобным и изменяемым (mutable). Новый пакет
java.time (LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Duration, Period) решает эти проблемы!
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(2024, Month.DECEMBER, 31);
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS); // Неизменяемый! Возвращает новый объект
System.out.println("Сегодня: " + today);
System.out.println("День рождения: " + birthday);
System.out.println("Через неделю: " + nextWeek);
// Вывод:
// Сегодня: 2023-10-27 (пример)
// День рождения: 2024-12-31
// Через неделю: 2023-11-03 (пример)
Используйте современный, потокобезопасный и интуитивно понятный API! ✨
#java #datetime #java8 #javatime #api #bestpractice
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤1🔥1👏1
Ключевое слово
interface используется для создания полностью абстрактных классов. Основное предназначение интерфейса - определять каким образом мы можем использовать класс, который его реализует. Создатель интерфейса определяет имена методов, списки аргументов и типы возвращаемых значений, но не реализует их поведение. Все методы неявно объявляются как public.Начиная с Java 8 в интерфейсах разрешается размещать реализацию методов по умолчанию
default и статических static методов.Интерфейс также может содержать и поля. В этом случае они автоматически являются публичными
public, статическими static и неизменяемыми final.Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤2
Java поддерживает Unicode во всех частях кода, включая комментарии и имена переменных. Это открывает неочевидные возможности для экспериментов, но требует осторожности в production-коде.
// \u000d System.out.println("Этот код выполнится!");
После компиляции символ
\u000d превращается в перенос строки, и код становится:
//
System.out.println("Этот код выполнится!");
Применение:
▸ Демонстрация скрытых уязвимостей в презентациях
▸ Образовательные эксперименты с компиляцией
▸ Технические розыгрыши (только для небоевых проектов!)
int размер = 10; // переменная "размер"
System.out.println(размер); // 10
▸ Локализация кода для образовательных проектов
▸ Поддержка специфических терминов на национальных языках
System.out.println("\u2591\u2592\u2593"); // Вывод символов псевдографики: ░ ▒ ▓ (разные уровни заливки)
System.out.println("\u265A \u265B"); // Вывод юникод-символов: ♚ ♛ (шахматные фигуры короля и королевы)
▸ Консольные интерфейсы с псевдографикой
▸ Визуализация данных в текстовом режиме
▸ Генерация ASCII-арта
String regex = "\\p{So}"; // Шаблон для эмодзи и символов
String text = "Alert! ⚠️";
System.out.println(text.replaceAll(regex, "[символ]")); // Alert! [символ]
⚠️ Важные предупреждения:
▸ Код с Unicode-трюками не проходит код-ревью в серьезных проектах
▸ Может вызывать проблемы с линтерами и статическими анализаторами
▸ Затрудняет поиск в кодовой базе (например,
\u000d)
// Локализация через ResourceBundle
ResourceBundle bundle = ResourceBundle.getBundle("Messages_ru"); // Загрузка ресурсов для локали ru
String message = bundle.getString("welcome"); // Получение локализованной строки по ключу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1
ThreadLocal— потокобезопасное хранилище данных ThreadLocal позволяет хранить данные, уникальные для каждого потока, без использования синхронизации. Это особенно полезно в многопоточных приложениях, где необходимо сохранять состояние для конкретного потока.
private static final ThreadLocal<DateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
void formatDate(Date date) {
System.out.println(dateFormat.get().format(date)); // У каждого потока свой экземпляр
}
▸ Избегает создания нового объекта для каждого вызова
▸ Гарантирует потокобезопасность без блокировок
public class SecurityContext {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void login(User user) {
currentUser.set(user);
}
public static User getCurrentUser() {
return currentUser.get();
}
public static void logout() {
currentUser.remove();
}
}
// В фильтре сервлета
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
SecurityContext.login((User) req.getAttribute("user"));
try {
chain.doFilter(req, res);
} finally {
SecurityContext.logout();
}
}
private static final ThreadLocal<Map<String, String>> cache =
ThreadLocal.withInitial(HashMap::new);
void processData(String key) {
if (!cache.get().containsKey(key)) {
cache.get().put(key, expensiveOperation(key));
}
System.out.println(cache.get().get(key));
}
⚠️ Опасности:
▸ Утечки памяти в пулах потоков (например, Tomcat)
▸ Неожиданное поведение при повторном использовании потоков
▸ Сложность отладки из-за неявной передачи данных
▸ Контекст пользователя в веб-приложениях
▸ Локальный кэш для тяжелых вычислений
▸ Параметры локали и форматирования дат
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤2🎉1
Лямбда-выражения на первый взгляд могут показаться чем-то сложным и загадочным, но на самом деле они просты и интуитивно понятны.
Лямбда-выражение — это лаконичный способ описания анонимной функции, которую можно передать в качестве параметра или сохранить в переменной для последующего использования.
Если говорить ещё проще, лямбда-выражение — это просто другой способ создания и реализации объекта определённого типа. Рассмотрим это на примере создания нового потока.
У класса
Thread есть конструктор:
public Thread(Runnable target) {
...
}
То есть в конструктор нужно передать объект типа
Runnable. До лямбда-выражений мы сделали бы так:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();
Здесь мы создаём анонимный класс, реализующий интерфейс
Runnable, с определённым методом run.Если использовать лямбда-выражение, тот же код будет выглядеть следующим образом:
Runnable r = () -> System.out.println("Hello World");
new Thread(r).start();
Или проще:
new Thread(() -> System.out.println("Hello World")).start();
Лямбда-выражение заменяет собой анонимный класс, который раньше был бы необходим для реализации
Runnable. Лямбда-выражение может использоваться только там, где ожидается реализация функционального интерфейса — интерфейса с единственным абстрактным методом. А интерфейс
Runnable именно такой:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Функциональный интерфейс должен содержать только один абстрактный метод, чтобы компилятор мог точно определить, какой метод реализует лямбда-выражение. В противном случае возникли бы неоднозначности и ошибки.
#java #lambda #Runnable
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤5🔥2🤣2
throws. Обычно связаны с внешними ресурсами: файлами, сетью.
public void readConfigFile() {
try (FileInputStream file = new FileInputStream("config.txt")) {
int data = file.read();
System.out.println("Данные: " + data);
} catch (FileNotFoundException e) {
System.err.println("Файл не найден!");
} catch (IOException e) {
System.err.println("Ошибка ввода-вывода: " + e.getMessage());
}
}
RuntimeException. Не требуют обработки, сигнализируют о логических ошибках.
public void printLength(String text) {
if (text == null) throw new IllegalArgumentException("Строка не может быть null!");
System.out.println("Длина: " + text.length());
}
public int divide(int a, int b) {
if (b == 0) throw new ArithmeticException("Деление на ноль!");
return a / b;
}
1. Для checked используйте try или try-with-resources.
2. Для unchecked проверяйте входные данные.
3. Документируйте исключения через @throws.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13🤝3👍1
type Vehicle {
id: ID!
type: String!
modelCode: String!
}
type Query {
vehicles: [Vehicle!]!
}@Controller
public class VehicleController {
@QueryMapping
public List vehicles() {
return List.of(new Vehicle("1", "bus", "XYZ123"));
}
}
{
vehicles {
id
type
}
}{
"data": {
"vehicles": [{ "id": "1", "type": "bus" }]
}
}public interface VehicleRepo extends JpaRepository, QuerydslPredicateExecutor {}
org.springframework.boot
spring-boot-starter-graphql
com.querydsl
querydsl-jpa
1. Включите GraphiQL для интерактивных запросов.
2. Используйте @MutationMapping для мутаций.
3. Настройте кеширование через CacheControl.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤1
@Entity
@Audited
public class Post {
@Id @GeneratedValue
private Long id;
private String title;
private String slug;
}
public interface PostRepository extends JpaRepository, RevisionRepository { } 💠 Доступны методы findRevisions() и findLastChangeRevision() для получения истории изменений.🔻 Envers создает таблицы _AUD и REVINFO для хранения версий и метаданных. При изменениях данные копируются в _AUD с типом операции (INSERT, UPDATE, DELETE).
Пример использования:
for (Revision rev : postRepository.findRevisions(postId)) {
System.out.println("Версия " + rev.getRevisionNumber() + ": " + rev.getEntity());
} 1. Добавьте зависимости hibernate-envers и spring-data-envers.
2. Включите @EnableJpaAuditing в конфигурации.
3. Для расширенного аудита используйте @CreatedBy и @LastModifiedBy.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👌2
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public Mono getUser(@PathVariable Long id) {
return userRepository.findById(id);
}
@GetMapping("/{id}/orders")
public Flux getUserOrders(@PathVariable Long id) {
return userRepository.findById(id)
.flatMapMany(orderRepository::findByUser);
}
}
WebClient.create("http://service.com")
.get()
.uri("/data")
.retrieve()
.bodyToFlux(Data.class)
.subscribe(System.out::println); 1. Используйте WebFlux для микросервисов с высокой нагрузкой.
2. Комбинируйте с R2DBC для реактивного доступа к БД.
3. Избегайте блокирующих вызовов (JDBC, JPA) в реактивных цепочках.
Please open Telegram to view this post
VIEW IN TELEGRAM
💯6❤3👍3