Java for Beginner
871 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
История технологии сегодня — 30 июня

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

Влади́мир Алекса́ндрович Михельсо́н (18 [30] июня 1860, Тульчин, Подольская губерния (ныне Винницкая область Украины) — 27 февраля 1927, Москва) — русский и советский физик и геофизикметеоролог, один из основоположников отечественной актинометриипрофессор. Михельсон первым применил методы статистической физики для определения функции распределения энергии в спектре излучения абсолютно чёрного тела. Обобщил эффект Доплера на случай, когда свет проходит через среду с изменяющимся показателем преломления. Установил зависимость скорости распространения волны горения от состава горючей газовой смеси (детонация и дефлаграция). Заложил основы теории взрывного горения и развил теорию горения газовой смеси в горелке Бунзена

Влади́мир Никола́евич Челоме́й (17 (30) июня 1914, Седльце, Российская империя — 8 декабря 1984, Москва, СССР) — советский конструктор ракетно-космической техники и учёный в области механики и процессов управления, организатор науки, педагог высшей школы. Основные научные труды В. Н. Челомея относятся к области конструкции и динамике машинтеории колебанийдинамической устойчивости упругих систем, теории сервомеханизмов. В 1986 году в Государственный реестр открытий СССР под № 314 занесено открытие «Явления аномально высокого прироста тяги в газовом эжекционном процессе с пульсирующей активной струёй».


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

1948 — учёные Уильям ШоклиУолтер Браттейн и Джон Бардин из «Bell Laboratories» объявили о создании транзистора.


#Biography #Birth_Date #Events #30июня
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #055]

Тема: CompletableFuture при вызове completeExceptionally не возвращает значение, но последующие thenApply не выполняются.

Проблема: В асинхронном программировании с CompletableFuture исключение, переданное через completeExceptionally, прерывает цепочку стадий, построенных через thenApplythenAccept и подобные методы. Они попросту игнорируются, а исключение "проваливается" дальше, пока не встретит обработчик.

Разработчики часто используют exceptionally для восстановления, но он имеет ограничение: он выполняется только при исключении и не знает о значении при успешном завершении. Это приводит к дублированию логики или потере доступа к результату при нормальной работе.

Решение: Используйте handle(BiFunction) или whenComplete(BiConsumer).

Эти методы вызываются всегда — как при нормальном завершении, так и при исключении. handle принимает би-функцию, которая получает результат и исключение (одно из них null), и может возвращать новое значение для продолжения цепочки. whenComplete — аналогично, но возвращает CompletableFuture того же типа и не может изменить результат; он подходит для сайд-эффектов (логирование, аудит).
public class CompletableFutureHandle {

public static void main(String[] args) throws ExecutionException, InterruptedException {
// Создаем будущее, которое завершится исключительно
CompletableFuture<String> failedFuture = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Ошибка в задаче");
});

//thenApply не выполнится, исключение игнорируется
CompletableFuture<String> badChain = failedFuture.thenApply(s -> s + " обработано");
// badChain.get() выбросит ExecutionException с исходной ошибкой

//handle: всегда вызывается, получает результат или исключение
CompletableFuture<String> handled = failedFuture.handle((result, ex) -> {
if (ex != null) {
System.err.println("Ошибка: " + ex.getMessage());
return "Значение по умолчанию";
}
return result + " (успех)";
});
System.out.println("handle result: " + handled.join()); // Значение по умолчанию

//whenComplete: для логирования, не меняет результат
CompletableFuture<String> logged = failedFuture.whenComplete((result, ex) -> {
if (ex != null) {
System.err.println("Логируем ошибку: " + ex);
} else {
System.out.println("Успешно: " + result);
}
});
// logged по-прежнему будет завершено исключительно
// logged.join() выбросит CompletionException

// Обработка через exceptionally (только при ошибке)
CompletableFuture<String> recovered = failedFuture.exceptionally(ex -> {
System.err.println("Восстановление: " + ex);
return "Восстановленное значение";
});
System.out.println("exceptionally: " + recovered.join());

// Комбинирование: handle может изменить тип
CompletableFuture<Integer> typed = failedFuture.handle((s, ex) -> {
return ex != null ? -1 : s.length();
});
System.out.println("handle with type: " + typed.join()); // -1
}
}


Объяснение:
 При вызове completeExceptionally будущее переходит в состояние "завершено с исключением".

Все последующие этапы, созданные через методы без обработки ошибок (thenApplythenCompose и т.д.), не выполняются, а исключение распространяется по цепочке. handle и whenComplete являются "терминальными" в смысле обработки ошибок — они всегда вызываются и предоставляют доступ к обоим исходам. handle может преобразовать ошибку в результат, что часто удобно для восстановления или трансформации. whenComplete предназначен для побочных действий и не влияет на значение будущего.

#Java #советы
👍6
Что выведет код?

import java.util.concurrent.CompletableFuture;

public class Task300626 {
public static void main(String[] args) throws Exception {
CompletableFuture<String> future = new CompletableFuture<>();

CompletableFuture<String> result = future
.thenApply(s -> s + "!")
.exceptionally(ex -> "fallback");

future.completeExceptionally(new RuntimeException("Oops"));
System.out.println(result.get());
}
}


#Tasks
👍2
👍2
Какие методы появились в String в Java 11? 🤓

Ответ:

Помимо repeat(), добавились: 
isBlank() — возвращает true, если строка пуста или содержит только пробелы. 
lines() — возвращает Stream<String> строк, разбитых по \n, \r, или \r\n. 
strip() — удаляет пробелы в начале и конце (понимает Unicode, в отличие от trim()). 
stripLeading() / stripTrailing() — соответственно. formatted(Object... args) — упрощает форматирование: "Hello %s".formatted("World").

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



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
История технологии сегодня — 01 июля

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

Михаи́л Миха́йлович Шульц (1 июля 1919 года, Петроград — 9 октября 2006 года, Санкт-Петербург) — учёный-химикфизикохимикхудожникДействительный член АН СССР (1979, с 1991 — РАН), Герой Социалистического Труда (1991), лауреат двух Государственных премий СССР. Шульц написал ряд работ по термодинамической теории, термодинамике гетерогенных систем, теории стёколхимии и электрохимии стекла, мембранной электрохимии, теории ионного обмена и фазовых равновесий многокомпонентных систем, теории стеклянного электрода. С именем учёного связано становление pH-метрии и ионометрии, организация производства измерительной аппаратуры и материалов, широко используемых в медицине, химической и ядерной промышленности, в авиа- и ракетно-космической технике, в сельском хозяйстве и многих других областях.


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

1896 — На Всероссийской промышленной выставке в Нижнем Новгороде представлен первый русский автомобиль (создатели — Евгений Яковлев и Пётр Фрезе).


#Biography #Birth_Date #Events #01июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)

Глава 1. Классический Java I/O (java.io)

SequenceInputStream – объединение потоков. Последовательное чтение и управление памятью

SequenceInputStream — декоратор над одним или несколькими InputStream, который представляет их как единый непрерывный поток. Данные читаются последовательно: сначала из первого потока до его исчерпания, затем из второго, и так далее. С точки зрения вызывающего кода это обычный InputStream — методы read(), available(), close() работают прозрачно, скрывая факт наличия нескольких underlying источников.

Ключевое архитектурное решение — ленивое переключение между потоками. SequenceInputStream не читает данные заранее и не буферизует их. Он делегирует вызовы текущему активному потоку, и только при достижении конца текущего потока (возврат -1) переключается на следующий. Это минимизирует потребление памяти: данные не дублируются, а передаются напрямую из underlying потоков в вызывающий код.

Конструкторы и внутреннее устройство
// Упрощенная структура из OpenJDK
public class SequenceInputStream extends InputStream {
// Текущий активный поток
private InputStream in;

// Перечисление оставшихся потоков
private Enumeration<InputStream> e;

// Конструктор для двух потоков
public SequenceInputStream(InputStream s1, InputStream s2) {
Vector<InputStream> v = new Vector<>(2);
v.addElement(s1);
v.addElement(s2);
e = v.elements();
nextStream(); // Установка первого потока
}

// Конструктор для произвольного количества потоков
public SequenceInputStream(Enumeration<InputStream> e) {
this.e = e;
nextStream(); // Установка первого потока
}

// Переключение на следующий поток
private void nextStream() {
if (in != null) {
in.close(); // Закрытие исчерпанного потока
}
in = e.hasMoreElements() ? e.nextElement() : null;
}
}

Конструктор с двумя потоками создает временный Vector — устаревшая synchronized коллекция. Это исторический артефакт: SequenceInputStream существует с Java 1.0, и Vector был стандартом того времени.

Создание Vector в heap — незначительная аллокация, но в современном коде предпочтительнее использовать конструктор с Enumeration, построенным из Iterator или Collections.enumeration().


Механика чтения и путь данных

Посимвольное чтение
public int read() throws IOException {
if (in == null) {
return -1; // Все потоки исчерпаны
}
int c = in.read(); // Делегирование текущему потоку
if (c == -1) {
nextStream(); // Переключение на следующий
return read(); // Рекурсивный вызов для нового потока
}
return c;
}


Путь данных для read():
[Java: sis.read()]
-> [SequenceInputStream: in != null?]
-> [in.read()] // Делегирование текущему потоку
-> [Например, FileInputStream.read()]
-> [Системный вызов read()]
-> [Page Cache -> нативный буфер -> byte в heap]
-> [c == -1?] // Конец текущего потока?
-> [Нет: возврат c]
-> [Да: nextStream()]
-> [in.close()] // Закрытие исчерпанного потока
-> [in = e.nextElement()] // Следующий поток
-> [Рекурсивный read()] // Чтение из нового потока

Критически важно: nextStream() автоматически закрывает исчерпанный поток. Это предотвращает утечку файловых дескрипторов. Однако если underlying потоки не поддерживают повторное открытие (как FileInputStream), они становятся недоступны после закрытия.

Блочное чтение
public int read(byte[] b, int off, int len) throws IOException {
if (in == null) {
return -1;
}
if (b == null) {
throw new NullPointerException();
}

int n = in.read(b, off, len); // Попытка чтения из текущего потока
if (n > 0) {
return n; // Данные прочитаны
}
if (n == -1) {
nextStream(); // Конец потока — переключение
return read(b, off, len); // Рекурсивный вызов
}
// n == 0: поток вернул 0 байт, но не EOF
return 0;
}

Блочное чтение более эффективно: данные копируются напрямую из underlying потока в предоставленный массив без промежуточных буферов SequenceInputStream. Это минимизирует копирование в памяти.


#Java #для_новичков #beginner #IO #NIO #SequenceInputStream
👍3
Роль garbage collector

Жизненный цикл объектов
public void concatenateFiles(String outputPath, String... inputPaths) 
throws IOException {

// Создание Enumeration из списка путей
Vector<InputStream> streams = new Vector<>();
for (String path : inputPaths) {
streams.add(new FileInputStream(path)); // Каждый поток — объект в heap
}

// SequenceInputStream создается в heap
// Внутренний Vector — объект в heap
try (SequenceInputStream sis = new SequenceInputStream(streams.elements());
FileOutputStream fos = new FileOutputStream(outputPath)) {

byte[] buffer = new byte[8192]; // Буфер в heap
int bytesRead;

// Цикл без аллокаций — GC не вовлечен
while ((bytesRead = sis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}

} // sis.close() закрывает текущий поток и все оставшиеся в Enumeration

// Vector, FileInputStream'ы, buffer — недостижимы, GC собирает
}


Анализ памяти:
Vector<InputStream> — коллекция в heap, содержит ссылки на все потоки
FileInputStream для каждого файла — объекты в heap с файловыми дескрипторами
SequenceInputStream — объект в heap с ссылкой на Vector и текущий поток
buffer[8192] — массив в heap, переиспользуется в цикле

При закрытии SequenceInputStream через try-with-resources вызывается close(), который закрывает текущий поток и все оставшиеся потоки в Enumeration. Это гарантирует освобождение файловых дескрипторов.

Проблема: утечка при исключении
// Антипаттерн: утечка при исключении в конструкторе
Vector<InputStream> streams = new Vector<>();
for (String path : paths) {
streams.add(new FileInputStream(path)); // Если здесь исключение — предыдущие потоки не закрыты
}
SequenceInputStream sis = new SequenceInputStream(streams.elements());


Если new FileInputStream() выбрасывает FileNotFoundException на N-м файле, предыдущие N-1 потоков остаются открытыми. Решение — явное управление ресурсами:
// Правильно: гарантированное закрытие при исключении
List<InputStream> streams = new ArrayList<>();
try {
for (String path : paths) {
streams.add(new FileInputStream(path));
}
Enumeration<InputStream> en = Collections.enumeration(streams);
try (SequenceInputStream sis = new SequenceInputStream(en)) {
// Использование
}
} catch (IOException e) {
// Закрытие всех созданных потоков при ошибке
for (InputStream is : streams) {
try { is.close(); } catch (IOException ignored) {}
}
throw e;
}


Практический пример: склеивание текстовых файлов

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class FileConcatenator {

// Объединение файлов с автоматическим закрытием
public void concatenate(String outputPath, String... inputPaths)
throws IOException {

// Создание списка потоков с гарантированным закрытием при ошибках
List<InputStream> streams = new ArrayList<>();
boolean success = false;

try {
for (String path : inputPaths) {
streams.add(new FileInputStream(path));
}

// SequenceInputStream объединяет все потоки
Enumeration<InputStream> en = Collections.enumeration(streams);
try (SequenceInputStream sis = new SequenceInputStream(en);
// Декодирование байтов в символы через UTF-8
InputStreamReader isr = new InputStreamReader(sis, StandardCharsets.UTF_8);
// Буферизация символьного ввода
BufferedReader reader = new BufferedReader(isr);
// Запись в выходной файл
FileOutputStream fos = new FileOutputStream(outputPath);
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
BufferedWriter writer = new BufferedWriter(osw)) {

String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}

success = true;
}

} finally {
// Закрытие всех потоков, если try-with-resources не сработал
if (!success) {
for (InputStream is : streams) {
try { is.close(); } catch (IOException ignored) {}
}
}
}
}

// Альтернатива: объединение только двух файлов
public void concatenateTwo(String outputPath, String path1, String path2)
throws IOException {

try (SequenceInputStream sis = new SequenceInputStream(
new FileInputStream(path1),
new FileInputStream(path2));
FileOutputStream fos = new FileOutputStream(outputPath)) {

// Прямое блочное копирование без промежуточных преобразований
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = sis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
// Оба FileInputStream закрыты автоматически:
// - первый при исчерпании (внутри nextStream)
// - второй при закрытии SequenceInputStream
}
}


Путь данных при склеивании текстовых файлов
[Файл 1 на диске: байты UTF-8]
-> [FileInputStream 1: системный вызов read()]
-> [SequenceInputStream: делегирование FileInputStream 1]
-> [InputStreamReader: декодирование байт -> char]
-> [BufferedReader: накопление в cb[8192], поиск \n]
-> [readLine(): создание String]
-> [BufferedWriter.write(String)]
-> [OutputStreamWriter: кодирование char -> байты UTF-8]
-> [FileOutputStream: системный вызов write()]
-> [Page Cache -> диск]

[Конец файла 1: FileInputStream 1.read() возвращает -1]
-> [SequenceInputStream.nextStream()]
-> [FileInputStream 1.close()] // Автоматическое закрытие
-> [FileInputStream 2 становится активным]

[Файл 2 на диске: байты UTF-8]
-> [FileInputStream 2: системный вызов read()]
-> [SequenceInputStream: делегирование FileInputStream 2]
-> [Тот же путь через Reader/Writer]

Ограничения и альтернативы
Невозможность произвольного доступа
SequenceInputStream — строго последовательный. После переключения на следующий поток вернуться к предыдущему невозможно — он закрыт. Это отличает его от ConcatenationInputStream в некоторых библиотеках или от ручного управления массивом потоков.


Отсутствие буферизации

SequenceInputStream не имеет собственного буфера. Если underlying потоки не буферизованы, каждый read() инициирует системный вызов. Рекомендуется оборачивать underlying потоки в BufferedInputStream:
// Правильно: буферизация каждого потока
Vector<InputStream> streams = new Vector<>();
for (String path : paths) {
streams.add(new BufferedInputStream(new FileInputStream(path)));
}
SequenceInputStream sis = new SequenceInputStream(streams.elements());


Современная альтернатива: Stream API Java 8
// Объединение через Stream API — более гибко, но с другой семантикой
public void concatenateModern(String outputPath, String... inputPaths)
throws IOException {

try (OutputStream out = new FileOutputStream(outputPath)) {
for (String path : inputPaths) {
Files.copy(Path.of(path), out); // Копирование файла в общий поток
}
}
}

Files.copy() использует transferTo() — оптимизированный механизм копирования через FileChannel, потенциально с zero-copy на уровне ОС. Это более эффективно, чем SequenceInputStream с промежуточным буфером.


#Java #для_новичков #beginner #IO #NIO #SequenceInputStream
👍3
Что выведет код?

import java.io.*;

public class Task010726 {
public static void main(String[] args) throws IOException {
ByteArrayInputStream bais1 = new ByteArrayInputStream(new byte[]{1, 2, 3});
ByteArrayInputStream bais2 = new ByteArrayInputStream(new byte[]{4, 5, 6});
SequenceInputStream sis = new SequenceInputStream(bais1, bais2);
byte[] buffer = new byte[5];
int len = sis.read(buffer, 0, 5);
System.out.println(len);
for (int i = 0; i < len; i++) {
System.out.print(buffer[i] + " ");
}
}
}


#Tasks
👍3
Варианты ответа:
Anonymous Quiz
38%
5 1 2 3 4 5
50%
3 1 2 3
0%
3 1 2 3 4
13%
5 1 2 3 4
👍3
Красоты Телецкого озера
🔥11