Java for Beginner
675 subscribers
549 photos
155 videos
12 files
841 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Что выведет код?

import java.util.function.Function;

public class Task190625 {
public static void main(String[] args) {
var x = (Function<Function<Integer, Integer>, Integer>)
f -> f.apply(10);

var y = x.apply(n -> n * 2);
System.out.println(y);
}
}


#Tasks
StreamAPI JAVA.
Великолепие минимализма

Видео посвященное изучению Stream API в Java.

Что мы узнали:
Что такое Stream API и зачем оно вообще нужно.
Что такое Императивный и Декларативный подходы.
Рассмотрели функциональные интерфейсы и их основные виды.
Рассмотрели основные функциональные промежуточные и терминальные методы.
Посмотрели все это на примерах.

Если хотите посмотреть с более подробными примерами и описанием:
GitHub -
https://github.com/Oleborn/StreamAPI_Research
(понравилось - поставь звездочку)


Ссылка на Youtube
Ссылка на Рутьюб

Смотрите, ставьте лайки, подписывайтесь на каналы!
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое Error в Java и чем он отличается от Exception? 🤓

Ответ:

Error и Exception — наследники Throwable.

Error обозначает серьезные системные ошибки, которые обычно нельзя обработать (например, OutOfMemoryError).

Exception — это ошибки, которые можно и нужно обрабатывать (например, IOException).

Программист редко обрабатывает Error, но должен учитывать Exception.

#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Best Practices и расширенные сценарии работы с Flyway

1. Организация миграций

1.1. Разделение миграций по окружениям

Проблема:
Разные БД для dev/test/prod могут требовать специфичных скриптов (например, тестовые данные только для dev).

Решение:
Структура папок:

db/
migration/
dev/
V1__Dev_only_data.sql
prod/
V1__Prod_indexes.sql
common/
V1__Base_schema.sql


Настройка application.yml для dev:
spring:
flyway:
locations: classpath:db/migration/common, classpath:db/migration/dev


1.2. Работа с существующей БД (baseline)

Проблема:
Flyway ожидает пустую БД или таблицу flyway_schema_history. Если БД уже используется, требуется инициализация.

Решение:
Включить baseline-on-migrate:

spring:
flyway:
baseline-on-migrate: true
baseline-version: 1.0 # Версия, с которой начнется контроль


Вручную через CLI:

flyway baseline -baselineVersion="1.0"


Важно:
После baseline миграции с версией ≤ baselineVersion игнорируются.


2. Тестирование миграций

2.1. Интеграция с Testcontainers + JUnit

Цель:
Проверить, что миграции применяются без ошибок в изолированной БД.

Пример теста (Java + JUnit 5):
import org.flywaydb.core.Flyway;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
public class FlywayMigrationTest {

@Container
private static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:15");

@Test
void testMigrations() {
Flyway flyway = Flyway.configure()
.dataSource(
postgres.getJdbcUrl(),
postgres.getUsername(),
postgres.getPassword()
)
.load();
flyway.migrate(); // Упадет, если есть ошибки
}
}


Что проверяем:
Применение всех миграций без исключений.
Соответствие схемы ожиданиям (можно добавить проверку через DataSource).



3. Работа в команде

3.1. Именование файлов

Правила:
Уникальность версий:
Используйте дату + порядковый номер: V20240315-1__Add_users_table.sql.

Описательные имена:
Плохо: V1__Changes.sql.
Хорошо: V20240315-2__Alter_users_add_email.sql.


3.2. Запрет изменения примененных скриптов

Проблема:
Изменение уже выполненного скрипта приводит к ошибке:

Validate failed: Migration checksum mismatch for V1__Create_table.sql


Решение:
Никогда не изменять содержимое V*-файлов после их применения.

Для исправлений:
Создать новую миграцию с корректировкой:
-- V20240315-3__Fix_users_table.sql
ALTER TABLE users ALTER COLUMN email SET NOT NULL;

Если критично, использовать flyway repair (пересчитывает контрольные суммы).

Исключение:
R__-миграции (повторяемые) можно изменять — они переприменяются при изменениях.


4. Расширенные сценарии

4.1. Миграции для нескольких схем БД

Настройка:
spring:
flyway:
schemas: public, audit
default-schema: public
В SQL-файлах укажите схему явно:

sql
-- V1__Create_audit_table.sql
CREATE TABLE audit.logs (
id SERIAL PRIMARY KEY,
message TEXT
);


4.2. Шаблонизация SQL

Проблема:
Разные SQL-синтаксисы для PostgreSQL/Oracle.

Решение (через Maven/Gradle):
Используйте placeholders в pom.xml:
<configuration>
<placeholders>
<table.prefix>${env}</table.prefix>
</placeholders>
</configuration>


В SQL:
CREATE TABLE ${table.prefix}_users (...);


4.3. Откаты (безопасная альтернатива)


Тактика:
Для деструктивных изменений (DROP, DELETE) создавать "обратные" миграции:
-- V5__Drop_users.sql
DROP TABLE users;

-- V6__Restore_users.sql (если нужно откатить)
CREATE TABLE users (...); -- Повторяем структуру из V1
INSERT INTO users (...) SELECT ... FROM backup_users;
Использовать резервные копии перед рискованными операциями.


#Java #middle #Flyway
Что выведет код?

import java.util.function.Supplier;

public class Task200625 {
public static void main(String[] args) {
Supplier<Integer> supplier = () -> {
System.out.print("A");
return 1;
};

System.out.print("B");
supplier.get();
System.out.print("C");
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
13%
"ABC"
38%
"BAC"
38%
"A"
13%
Exception
Продолжаем выбирать темы для разбора и голосовать за рассмотрение предложенных! 🤓

Голосуем за тему к рассмотрению в эти выходные!

Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)

Не стесняемся! ✌️
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое Thread.sleep() и чем он отличается от Thread.yield()? 🤓

Ответ:

Thread.sleep(long millis) приостанавливает выполнение текущего потока на указанное время (в миллисекундах), не освобождая монитор. Может выбросить InterruptedException.


Thread.yield() дает возможность другим потокам с таким же приоритетом выполниться, но не гарантирует переключение.

Пример:
Thread.sleep(1000); // Пауза на 1 секунду
Thread.yield(); // Уступить процессор другим потокам

#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Ключевые методы Optional

Optional предоставляет множество методов для работы с данными:

isPresent(): Возвращает true, если значение присутствует.
isEmpty() (добавлен в Java 11): Возвращает true, если значение отсутствует.
ifPresent(Consumer<? super T> consumer): Выполняет действие, если значение присутствует.
orElse(T other): Возвращает значение, если оно присутствует, или other, если отсутствует.
orElseGet(Supplier<? extends T> supplier): Возвращает значение или результат выполнения Supplier, если значение отсутствует.
orElseThrow(Supplier<? extends X> exceptionSupplier): Возвращает значение или выбрасывает исключение, если значение отсутствует.
map(Function<? super T, ? extends U> mapper): Преобразует значение, если оно присутствует, возвращая новый Optional.
flatMap(Function<? super T, Optional<U>> mapper): Преобразует значение, возвращая Optional, результат которого не оборачивается в дополнительный Optional.
filter(Predicate<? super T> predicate): Возвращает Optional, содержащий значение, если оно удовлетворяет предикату, или пустой Optional.
or(Supplier<? extends Optional<? extends T>> supplier) (добавлен в Java 9): Возвращает текущий Optional, если значение присутствует, или результат Supplier.


Пример использования методов
Optional<String> name = Optional.ofNullable(getName()); // Может вернуть null
String result = name
.map(String::toUpperCase)
.filter(s -> s.length() > 3)
.orElse("DEFAULT");
System.out.println(result); // Выведет преобразованное значение или "DEFAULT"

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



Преимущества Optional
Безопасность от NPE: Optional явно указывает на возможность отсутствия значения, заставляя разработчика обрабатывать этот случай.
Читаемость кода: Код с Optional более выразителен, так как сигнализирует о потенциальном отсутствии значения.
Избежание явного null: Уменьшает использование проверок if (value != null), делая код чище.
Интеграция с функциональным программированием: Методы map, flatMap и filter позволяют использовать Optional в цепочках обработки данных, интегрируясь с Stream API.



Практическое использование Optional

Интеграция со Stream API
Optional часто используется в связке со Stream API для обработки данных, которые могут отсутствовать. Например:
Optional<User> user = findUserById(123);
Stream<User> userStream = user.stream(); // Преобразует Optional в Stream
userStream.forEach(System.out::println);

Метод stream() (добавлен в Java 9) позволяет преобразовать Optional в Stream, содержащий либо одно значение, либо пустой.


Обработка вложенных объектов
Optional особенно полезен при работе с цепочками объектов, где каждое звено может быть null. Без Optional код выглядел бы так:
String city = null;
if (user != null && user.getAddress() != null && user.getAddress().getCity() != null) {
city = user.getAddress().getCity();
}


С использованием Optional код становится более компактным:
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");


Метод flatMap полезен, если методы возвращают Optional:
Optional<String> city = Optional.ofNullable(user)
.flatMap(u -> Optional.ofNullable(u.getAddress()))
.flatMap(a -> Optional.ofNullable(a.getCity()));


Использование в API
Optional часто применяется в публичных API для возврата значений, которые могут отсутствовать. Например, метод Map.getOrDefault может быть заменен на Optional:
Map<String, String> map = new HashMap<>();
Optional<String> value = Optional.ofNullable(map.get("key"));


#Java #Training #Medium #Optional
Антипаттерны и ограничения

Использование Optional для полей класса: Optional не предназначен для хранения состояния в полях класса, так как он не сериализуем (Optional не реализует Serializable). Вместо этого используйте null для полей или коллекции.
// Антипаттерн
public class User {
private Optional<String> name; // Не рекомендуется
}


Чрезмерное использование isPresent():
Проверки isPresent() с последующим вызовом get() сводят на нет преимущества Optional:

// Антипаттерн
if (optional.isPresent()) {
return optional.get();
}


Вместо этого используйте orElse, orElseGet или map
:
return optional.orElse(defaultValue);


Передача Optional в методы: Передача Optional в качестве аргумента метода может усложнить API. Лучше передавать значение или null:
// Антипаттерн
void process(Optional<String> value) { ... }

// Лучше
void process(String value) { ... }


Производительность: Создание объектов Optional в циклах или в высокопроизводительных системах может привести к накладным расходам. В таких случаях рассмотрите использование null или оптимизируйте код.
Сериализация: Optional не реализует Serializable, что делает его непригодным для использования в сериализуемых классах. Если сериализация необходима, преобразуйте Optional в null или значение перед сохранением.
Потокобезопасность: Optional не является потокобезопасным, так как он не предназначен для конкурентного доступа. Если значение внутри Optional изменяется в многопоточной среде, используйте синхронизацию или потокобезопасные альтернативы.


Производительность и оптимизация
Создание объектов: Каждый вызов Optional.of или ofNullable создает новый объект (кроме empty(), который использует синглтон). В критически важных участках кода минимизируйте создание Optional.
Кэширование: Если Optional возвращается часто используемым методом, рассмотрите кэширование результата, чтобы избежать повторного создания объектов.
Ленивые вычисления: Используйте orElseGet вместо orElse, если альтернативное значение требует вычислений:

// Менее эффективно
String result = optional.orElse(computeExpensiveDefault());

// Более эффективно
String result = optional.orElseGet(() -> computeExpensiveDefault());



Рекомендации

- Используйте Optional только там, где это имеет смысл: Применяйте Optional для возвращаемых значений методов, где отсутствие значения — это ожидаемый сценарий. Не используйте его для всех случаев, чтобы избежать ненужной сложности.
- Интеграция с функциональными API: Максимально используйте методы map, flatMap и filter для обработки данных в функциональном стиле. Это улучшает читаемость и поддерживаемость кода.
- Проверка на обратную совместимость: Если вы разрабатываете публичное API, учитывайте, что клиенты на Java 7 и ниже не смогут использовать Optional. В таких случаях предоставляйте альтернативные методы, возвращающие null.
- Тестирование: Убедитесь, что тесты покрывают оба случая — присутствие и отсутствие значения в Optional. Это гарантирует корректную обработку всех сценариев.
- Рефакторинг старого кода: При рефакторинге кода, использующего null, заменяйте проверки на Optional, но только если это улучшает читаемость и безопасность. Не заменяйте null на Optional механически.



Пример реального сценария

Предположим, у нас есть сервис, который возвращает информацию о пользователе:
public class UserService {
public Optional<User> findUserById(long id) {
// Имитация поиска в базе данных
return id == 123 ? Optional.of(new User("Alice")) : Optional.empty();
}

public Optional<String> getUserCity(long id) {
return findUserById(id)
.flatMap(user -> Optional.ofNullable(user.getAddress()))
.flatMap(address -> Optional.ofNullable(address.getCity()));
}
}

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


#Java #Training #Medium #Optional