Для того что бы определить лямбда-выражение, нам нужен функциональный интерфейс. Изобретём свой:
@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
1.
2.
3.
4.
implementation 'androidx.room:room-runtime:2.6.1'
annotationProcessor 'androidx.room:room-compiler:2.6.1'
implementation 'androidx.room:room-ktx:2.6.1'
@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
public int id;
@ColumnInfo(name = "name")
public String name;
@ColumnInfo(name = "age")
public int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
@Dao
public interface UserDao {
@Insert
void insert(User user);
@Query("SELECT * FROM users")
List getAllUsers();
@Delete
void delete(User user);
}
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, "database-name").build();
UserDao userDao = db.userDao();
// Получение LiveData-списка всех пользователей
@Query("SELECT * FROM users")
LiveData<List<User>> getAllUsers(); // LiveData<List<User>> - тип возвращаемого значения
userDao.getAllUsers().observe(this, users -> {
// Обновление интерфейса при изменении данных
});static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE users ADD COLUMN email TEXT");
}
};
AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, "database-name")
.addMigrations(MIGRATION_1_2)
.build();1.
2.
3.
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertOrUpdate(User user);
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3
В Java 9 появилось два новых метода, полезных для выбора элементов потока с хорошей производительностью: takeWhile и dropWhile.
Допустим, у нас есть следующий список блюд:
List<Dish> specialMenu = Arrays.asList(
new Dish("seasonal fruit", 120),
new Dish("prawns", 300),
new Dish("rice", 350),
new Dish("chicken", 400),
new Dish("french fries", 530));
Для получения блюд с калорийностью меньше 320, можно воспользоваться операцией
filter. Недостаток операции filter в том, что она требует прохода в цикле по всему потоку данных с применением предиката ко всем элементам.В нашем примере список уже отсортирован по числу калорий. Вместо того, чтобы пройтись по каждому элементу, можно прекратить работу сразу же после обнаружения блюда, содержащего 320 калорий или более. В случае небольшого списка это может показаться не таким уж громадным преимуществом, но при работе с потенциально большим потоком элементов окажется весьма полезным.
Поможет нам в этом операция
takeWhile! Она позволяет выполнить срез любого потока данных (даже бесконечного) с помощью предиката. И, к счастью, она прекращает работу сразу же по обнаружении неподходящего элемента. Вот как ее следует использовать:
List<Dish> sliceMenu1
= specialMenu.stream()
.takeWhile(dish -> dish.getCalories() < 320)
.collect(toList());
#java #stream #takeWhile
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20❤2
public class Demo {
public static void workload(int a) {
System.out.println(a + 1); // Простая операция
}
public static void main(String[] args) {
for (int i = 0; i < 100_000; i++) workload(i);
}
}
java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler Demo
import org.graalvm.polyglot.*;
public class Polyglot {
public static void main(String[] args) {
try (Context context = Context.create()) {
int result = context.eval("js", "Math.pow(2, 10)").asInt();
System.out.println(result); // 1024
}
}
}
native-image -jar app.jar
./app
1. Для мультиязычности добавьте org.graalvm.sdk.
2. Используйте @CEntryPoint для нативных методов.
3. Тестируйте производительность через jmh.
GraalVM позволяет запускать Python и Ruby внутри Java через Polyglot API.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2