Java for Beginner
744 subscribers
719 photos
202 videos
12 files
1.17K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Тестирование и отладка проекта

После реализации протестируйте проект, чтобы убедиться, что всё работает.

Запустите проект:
Правой кнопкой на файле Main.java → Run 'Main.main()'.
В консоли IDE вы должны увидеть вывод списка книг с их деталями.


Отладка:
Если ошибки: Проверьте синтаксис (точки с запятой, скобки).
Используйте отладчик: Установите breakpoint (красная точка слева от строки в main), запустите в debug-режиме (Shift+F9) и шагайте по коду (F8).
Общие проблемы: NullPointerException (если массив не инициализирован), IndexOutOfBoundsException (если выход за пределы массива).


Проверьте вывод:
Убедитесь, что книги выводятся в порядке добавления в массив.
Попробуйте изменить размер массива или добавить больше книг — увидите, как фиксированный размер ограничивает (это мотивирует к коллекциям позже).



Полезные советы для новичков

Организация кода: Используйте пакеты для группировки (например, library.models для Book).
Инкапсуляция: Даже в простом проекте делайте поля private и добавьте геттеры/сеттеры, если нужно изменять.
Массивы vs коллекции: Заметьте ограничения массива (фиксированный размер, ручное управление) — в следующих уроках заменим на ArrayList.
Комментарии: Добавляйте // комментарии к шагам, чтобы код был читаемым.
Версионирование: Если используете Git, создайте репозиторий и закоммитьте начальную версию проекта.
Ресурсы: Документация Oracle по классам и массивам для напоминания синтаксиса.



Практическое задание

Задача 1: Расширьте класс Book, добавив приватное поле isbn (String) и обновите конструктор и метод printDetails() для его включения.
Задача 2: Увеличьте массив до 5 элементов, добавьте больше книг и убедитесь, что вывод работает.
Задача 3: Попробуйте вывести только книги после определенного года — используйте if в цикле перебора массива.

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


#Java #для_новичков #beginner #Collections #Практика
👍4🔥1
Аспектно-ориентированное программирование в Java (AOP)

АОП — это подход к программированию, который позволяет отделить "сквозные" concerns (это слово значит "заботы" или "аспекты" — повторяющийся код, не связанный с основной логикой, например, логирование или проверка прав доступа) от основной бизнес-логики. В обычном объектно-ориентированном программировании такой код разбросан по всему приложению, что делает его сложным в поддержке. АОП позволяет "вплести" этот код в нужные места автоматически, без изменения основного кода.

Почему АОП полезно?

Представь, что в твоём приложении нужно логировать каждый вызов метода сервиса: записывать, кто вызвал, когда и с какими параметрами. Без АОП ты добавишь строки логирования в каждый метод — это загрязнит код и нарушит принцип "единственной ответственности". С АОП ты создаёшь отдельный "аспект" (модуль для сквозной логики), который автоматически применяется к нужным методам.

Плюсы:
Чистый код: Основная логика не смешивается с вспомогательной.
Легко изменять: Измени аспект — и всё приложение обновится.
Переиспользование: Один аспект для множества мест.
Примеры использования: Логирование, транзакции (атомарные операции с базой данных), кэширование, обработка ошибок, безопасность.


В Java АОП реализуется через библиотеки вроде AspectJ (полноценный язык АОП) или Spring AOP (упрощённая версия, интегрированная в Spring). Spring AOP проще для новичков, использует прокси (заместители объектов) и подходит для большинства задач. Если нужно что-то сложное, как аспекты на уровне полей, переходи к AspectJ, который Spring тоже поддерживает.


Настройка проекта в Spring

Давай создадим простой проект. Предполагаем, у тебя Spring Boot (фреймворк для быстрой разработки). Используй Spring Initializr для генерации.


Добавь зависимости в pom.xml (файл конфигурации сборки Maven):
xml<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
spring-boot-starter-aop включает всё необходимое для АОП.


Включи АОП в конфигурации. В основном классе приложения добавь аннотацию (метку):
javaimport org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy // Включает автоматическое создание прокси для аспектов
public class AopApplication {
public static void main(String[] args) {
SpringApplication.run(AopApplication.class, args);
}
}
Это говорит Spring: "Используй АОП с прокси".



Основные понятия АОП

Аспект: Класс с логикой, которая применяется сквозно. Обозначается @Aspect.
Совет (Advice): Что именно делать — до, после или вокруг метода. Например,
@Before — перед вызовом.
Точка присоединения (Join Point): Место в коде, где аспект применяется, например, вызов метода.
Точка среза (Pointcut): Выражение, определяющее, где применять аспект, например, все методы в пакете сервисов.
Введение (Introduction): Добавление новых методов или интерфейсов (редко, но мощно).
Вплетение (Weaving): Процесс применения аспекта — в Spring это на этапе выполнения (runtime) через прокси.



#Java #middle #on_request #AOP
👍1
Пример: Аспект для логирования

Создадим сервис — класс с бизнес-логикой:
javaimport org.springframework.stereotype.Service;

@Service // Обозначает, что это сервис, Spring создаст экземпляр
public class MyService {
public String doSomething(String input) {
return "Результат: " + input.toUpperCase(); // Простая логика
}
}


Теперь аспект для логирования:

javaimport org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect // Это аспект
@Component // Spring зарегистрирует его
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))") // Pointcut: все методы в пакете service
public void logBefore(JoinPoint joinPoint) { // JoinPoint — информация о точке
System.out.println("Вызов метода: " + joinPoint.getSignature().getName());
System.out.println("Аргументы: " + Arrays.toString(joinPoint.getArgs()));
}
}
Здесь @Before значит "выполни перед методом". execution — выражение для pointcut: * значит любой возврат, com.example.service..(..) — любой класс в пакете service, любой метод с любыми аргументами.

Если вызвать myService.doSomething("hello"), в консоли увидишь лог перед результатом.


Более сложный пример: Аспект вокруг метода

Для обработки ошибок или измерения времени используй @Around — он оборачивает метод.
javaimport org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TimingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object measureTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // Выполняет оригинальный метод
long end = System.currentTimeMillis();
System.out.println("Время выполнения: " + (end - start) + " мс");
return result; // Возвращает результат метода
}
}
ProceedingJoinPoint позволяет контролировать вызов: можно пропустить метод, изменить аргументы или результат. Идеально для транзакций или кэша.


Продвинутые советы для опытных разработчиков

Производительность: Прокси в Spring добавляют overhead (небольшую задержку). Для критичных мест используй compile-time weaving из AspectJ.
Порядок аспектов: Если несколько аспектов на одном методе, используй
@Order(1) для приоритета (меньше число — выше приоритет).
Обработка исключений: В
@Around лови Throwable, логируй и перебрасывай, чтобы не глотать ошибки.
Тестирование: Используй
@EnableAspectJAutoProxy в тестах, моки (заменители) для аспектов с Mockito.
Интеграция с другими модулями: Spring Security или Spring Cache часто используют АОП внутри — изучи их исходники для идей.
Ограничения: Spring AOP работает только на методах бинов (объектов, управляемых Spring). Для статических методов или конструкторов нужен AspectJ.



#Java #middle #on_request #AOP
👍4
Реактивное программирование

Базовые операторы в Reactor: map, filter, flatMap


Операторы — это методы на Mono/Flux, которые позволяют строить конвейеры: преобразовывать, фильтровать и комбинировать данные асинхронно. Представьте их как звенья в цепи: каждый берёт входной поток, меняет его и передаёт дальше. Сегодня разберём три фундаментальных: map (преобразование элементов), filter (фильтрация) и flatMap (плоское преобразование, для слияния подпотоков). Эти операторы — основа для сложных сценариев, они решают проблемы из первого поста, позволяя писать декларативный код вместо ручных циклов и ожиданий.


Операторы в Reactor — декларативные: вы описываете, что делать с данными, а библиотека заботится об асинхронности, backpressure и ошибках. Они не меняют исходный поток (иммутабельны), а создают новый. Это делает код читаемым и тестируемым.



Map: простое преобразование элементов

Map — оператор для изменения каждого элемента потока. Он берёт входной элемент, применяет функцию и выдаёт результат. Синхронный: функция должна быть быстрой и без блокировок. Идеален для конвертации типов, вычислений или форматирования.

Пример на Flux:
import reactor.core.publisher.Flux;
Flux<String> originalFlux = Flux.just("яблоко", "банан", "вишня");
Flux<String> transformed = originalFlux.map(fruit -> fruit.toUpperCase()); // Преобразование в верхний регистр
transformed.subscribe(System.out::println); // Вывод: "ЯБЛОКО", "БАНАН", "ВИШНЯ"

Здесь map применяет лямбду к каждому элементу последовательно. Если ошибка в функции — сработает onError.


На Mono:
Mono<Integer> num = Mono.just(5).map(x -> x * 2); // Результат: 10


Почему map полезен? В традиционных подходах (как в CompletableFuture.thenApply) вы строите цепочки, но рискуете вложенностью. В Reactor map делает конвейер линейным: читается как последовательный код, но работает асинхронно. Поддерживает backpressure: если подписчик запрашивает n, map передаёт запрос upstream (источнику).


Filter: отбор элементов по условию

Filter — для пропуска только нужных элементов. Принимает предикат (функцию, возвращающую true/false) и пропускает те, для которых true. Остальные игнорируются — поток "сужается".

Пример на Flux:
Flux<Integer> numbers = Flux.range(1, 10);
Flux<Integer> evenNumbers = numbers.filter(num -> num % 2 == 0); // Только чётные
evenNumbers.subscribe(System.out::println); // Вывод: 2, 4, 6, 8, 10

Если поток пустой или ничего не проходит — onComplete сработает без onNext.


На Mono:
Mono<String> word = Mono.just("привет").filter(w -> w.length() > 7); // Не пройдёт — пустой Mono


Filter экономит ресурсы: ненужные элементы не обрабатываются дальше в цепи. В отличие от императивных циклов (где вы фильтруете в for с if), здесь всё асинхронно и с backpressure — запросы передаются источнику только для прошедших элементов.

Комбинация с map: numbers.filter(num -> num > 5).map(num -> num * 10).subscribe(); // 60, 70, 80, 90, 100
Это строит конвейер: фильтр → преобразование, без ручных переменных.



#Java #middle #Reactor #map #filter #flatMap
👍2
FlatMap: плоское преобразование для асинхронных подпотоков

FlatMap — мощный оператор для случаев, когда из одного элемента нужно создать подпоток (Publisher), и слить их в плоский результат. Это как map, но для асинхронных или множественных выходов: он "разворачивает" вложенные потоки. Полезен для запросов в цикле: например, для каждого пользователя — асинхронно запросить данные.


Пример на Flux:
Flux<String> fruits = Flux.just("яблоко", "банан");
Flux<Character> letters = fruits.flatMap(fruit -> Flux.fromArray(fruit.toCharArray())); // Из строки — поток символов
letters.subscribe(System.out::println); // Вывод: я, б, л, о, к, о, б, а, н, а, н (в возможном перемешанном порядке, если асинхронно)

Здесь flatMap берёт строку, создаёт Flux из символов и сливает всё в один поток. В отличие от map (который вернул бы Flux<Flux<Character>> — вложенный), flatMap "сплющивает".



Асинхронный пример: симулируем API-запросы.

import java.time.Duration;
Flux<String> users = Flux.just("user1", "user2");
Flux<String> data = users.flatMap(user -> Mono.just("Данные для " + user).delayElement(Duration.ofSeconds(1))); // Асинхронный подпоток с задержкой
data.subscribe(System.out::println); // Вывод через секунды: "Данные для user1", "Данные для user2" (параллельно, если scheduler позволяет)

FlatMap уважает backpressure: запрашивает у подпотоков по мере нужды. Но осторожно: если подпотоки бесконечные — рискуете перегрузкой. Параметр concurrency (flatMap(func, concurrency)) ограничивает параллелизм.


Почему flatMap решает проблемы? В традиционных подходах (циклы с Future) вы ждёте каждый запрос, блокируя. Здесь — асинхронное слияние, без ожиданий и callback-ада: цепочка читаема.



Практические советы и подводные камни

Читаемость: цепочки операторов пишите по строкам для ясности: flux.filter(...).map(...).flatMap(...);
Ошибки: если в map/flatMap исключение — onError. Используйте handle() для условной обработки.
Производительность: в flatMap устанавливайте concurrency (default 256) для контроля параллелизма: flatMap(func, 4) — max 4 подпотока одновременно.
Камень: блокирующий код в лямбдах — сломает асинхронность. Для IO — используйте flatMap с Mono.fromCallable и publishOn(Schedulers.boundedElastic()).
Тестирование: StepVerifier.create(
flux.map(...)).expectNext("ЯБЛОКО").verifyComplete();


#Java #middle #Reactor #map #filter #flatMap
👍3
Коллекции в Java

Глава 3. Set — множества

Интерфейс Set. Особенности множеств


Интерфейс Set<E> — это часть Java Collections Framework (JCF), который представляет коллекцию уникальных элементов без дубликатов. Set является подинтерфейсом Collection, но с ключевым отличием: он не позволяет хранить одинаковые элементы. Если вы пытаетесь добавить дубликат, операция игнорируется.

Основные характеристики Set:
Уникальность элементов: Да, дубликаты не хранятся.
Упорядоченность: В общем случае нет (зависит от реализации).
Сортировка: Нет по умолчанию, но возможна в подтипах.
Время доступа (Big O): Для основных операций (add, remove, contains) — O(1) в HashSet, O(log n) в TreeSet.


Set моделирует математическое множество: элементы уникальны, нет индексации, фокус на наличии/отсутствии.

Методы Set (наследуются от Collection, но с нюансами):
boolean add(E e): Добавляет элемент, если его нет (возвращает true, если добавлен).
boolean remove(Object o): Удаляет элемент, если он есть.
boolean contains(Object o): Проверяет наличие.
int size(): Размер.
boolean isEmpty(): Пустота.
Iterator<E> iterator(): Для перебора (нет порядка по умолчанию).
void clear(): Очистка.

Нюанс: Set не имеет get(int index) — нет индексации, как в List. Перебор через Iterator или for-each.


Особенности множеств в Java

Множества в Java имеют несколько важных особенностей, которые определяют их использование:

Уникальность элементов:
Set автоматически предотвращает дубликаты на основе методов equals() и hashCode() (для HashSet) или compareTo() (для TreeSet).
Если добавить существующий элемент, add() возвращает false, коллекция не меняется.
Нюанс: Для custom классов обязательно переопределите equals() и hashCode() (используйте Objects.equals() и Objects.hash()). Без этого уникальность по ссылке, не по значению.


Отсутствие гарантированного порядка:
В HashSet порядок непредсказуем (зависит от хэша).
В LinkedHashSet — порядок вставки.
В TreeSet — сортированный порядок.
Нюанс: Не полагайтесь на порядок в HashSet — он может измениться при ресайзе.


Null элементы:
HashSet и LinkedHashSet позволяют один null.
TreeSet — нет (NullPointerException, так как сравнивает).
Нюанс: Null в Set — редко рекомендуется, но возможно.


Итерация и модификация:
Перебор через for-each или Iterator.
Нюанс: Во время итерации нельзя модифицировать Set (ConcurrentModificationException). Используйте Iterator.remove() для удаления.


Производительность:

HashSet: O(1) для add/remove/contains (средний случай).
TreeSet: O(log n), но с автосортировкой.
Нюанс: HashSet требует хорошего hashCode() — плохой приводит к O(n) worst-case.


Синхронизация:
Стандартные реализации не thread-safe. Для многопоточности: Collections.synchronizedSet(Set set) или ConcurrentHashSet (из Guava).


#Java #для_новичков #beginner #Collections #Set
👍6