Лямбда-выражения на первый взгляд могут показаться чем-то сложным и загадочным, но на самом деле они просты и интуитивно понятны.
Лямбда-выражение — это лаконичный способ описания анонимной функции, которую можно передать в качестве параметра или сохранить в переменной для последующего использования.
Если говорить ещё проще, лямбда-выражение — это просто другой способ создания и реализации объекта определённого типа. Рассмотрим это на примере создания нового потока.
У класса
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
В Java существует несколько «типов» (форм) классов, каждый из которых решает свои задачи и обладает своими особенностями. Рассмотрим основные из них:
1. Топ-level класс
Класс, объявленный в собственном файле .java (или несколько в одном файле, но только один может быть
public).Когда используется: для большинства объектов предметной области.
2. Статический вложенный класс (static nested class)
Объявляется внутри другого класса с модификатором
static.Когда используется: чтобы логически сгруппировать вспомогательный класс и избежать загрязнения пакета; не требует ссылки на экземпляр внешнего класса.
3. Нестатический внутренний класс (inner class)
Нестатический класс внутри другого класса. Имеет неявную ссылку на экземпляр внешнего.
Когда используется: когда класс не имеет смысла отдельно от конкретного экземпляра внешнего класса.
4. Локальный класс (local class)
Объявляется внутри метода. Видим только внутри этого метода.
Когда используется: для единичной вспомогательной логики, зависящей от локальных переменных.
5. Анонимный класс (anonymous class)
Определяется прямо при создании объекта, без имени. Часто для реализации интерфейсов/абстрактных классов «на месте».
Когда используется: когда нужна «быстрая» одноразовая реализация интерфейса или абстрактного класса.
6. Абстрактный класс (abstract class)
Может содержать как реализованные, так и абстрактные (без реализации) методы; сам не может быть инстанцирован.
Когда используется: когда есть общая часть реализации для набора подклассов, но нужна возможность «заставить» их реализовать конкретные методы.
7. Финальный класс (final class)
Нельзя унаследовать.
Когда используется: чтобы зафиксировать поведение и запретить расширение (например, для безопасности).
8. Перечисление (enum)
Специальный вид класса для фиксированного набора констант; может содержать поля и методы.
Когда используется: для ограничения значений до заранее известных.
9. Запись (record) [Java 16+]
Лаконичная форма класса-носителя данных: автоматом генерируются конструктор,
equals(), hashCode(), toString().Когда используется: для неизменяемых DTO/Value-объектов.
10. Запечатанный класс (sealed class) [Java 17+]
Позволяет закрыть иерархию наследования: указать, какие классы могут расширять/реализовывать.
Когда используется: когда важно контролировать все возможные подклассы (например, при реализации «альгебраического типа»).
11. Аннотации (annotation types)
Специальный «интерфейс» с префиксом
@interface для метаданных.Когда используется: для декларации информации, обрабатываемой компилятором или фреймворками (например,
@Override, @Entity).Каждый из этих типов классов/типов в Java помогает структурировать код, задавая чёткие контракты, ограничивая использование и сокращая «бесполезный» шаблонный код.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤7🔥2
import java.util.Random;
import java.util.Scanner;
public class RockPaperScissors {
public static void main(String[] args) {
String[] options = { "Камень", "Ножницы", "Бумага" };
Random rnd = new Random();
Scanner sc = new Scanner(System.in);
int userScore = 0, compScore = 0;
System.out.println("Игра «Камень, ножницы, бумага». Введите 0-Камень, 1-Ножницы, 2-Бумага, q-выход.");
while (true) {
System.out.print("Ваш выбор: ");
String inp = sc.nextLine();
if (inp.equalsIgnoreCase("q")) break;
int user, comp = rnd.nextInt(3);
try {
user = Integer.parseInt(inp);
if (user < 0 || user > 2) throw new NumberFormatException();
} catch (NumberFormatException e) {
System.out.println("Некорректно, попробуйте снова.");
continue;
}
System.out.printf("Вы: %s, Компьютер: %s → ", options[user], options[comp]);
int outcome = (user - comp + 3) % 3;
// 0=ничья, 2=победа пользователя, 1=проигрыш
if (outcome == 0) {
System.out.println("Ничья");
} else if (outcome == 2) {
System.out.println("Вы выигрываете");
userScore++;
} else {
System.out.println("Проигрываете");
compScore++;
}
System.out.printf("Счёт %d:%d%n", userScore, compScore);
}
System.out.println("Игра окончена. Финальный счёт " + userScore + ":" + compScore);
sc.close();
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
😁14👍3😍3
Когда требуется конкатенировать строки с использованием
Stream, можно выбрать один из двух методов: Stream.reduce() или Stream.collect(Collectors.joining()).Пример с
Stream.reduce(): List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().reduce("", (a, b) -> a + b);
System.out.println(result); // Str1Str2Str3Пример с
Collectors.joining(): List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().collect(Collectors.joining());
System.out.println(result); // Str1Str2Str3Использование
reduce() для конкатенации строк не является оптимальным с точки зрения производительности. При каждом вызове операции +, создается новая строка, так как строки в Java неизменяемы. Это приводит к увеличению нагрузки на память из-за создания множества промежуточных объектов.В свою очередь, метод
Collectors.joining() использует StringBuilder для сборки строк, что значительно эффективнее. Он избегает создания лишних объектов и снижает потребление памяти.#java #Stream #reduce #joining
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤1
// Для импорта конкретного статического члена
import static package.Clazz.member;
// Для импорта всех статических членов класса
import static package.Clazz.*;
Пример:
public class MathUtils {
public static final double PI = 3.141592653589793;
public static int add(int a, int b) {
return a + b;
}
public static int subtract(int a, int b) {
return a - b;
}
}
import static MathUtils.PI;
import static MathUtils.add;
public class Main {
public static void main(String[] args) {
System.out.println("Value of PI: " + PI);
System.out.println("Addition result: " + add(5, 3));
}
}
import static MathUtils.*;
public class Main {
public static void main(String[] args) {
System.out.println("Value of PI: " + PI);
System.out.println("Addition result: " + add(5, 3));
System.out.println("Subtraction result: " + subtract(5, 3));
}
}
Плюсы: читаемость, компактность кода.
Минусы:
- не очевидно откуда берется поле или метод
- импорт всех статических членов может привести к конфликтам имен, если разные классы содержат статические члены с одинаковыми именами
#java #static #import
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥4❤1
Предположим, есть метод, который может выбросить
IOException и FileNotFoundException в какой последовательности должны идти блоки catch? Сколько блоков catch будет выполнено?Общее правило: обрабатывать исключения нужно от «младшего» к старшему. То есть нельзя поставить в первый блок
catch(Exception ex) {}, иначе все дальнейшие блоки catch() уже ничего не смогут обработать, так как любое исключение будет соответствовать обработчику catch(Exception ex).Таким образом, исходя из факта, что
FileNotFoundException extends IOException сначала нужно обработать FileNotFoundException, а затем уже IOException:
void method() {
try {
//...
} catch (FileNotFoundException ex) {
//...
} catch (IOException ex) {
//...
}
}
В Java 7 стала доступна новая языковая конструкция, с помощью которой можно перехватывать несколько исключений одним блоком
catch:
try {
//...
} catch(IOException | SQLException ex) {
//...
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤3🔥1
Исключения делятся на несколько классов, но все они имеют общего предка — класс
Throwable, потомками которого являются классы Exception и Error.Ошибки (Errors) представляют собой более серьёзные проблемы, которые, согласно спецификации Java, не следует обрабатывать в собственной программе, поскольку они связаны с проблемами уровня JVM. Например, исключения такого рода возникают, если закончилась память доступная виртуальной машине.
Исключения (Exceptions) являются результатом проблем в программе, которые в принципе решаемы, предсказуемы и последствия которых возможно устранить внутри программы. Например, произошло деление целого числа на ноль.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1
Generics - это технический термин, обозначающий набор свойств языка позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры.
Примером использования обобщенных типов может служить Java Collection Framework. Так, класс
LinkedList<E> - типичный обобщенный тип. Он содержит параметр E, который представляет тип элементов, которые будут храниться в коллекции. Создание объектов обобщенных типов происходит посредством замены параметризированных типов реальными типами данных. Вместо того, чтобы просто использовать LinkedList, ничего не говоря о типе элемента в списке, предлагается использовать точное указание типа LinkedList<String>, LinkedList<Integer> и т.п.Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤2
try — данное ключевое слово используется для отметки начала блока кода, который потенциально может привести к ошибке. catch — ключевое слово для отметки начала блока кода, предназначенного для перехвата и обработки исключений в случае их возникновения. finally — ключевое слово для отметки начала блока кода, который является дополнительным. Этот блок помещается после последнего блока catch. Управление передаётся в блок finally в любом случае, было выброшено исключение или нет.Общий вид конструкции для обработки исключительной ситуации выглядит следующим образом:
try {
//код, который потенциально может привести к исключительной ситуации
}
catch(SomeException e ) { //в скобках указывается класс конкретной ожидаемой ошибки
//код обработки исключительной ситуации
}
finally {
//необязательный блок, код которого выполняется в любом случае
}
Возможно ли использование блока try-finally (без catch)?
Такая запись допустима, но смысла в такой записи не так много, всё же лучше иметь блок
catch, в котором будет обрабатываться необходимое исключение.Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤6❤🔥1
Множитель создает зависимость значения хэш кода от очередности обработки полей, что в итоге порождает лучшую хэш функцию.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤2👍1
Часто нам нужно проверить является ли объект экземпляром определенного класса, например в методе equals(). Существует несколько способов это сделать. Разберём два из них.
instanceof проверяет, является ли ссылка на объект в левой части экземпляром типа в правой части или каким-либо подтипом.
getClass() == ... проверяет, идентичны ли типы.
Выбирать способ проверки типа нужно исходя из решаемой задачи. В методе equals() можно использовать оба способа в зависимости от того собираетесь ли вы позволять сравнивать объект определенного класса и объект класса наследника.
class Parent { }
class Child extends Parent { }
public class Test {
public static void main(String[] args) {
Parent parent = new Parent();
Child child = new Child();
System.out.println(parent instanceof Parent); // true
System.out.println(child instanceof Parent); // true
System.out.println(parent.getClass() == Parent.class); // true
System.out.println(child.getClass() == Parent.class); // false
}
}
#java #instanceof #getClass
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤4
Общий совет: выбирать поля, которые с большой долью вероятности будут различаться. Для этого необходимо использовать уникальные, лучше всего примитивные поля, например, такие как
id, uuid. При этом нужно следовать правилу, если поля задействованы при вычислении hashCode(), то они должны быть задействованы и при выполнении equals().hashCode()?Да, могут. Метод
hashCode() не гарантирует уникальность возвращаемого значения. Ситуация, когда у разных объектов одинаковые хэш коды называется коллизией. Вероятность возникновения коллизии зависит от используемого алгоритма генерации хэш кода.Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4
&& и || называются сокращёнными логическими операторами AND и OR соответственно, или операторами короткой схемы вычислений. В спецификации Java их ещё зовут условными. Значения их операндов могут быть только булева типа.В отличие от двойных, одиночные
& и | называются операторами полной схемы вычислений. Значения их операндов могут быть как только булевыми, так и только целочисленными (вместе с оператором ^ они используются в побитовых операциях).В чём разница?
В том, что для операторов
& и | всегда вычисляются значения обоих операндов, а при работе операторов && и || второй операнд вычисляется только по необходимости.То есть иногда результат выражения однозначно определён уже по первому операнду:
&& равен false, то второй не вычисляется, так как уже понятно, что результат всего выражения будет false.|| равен true, то второй не вычисляется, так как уже понятно, что || вернёт true.&& и || используют как операторы булевой логики. Они оперируют значениями только булева типа и применяются только в логических выражениях.Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤2