Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Инкапсуляция состояния внутри операций

Главное волшебство декларативного стиля — исчезновение явного состояния из поля зрения программиста. Но состояние не исчезает физически — оно мигрирует внутрь абстракций, становясь их ответственностью, а не вашей.
Операция filter не требует от вас объявления списка для результатов. Она принимает предикат (функцию, возвращающую boolean) и возвращает новый поток, содержащий только элементы, удовлетворяющие предикату. Где хранятся отфильтрованные элементы? В недрах реализации. Когда именно происходит фильтрация? Тогда, когда потребуется — благодаря ленивости вычислений.

Ленивые вычисления (lazy evaluation) — фундаментальная концепция функционального программирования, перенятая Stream API. Промежуточные операции (filter, map, sorted, limit) не выполняют немедленной обработки. Они строят декларативное описание конвейера, создавая цепочку объектов-операций. Реальная работа начинается только при вызове терминальной операции (collect, forEach, reduce, findFirst и др.).

Это можно продемонстрировать явно:
Stream<Order> stream = orders.stream()
.filter(order -> {
System.out.println("Фильтрация: " + order.getId());
return order.getStatus() == Status.COMPLETED;
})
.map(order -> {
System.out.println("Маппинг: " + order.getId());
return order.calculateFinalAmount();
});

// На этом этапе ничего не напечатано — построен только чертёж
System.out.println("Конвейер построен");

List<BigDecimal> amounts = stream.limit(3).collect(Collectors.toList());
// Только сейчас начинается выполнение, и оно остановится после 3 подходящих элементов



Ленивость позволяет оптимизировать выполнение без участия программиста.
В императивном коде мы явно писали сортировку всех выполненных заказов, а затем отрезали первые три. Stream API может оптимизировать это: операция limit(3) подаёт сигнал вверх по конвейеру, что достаточно найти только три элемента. Если источник упорядочен или позволяет частичную обработку, промежуточные операции могут сократить объём работы.

Коллекторы (Collectors) — крайний пример инкапсуляции состояния.

Когда мы пишем Collectors.toList(), мы не видим, как создаётся список, как он наращивается, как управляется его ёмкость. Это скрыто внутри класса Collector, который инкапсулирует четыре функции: supplier (поставщик начального состояния), accumulator (накопитель), combiner (комбайнер для параллельного выполнения) и finisher (финализатор).
// Упрощённая логика toList() внутри — мы описываем, а не исполняем
Collector<T, ?, List<T>> toListCollector = Collector.of(
ArrayList::new, // Supplier: создаём пустой список (начальное состояние)
List::add, // Accumulator: добавляем элемент (переход состояния)
(left, right) -> { // Combiner: объединяем два состояния при параллелизме
left.addAll(right);
return left;
}
);


Состояние здесь — список ArrayList — существует, но оно локализовано внутри процесса редукции. Программист не имеет к нему доступа, не может случайно испортить его в другом месте, не должен заботиться о его освобождении.


#Java #для_новичков #beginner #stream_api #declarative
👍3🔥3
Абстракция «Поток»: три составляющие

Stream в Java — это не просто итератор с функциональным синтаксисом. Это комплексная абстракция, включающая три фундаментальных компонента: источник данных (source), конвейер операций (pipeline) и механизм исполнения (execution strategy).

Источник данных определяет характеристики потока.
Это может быть коллекция (Collection.stream()), массив (Arrays.stream()), диапазон чисел (IntStream.range()), генератор (Stream.generate(), Stream.iterate()), или даже ресурс ввода-вывода (Files.lines()). Каждый источник предоставляет Spliterator — специализированный итератор, поддерживающий разделение (splitting) для параллельной обработки.

Spliterator (сочетание слов "splittable" и "iterator")
— ключевой компонент, отличающий Stream от традиционного Iterator. Он не только перебирает элементы, но и сообщает о своих характеристиках: упорядочен ли поток (ORDERED), имеет ли он точный размер (SIZED), допускает ли null-элементы (NONNULL), поддерживает ли параллельное разделение (CONCURRENT). Эти характеристики позволяют оптимизировать конвейер: например, если источник SIZED и конвейер не изменяет количество элементов (только filter — изменяет, map — нет), Stream API может предвыделить точный размер результата, избегая расширения массивов.

Конвейер операций — это двусвязный граф объектов PipelineHelper. Каждая промежуточная операция оборачивает предыдущий стад конвейера в новый объект, добавляя своё поведение. Это реализация паттерна "декоратор" (Decorator): FilterOps оборачивает MapOps, который оборачивает исходный Head потока. Терминальная операция запускает обход этого графа в обратном порядке — от последней операции к первой, запрашивая элементы у источника и пропуская их через цепочку.

Стратегия исполнения — скрытая суперсила Stream API. Тот же декларативный чертёж может выполняться последовательно (sequential) или параллельно (parallel).

Переключение — одна операция:
List<BigDecimal> result = orders.parallelStream()  // Или .stream().parallel()
.filter(order -> complexValidation(order)) // Выполняется в разных потоках
.map(Order::calculateFinalAmount)
.collect(Collectors.toList());


Здесь фреймворк сам разбивает источник на части (используя Spliterator.trySplit()), распределяет работу между потоками пула ForkJoinPool, синхронизирует результаты через комбайнеры коллекторов. Программист описал "что" (фильтрация и маппинг), система решила "как" (параллельно ли, сколько потоков, как балансировать нагрузку).


Цена абстракции: что мы теряем


Мощь декларативного подхода не бесплатна. Понимание издержек необходимо для принятия архитектурных решений.

Нагрузка на сборщик мусора (GC) — наиболее осязаемая цена. Каждая промежуточная операция создаёт объект — экземпляр внутреннего класса AbstractPipeline. Для цепочки из пяти операций создаётся пять объектов-обёрток плюс объекты лямбда-выражений (или ссылки на методы, реализованные через MethodHandle). Терминальная операция создаёт ещё один объект-редуцер. Для больших цепочек на короткоживущих данных это может давить на Young Generation, вызывая частые minor GC.

Лямбда-выражения в Java — не просто синтаксический сахар для анонимных классов. Они реализованы через invokedynamic и LambdaMetafactory, что генерирует скрытые классы в runtime. Хотя это эффективнее анонимных классов, всё равно создаётся объект-замыкание, захватывающий внешние переменные.

Пример потенциальной проблемы:
// В цикле создаём стримы — порождаем множество объектов
for (List<Record> batch : batches) {
batch.stream()
.filter(r -> r.isActive())
.map(r -> transform(r))
.collect(Collectors.toList());
// Все объекты Pipeline здесь становятся мусором
}


Для высокочастотных операций в критических секциях императивный код может быть предпочтительнее из-за предсказуемого распределения памяти.

#Java #для_новичков #beginner #stream_api #declarative
🔥4👍2
Сложность отладки — аспект, больно знакомый при работе со Stream API. Когда исключение происходит внутри лямбды, стектрейс уходит в глубины java.util.stream:
java.lang.NullPointerException
at com.example.Service.lambda$process$0(Service.java:25)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at com.example.Service.process(Service.java:26)


Между вашим кодом (строка 25) и точкой вызова (строка 26) — шесть фреймов внутренней механики Stream API. В отладчике сложно отследить, над каким именно элементом потока выполняется операция, потому что состояние скрыто внутри Sink — объектов, реализующих интерфейс потребителей данных в конвейере.

Стратегии борьбы: использование peek() для вставки логирования (хотя это изменяет семантику конвейера), извлечение лямбд в именованные методы (улучшает стектрейс), использование IntelliJ IDEA Stream Debugger — визуального инструмента, показывающего прохождение элементов через стадии конвейера.

Потеря явного контроля — фундаментальная компромисс. Когда мы делегируем управление состоянием Stream API, мы теряем возможность микрооптимизации. Мы не можем прервать обработку в произвольной точке, кроме как через операции findFirst, findAny, anyMatch — и они прерывают весь конвейер, а не отдельную операцию. Мы не можем переиспользовать промежуточную коллекцию, потому что она никогда не материализуется. Мы не можем легко обработать исключение для одного элемента и продолжить — весь конвейер обрушится, если лямбда бросит unchecked exception.

Сравним с императивным кодом для сложной обработки с частичным восстановлением:
// Императивно: можем обработать ошибку элемента и продолжить
List<Result> results = new ArrayList<>();
for (Record record : records) {
try {
Result r = process(record);
if (r.isValid()) {
results.add(r);
}
} catch (ProcessingException e) {
log.warn("Пропускаем запись {}: {}", record.getId(), e.getMessage());
// Продолжаем цикл
}
}


В Stream API такая логика требует оборачивания операции в try-catch внутри лямбды, что нарушает чистоту функционального стиля, или использования map с результирующим типом, содержащим Either успех или ошибку — что усложняет конвейер.

Отсутствие доступа к индексу — ещё одно ограничение. Поток — это абстракция последовательности без адресации. Если логика требует знания позиции элемента (например, "обработать каждый второй элемент"), приходится использовать IntStream.range() как мост к индексам или явно поддерживать счётчик вне потока — что возвращает нас к управлению состоянием.

#Java #для_новичков #beginner #stream_api #declarative
👍4🔥1
Что выведет код?

import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Task060226 {
public static void main(String[] args) {
String result = Stream.of("a", "b", "c", "d")
.collect(Collectors.collectingAndThen(
Collectors.joining("+"),
String::toUpperCase
));

System.out.println(result);
}
}


#Tasks
🔥5
Варианты ответа:
Anonymous Quiz
85%
A+B+C+D
7%
a+b+c+d
7%
ABCD
0%
abcd
🔥3👍1
Какие области памяти (memory areas) вы знаете в JVM? 🤓

Ответ:

Память JVM делится на несколько областей:
Heap (Куча) — общее хранилище для всех объектов и массивов (делится на Young и Old Generation).
Stack (Стек) — хранит локальные переменные и вызовы методов для каждого потока.
Metaspace (ранее PermGen) — хранит метаданные классов, загруженных ClassLoader.
Native Method Stack — для нативных методов.
PC Register — хранит адрес текущей исполняемой инструкции для каждого потока.

Heap и Metaspace являются общими для всех потоков.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
История IT-технологий сегодня — 07 Февраля

ℹ️ Кто родился в этот день

Ван Ань (кит. упр. 王安, пиньинь Wáng Ān, англ. An Wang, встречается транскрипция Эн Ванг; 7 февраля 1920 года — 24 марта 1990 года) — инженер по вычислительной технике и изобретатель, основатель компьютерной компании Wang Laboratories. Американец китайского происхождения.


🌐 Знаковые события

1984 — астронавт НАСА Брюс Маккэндлесс (специалист полёта шаттла Челленджер STS-41B) стал первым человеком, работавшим в открытом космическом пространстве без какой-либо связи с кораблём, в свободном полёте.


#Biography #Birth_Date #Events #07февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
С 31.01 по 06.02
Предыдущий пост(с 24.01 по 30.01)

Воскресный мотивационный пост:
Не было мотивации

Запись встреч/видео:
4. Spring Boot Actuator: телеметрия вашего микросервиса

Обучающие статьи:
Java:
Раздел 7. Алгоритмы

Глава 5: Рекурсия, деревья и введение в динамическое программирование
Практическое применение рекурсии в библиотечной системе

Раздел 8. Stream API и функциональный стиль

Глава 1: Философский фундамент. От шагов к преобразованиям
Императивный код — машина Тьюринга в миниатюре
Декларативный стиль — чертёж результата

Советы по Java:
[Совет по Java #001] Всегда переопределяйте equals() и hashCode() вместе, особенно для сущностей, которые будут храниться в HashSet или использоваться как ключи в HashMap.
[Совет по Java #002] Используйте StringBuilder для конкатенации строк в циклах. String в цикле создает множество объектов и убивает производительность.

Полезные статьи и видео:
Моя статья: State-first архитектура: поиск другого способа управления бизнес-логикой

Подробный гайд для айтишников: как понять, что вы пришли к врачу–мракобесу?
Мы построили мир, который больше не понимаем или почему NASA не может скопировать свой же двигатель


Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍3🔥2
Media is too big
VIEW IN TELEGRAM
Смешно же)
🤣3🤓3🔥1🤯1😱1
История IT-технологий сегодня — 08 Февраля

ℹ️ Кто родился в этот день

Никола́й Я́ковлевич Матю́хин (1927—1984) — учёный, крупный специалист в области вычислительный техники, конструктор многих ЭВМ. Принимал участие в разработке ЭВМ М-1 и её последующих поколений. Лауреат Государственной премии СССР.

Матюхин одним из первых высказал идею об автоматических средствах проектирования и моделирования. В своих работах он описал принципы работы, на которых основаны и многие современные САПР.
На базе ЕС ЭВМ Матюхин впервые создал многомашинные системы (кластеры), разработал архитектурные принципы построения подобных машин под системы реального времени.



Че́стер Ка́рлсон (англ. Chester Carlson; 8 февраля 1906; Сиэтл — 19 сентября 1968; Нью-Йорк)американский физик по образованию, адвокат по профессии и изобретатель по призванию. Именно Карлсону принадлежит право на изобретение принципа электрографии или ксерографии, одного из основных изобретений XX века.

Первый оттиск Карлсон и Корнеи получили в своей домашней лаборатории в Нью-Йорке 22 октября 1938 года. Патент на эту технологию был получен 6 октября 1942 года. Долгое время Карлсон безуспешно пытался внедрить своё изобретение, доказывая, что оно абсолютно необходимо для бизнеса, но ему по очереди отказали в 20 компаниях, ссылаясь на то, что его изобретение слишком громоздко и сильно пачкает листы, к тому же человек может значительно лучше справиться с задачей копирования. Удача улыбнулась изобретателю в 1944 году в Battelle Institute, расположенном в штате Огайо. Там ему предложили усовершенствовать технологию и даже нашли точное слово для названия данного процесса — «электрофотография». После чего лицензию на дальнейшую разработку и производство копировальных аппаратов приобрела фирма Haloid Company. Именно тогда было решено, что слово «электрофотография» слишком научное и может отпугнуть потенциального покупателя. Помощь в поиске более удачного названия оказал местный профессор-филолог. Он придумал термин «ксерография» от др.-греч. слов др.-греч. ξερός «сухой» и γράφω «пишу», а потом уже сам изобретатель Карлсон додумался сократить слово до простого «ксерокс». В итоге в 1948 году первые ксероксы появились на рынке под названием Model A. После выпуска в 1959 году первой полностью автоматической модели Xerox 914 компания Haloid сменила название на Xerox Corporation.


🌐 Знаковые события

1879 — на заседании Канадского королевского института сэр Сэндфорд Флеминг впервые предложил идею о введении всемирного времени.


#Biography #Birth_Date #Events #08февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Честное мнение о курсе Евгения Борисова 😂
👍1🤯1🤣11
This media is not supported in your browser
VIEW IN TELEGRAM
Она же ошиблась, да?
🤣6🔥1🆒1
Я не забыл про видео, но так как вечером опять печальный интернет, загружу завтра с утра пораньше))

🧑‍💻 Всем хорошего вечера))
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥21
5. Бизнес-метрики в Spring Boot: Micrometer и реальная Observability

Стоп! Ваш Spring Boot приложение врет вам.

Actuator показывает, что CPU на 20%, память в норме, запросы обрабатываются. Но бизнес простаивает. Заказы не создаются, пользователи уходят, а вы узнаете об этом последним.

В этом видео мы устраним этот разрыв. Я объясню как заставить ваше приложение говорить не о процессах Java, а о бизнесе.

Что вы узнаете:

🔹 Проблема технических метрик: Почему actuator/health — это ложь, когда речь идет о бизнесе
🔹 Практика: Добавим в OrderHub метрики, которые действительно важны:
orders.created.total — сколько заказов создано
orders.created.duration — сколько времени занимает создание
orders.retrieved.total — сколько заказов запрошено
orders.retrieved.duration — сколько времени занимает запрос
Разделение на успехи и ошибки через теги

🔹 AOP-магия: Создадим аннотацию
@BusinessMetric, которая автоматически собирает метрики без изменения бизнес-кода
🔹 Production-мышление: Как правильно именовать метрики, контролировать cardinality тегов и готовить данные для Prometheus

Исходный код проекта на GitHub очень ждет Ваших звезд ☺️

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

Смотрите, ставьте лайки, подписывайтесь на каналы!✌️
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥3🆒1
Всегда было любопытно, зачем таким девушкам мой канал про Java 😨

Вот честно? Неужто они Java ИЗУЧАЮТ? 🆘

P.S. Девушка покинула канал 😭
Please open Telegram to view this post
VIEW IN TELEGRAM
🤣3🔥2🤓1
История IT-технологий сегодня — 09 Февраля

ℹ️ Кто родился в этот день

Буркхард Хайм (нем. Burkhard Heim, 9 февраля 1925—14 января 2001) — немецкий физик-теоретик. В 1950-х годах разработал собственную квантовую теорию — теория Хайма (обнародована в 1957) — на основе теории относительности Альберта Эйнштейна.[1]

Предполагается, что на основе его теории (или теорий, разработанных в дальнейшем), человечество сможет строить корабли, способные достичь планет Солнечной системы за несколько часов. Предполагается, что с помощью так называемого «прыжка в гиперпространство» можно будет долететь к звезде, лежащей в десятке световых лет от Земли, за 80 дней по земному и корабельному времени.[2]

Буркхард Хайм не знал английского и не публиковал свои выкладки на нём; по этой причине его теория до сих пор не слишком известна среди физиков.



🌐 Знаковые события

1959 – Первая межконтинентальная баллистическая ракета Р-7 «Семёрка» принята на вооружение в Плесецке , СССР .


#Biography #Birth_Date #Events #09февраля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #003]

Тема: Никогда не используйте float и double для точных расчетов с деньгами. Используйте BigDecimal или целые числа (копейки, центы).

Проблема: Типы с плавающей запятой (float и double) реализуют двоичную арифметику IEEE 754 и не могут точно представлять большинство десятичных дробей. Это приводит к ошибкам округления и потере точности уже при простейших арифметических операциях.

Данная особенность является фундаментальной, а не багом языка. Например, результат вычисления 0.1 + 0.2 в типе double не будет равен 0.3, а составит 0.30000000000000004. В финансовых, бухгалтерских и любых других расчетах, где важна точность до минимальной единицы (копейки, цента), такие ошибки неприемлемы. Они накапливаются при множественных операциях, приводя к существенным расхождениям в итоговых суммах, что является критическим дефектом.

Решение: Для точных десятичных вычислений необходимо использовать тип java.math.BigDecimal, который предназначен для представения произвольной точности знаков после запятой и предоставляет полный контроль над округлением. Альтернативный, высокопроизводительный подход — выполнять все расчеты в целочисленных типах (long, int), оперируя минимальными единицами валюты (например, копейками, а не рублями).

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


import java.math.BigDecimal;
import java.math.RoundingMode;

public class MonetaryCalculation {
public static void main(String[] args) {
//Антипаттерн: Ошибка округления.
double dollars = 1.00;
double centsToAdd = 0.10;
for (int i = 0; i < 10; i++) {
dollars += centsToAdd; // Приблизительное вычисление
}
System.out.println("Double result: " + dollars); // Выведет ~2.0999999999999996

//Решение 1: Использование BigDecimal (с конструктором из String!).
BigDecimal bigDollars = new BigDecimal("1.00");
BigDecimal bigCents = new BigDecimal("0.10");
for (int i = 0; i < 10; i++) {
bigDollars = bigDollars.add(bigCents); // Точное вычисление
}
System.out.println("BigDecimal result: " + bigDollars); // Точно 2.00

// Важно: Задание масштаба и режима округления для операций.
BigDecimal a = new BigDecimal("10.00");
BigDecimal b = new BigDecimal("3.00");
BigDecimal division = a.divide(b, 2, RoundingMode.HALF_UP); // 3.33
System.out.println("Division with rounding: " + division);

//Решение 2: Использование целых чисел (в копейках).
long balanceInKopecks = 100L; // 1 рубль = 100 копеек
long kopecksToAdd = 10L; // 10 копеек
for (int i = 0; i < 10; i++) {
balanceInKopecks += kopecksToAdd;
}
double resultInRubles = balanceInKopecks / 100.0; // Преобразование для вывода
System.out.println("Long (kopecks) result: " + resultInRubles); // Точно 2.0
}
}


Объяснение: Ключевой нюанс при работе с BigDecimal — использование конструктора, принимающего String. Конструктор BigDecimal(double) также подвержен ошибкам представления, так как передаваемое ему значение double уже содержит погрешность. BigDecimal является неизменяемым (immutable), каждая операция возвращает новый объект.

Он требует явного указания MathContext или RoundingMode для операций, которые могут привести к бесконечной десятичной дроби (например, деление), что делает вычисления полностью контролируемыми. Целочисленный подход (long) лишен накладных расходов на создание объектов и сложную арифметику, поэтому он предпочтителен в высоконагруженных системах, но требует аккуратного ручного управления переполнением и преобразованием для отображения.


#Java #советы
👍9🔥1
Что выведет код?

public class Task090226 {
public static void main(String[] args) {
double price = 0.10;
double total = 0.00;
int items = 0;

for (int i = 0; i < 10; i++) {
total += price;
}

System.out.println(total == 1.00);
System.out.println(total);
}
}


#Tasks
👍2
👍2🤯1
Что такое класс-загрузчик (ClassLoader) 🤓

Ответ:

ClassLoader
— это часть JVM, отвечающая за динамическую загрузку классов в память во время выполнения.

Загрузка происходит иерархически и по принципу делегирования.

Существует три основных встроенных загрузчика:
- Bootstrap ClassLoader (загружает классы Java, написан на C++),
- Extension ClassLoader (загружает классы из jre/lib/ext),
- Application/System ClassLoader (загружает классы из classpath приложения).

Разработчики могут создавать свои загрузчики, наследуясь от ClassLoader, что используется, например, в сервлет-контейнерах (Tomcat) или для реализации горячего перезапуска кода.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥2