История технологии сегодня — 22 июня
ℹ️ Кто родился в этот день
Доктор Ко́нрад Эрнст О́тто Цу́зе (нем. Dr. Konrad Ernst Otto Zuse; 22 июня 1910, Берлин, Германская империя — 18 декабря 1995, Хюнфельд, Германия) — немецкий инженер, один из пионеров компьютеростроения. Наиболее известен как создатель первого действительно работающего электро-механического программируемого компьютера Z3 (1941) и первого языка программирования высокого уровня Планкалькюль (1948).
Гео́ргий (Ю́рий) Ви́кторович Вульф (22 июня 1863, Нежин, Российская Империя — 25 декабря 1925, Москва, СССР) — российский учёный-кристаллограф, автор «сетки Вульфа» и формулы Брэгга — Вульфа. Автор более 150 работ по кристаллографии, кристаллофизике, кристаллооптике, рентгеноструктурному анализу, минералогии.
Ге́рман Минко́вский (нем. Hermann Minkowski; 22 июня 1864, Алексоты, Августовская губерния, Мариампольский уезд, Российская империя — 12 января 1909, Гёттинген, Германская империя) — немецкий математик, разработавший геометрическую теорию чисел и геометрическую четырёхмерную модель теории относительности. Первые результаты Минковского касались теории квадратичных форм. В 1896 году опубликован результат, ныне известный как теорема Минковского о выпуклом теле — о том, что выпуклая область n-мерного пространства, объёмом >=2n и симметричная относительно начала координат, непременно содержит точку с целочисленными координатами, отличную от начала координат. По словам Касселса[17], вся геометрия чисел основана на этом результате. После создания геометрии чисел Минковский много и плодотворно работает над применением полученных результатов в других областях теории чисел: диофантовы приближения, теория многогранников и другие. Ему принадлежат фундаментальные достижения в геометрии выпуклых тел. В 1907 году Минковский предложил геометрическое представление кинематики теории относительности, введя четырёхмерное псевдоевклидово пространство (известное сейчас как пространство Минковского). В этой модели время и пространство представляют собой не различные сущности, а взаимосвязанные измерения единого пространства-времени. Все релятивистские эффекты получили наглядное геометрическое истолкование.
🌐 Знаковые события
1978 — обнаружен спутник Плутона — Харон.
#Biography #Birth_Date #Events #22июня
Доктор Ко́нрад Эрнст О́тто Цу́зе (нем. Dr. Konrad Ernst Otto Zuse; 22 июня 1910, Берлин, Германская империя — 18 декабря 1995, Хюнфельд, Германия) — немецкий инженер, один из пионеров компьютеростроения. Наиболее известен как создатель первого действительно работающего электро-механического программируемого компьютера Z3 (1941) и первого языка программирования высокого уровня Планкалькюль (1948).
Гео́ргий (Ю́рий) Ви́кторович Вульф (22 июня 1863, Нежин, Российская Империя — 25 декабря 1925, Москва, СССР) — российский учёный-кристаллограф, автор «сетки Вульфа» и формулы Брэгга — Вульфа. Автор более 150 работ по кристаллографии, кристаллофизике, кристаллооптике, рентгеноструктурному анализу, минералогии.
Ге́рман Минко́вский (нем. Hermann Minkowski; 22 июня 1864, Алексоты, Августовская губерния, Мариампольский уезд, Российская империя — 12 января 1909, Гёттинген, Германская империя) — немецкий математик, разработавший геометрическую теорию чисел и геометрическую четырёхмерную модель теории относительности. Первые результаты Минковского касались теории квадратичных форм. В 1896 году опубликован результат, ныне известный как теорема Минковского о выпуклом теле — о том, что выпуклая область n-мерного пространства, объёмом >=2n и симметричная относительно начала координат, непременно содержит точку с целочисленными координатами, отличную от начала координат. По словам Касселса[17], вся геометрия чисел основана на этом результате. После создания геометрии чисел Минковский много и плодотворно работает над применением полученных результатов в других областях теории чисел: диофантовы приближения, теория многогранников и другие. Ему принадлежат фундаментальные достижения в геометрии выпуклых тел. В 1907 году Минковский предложил геометрическое представление кинематики теории относительности, введя четырёхмерное псевдоевклидово пространство (известное сейчас как пространство Минковского). В этой модели время и пространство представляют собой не различные сущности, а взаимосвязанные измерения единого пространства-времени. Все релятивистские эффекты получили наглядное геометрическое истолкование.
1978 — обнаружен спутник Плутона — Харон.
#Biography #Birth_Date #Events #22июня
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
[Совет по Java #052]
Тема:
Проблема: В Java 8 метод
Разработчики часто используют
Решение: Используйте
Объяснение: Метод
Затем для каждого следующего элемента применяется
#Java #советы
Тема:
Stream.iterate(seed, predicate, unaryOperator) в Java 9 позволяет создавать ограниченные стримы без явного вызова limit(). Проблема: В Java 8 метод
Stream.iterate(seed, unaryOperator) генерирует бесконечный стрим, который требует явного ограничения через limit(). Это неудобно, когда условие завершения зависит от самих значений, а не от количества элементов. Разработчики часто используют
limit с заранее неизвестным числом итераций или добавляют takeWhile (Java 9), что может быть избыточно. В Java 9 появилась перегрузка iterate(seed, predicate, unaryOperator), которая позволяет задать условие продолжения. Однако важно понимать: predicate проверяется до применения unaryOperator к текущему элементу, а не после. Это значит, что если первый элемент не удовлетворяет предикату, стрим будет пустым, а не выдаст его и остановится. Ошибка в логике может привести к неожиданному пропуску элементов или бесконечному циклу, если предикат всегда true.Решение: Используйте
Stream.iterate с предикатом для компактного создания конечных последовательностей, когда условие зависит от значения элемента. Убедитесь, что предикат правильно определяет момент остановки: он проверяется перед каждой итерацией, начиная с начального значения. Если предикат возвращает false для начального элемента, стрим не сгенерирует ни одного элемента. Для условий, которые зависят от текущего элемента (например, пока число меньше 100), это работает корректно.public class StreamIteratePredicate {
public static void main(String[] args) {
//Java 8: бесконечный стрим + limit
Stream.iterate(1, i -> i * 2)
.limit(5)
.forEach(System.out::println); // 1, 2, 4, 8, 16
//Java 9: ограниченный стрим через predicate (проверка ДО генерации)
Stream.iterate(1, i -> i <= 16, i -> i * 2)
.forEach(System.out::println); // 1, 2, 4, 8, 16 (включает 16, т.к. проверка на 16 == true)
// Внимание: если начальное значение не удовлетворяет предикату — стрим пуст
Stream.iterate(100, i -> i <= 16, i -> i / 2)
.forEach(System.out::println); // Нет вывода!
//Опасный пример: предикат всегда true — бесконечный стрим (без limit)
// Stream.iterate(1, i -> true, i -> i + 1).forEach(...) // зависнет
//Альтернатива с takeWhile (Java 9) — условие проверяется ПОСЛЕ генерации
Stream.iterate(1, i -> i * 2)
.takeWhile(i -> i <= 16)
.forEach(System.out::println); // 1, 2, 4, 8, 16
// Разница: если нужно включить элемент, который не удовлетворяет условию — takeWhile подходит
// Например, генерация чисел до первого, превышающего 5
Stream.iterate(1, i -> i + 1)
.takeWhile(i -> i <= 5)
.forEach(System.out::println); // 1,2,3,4,5 (включает 5)
// Но с iterate с predicate: если i = 6, то предикат i <= 5 даст false до генерации 6, поэтому 5 включена
Stream.iterate(1, i -> i <= 5, i -> i + 1)
.forEach(System.out::println); // 1,2,3,4,5 — то же самое
}
}Объяснение: Метод
Stream.iterate(seed, predicate, unaryOperator) работает по следующему алгоритму: сначала проверяет predicate.test(seed). Если false, стрим завершается. Если true, выдает seed как первый элемент. Затем для каждого следующего элемента применяется
unaryOperator к предыдущему, и перед выдачей проверяется predicate.test(next). Таким образом, предикат определяет, должен ли следующий элемент быть включен. Это отличается от takeWhile, который проверяет условие на текущем элементе и включает его, даже если следующее нарушит условие. Выбор между ними зависит от семантики: если условие завершения относится к самому значению (например, "пока значение меньше 100"), то iterate с предикатом и takeWhile дадут одинаковый результат для корректных данных. Но если начальное значение не соответствует условию, iterate выдаст пустой стрим, а takeWhile — тоже пустой, потому что первый элемент не проходит. В сложных сценариях, где нужно включить элемент, нарушающий условие, используйте takeWhile или ручной контроль. #Java #советы
👍3
15. Saga Choreography: от HTTP к Event-Driven архитектуре
В этом видео мы делаем первый по-настоящему архитектурный шаг в серии OrderHub: заменяем синхронный HTTP-вызов между микросервисами на асинхронную Saga Choreography.
До этого видео мы уже настроили Kafka, Outbox, Debezium и OpenTelemetry, но самая важная бизнес-операция – оплата заказа – всё ещё выполнялась синхронно: клиент ждал ответа от payment-service, а при его отказе заказ не создавался.
Мы исправляем это:
🔵 Убираем блокирующий HTTP-вызов из OrderService
🔵 Вводим новый статус AWAITING_PAYMENT
🔵 Превращаем payment-service в полноценного участника Saga (Kafka consumer + producer)
🔵 Добавляем PaymentEventConsumer в order-service, который обрабатывает PaymentCompletedEvent и PaymentFailedEvent
🔵 Реализуем компенсирующую транзакцию (cancelOrder) – вместо rollback делаем новое действие
Что вы узнаете
🔵 Почему синхронный HTTP – это антипаттерн в микросервисах
🔵 Что такое Saga Choreography и чем она отличается от Orchestration
🔵 Как перейти от ACID к BASE и от rollback к компенсации
🔵 Как сделать бизнес-операцию асинхронной с помощью Kafka и Outbox
🔵 Как реализовать компенсирующую транзакцию на практике
Исходный код проекта на GitHub наверняка заслуживает Ваших звезд!🙂
Ссылка на Youtube
Ссылка на Рутьюб
Смотрите, ставьте лайки, подписывайтесь на каналы!✌️
Жду ваших реакций и оценок🙂
В этом видео мы делаем первый по-настоящему архитектурный шаг в серии OrderHub: заменяем синхронный HTTP-вызов между микросервисами на асинхронную Saga Choreography.
До этого видео мы уже настроили Kafka, Outbox, Debezium и OpenTelemetry, но самая важная бизнес-операция – оплата заказа – всё ещё выполнялась синхронно: клиент ждал ответа от payment-service, а при его отказе заказ не создавался.
Мы исправляем это:
🔵 Убираем блокирующий HTTP-вызов из OrderService
🔵 Вводим новый статус AWAITING_PAYMENT
🔵 Превращаем payment-service в полноценного участника Saga (Kafka consumer + producer)
🔵 Добавляем PaymentEventConsumer в order-service, который обрабатывает PaymentCompletedEvent и PaymentFailedEvent
🔵 Реализуем компенсирующую транзакцию (cancelOrder) – вместо rollback делаем новое действие
Что вы узнаете
🔵 Почему синхронный HTTP – это антипаттерн в микросервисах
🔵 Что такое Saga Choreography и чем она отличается от Orchestration
🔵 Как перейти от ACID к BASE и от rollback к компенсации
🔵 Как сделать бизнес-операцию асинхронной с помощью Kafka и Outbox
🔵 Как реализовать компенсирующую транзакцию на практике
Исходный код проекта на GitHub наверняка заслуживает Ваших звезд!
Ссылка на Youtube
Ссылка на Рутьюб
Смотрите, ставьте лайки, подписывайтесь на каналы!
Жду ваших реакций и оценок
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Что выведет код?
#Tasks
import java.util.stream.Stream;
public class Task220626 {
public static void main(String[] args) {
Stream.iterate(1, i -> i <= 3, i -> i + 1)
.peek(System.out::print)
.anyMatch(i -> i > 1);
}
}
#Tasks
👍2
Please open Telegram to view this post
VIEW IN TELEGRAM
🤣11🤯1
Что такое 🤓
Ответ:
boolean — примитивный тип, принимающий два значения: true и false.
Размер в памяти явно не специфицирован (зависит от JVM, обычно 1 байт, но в массивах — 1 байт на элемент, а в стеке может быть 4 байта).
Не является числовым типом — не может быть преобразован в int или наоборот (в отличие от C/C++).
Используется в условных выражениях (if, while), логических операторах (&&, ||, !) и для хранения флагов.
#собеседование
boolean в Java? Сколько значений он может принимать? Ответ:
Размер в памяти явно не специфицирован (зависит от JVM, обычно 1 байт, но в массивах — 1 байт на элемент, а в стеке может быть 4 байта).
Не является числовым типом — не может быть преобразован в int или наоборот (в отличие от C/C++).
Используется в условных выражениях (if, while), логических операторах (&&, ||, !) и для хранения флагов.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
История технологии сегодня — 23 июня
ℹ️ Кто родился в этот день
А́лан Мэ́тисон Тью́ринг (англ. Alan Mathison Turing [ˈtjʊərɪŋ]; 23 июня 1912, Вестминстер — 7 июня 1954, Чешир) — британский математик, логик и криптограф, оказавший существенное влияние на развитие информатики. Офицер ордена Британской империи (OBE, 1945), член Лондонского королевского общества (1951). Предложенная им в 1936 году абстрактная вычислительная «Машина Тьюринга», которую можно считать моделью компьютера общего назначения, позволила формализовать понятие алгоритма и до сих пор используется во множестве теоретических и практических исследований. Научные труды А. Тьюринга — общепризнанный вклад в основания информатики (и в частности — теории искусственного интеллекта).
🌐 Знаковые события
1868 — Кристофер Лэтем Шоулз из Висконсина запатентовал пишущую машинку. Размерами она была с большой стол, клавиатура её напоминала рояль, печатала машинка только большими буквами, причём машинистка не видела результата своей работы, поскольку бумага находилась под планшетом.
1991 — выпущена видеоигра Sonic the Hedgehog для платформы Sega Mega Drive (первая игра этой серии).
#Biography #Birth_Date #Events #23июня
А́лан Мэ́тисон Тью́ринг (англ. Alan Mathison Turing [ˈtjʊərɪŋ]; 23 июня 1912, Вестминстер — 7 июня 1954, Чешир) — британский математик, логик и криптограф, оказавший существенное влияние на развитие информатики. Офицер ордена Британской империи (OBE, 1945), член Лондонского королевского общества (1951). Предложенная им в 1936 году абстрактная вычислительная «Машина Тьюринга», которую можно считать моделью компьютера общего назначения, позволила формализовать понятие алгоритма и до сих пор используется во множестве теоретических и практических исследований. Научные труды А. Тьюринга — общепризнанный вклад в основания информатики (и в частности — теории искусственного интеллекта).
1868 — Кристофер Лэтем Шоулз из Висконсина запатентовал пишущую машинку. Размерами она была с большой стол, клавиатура её напоминала рояль, печатала машинка только большими буквами, причём машинистка не видела результата своей работы, поскольку бумага находилась под планшетом.
1991 — выпущена видеоигра Sonic the Hedgehog для платформы Sega Mega Drive (первая игра этой серии).
#Biography #Birth_Date #Events #23июня
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)
Глава 1. Классический Java I/O (java.io)
BufferedReader – построчное чтение. Путь данных и управление памятью
Ключевое архитектурное решение — разделение между транспортом и семантикой.
Внутреннее устройство
Поле
Механика readLine()
Ключевые особенности:
Универсальность разделителей.
Разделитель не включается в результат. Возвращенная
Признак конца потока.
Путь данных в памяти
Жизненный цикл объектов в heap
При каждом вызове readLine() создаются объекты:
Внутренний массив
Это создает дополнительное давление на GC для файлов с очень длинными строками.
Роль garbage collector
Сценарий: чтение файла с миллионом строк
Память в процессе:
Если вызывающий код не сохраняет
Проблема: сохранение всех строк
Здесь все
Решение — потоковая обработка без накопления:
#Java #для_новичков #beginner #IO #NIO #BufferedReader
Глава 1. Классический Java I/O (java.io)
BufferedReader – построчное чтение. Путь данных и управление памятью
BufferedReader — декоратор над Reader, добавляющий буферизацию символьного ввода и специализированные методы для построчного чтения. В отличие от базового Reader, который оперирует массивами char[], BufferedReader предоставляет readLine() — метод, возвращающий строку текста, завершающуюся символом перевода строки.Ключевое архитектурное решение — разделение между транспортом и семантикой.
InputStreamReader декодирует байты в символы. BufferedReader накапливает символы в буфере и распознает логические строки, абстрагируя приложение от деталей разделителей строк (\n, \r, \r\n).Внутреннее устройство
// Упрощенная структура из OpenJDK
public class BufferedReader extends Reader {
// Буфер символов в heap JVM
private char[] cb;
// Количество символов в буфере
private int nChars;
// Текущая позиция чтения
private int nextChar;
// Маркер для mark/reset
private int markedChar = UNMARKED;
private int readAheadLimit = 0;
// Признак пропуска \n после \r
private boolean skipLF = false;
// Конструкторы
public BufferedReader(Reader in) {
this(in, defaultCharBufferSize); // 8192 по умолчанию
}
public BufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
cb = new char[sz]; // Выделение в heap
}
}
Поле
cb — массив char[] в heap, служащий кольцевым буфером для накопления символов из underlying Reader. Размер по умолчанию 8192 символов, что соответствует 16 KB памяти heap (каждый char — 2 байта).Механика readLine()
readLine() выполняет последовательный поиск символов перевода строки в буфере, накапливая символы до разделителя во временный StringBuilder, и создает String при нахождении \n или \r\n:// Упрощенная логика readLine()
public String readLine() throws IOException {
StringBuilder sb = new StringBuilder(defaultExpectedLineLength);
while (true) {
if (nextChar >= nChars) {
fill(); // Заполнение буфера из underlying Reader
if (nextChar >= nChars) {
// Конец потока
return sb.length() > 0 ? sb.toString() : null;
}
}
char c = cb[nextChar++];
if (c == '\n') {
if (skipLF) {
skipLF = false;
continue; // Пропуск \n после \r
}
return sb.toString(); // Строка завершена
}
if (c == '\r') {
skipLF = true;
return sb.toString(); // Строка завершена, \n будет пропущен
}
sb.append(c); // Накопление символов
}
}
Ключевые особенности:
Универсальность разделителей.
\n (Unix), \r (старый Mac), \r\n (Windows) распознаются корректно.Разделитель не включается в результат. Возвращенная
String не содержит \n или \r.Признак конца потока.
null возвращается, когда достигнут EOF и StringBuilder пуст.Путь данных в памяти
[Диск: файл в байтах UTF-8]
-> [Page Cache ОС]
-> [FileInputStream: системный вызов read()]
-> [InputStreamReader: декодирование байт -> char]
-> [BufferedReader.fill(): копирование char[] в cb[8192]]
-> [BufferedReader.readLine(): поиск \n в cb]
-> [StringBuilder: накопление символов строки]
-> [StringBuilder.toString(): создание String в heap]
-> [Возврат String]
Жизненный цикл объектов в heap
При каждом вызове readLine() создаются объекты:
StringBuilder — временный накопитель символов. Создается при каждом вызове readLine(), живет до возврата String. Собирается GC немедленно.String — результат. Создается через StringBuilder.toString(). В современных JDK (9+) String хранит byte[] вместо char[], с компактным представлением для латиницы (1 байт на символ) и UTF-16 для остального.Внутренний массив
StringBuilder — char[], расширяющийся при необходимости. При превышении емкости создается новый массив большего размера с System.arraycopy.// Длинная строка превышает начальную емкость StringBuilder
StringBuilder sb = new StringBuilder(80); // Начальный массив char[80]
// Строка длиной 200 символов
// sb расширяется: char[80] -> char[160] -> char[320] -> ...
// Старые массивы становятся мусором
Это создает дополнительное давление на GC для файлов с очень длинными строками.
Роль garbage collector
Сценарий: чтение файла с миллионом строк
public long countLines(String path) throws IOException {
long lineCount = 0;
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
lineCount++;
// line — новая String в heap
// StringBuilder и его внутренний char[] — мусор
}
} // reader закрыт, cb освобожден
return lineCount;
}Память в процессе:
cb[8192] — буфер BufferedReader, живет до закрытияStringBuilder + внутренний char[] — при каждом вызове readLine()String — при каждом вызове readLine(), возвращается вызывающемуЕсли вызывающий код не сохраняет
String, она становится мусором после итерации. Для файла с 1 000 000 строк создается 1 000 000 объектов String + 1 000 000 StringBuilder + несколько расширенных char[] для длинных строк. Все они короткоживущие и собираются minor GC в young generation.Проблема: сохранение всех строк
// Антипаттерн: удержание всех строк в памяти
public List<String> readAllLines(String path) throws IOException {
List<String> lines = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line); // Все String удерживаются списком
}
}
return lines; // Возврат: все строки в heap
}
Здесь все
String удерживаются списком lines. Для файла 100MB с ~1 000 000 строк это ~100MB в String + оверхед ArrayList. GC не может собрать эти String, пока список достижим. Это приводит к увеличению old generation и потенциальному OutOfMemoryError.Решение — потоковая обработка без накопления:
// Правильно: потоковая обработка без хранения всех строк
public void processLines(String path, Consumer<String> processor) throws IOException {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
processor.accept(line);
// line становится недостижимой после итерации, если processor не сохраняет
}
}
}
#Java #для_новичков #beginner #IO #NIO #BufferedReader
👍4
Проблема: сохранение всех строк
Здесь все
Решение — потоковая обработка без накопления:
Практический пример: чтение файла построчно с подсчетом
Анализ памяти:
Все
Оптимизация: избежание хранения самой длинной строки
Здесь ни одна
Проблема: очень длинные строки
Если файл содержит строку, превышающую размер
Расширение
Для предотвращения можно указать начальную емкость:
Однако
Альтернатива: Files.lines() и Stream API
Java 8 предоставляет
Внутри
#Java #для_новичков #beginner #IO #NIO #BufferedReader
// Антипаттерн: удержание всех строк в памяти
public List<String> readAllLines(String path) throws IOException {
List<String> lines = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line); // Все String удерживаются списком
}
}
return lines; // Возврат: все строки в heap
}
Здесь все
String удерживаются списком lines. Для файла 100MB с ~1 000 000 строк это ~100MB в String + оверхед ArrayList. GC не может собрать эти String, пока список достижим. Это приводит к увеличению old generation и потенциальному OutOfMemoryError.Решение — потоковая обработка без накопления:
// Правильно: потоковая обработка без хранения всех строк
public void processLines(String path, Consumer<String> processor) throws IOException {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
processor.accept(line);
// line становится недостижимой после итерации, если processor не сохраняет
}
}
}
Практический пример: чтение файла построчно с подсчетом
import java.io.*;
import java.nio.charset.StandardCharsets;
public class LineCounter {
public LineStats analyzeFile(String path) throws IOException {
long lineCount = 0;
long totalChars = 0;
long maxLineLength = 0;
String longestLine = null;
// BufferedReader с underlying InputStreamReader и FileInputStream
// cb[8192] создается в heap при конструировании
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
lineCount++;
long lineLength = line.length();
totalChars += lineLength;
if (lineLength > maxLineLength) {
maxLineLength = lineLength;
longestLine = line; // Сохранение ссылки — String остается в heap!
}
}
} // close() освобождает cb, но longestLine удерживается
return new LineStats(lineCount, totalChars, maxLineLength, longestLine);
}
public record LineStats(long lineCount, long totalChars,
long maxLineLength, String longestLine) {}
}
Анализ памяти:
cb[8192] — освобожден при закрытии, GC собираетВсе
String из readLine() — собраны GC, кроме longestLinelongestLine — удерживается возвращаемым LineStats, живет до потери ссылкиОптимизация: избежание хранения самой длинной строки
// Если нужна только длина, не хранить строку
public LineStats analyzeFileOptimized(String path) throws IOException {
long lineCount = 0;
long totalChars = 0;
long maxLineLength = 0;
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
lineCount++;
long lineLength = line.length();
totalChars += lineLength;
maxLineLength = Math.max(maxLineLength, lineLength);
// line не сохраняется — собирается GC
}
}
return new LineStats(lineCount, totalChars, maxLineLength, null);
}
Здесь ни одна
String не удерживается. Все собираются minor GC, память остается минимальной даже для гигабайтных файлов.Проблема: очень длинные строки
Если файл содержит строку, превышающую размер
cb (8192 символов), readLine() выполняет несколько итераций fill():// Строка длиной 100 000 символов без \n
// readLine() выполняет ~13 вызовов fill(), накапливая в StringBuilder
StringBuilder sb = new StringBuilder(80); // Начальный массив char[80]
// При достижении 80: расширение до 160, копирование
// При достижении 160: расширение до 320, копирование
// ...
// Итоговый массив char[131072] для 100000 символов
Расширение
StringBuilder создает промежуточные массивы char[], которые становятся мусором. Для предотвращения можно указать начальную емкость:
// Если известна ожидаемая длина строки
StringBuilder sb = new StringBuilder(expectedLineLength);
Однако
readLine() не позволяет контролировать StringBuilder — он создается внутри метода с фиксированной начальной емкостью.Альтернатива: Files.lines() и Stream API
Java 8 предоставляет
Files.lines() — потоковый API для построчного чтения:// Потоковое чтение без явного BufferedReader
public long countLinesStream(String path) throws IOException {
try (Stream<String> lines = Files.lines(Path.of(path), StandardCharsets.UTF_8)) {
return lines.count();
}
}
Внутри
Files.lines() создает BufferedReader, но инкапсулирует его в Stream. Преимущество — ленивое выполнение и встроенные методы обработки. Недостаток — Stream требует закрытия, иначе файловый дескриптор остается открытым.#Java #для_новичков #beginner #IO #NIO #BufferedReader
👍5
Что выведет код?
#Tasks
import java.io.*;
public class Task230626 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new StringReader("Hello"));
br.reset();
System.out.println("Done");
}
}
#Tasks
👍2
Варианты ответа:
Anonymous Quiz
64%
Done
21%
Исключение IOException
7%
Исключение NullPointerException
7%
Исключение UnsupportedOperationException
👍3
Что такое текстовые блоки (Text Blocks)? 🤓
Ответ:
Текстовые блоки (Java 13 как preview, финал в Java 15) — это многострочные строки, упрощающие написание JSON, XML, SQL и HTML.
Обрамляются тремя двойными кавычками """. Сохраняют отступы относительно позиции закрывающих кавычек.
Пример: String json = """ {"name": "Alice"} """;.
Не требуют экранирования кавычек и переводов строк. Убирают общий пробельный отступ (indentation stripping).
Удобны для встраиваемых шаблонов. Поддерживают интерполяцию через String.formatted().
#собеседование
Ответ:
Обрамляются тремя двойными кавычками """. Сохраняют отступы относительно позиции закрывающих кавычек.
Пример: String json = """ {"name": "Alice"} """;.
Не требуют экранирования кавычек и переводов строк. Убирают общий пробельный отступ (indentation stripping).
Удобны для встраиваемых шаблонов. Поддерживают интерполяцию через String.formatted().
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
История технологии сегодня — 24 июня
ℹ️ Кто родился в этот день
Виктор Франц Гесс (нем. Victor Franz Hess; 24 июня 1883, замок Вальдштейн, Штирия, Австро-Венгрия — 17 декабря 1964, Нью-Йорк, США) — австрийский и американский физик, нобелевский лауреат 1936 года (совместно с Карлом Андерсоном), награждённый за открытие космических лучей.
Ма́ртин Лью́ис Перл (англ. Martin Lewis Perl; 24 июня 1927, Нью-Йорк — 30 сентября 2014, Пало-Алто) — американский физик, профессор, общественный деятель, лауреат премии Вольфа (1982) за открытие ряда элементарных частиц, в том числе кварков, и Нобелевской премии по физике 1995 года «за открытие тау-лептона» (вторую половину премии получил Фредерик Райнес «за экспериментальное обнаружение нейтрино»).
🌐 Знаковые события
1963 — в столице Великобритании Лондоне, в студии Би-Би-Си, впервые продемонстрирован бытовой видеомагнитофон.
#Biography #Birth_Date #Events #24июня
Виктор Франц Гесс (нем. Victor Franz Hess; 24 июня 1883, замок Вальдштейн, Штирия, Австро-Венгрия — 17 декабря 1964, Нью-Йорк, США) — австрийский и американский физик, нобелевский лауреат 1936 года (совместно с Карлом Андерсоном), награждённый за открытие космических лучей.
Ма́ртин Лью́ис Перл (англ. Martin Lewis Perl; 24 июня 1927, Нью-Йорк — 30 сентября 2014, Пало-Алто) — американский физик, профессор, общественный деятель, лауреат премии Вольфа (1982) за открытие ряда элементарных частиц, в том числе кварков, и Нобелевской премии по физике 1995 года «за открытие тау-лептона» (вторую половину премии получил Фредерик Райнес «за экспериментальное обнаружение нейтрино»).
1963 — в столице Великобритании Лондоне, в студии Би-Би-Си, впервые продемонстрирован бытовой видеомагнитофон.
#Biography #Birth_Date #Events #24июня
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #053]
Тема:
Проблема: Статический метод
Если два элемента стрима отображаются на один и тот же ключ, коллектор выбрасывает
Решение: Используйте перегруженную версию
Наиболее частые стратегии: оставить первое значение (
Также существует
Объяснение: Базовая версия
Указывая
#Java #советы
Тема:
Collectors.toMap при дублирующихся ключах выбрасывает IllegalStateException.Проблема: Статический метод
Collectors.toMap(Function keyMapper, Function valueMapper) создает коллектор, который собирает элементы стрима в HashMap. Если два элемента стрима отображаются на один и тот же ключ, коллектор выбрасывает
IllegalStateException с сообщением "duplicate key". Это поведение часто оказывается неожиданным для разработчиков, которые полагаются на уникальность ключей в данных, но из-за ошибок в данных или бизнес-логике дубликаты возникают. В результате приложение падает в рантайме, и диагностика может быть затруднена, особенно в больших стримах. Даже если вероятность дубликатов мала, надежность кода требует явного определения стратегии разрешения конфликтов.Решение: Используйте перегруженную версию
Collectors.toMap(keyMapper, valueMapper, mergeFunction), где mergeFunction — это BinaryOperator, определяющий, какое значение сохранить при конфликте. Наиболее частые стратегии: оставить первое значение (
(oldValue, newValue) -> oldValue), взять последнее ((old, new) -> new), объединить значения (например, конкатенировать строки или суммировать числа). Для сложной агрегации используйте Collectors.groupingBy в связке с downstream-коллектором. Также существует
Collectors.toConcurrentMap для потокобезопасных карт.import java.util.*;
import java.util.stream.*;
public class ToMapDuplicate {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "apple", "cherry");
//Антипаттерн: выбросит IllegalStateException
try {
Map<String, Integer> bad = items.stream()
.collect(Collectors.toMap(s -> s, String::length));
} catch (IllegalStateException e) {
System.err.println("Duplicate key: " + e.getMessage());
}
//Решение 1: оставить первое значение
Map<String, Integer> first = items.stream()
.collect(Collectors.toMap(
s -> s,
String::length,
(oldVal, newVal) -> oldVal
));
System.out.println("First: " + first); // {banana=6, apple=5, cherry=6}
//Решение 2: взять последнее значение (часто для перезаписи)
Map<String, Integer> last = items.stream()
.collect(Collectors.toMap(
s -> s,
String::length,
(old, newVal) -> newVal
));
System.out.println("Last: " + last);
//Решение 3: объединить значения (например, список длин)
Map<String, List<Integer>> grouped = items.stream()
.collect(Collectors.toMap(
s -> s,
s -> new ArrayList<>(List.of(s.length())),
(list1, list2) -> { list1.addAll(list2); return list1; }
));
System.out.println("Grouped: " + grouped);
//Альтернатива: groupingBy для агрегации
Map<String, Long> countByKey = items.stream()
.collect(Collectors.groupingBy(
s -> s,
Collectors.counting()
));
System.out.println("Counts: " + countByKey);
}
}
Объяснение: Базовая версия
toMap вызывает Map.merge(key, value, throwingMerger()), где throwingMerger просто бросает IllegalStateException. Это сделано для того, чтобы предотвратить случайную потерю данных без явного указания стратегии. Указывая
mergeFunction, вы берете на себя ответственность за разрешение конфликтов. Выбор стратегии зависит от бизнес-правил: для уникальных идентификаторов лучше перезаписывать последним значением; для агрегации — использовать groupingBy с downstream; для подсчета частоты — groupingBy с counting(). #Java #советы
👍5
Что выведет код?
#Tasks
import java.util.*;
import java.util.stream.*;
public class Task240626 {
static class Person240626 {
int id;
String name;
Person240626(int id, String name) {
this.id = id;
this.name = name;
}
}
public static void main(String[] args) {
List<Person240626> people = Arrays.asList(
new Person240626(1, "Alice"),
new Person240626(2, "Bob"),
new Person240626(1, "Charlie")
);
try {
Map<Integer, String> map = people.stream()
.collect(
Collectors.toMap(p -> p.id, p -> p.name)
);
System.out.println("Success");
} catch (Exception e) {
System.out.println(e.getClass().getSimpleName());
}
}
}
#Tasks
👍2
Варианты ответа:
Anonymous Quiz
47%
Success
35%
IllegalStateException
18%
IllegalArgumentException
0%
NullPointerException
👍4
Что такое 🤓
Ответ:
Начиная с Java 12 (preview) и финал в Java 14, switch может быть выражением, возвращающим значение.
Используется -> вместо : и yield для возврата значения из блока.
Пример:
String result = switch (day) { case MON -> "Work"; case SAT, SUN -> "Weekend"; default -> throw ...; };.
Также поддерживается блок: case MON -> { yield "Work"; }. Это делает код лаконичнее, устраняет fall-through (прорыв) и позволяет использовать switch в функциональном стиле.
Важно, что для выражения должны быть покрыты все возможные значения (или default).
#собеседование
switch-выражения (Switch Expressions)? Ответ:
Используется -> вместо : и yield для возврата значения из блока.
Пример:
String result = switch (day) { case MON -> "Work"; case SAT, SUN -> "Weekend"; default -> throw ...; };.
Также поддерживается блок: case MON -> { yield "Work"; }. Это делает код лаконичнее, устраняет fall-through (прорыв) и позволяет использовать switch в функциональном стиле.
Важно, что для выражения должны быть покрыты все возможные значения (или default).
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологии сегодня — 25 июня
ℹ️ Кто родился в этот день
Шамиль Гимбатович Алиев (род. 25 июня 1943, Тануси, Хунзахский район) — доктор технических наук, профессор. Заслуженный деятель наук Дагестанской АССР. Разработчик ракетного оружия и космических технологий. Известен как один из авторов монографий «Низкочастотное излучение развитых кавитационных течений», «Фундаментальные технические комплексы. Теория аналитического проектирования» и др. Разработчик торпед, установленных на атомной подводной лодке «Курск». Знаток проблем устойчивости вихревой пелены за крылом конечного размаха. Со второй половины 1980-х годов на крупнейших выставках оружия (на Международной Морской выставке в Сиднее в 2004 году и др.) позиционируется как «идеолог и конструктор надводного и подводного оружия аэрокосмического комплекса России» и «разработчик торпедного оружия России».
Алексе́й Алексе́евич Абрико́сов (25 июня 1928, Москва — 29 марта 2017, Пало-Алто, США) — советский, российский и американский физик-теоретик. Лауреат Нобелевской премии по физике(2003, совместно с Виталием Гинзбургом и Энтони Леггетом). В 2003 году, совместно с В. Л. Гинзбургом и Э. Леггетом, получил Нобелевскую премию по физике за «основополагающие работы по теории сверхпроводников и сверхтекучих жидкостей».
Йоханнес Ханс Даниель Йе́нсен (нем. Johannes Hans Daniel Jensen; 25 июня 1907, Гамбург — 11 февраля 1973, Гейдельберг) — немецкий учёный, физик-ядерщик, лауреат Нобелевской премии по физике в 1963 году. Половина премии, совместно с Марией Гёпперт-Майер, «за открытия, касающиеся оболочечной структуры ядра». Вторую половину премии получил Юджин Вигнер.
Герман Юлиус Оберт (нем. Hermann Julius Oberth, 25 июня 1894 — 28 декабря 1989) — немецкий учёный и инженер в области космонавтики и ракетостроения, один из основоположников современной ракетной техники.
🌐 Знаковые события
1966 — запускается первый советский метеоспутник «Космос-122».
1974 — запуск советской орбитальной космической станции «Салют-3».
1998 — официально вышла операционная система «Windows 98».
#Biography #Birth_Date #Events #25июня
Шамиль Гимбатович Алиев (род. 25 июня 1943, Тануси, Хунзахский район) — доктор технических наук, профессор. Заслуженный деятель наук Дагестанской АССР. Разработчик ракетного оружия и космических технологий. Известен как один из авторов монографий «Низкочастотное излучение развитых кавитационных течений», «Фундаментальные технические комплексы. Теория аналитического проектирования» и др. Разработчик торпед, установленных на атомной подводной лодке «Курск». Знаток проблем устойчивости вихревой пелены за крылом конечного размаха. Со второй половины 1980-х годов на крупнейших выставках оружия (на Международной Морской выставке в Сиднее в 2004 году и др.) позиционируется как «идеолог и конструктор надводного и подводного оружия аэрокосмического комплекса России» и «разработчик торпедного оружия России».
Алексе́й Алексе́евич Абрико́сов (25 июня 1928, Москва — 29 марта 2017, Пало-Алто, США) — советский, российский и американский физик-теоретик. Лауреат Нобелевской премии по физике(2003, совместно с Виталием Гинзбургом и Энтони Леггетом). В 2003 году, совместно с В. Л. Гинзбургом и Э. Леггетом, получил Нобелевскую премию по физике за «основополагающие работы по теории сверхпроводников и сверхтекучих жидкостей».
Йоханнес Ханс Даниель Йе́нсен (нем. Johannes Hans Daniel Jensen; 25 июня 1907, Гамбург — 11 февраля 1973, Гейдельберг) — немецкий учёный, физик-ядерщик, лауреат Нобелевской премии по физике в 1963 году. Половина премии, совместно с Марией Гёпперт-Майер, «за открытия, касающиеся оболочечной структуры ядра». Вторую половину премии получил Юджин Вигнер.
Герман Юлиус Оберт (нем. Hermann Julius Oberth, 25 июня 1894 — 28 декабря 1989) — немецкий учёный и инженер в области космонавтики и ракетостроения, один из основоположников современной ракетной техники.
1966 — запускается первый советский метеоспутник «Космос-122».
1974 — запуск советской орбитальной космической станции «Салют-3».
1998 — официально вышла операционная система «Windows 98».
#Biography #Birth_Date #Events #25июня
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)
Глава 1. Классический Java I/O (java.io)
BufferedWriter – запись строк. Буферизация и путь данных в памяти JVM
Ключевое отличие от
Внутреннее устройство
Поле
Механика write(String) и путь данных
Накопление в буфере
Путь данных при
Никаких системных вызовов, никакого кодирования. Пять символов копируются в
Сброс буфера: flushBuffer()
Путь данных при заполнении буфера:
Ключевой момент:
Метод newLine(): платформенная независимость
#Java #для_новичков #beginner #IO #NIO #BufferedWriter
Глава 1. Классический Java I/O (java.io)
BufferedWriter – запись строк. Буферизация и путь данных в памяти JVM
BufferedWriter — декоратор над Writer, добавляющий внутреннюю буферизацию символьного вывода. Он решает проблему, зеркальную BufferedReader: накладные расходы системных вызовов при частых мелких операциях записи. Вместо немедленной отправки каждого символа или строки в underlying Writer, BufferedWriter накапливает данные в массиве char[] в heap и сбрасывает их пачками.Ключевое отличие от
BufferedOutputStream: BufferedWriter работает с символами char (16 бит), а не байтами. Это означает, что его внутренний буфер хранит char[], а преобразование в байты происходит позже — в underlying OutputStreamWriter, который кодирует символы в UTF-8 или другую кодировку.Внутреннее устройство
// Упрощенная структура из OpenJDK
public class BufferedWriter extends Writer {
// Внутренний буфер символов в heap JVM
private char[] cb;
// Текущая позиция записи в буфере
private int nChars;
// Ссылка на underlying Writer
private Writer out;
// Разделитель строки для newLine()
private String lineSeparator;
// Конструкторы
public BufferedWriter(Writer out) {
this(out, defaultCharBufferSize); // 8192 по умолчанию
}
public BufferedWriter(Writer out, int sz) {
super(out);
if (sz <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
this.out = out;
cb = new char[sz]; // Выделение в heap: 8192 * 2 = 16384 байт
nChars = 0;
lineSeparator = System.lineSeparator(); // Платформенный разделитель
}
}
Поле
cb — массив char[] размером 8192 по умолчанию, что занимает 16 KB в heap. lineSeparator инициализируется значением System.lineSeparator(), которое равно \n на Unix/Linux/macOS и \r\n на Windows.Механика write(String) и путь данных
Накопление в буфере
public void write(String s, int off, int len) throws IOException {
// Если строка не влезает в оставшийся буфер — сброс
if (len >= cb.length - nChars) {
flushBuffer(); // Сброс накопленного в underlying Writer
}
if (len >= cb.length) {
// Строка больше буфера — запись напрямую, минуя буфер
writeDirect(s, off, len);
} else {
// Копирование строки в буфер
s.getChars(off, off + len, cb, nChars); // Быстрое копирование из String
nChars += len;
}
}Путь данных при
write("Hello"):[Java: bw.write("Hello")]
-> [BufferedWriter: len=5 < cb.length - nChars?]
-> [s.getChars(0, 5, cb, nChars)] // Копирование char[] из String в buf
-> [System.arraycopy: копирование 5 char в cb]
-> [nChars += 5]
-> [Возврат]Никаких системных вызовов, никакого кодирования. Пять символов копируются в
cb через System.arraycopy — нативный метод, оптимизированный JVM.Сброс буфера: flushBuffer()
private void flushBuffer() throws IOException {
if (nChars > 0) {
out.write(cb, 0, nChars); // Запись в underlying Writer
nChars = 0; // Сброс позиции
}
}Путь данных при заполнении буфера:
[BufferedWriter: flushBuffer()]
-> [OutputStreamWriter.write(cb, 0, nChars)]
-> [StreamEncoder: кодирование char[] -> байты UTF-8]
-> [ByteBuffer: накопление закодированных байт]
-> [FileOutputStream.write(byte[]): системный вызов]
-> [Нативный буфер userspace]
-> [Ядро ОС: syscall write]
-> [Page Cache: dirty pages]
Ключевой момент:
BufferedWriter не знает о кодировке. Он накапливает char[], а кодирование в байты происходит в OutputStreamWriter. Это означает, что буфер BufferedWriter содержит символы, а буфер StreamEncoder — байты. Два уровня буферизации создают двойное копирование при сбросе.Метод newLine(): платформенная независимость
public void newLine() throws IOException {
write(lineSeparator); // Запись \n или \r\n из поля lineSeparator
}
newLine() записывает разделитель строки, определенный при конструировании. Это позволяет генерировать файлы с правильными разделителями для целевой платформы, независимо от платформы выполнения.// На Unix: запись \n
// На Windows: запись \r\n
// Результат одинаков для кода, различается для файла
#Java #для_новичков #beginner #IO #NIO #BufferedWriter
👍4