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

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
InputStream: чтение байтов

InputStream — абстрактный класс, определяющий контракт для всех входных байтовых потоков. Его методы реализуют семантику последовательного чтения: данные извлекаются из источника в порядке FIFO, и невозможно вернуться назад без специальных механизмов (mark/reset, доступных не во всех реализациях).

int read() — читает один байт из потока. Возвращает значение в диапазоне 0-255 как int, что позволяет различать валидные байты (0-255) от признака конца потока. Когда достигнут конец потока, метод возвращает -1. Этот контракт фундаментален: -1 — единственный способ узнать, что данные в источнике исчерпаны. Возврат int вместо byte обусловлен необходимостью представления 256 возможных значений байта плюс специальное значение -1 для EOF. Тип byte в Java знаковый и имеет диапазон -128..127, что не позволил бы различить байт 0xFF (255 беззнаковый, -1 знаковый) от признака конца потока.

int read(byte[] b) — читает байты в предоставленный массив, заполняя его полностью или частично. Возвращает количество фактически прочитанных байт, или -1 при достижении конца потока. Этот метод существенно эффективнее посимвольного чтения, так как уменьшает количество системных вызовов. Однако он не гарантирует заполнение всего массива — при неполных данных возвращается меньшее количество байт, и приложение должно обработать эту ситуацию.

int read(byte[] b, int off, int len) — читает до len байт в массив b, начиная с позиции off. Возвращает количество прочитанных байт или -1. Этот метод предоставляет максимальный контроль над буферизацией.

long skip(long n)
— пропускает до n байт в потоке. Возвращает количество фактически пропущенных байт, которое может быть меньше n.

int available() — возвращает оценку количества байт, которые можно прочитать без блокирования. Для файловых потоков это обычно оставшийся размер файла, для сетевых — количество данных в сокет-буфере. Значение 0 не означает конец потока, а только отсутствие немедленно доступных данных.

void close() — закрывает поток и освобождает связанные системные ресурсы: файловые дескрипторы, сокеты, соединения с базой данных. Реализации InputStream не объявляют close() как AutoCloseable, но все конкретные подклассы реализуют этот интерфейс, что позволяет использовать try-with-resources.

void mark(int readlimit) и void reset() — поддержка маркировки позиции в потоке для последующего возврата. Доступность проверяется через boolean markSupported(). Большинство потоков, основанных на внешних ресурсах (файлы, сети), не поддерживают эту операцию.


Контракт read(): возврат -1 при конце потока

Контракт метода read() требует особого внимания. Метод блокирует выполнение до тех пор, пока не станет доступен хотя бы один байт, не произойдет IOException, или не будет достигнут конец потока. При нормальном завершении потока возвращается -1, и все последующие вызовы read() также возвращают -1.

Этот контракт определяет стандартный паттерн чтения:
// Антипаттерн: чтение байта за байтом без буферизации
public void readByteByByte(InputStream in) throws IOException {
int byteValue;
while ((byteValue = in.read()) != -1) {
processByte(byteValue);
}
}

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

Правильный подход — буферизованное чтение:
// Паттерн: буферизованное чтение с обработкой частичного заполнения
public void readBuffered(InputStream in) throws IOException {
byte[] buffer = new byte[8192]; // 8KB — типичный размер страницы ОС
int bytesRead;

while ((bytesRead = in.read(buffer)) != -1) {
processBuffer(buffer, bytesRead);
}
}

Здесь read(buffer) читает до 8192 байт за один системный вызов. Возвращаемое значение bytesRead обязательно проверяется: оно может быть меньше размера буфера, особенно при чтении из сети или при достижении конца файла. Признак конца потока -1 завершает цикл.

#Java #для_новичков #beginner #IO #NIO #InputStream #OutputStream
👍5
OutputStream: запись байтов

OutputStream — абстрактный класс, определяющий контракт для всех выходных байтовых потоков. Семантика записи также последовательная: байты отправляются в назначение в порядке вызова методов write.

void write(int b) — записывает один байт. Параметр типа int, но записываются младшие 8 бит. Старшие 24 бита игнорируются. Это согласовано с read(), возвращающим int.

void write(byte[] b) — записывает весь массив байт.

void write(byte[] b, int off, int len) — записывает len байт из массива b, начиная с позиции off.

void flush() — принудительно сбрасывает буферизованные данные в назначение. Многие реализации OutputStream используют внутреннюю буферизацию для повышения производительности: данные накапливаются в памяти и записываются пачками. flush() гарантирует, что все накопленные данные немедленно отправлены. Это критично для сценариев, где задержка недопустима: протоколы запрос-ответ, логирование в реальном времени, интерактивные сессии.

void close() — закрывает поток, освобождает ресурсы и неявно вызывает flush() для сброса оставшихся буферизованных данных. После закрытия потока любые последующие операции выбрасывают IOException.


Контракт flush(): гарантия доставки


Метод flush() не гарантирует физическую запись на диск или отправку по сети — он гарантирует только передачу данных из пользовательского буфера в операционную систему. Фактическая запись на носитель контролируется ОС и может быть отложена.

Для гарантии физической записи используется FileChannel.force(true) в NIO.
// Пример: flush перед ожиданием ответа
public void sendRequest(OutputStream out, byte[] request) throws IOException {
out.write(request);
out.flush(); // Гарантирует, что запрос отправлен до чтения ответа

// Теперь можно читать ответ, не рискуя deadlock из-за буферизации
}



Цепочка оберток: паттерн декоратора

Архитектура Java I/O построена на паттерне декоратора: базовые потоки (FileInputStream, SocketInputStream) предоставляют минимальную функциональность, а обертки (BufferedInputStream, DataInputStream, GZIPInputStream) добавляют возможности без изменения базового интерфейса.
// Цепочка оберток: буферизация + типизированное чтение + сжатие
try (InputStream fis = new FileInputStream("data.bin");
BufferedInputStream bis = new BufferedInputStream(fis, 8192);
DataInputStream dis = new DataInputStream(bis);
GZIPInputStream gzis = new GZIPInputStream(dis)) {

int magicNumber = dis.readInt(); // Чтение 4-байтового int
long timestamp = dis.readLong(); // Чтение 8-байтового long
double value = dis.readDouble(); // Чтение 8-байтового double
String label = dis.readUTF(); // Чтение строки в модифицированном UTF-8
}

Важное правило: закрывается только внешняя обертка. Вызов close() на GZIPInputStream проксируется через всю цепочку к FileInputStream, корректно освобождая все ресурсы. try-with-resources обрабатывает это автоматически.

Практический пример: копирование бинарного файла
public void copyBinaryFile(String sourcePath, String destPath) throws IOException {
// Буфер 8KB — оптимальный размер для большинства файловых систем
byte[] buffer = new byte[8192];

try (InputStream in = new FileInputStream(sourcePath);
OutputStream out = new FileOutputStream(destPath)) {

int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}

out.flush(); // Явный flush перед закрытием
}
}

Этот пример демонстрирует ключевые практики: буферизованное чтение для минимизации системных вызовов, проверку возвращаемого значения read для корректной обработки частичного чтения, запись ровно прочитанного количества байт, и явный flush перед закрытием. try-with-resources гарантирует закрытие обоих потоков даже при исключении.


#Java #для_новичков #beginner #IO #NIO #InputStream #OutputStream
👍5
Вакансия_Backend_Разработчик_2026_МИС3.pdf
95.2 KB
❗️❗️❗️ Ребят есть интересная вакансия 🧑‍💻

Требования:
- как я понял, от 1 года, но в резюме этого нет 🤫
- вилка 90-120к, не фонтан, но требования, возможно будут проще 🤷‍♀️
- гибрид или удаленка для ответственных 🙃
- наличие практического опыта работы с нейросетями/ИИ для ускорения разработки (не говорите, что у вас нет) 😄

Контакт:
https://t.me/OlesyaSbytova
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1🤯1
Что выведет код?

import java.io.*;

public class Task010626 {
public static void main(String[] args) throws IOException {
byte[] data = {10, 20, 30, 40, 50};
InputStream is = new BufferedInputStream(new ByteArrayInputStream(data), 2);
is.mark(1);
is.read();
is.read();
is.reset();
System.out.println(is.read());
}
}



#Tasks
👍2
Варианты ответа:
Anonymous Quiz
38%
10
25%
20
19%
30
19%
Исключение
👍2
Как работает finally с return?🤓

Ответ:

Если в блоках try или catch есть оператор return, то блок finally всё равно выполнится перед фактическим возвратом значения.

Если finally также содержит return, то он переопределит возвращаемое значение из try/catch.

Исключение, выброшенное в finally, будет иметь приоритет над исключением из try/catch. Поэтому в finally не рекомендуется использовать return или бросать исключения — это может скрыть важную информацию.

Лучше использовать finally только для освобождения ресурсов.


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

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

Оле́г Бори́сович Лупа́нов (2 июня 1932 — 3 мая 2006) — советский и российский математикакадемик Российской академии наук, декан механико-математического факультета МГУ (1980—2006), главный научный сотрудник Института прикладной математики им. М. В. Келдыша (1993—2006), специалист по дискретной математикематематической кибернетикематематической логике. В 1958 году под руководством А. А. Ляпунова защитил кандидатскую диссертацию «О синтезе контактных схем». Докторская диссертация — «Об aсимптотических закономерностях синтезa схем из функциональных элементов» (1963). Разработал асимптотически наилучший метод синтеза схем из функциональных элементов — так называемый метод Лупанова (англ. Lupanov representation).


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

1857 — Джеймс Гиббс из Вирджинии запатентовал однониточную стежковую швейную машинку.


#Biography #Birth_Date #Events #02июня
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
This media is not supported in your browser
VIEW IN TELEGRAM
Напоминаю ребят, что набираю желающих изучить Java 🤓

Возьму еще буквально пару человек.🤫

Пишите @oleborn 😎
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #045]

Тема: volatile не гарантирует атомарность.

Проблема: Ключевое слово volatile обеспечивает видимость изменений между потоками (запись в volatile переменную происходит в main memory, чтение — также из main memory) и запрещает переупорядочивание инструкций.

Однако оно не делает операции атомарными. Операция инкремента (count++) состоит из трех действий: чтение значения, увеличение на 1, запись нового значения. Два потока могут одновременно прочитать одно и то же значение, увеличить его и записать обратно. В результате один инкремент будет потерян, и финальное значение будет меньше ожидаемого.

Решение: Для атомарных счетчиков используйте классы из пакета java.util.concurrent.atomicAtomicIntegerAtomicLong.

Их методы incrementAndGet()getAndIncrement() и другие гарантируют атомарность без блокировок (через низкоуровневые CAS-операции). Если нужна более сложная логика (условное обновление), используйте compareAndSet(). Для примитивов long и double volatile обеспечивает атомарность записи/чтения на 64-битных платформах согласно спецификации Java (начиная с Java 5), но инкремент все равно не атомарен.

Для простых флагов (boolean, ссылки) volatile подходит.
public class VolatileNotAtomic {

//Антипаттерн: volatile без атомарности
private static volatile int volatileCounter = 0;

//Решение: AtomicInteger
private static final AtomicInteger atomicCounter = new AtomicInteger(0);

public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);

// Неправильный счетчик
for (int i = 0; i < 1000; i++) {
executor.submit(() -> volatileCounter++);
}

// Правильный счетчик
for (int i = 0; i < 1000; i++) {
executor.submit(() -> atomicCounter.incrementAndGet());
}

executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);

System.out.println("volatile count: " + volatileCounter); // почти всегда < 1000
System.out.println("Atomic count: " + atomicCounter.get()); // всегда 1000

// Другие методы AtomicInteger
AtomicInteger ai = new AtomicInteger(0);
int old = ai.getAndSet(10); // old = 0, new = 10
int updated = ai.incrementAndGet(); // 11
boolean set = ai.compareAndSet(11, 20); // true, меняет 11 на 20

// Для long: AtomicLong
// Для boolean: AtomicBoolean
// Для ссылок: AtomicReference<V>
}
}


Объяснение:
 volatile гарантирует, что чтение и запись переменной происходят непосредственно из основной памяти (стека основного потока), но не делает составные операции атомарными.

Инкремент — это read-modify-write. Даже если переменная volatile, два потока могут одновременно считать одно значение, каждый увеличит его до n+1, и оба запишут n+1, потеряв одно приращение. AtomicInteger использует инструкцию CAS (Compare-And-Swap), аппаратно поддерживаемую процессором, которая выполняет сравнение и обмен за одну атомарную операцию. CAS не требует блокировок и обычно быстрее synchronized.

#Java #советы
👍5
Начал работу над новым видео... 🧑‍💻

Через пару дней - ждите))
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9
Что выведет код?

public class Task020626 {
private static volatile int counter = 0;

public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter++;
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter++;
}
});

t1.start();
t2.start();
t1.join();
t2.join();

System.out.println(counter);
}
}



#Tasks
👍4
Варианты ответа:
Anonymous Quiz
19%
20000
76%
Меньше или равно 20000
0%
10000
5%
0
👍2
🤣🤣🤣 по-моему это знак
🤣5🤓4
Что такое NavigableMap и NavigableSet?🤓

Ответ:

NavigableMap (реализация — TreeMap)
и NavigableSet (TreeSet) расширяют SortedMap/SortedSet, добавляя методы для навигации, близкие к запросам "ближайший меньший/больший элемент".

Полезные методы: 
lowerKey() / lowerEntry() (строго меньше), 
floorKey() (меньше или равно), 
ceilingKey() (больше или равно), 
higherKey() (строго больше), 
descendingMap() (обратный порядок), 
subMap(from, to) (диапазон с опциями включения границ).

Удобно для поиска интервалов, например, в системах бронирования.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Всем доброго летнего утра! 👋

Вот вывел своего конька погулять, впервые в этом году 🚲

Че нам безработным🤣🤣🤣

А как ваше утро проходит?
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2
История технологии сегодня — 3 июня

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

И́горь Ростисла́вович Шафаре́вич (3 июня 1923, Житомир — 19 февраля 2017, Москва) — советский и российский математик, доктор физико-математических наук, профессор, академик Российской академии наук (1991; член-корреспондент Академии наук СССР с 1958). Основные труды посвящены алгебретеории чисел и алгебраической геометрии.

Дьёрдь фон Бе́кеши (Джордж) (венг. Békésy György; нем. Georg von Békésy; 3 июня 1899, Будапешт — 13 июня 1972, Гонолулу, Гавайи, США) — венгерско-американский физик, биофизик и физиолог, лауреат Нобелевской премии в области медицины 1961 года. Основные труды по биофизике и физиологии слуха. Открыл закономерности колебаний базилярной мембраны улитки внутреннего уха при действии звука и сформулировал теорию первичного амплитудно-частотного анализа звуков в органе слуха. Изучал передачу звука в среднем ухе. Предложил метод и прибор оценки слуха человека, порога различения слуха (аудиометр Бекеши). Исследования по костной проводимости звука, пространственному слуху и контрасту восприятия в сенсорных системах.


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

1980 — вследствие сбоя компьютера, сообщившего о советском ядерном нападении, в США объявлена ядерная тревога. В течение десяти минут мир находился на краю ядерной войны.


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

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

FileInputStream – чтение байтов из файла. Путь данных в памяти JVM

FileInputStream — конкретная реализация InputStream, предназначенная для чтения байтов из файловой системы. Она представляет собой низкоуровневый мост между Java-кодом и операционной системой: каждый вызов read() транслируется в системный вызов read() ядра ОС, который взаимодействует с файловой системой и блочным устройством хранения.

В отличие от BufferedInputStream, FileInputStream не выполняет внутреннюю буферизацию. Каждый вызов read() напрямую обращается к ОС, что делает посимвольное чтение через FileInputStream крайне неэффективным для больших файлов. Однако именно эта прямота делает FileInputStream полезным для сценариев, где требуется точный контроль над каждым системным вызовом: чтение с устройств с поблочным доступом, работа с pipe-файлами, или как underlying поток для буферизующих оберток.


Конструкторы и жизненный цикл файлового дескриптора


FileInputStream предоставляет два основных конструктора:
// Конструктор по строковому пути
public FileInputStream(String name) throws FileNotFoundException

// Конструктор по объекту File
public FileInputStream(File file) throws FileNotFoundException


Оба конструктора выполняют одну и ту же последовательность операций на уровне JVM и ОС:
Валидация параметра. Проверка на null, проверка существования файла через системный вызов stat() или аналог.
Открытие файла. Системный вызов open() создает файловый дескриптор (file descriptor) — целочисленный идентификатор, представляющий открытый файл в контексте процесса.
Создание объекта FileInputStream. Выделение объекта в heap JVM, инициализация полей, включая нативное поле fd типа FileDescriptor, которое хранит целочисленный дескриптор ОС.

Файловый дескриптор — это ограниченный ресурс операционной системы. Каждый процесс имеет лимит на количество открытых дескрипторов (обычно 1024 по умолчанию, расширяемый до миллионов через ulimit). Незакрытый FileInputStream удерживает дескриптор до вызова close() или финализации объекта GC, что создает риск исчерпания лимита при интенсивном открытии файлов без закрытия.


FileNotFoundException: семантика и обработка


Конструкторы FileInputStream объявляют throws FileNotFoundException — checked-исключение, которое компилятор обязывает обрабатывать.

Это исключение возникает в трех основных сценариях:
Файл не существует по указанному пути
Файл существует, но является директорией, а не обычным файлом
Файл существует, но приложение не имеет прав на чтение
public byte[] readFileContent(String path) {
try (FileInputStream fis = new FileInputStream(path)) {
return fis.readAllBytes();
} catch (FileNotFoundException e) {
// Файл не существует или недоступен — бизнес-решение
logger.warn("File not found: {}", path);
return new byte[0];
} catch (IOException e) {
// Другие I/O ошибки при чтении
logger.error("Failed to read file: {}", path, e);
throw new RuntimeException("File read error", e);
}
}

FileNotFoundException наследует IOException и является восстановимым сбоем в контексте файловых операций. Приложение может предпринять альтернативные действия: использовать значение по умолчанию, создать файл, запросить другой путь. В отличие от RuntimeException, checked-природа FileNotFoundException форсирует явную обработку в коде.


#Java #для_новичков #beginner #IO #NIO #FileInputStream
👍4
Путь данных от диска до heap JVM

Уровень 1: Физическое чтение с носителя

Данные хранятся на блочном устройстве — SSD, HDD, NVMe — в секторах фиксированного размера (обычно 512 байт или 4KB). Когда ОС запрашивает чтение файла, драйвер устройства транслирует логические блоки файловой системы в физические адреса секторов. Для SSD контроллер выполняет wear leveling и garbage collection на уровне флеш-памяти, независимо от JVM.

Уровень 2: Страничный кэш ядра (Page Cache)

Операционная система поддерживает страничный кэш — область оперативной памяти ядра, где хранятся недавно прочитанные страницы файлов. Размер страницы обычно 4KB. При первом чтении файла данные загружаются с диска в page cache. При повторном чтении тех же данных ОС возвращает их из page cache без обращения к диску.
Page cache — это память ядра, недоступная напрямую из Java. JVM не управляет этой памятью и не видит её в heap. Однако интенсивное чтение больших файлов вытесняет другие страницы из кэша, что может повлиять на производительность всей системы.

Уровень 3: Системный вызов и нативный буфер

Когда FileInputStream.read() вызывается, JVM выполняет системный вызов read() через JNI (Java Native Interface). Ядро ОС копирует данные из page cache в нативный буфер в userspace — память процесса JVM за пределами heap. Это первое копирование данных.
// read() вызывает нативный метод, который выполняет системный вызов
public native int read() throws IOException;

Нативный метод read0() в FileInputStream.c из OpenJDK вызывает IO_Read на POSIX-системах, который оборачивает read(fd, buf, 1) для посимвольного чтения или read(fd, buf, len) для буферизованного.

Уровень 4: Копирование в heap JVM

После выполнения системного вызова данные копируются из нативного буфера в массив byte[] в heap JVM. Это второе копирование. Только после этого данные становятся доступны Java-коду.
// Путь данных для read():
// 1. Диск -> page cache (ядро)
// 2. page cache -> нативный буфер (userspace, вне heap)
// 3. нативный буфер -> byte[] в heap JVM
// 4. Java-код читает byte[0]

Для read(byte[] b) путь аналогичен, но шаг 3 копирует до b.length байт за одну операцию.

Пример: посимвольное чтение и нагрузка на память
public void readByteByByte(String path) throws IOException {
try (FileInputStream fis = new FileInputStream(path)) {
int byteValue;
// Каждая итерация: системный вызов + копирование в heap для 1 байта
while ((byteValue = fis.read()) != -1) {
System.out.printf("%02X ", byteValue);
}
}
}


В этом коде каждый вызов read() порождает полный путь данных для одного байта.

С точки зрения памяти:
Объект FileInputStream создается в heap и живет до выхода из try
Локальная переменная byteValue хранится в стеке потока
Никаких массивов в heap не создается при каждом вызове
Проблема не в GC, а в количестве системных вызовов
Однако при выводе в System.out каждый вызов printf создает объекты String и StringBuilder в heap для форматирования, что нагружает GC при больших файлах.


#Java #для_новичков #beginner #IO #NIO #FileInputStream
👍4
Буферизация и сокращение пути данных

BufferedInputStream добавляет промежуточный массив в heap, сокращая количество системных вызовов:
public void readBuffered(String path) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path))) {
int byteValue;
// Первый read(): чтение 8192 байт в buf[8192] одним системным вызовом
// Последующие read(): возврат из buf без системных вызовов
while ((byteValue = bis.read()) != -1) {
System.out.printf("%02X ", byteValue);
}
}
}


Путь данных с BufferedInputStream:

Первый read(): диск -> page cache -> нативный буфер -> buf[8192] в heap -> возврат buf[0]
Второй read(): buf[1] из heap (системный вызов отсутствует, нативный буфер не задействован)
После исчерпания buf: повторение шага 1

Массив buf создается при конструировании BufferedInputStream и живет до закрытия потока. GC не вовлечен в работу цикла чтения, так как массив переиспользуется.


Роль garbage collector

Жизненный цикл объектов I/O
try (FileInputStream fis = new FileInputStream(path)) {
// fis — ссылка в стеке, объект FileInputStream в heap
// fis.fd — объект FileDescriptor в heap, хранит int fd (файловый дескриптор ОС)
// При использовании BufferedInputStream: bis.buf — byte[] в heap
}
// После выхода из try:
// 1. Вызывается fis.close() — закрывается файловый дескриптор ОС
// 2. Ссылка fis выходит из области видимости
// 3. Объект FileInputStream становится недостижимым
// 4. GC может собрать объект и связанные FileDescriptor при следующей сборке


Важно: FileInputStream реализует AutoCloseable, и try-with-resources гарантирует вызов close(). Если бы мы использовали старый подход без try-with-resources:
// Антипаттерн: утечка файлового дескриптора
FileInputStream fis = new FileInputStream(path);
// Если здесь возникнет исключение, close() не вызовется
fis.read();
fis.close();

В этом случае при исключении между read() и close() файловый дескриптор остается открытым. Объект FileInputStream все еще достижим через локальную переменную до выхода из метода, но если исключение пробрасывается наверх, метод завершается без закрытия. GC финализирует объект и закроет дескриптор, но финализация непредсказуема по времени — может пройти секунды или минуты до освобождения ресурса.


Финализация и Cleaner

В современных версиях Java FileInputStream использует java.lang.ref.Cleaner вместо устаревшего метода finalize(). Cleaner регистрирует cleanup-действие, которое выполняется, когда объект становится фантомно достижимым (phantom reachable) — то есть недостижимым, но еще не собранным GC.
// Упрощенная логика из OpenJDK
private final Cleanable cleanable;
private final FileCleanable cleanup;

public FileInputStream(File file) throws FileNotFoundException {
// ...
this.fd = new FileDescriptor();
this.cleanup = new FileCleanable(this.fd);
this.cleanable = Cleaner.create().register(this, cleanup);
}

FileCleanable — это Runnable, который вызывает close0(fd) — нативный метод закрытия дескриптора. Этот механизм гарантирует освобождение ресурса даже при забывчивости разработчика, но не заменяет явный вызов close(): Cleaner работает в отдельном потоке с низким приоритетом, и задержка может быть значительной.

#Java #для_новичков #beginner #IO #NIO #FileInputStream
👍4
А вы еще ищете работу? 😂

Это реальный скрин, блеать
🤯4
Что выведет код?

import java.io.*;

public class Task030626 {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("missing.txt")) {
System.out.println("Opened");
} catch (IOException e) {
System.out.println(e.getClass().getSimpleName());
}
}
}


#Tasks
👍3