Что такое 🤓
Ответ:
ForkJoinPool.commonPool() — это общий пул потоков для приложения (Java 8), используемый по умолчанию в параллельных стримах (parallelStream()) и CompletableFuture (в некоторых методах).
Размер по умолчанию = количество процессоров − 1.
Не рекомендуется блокировать (спать, ввод-вывод) внутри задач, выполняющихся в commonPool, так как это замедлит другие задачи.
Если ваши задачи могут блокироваться, создавайте отдельный ForkJoinPool. Используйте commonPool для коротких вычислительных задач, не блокирующих IO.
#собеседование
ForkJoinPool.commonPool() и когда его использовать? Ответ:
Размер по умолчанию = количество процессоров − 1.
Не рекомендуется блокировать (спать, ввод-вывод) внутри задач, выполняющихся в commonPool, так как это замедлит другие задачи.
Если ваши задачи могут блокироваться, создавайте отдельный ForkJoinPool. Используйте commonPool для коротких вычислительных задач, не блокирующих IO.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологии сегодня — 23 июля
ℹ️ Кто родился в этот день
Влади́мир Я́ковлевич Кли́мов (1892—1962) — советский учёный в области авиационного моторостроения, конструктор авиационных двигателей, генерал-майор инженерно-авиационной службы (1944), академик АН СССР (1953). В ходе научных исследований и конструкторской работы Климовым были разработаны и внедрены специальная закрытая система жидкостного охлаждения поршневых авиационных двигателей под давлением, воздушный нагнетатель с двухскоростным приводом, усовершенствованная система газораспределения, система питания топливовоздушной смесью мощных и высокооборотных авиадвигателей, предложен ряд оригинальных решений в конструкциях ТРД. Климов внёс существенный вклад в развитие теории смазки, в решение проблем уравновешивания поршневых авиадвигателей и др. вопросов двигателестроения.
🌐 Знаковые события
1874 — Александр Николаевич Лодыгин получил привилегию № 1619 на лампу накаливания. Его изобретение было запатентовано и в нескольких европейских странах, Петербургская АН присудила ему в этом году Ломоносовскую премию, а в конце года было создано «Товарищество электрического освещения А. Н. Лодыгин и Ко».
1903 — спустя месяц после образования корпорации «Ford Motor Company» она выпустила в продажу свой первый автомобиль «модель А». В течение первого года своего существования компания произвела 1700 автомобилей. Первый автомобиль, выставленный на продажу, был описан как «наиболее совершенная машина на рынке, которую в состоянии водить даже 15-летний мальчик». Первый автомобиль был продан доктору Е. Пфеннингу (англ. Ernst Pfenning) из Чикаго, который купил машину месяц спустя после регистрации компании, к большой радости обеспокоенных акционеров, нервно наблюдавших за уменьшением банковского баланса до $223.
1980 — запуск космического корабля Союз-37, в составе его экипажа в космос отправился первый космонавт из Азии, вьетнамец Фам Туан (Phạm Tuân).
2015 — с помощью телескопа «Кеплер» найдена первая экзопланета земного типа, которая находится в зоне обитаемости солнцеподобной звезды — Kepler-452 b.
#Biography #Birth_Date #Events #23июля
Влади́мир Я́ковлевич Кли́мов (1892—1962) — советский учёный в области авиационного моторостроения, конструктор авиационных двигателей, генерал-майор инженерно-авиационной службы (1944), академик АН СССР (1953). В ходе научных исследований и конструкторской работы Климовым были разработаны и внедрены специальная закрытая система жидкостного охлаждения поршневых авиационных двигателей под давлением, воздушный нагнетатель с двухскоростным приводом, усовершенствованная система газораспределения, система питания топливовоздушной смесью мощных и высокооборотных авиадвигателей, предложен ряд оригинальных решений в конструкциях ТРД. Климов внёс существенный вклад в развитие теории смазки, в решение проблем уравновешивания поршневых авиадвигателей и др. вопросов двигателестроения.
1874 — Александр Николаевич Лодыгин получил привилегию № 1619 на лампу накаливания. Его изобретение было запатентовано и в нескольких европейских странах, Петербургская АН присудила ему в этом году Ломоносовскую премию, а в конце года было создано «Товарищество электрического освещения А. Н. Лодыгин и Ко».
1903 — спустя месяц после образования корпорации «Ford Motor Company» она выпустила в продажу свой первый автомобиль «модель А». В течение первого года своего существования компания произвела 1700 автомобилей. Первый автомобиль, выставленный на продажу, был описан как «наиболее совершенная машина на рынке, которую в состоянии водить даже 15-летний мальчик». Первый автомобиль был продан доктору Е. Пфеннингу (англ. Ernst Pfenning) из Чикаго, который купил машину месяц спустя после регистрации компании, к большой радости обеспокоенных акционеров, нервно наблюдавших за уменьшением банковского баланса до $223.
1980 — запуск космического корабля Союз-37, в составе его экипажа в космос отправился первый космонавт из Азии, вьетнамец Фам Туан (Phạm Tuân).
2015 — с помощью телескопа «Кеплер» найдена первая экзопланета земного типа, которая находится в зоне обитаемости солнцеподобной звезды — Kepler-452 b.
#Biography #Birth_Date #Events #23июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)
Глава 2. Современный NIO.2 (java.nio.file)
Files.lines() и WatchService — построчное чтение и мониторинг файловой системы
Два механизма в Java NIO.2 решают противоположные задачи:
Files.lines(Path) — построчное чтение текстовых файлов
Метод
Важно:
BufferedReader — это класс в Java, который буферизует чтение символов из потока. Вместо чтения по одному символу или байту он читает блоками (обычно 8 КБ) в буфер в памяти, а затем отдаёт символы из буфера. Это снижает количество системных вызовов read() с O(N) до O(N / buffer_size).
Files.lines(Path, Charset) — с указанием кодировки
Если кодировка не указана, используется
Кодировка (charset, character encoding) — это схема сопоставления байтовых последовательностей символам текста. UTF-8 — переменная длина (1–4 байта на символ), обратно совместима с ASCII. ISO-8859-1 (Latin-1) — фиксированная 1 байт на символ, покрывает западноевропейские языки.
Ленивость и потребление памяти
В отличие от
WatchService — отслеживание изменений в файловой системе
WatchService — это сервис в NIO.2, который использует нативные механизмы ОС для мониторинга изменений в файловой системе. Вместо периодического опроса (polling) он работает по событийной модели: ОС уведомляет JVM, когда происходит зарегистрированное событие.
Polling (опрос) — это стратегия проверки состояния, при которой программа периодически запрашивает ресурс (например, проверяет время модификации файла). Это расходует CPU впустую. Событийная модель (event-driven) — противоположный подход, при котором программа ждёт уведомления от ОС и не потребляет ресурсы в ожидании.
Нативные механизмы ОС
WatchService в Java — это тонкая обёртка над нативными API:
Linux:
macOS:
Windows:
Важно:
Coalescing (слияние событий) — это оптимизация, при которой несколько последовательных событий над одним и тем же файлом объединяются в одно. Например, множественные быстрые записи в файл могут породить только одно событие ENTRY_MODIFY.
Создание WatchService
Регистрация директории
WatchKey — это объект, представляющий регистрацию директории в WatchService. Он содержит ссылку на зарегистрированный путь и очередь событий. Когда WatchKey становится недействительным (например, директория удалена), его метод isValid() возвращает false.
#Java #для_новичков #beginner #IO #NIO #Files #Files_lines #WatchService
Глава 2. Современный NIO.2 (java.nio.file)
Files.lines() и WatchService — построчное чтение и мониторинг файловой системы
Два механизма в Java NIO.2 решают противоположные задачи:
Files.lines() читает содержимое файла построчно, а WatchService наблюдает за изменениями в файловой системе без активного чтения. Первый экономит память при обработке больших текстовых файлов, второй — CPU при ожидании событий. Оба требуют строгого управления ресурсами, так как работают с нативными дескрипторами ОС, невидимыми для GC.Files.lines(Path) — построчное чтение текстовых файлов
Метод
Files.lines(Path path) возвращает Stream<String>, который лениво читает файл построчно. Каждая строка декодируется из байтов файла в символы с использованием кодировки по умолчанию (StandardCharsets.UTF_8).import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;
public class LineReader {
public void readLines(Path file) throws IOException {
// Stream<String> — ленивое чтение, строки появляются по мере обхода
try (Stream<String> lines = Files.lines(file)) {
lines.forEach(line -> System.out.println(line));
}
// BufferedReader закрыт автоматически try-with-resources
}
}
Важно:
Files.lines() открывает BufferedReader под капотом, который держит файловый дескриптор открытым. Поток Stream<String> должен быть закрыт через try-with-resources или явный close(). Если поток не закрыт, дескриптор останется открытым даже после того, как Stream будет собран GC.BufferedReader — это класс в Java, который буферизует чтение символов из потока. Вместо чтения по одному символу или байту он читает блоками (обычно 8 КБ) в буфер в памяти, а затем отдаёт символы из буфера. Это снижает количество системных вызовов read() с O(N) до O(N / buffer_size).
Files.lines(Path, Charset) — с указанием кодировки
import java.nio.charset.StandardCharsets;
public void readWithEncoding(Path file) throws IOException {
// Явное указание кодировки — обязательно для файлов не в UTF-8
try (Stream<String> lines = Files.lines(file, StandardCharsets.ISO_8859_1)) {
lines.forEach(System.out::println);
}
}
Если кодировка не указана, используется
Charset.defaultCharset(), которая зависит от ОС и локали. На Linux обычно UTF-8, на Windows — Windows-1252 или CP1251. Неявное использование дефолтной кодировки — распространённый источник багов при переносе приложения между платформами.Кодировка (charset, character encoding) — это схема сопоставления байтовых последовательностей символам текста. UTF-8 — переменная длина (1–4 байта на символ), обратно совместима с ASCII. ISO-8859-1 (Latin-1) — фиксированная 1 байт на символ, покрывает западноевропейские языки.
Ленивость и потребление памяти
Files.lines() — ленивый поток. Строка читается из файла только когда терминальная операция запрашивает следующий элемент. Это позволяет обрабатывать файлы любого размера с константным потреблением памяти:public void processHugeFile(Path file) throws IOException {
try (Stream<String> lines = Files.lines(file)) {
// Фильтрация и подсчёт — каждая строка обрабатывается и отбрасывается
long errorCount = lines.filter(line -> line.contains("ERROR"))
.count();
System.out.println("Ошибок: " + errorCount);
// Память не зависит от размера файла — только от длины одной строки
}
}В отличие от
Files.readAllLines(), который загружает весь файл в List<String> (потенциально гигабайты в куче), Files.lines() держит в памяти только текущую строку и буфер BufferedReader (8 КБ).WatchService — отслеживание изменений в файловой системе
WatchService — это сервис в NIO.2, который использует нативные механизмы ОС для мониторинга изменений в файловой системе. Вместо периодического опроса (polling) он работает по событийной модели: ОС уведомляет JVM, когда происходит зарегистрированное событие.
Polling (опрос) — это стратегия проверки состояния, при которой программа периодически запрашивает ресурс (например, проверяет время модификации файла). Это расходует CPU впустую. Событийная модель (event-driven) — противоположный подход, при котором программа ждёт уведомления от ОС и не потребляет ресурсы в ожидании.
Нативные механизмы ОС
WatchService в Java — это тонкая обёртка над нативными API:
Linux:
inotify — система уведомлений о событиях файловой системы на уровне ядра. Каждый WatchService создаёт экземпляр inotify через системный вызов inotify_init().macOS:
FSEvents — фреймворк уведомлений о изменениях файловой системы. Работает на уровне всей ФС, а не отдельных директорий, что даёт другую семантику событий.Windows:
ReadDirectoryChangesW — WinAPI функция для асинхронного чтения изменений директории.Важно:
WatchService не гарантирует доставку всех событий и не гарантирует порядок. Некоторые события могут быть пропущены при высокой нагрузке, и события могут приходить пачками (coalescing).Coalescing (слияние событий) — это оптимизация, при которой несколько последовательных событий над одним и тем же файлом объединяются в одно. Например, множественные быстрые записи в файл могут породить только одно событие ENTRY_MODIFY.
Создание WatchService
import java.nio.file.*;
public class WatchServiceFactory {
public WatchService createWatchService() throws IOException {
// Получаем WatchService для файловой системы по умолчанию
WatchService watchService = FileSystems.getDefault().newWatchService();
return watchService;
}
}
FileSystems.getDefault().newWatchService() создаёт нативный дескриптор inotify (Linux), регистрирует callback в FSEvents (macOS) или создаёт объект ожидания ReadDirectoryChangesW (Windows). Этот дескриптор существует в нативной памяти вне кучи JVM.Регистрация директории
import java.nio.file.*;
public class DirectoryRegistrar {
public void registerDirectory(Path dir, WatchService watchService) throws IOException {
// Регистрируем директорию на отслеживание создания, удаления и модификации
WatchKey key = dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE, // создание файла/директории
StandardWatchEventKinds.ENTRY_DELETE, // удаление
StandardWatchEventKinds.ENTRY_MODIFY // изменение содержимого или метаданных
);
// WatchKey — это токен, идентифицирующий зарегистрированную директорию
// Его можно использовать для отмены регистрации
System.out.println("Директория зарегистрирована: " + dir);
}
}
WatchKey — это объект, представляющий регистрацию директории в WatchService. Он содержит ссылку на зарегистрированный путь и очередь событий. Когда WatchKey становится недействительным (например, директория удалена), его метод isValid() возвращает false.
#Java #для_новичков #beginner #IO #NIO #Files #Files_lines #WatchService
👍5
Ожидание событий: take() vs poll()
Важно: после обработки событий необходимо вызвать
Обработка WatchEvent
Важные нюансы WatchService
Событие CREATE приходит до завершения записи: если процесс создаёт файл и медленно пишет в него,
Событие MODIFY может приходить многократно: одна логическая операция записи может породить серию событий
Символические ссылки: WatchService следует по символическим ссылкам при регистрации. Если зарегистрировать ссылку, события будут приходить для целевой директории.
Рекурсивный мониторинг: WatchService не мониторит поддиректории автоматически. Для рекурсивного мониторинга нужно обходить дерево и регистрировать каждую директорию отдельно, а при появлении новой директории (событие
OVERFLOW: при слишком быстром потоке событий внутренний буфер ОС может переполниться. В этом случае приходит событие
Путь байтов в памяти JVM при построчном чтении
Files.lines(): путь данных
При
Метод создаёт
При итерации по
Чтение байтов из файла через
Декодирование байтов в символы через
Поиск символа перевода строки (
Создание нового объекта
Объект
После обработки строки в терминальной операции (например,
При закрытии
Путь байтов при WatchService
При создании
При регистрации директории (
При вызове
Объекты
При закрытии
Важно:
#Java #для_новичков #beginner #IO #NIO #Files #Files_lines #WatchService
import java.nio.file.*;
import java.util.concurrent.TimeUnit;
public class EventPoller {
// Блокирующее ожидание: поток ждёт, пока не появится событие
public void blockingWait(WatchService watchService) throws InterruptedException {
// take() блокирует поток до появления события
// Прерывается только при InterruptedException или закрытии WatchService
WatchKey key = watchService.take();
processKey(key);
}
// Неблокирующая проверка: возвращает null, если событий нет
public void nonBlockingPoll(WatchService watchService) {
WatchKey key = watchService.poll();
if (key != null) {
processKey(key);
} else {
System.out.println("Нет новых событий");
}
}
// Ожидание с таймаутом
public void timedPoll(WatchService watchService) throws InterruptedException {
// Ждём 5 секунд, если событий нет — возвращаем null
WatchKey key = watchService.poll(5, TimeUnit.SECONDS);
if (key != null) {
processKey(key);
}
}
private void processKey(WatchKey key) {
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context(); // имя файла, вызвавшего событие
System.out.println("Событие: " + kind.name() + " для " + fileName);
}
// Сбрасываем ключ, чтобы получать новые события
boolean valid = key.reset();
if (!valid) {
System.out.println("Ключ больше не валиден — директория удалена");
}
}
}
take() — блокирующий метод, который переводит поток в состояние WAITING. Поток не потребляет CPU в этом состоянии. ОС пробуждает поток, когда приходит событие inotify/FSEvents/ReadDirectoryChangesW.poll() — неблокирующий. Если событий нет, немедленно возвращает null. Полезен в циклах, где нужно делать другую работу между проверками.poll(timeout, TimeUnit) — блокирует на указанное время, затем возвращает null или WatchKey.Важно: после обработки событий необходимо вызвать
key.reset(). Это сбрасывает состояние ключа и позволяет WatchService снова наполнять его событиями. Если reset() не вызван, ключ перестаёт получать новые события. Если reset() возвращает false, ключ больше не валиден (обычно потому, что директория удалена).Обработка WatchEvent
WatchEvent<?> event = ...;
// Тип события: ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY
WatchEvent.Kind<?> kind = event.kind();
// Имя файла или директории, вызвавшей событие (относительно зарегистрированной директории)
Path context = (Path) event.context();
// Количество событий данного типа для этого файла (может быть > 1 при coalescing)
int count = event.count();
event.context() возвращает Path, но это не абсолютный путь — это имя файла относительно зарегистрированной директории. Для получения абсолютного пути нужно резолвить через watchable() ключа:Path dir = (Path) key.watchable(); // зарегистрированная директория
Path absolute = dir.resolve(context); // полный путь
Важные нюансы WatchService
Событие CREATE приходит до завершения записи: если процесс создаёт файл и медленно пишет в него,
ENTRY_CREATE приходит сразу после появления inode. Файл может быть ещё пустым или заблокирован на запись. Решение — ожидание готовности (как в waitForFileReady()) или использование временных имён с последующим атомарным переименованием.Событие MODIFY может приходить многократно: одна логическая операция записи может породить серию событий
ENTRY_MODIFY. Приложение должно быть готово к дедупликации или игнорированию промежуточных событий.Символические ссылки: WatchService следует по символическим ссылкам при регистрации. Если зарегистрировать ссылку, события будут приходить для целевой директории.
Рекурсивный мониторинг: WatchService не мониторит поддиректории автоматически. Для рекурсивного мониторинга нужно обходить дерево и регистрировать каждую директорию отдельно, а при появлении новой директории (событие
ENTRY_CREATE для директории) регистрировать её динамически.OVERFLOW: при слишком быстром потоке событий внутренний буфер ОС может переполниться. В этом случае приходит событие
OVERFLOW, и часть событий теряется. Приложение должно обрабатывать это как сигнал к полному пересканированию директории.Путь байтов в памяти JVM при построчном чтении
Files.lines(): путь данных
При
Files.lines(file):Метод создаёт
BufferedReader — объект в куче, который оборачивает InputStreamReader, который оборачивает FileInputStream. Каждый из этих объектов — в куче. FileInputStream содержит файловый дескриптор (целое число в нативной памяти).BufferedReader выделяет буфер char[] размером 8192 символов (по умолчанию) в Young Generation. Этот буфер переиспользуется для всех операций чтения.При итерации по
Stream<String> каждый вызов readLine() выполняет:Чтение байтов из файла через
FileInputStream.read() в буфер BufferedReader.Декодирование байтов в символы через
CharsetDecoder (для UTF-8 — переменная длина, 1–4 байта на символ).Поиск символа перевода строки (
\n, \r\n или \r).Создание нового объекта
String для строки.Объект
String создаётся в Young Generation (Eden). Если строка длинная (например, CSV-строка с тысячей символов), она может быть размещена сразу в Old Generation как humongous object (в G1 GC, если размер превышает половину региона).После обработки строки в терминальной операции (например,
count() или forEach()) ссылка на String исчезает. Объект становится мусором и собирается Minor GC. Буфер BufferedReader остаётся живым до закрытия потока.При закрытии
Stream (через try-with-resources) вызывается BufferedReader.close(), который закрывает FileInputStream, который выполняет системный вызов close() для освобождения файлового дескриптора. Если поток не закрыт, дескриптор утечёт.Путь байтов при WatchService
При создании
WatchService:FileSystems.getDefault().newWatchService() создаёт объект WatchService в куче. На Linux это обёртка над InotifyWatchService, который выполняет системный вызов inotify_init().inotify_init() создаёт экземпляр inotify в ядре Linux и возвращает файловый дескриптор. Этот дескриптор хранится в нативной памяти JVM, ассоциированной с объектом WatchService.При регистрации директории (
dir.register(...)) выполняется inotify_add_watch(), который добавляет "watch" (наблюдение) за директорией в ядре. Ядро начинает отслеживать события для этой директории и всех её прямых потомков.inotify в ядре использует собственный буфер событий (обычно 16 КБ по умолчанию, настраивается через /proc/sys/fs/inotify/max_queued_events). Если событий больше, чем вмещает буфер, приходит OVERFLOW.При вызове
watchService.take() поток Java блокируется на системном вызове read() от дескриптора inotify. Поток переводится в состояние WAITING — он не потребляет CPU. Когда событие появляется, ядро пробуждает поток, read() возвращает структуру события, и JVM создаёт объекты WatchKey и WatchEvent в куче.Объекты
WatchKey и WatchEvent создаются в Young Generation. WatchKey может переживать несколько циклов событий, если его не сбросить через reset(). WatchEvent — одноразовый объект, который становится мусором после обработки.При закрытии
WatchService (watchService.close()) выполняется inotify_rm_watch() для всех зарегистрированных наблюдений и close() для дескриптора inotify. Это освобождает ресурсы ядра. Если WatchService не закрыт, дескриптор inotify остаётся открытым до завершения процесса.Важно:
inotify на Linux не рекурсивен. WatchService регистрирует наблюдение только для указанной директории, не для поддиректорий. Для рекурсивного мониторинга Java обходит дерево и создаёт отдельный inotify_add_watch() для каждой директории. Каждый watch потребляет ресурсы ядра (inode наблюдения), и их количество ограничено (/proc/sys/fs/inotify/max_user_watches, обычно 8192).#Java #для_новичков #beginner #IO #NIO #Files #Files_lines #WatchService
👍4
Что выведет код?
#Tasks
import java.nio.file.*;
import java.util.*;
import java.util.stream.Stream;
public class FilesLinesTricky {
public static void main(String[] args) throws Exception {
Path file = Paths.get("test.txt");
Files.write(file, Arrays.asList("A", "B", "C"));
try (Stream<String> lines = Files.lines(file)) {
Iterator<String> it = lines.iterator();
System.out.print(it.next());
System.out.print(it.next());
Files.write(file, Arrays.asList("D", "E"), StandardOpenOption.APPEND);
while (it.hasNext()) {
System.out.print(it.next());
}
}
}
}
#Tasks
👍3
👍3
Media is too big
VIEW IN TELEGRAM
Небольшая демонстрация предрелизной логики Devforge.ru 🙈
Жду оценок и мнений!😏
пысы: схемы можно писать через язык Mermaid🤓
Жду оценок и мнений!
пысы: схемы можно писать через язык Mermaid
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
Что такое 🤓
Ответ:
ZGC (Java 11 как экспериментальный, стабилен с Java 15) — это низколатентный сборщик мусора, работающий с паузами менее 10 мс (в основном менее 1 мс), даже для терабайтных куч.
Использует механизм colored pointers и load barriers. Работает параллельно с приложением, не останавливая его для полной сборки (concurrent).
Не требует больших тюнингов. Подходит для приложений реального времени, микросервисов с низкими SLI по задержкам.
Альтернатива — Shenandoah (от Red Hat), похожий по идеологии.
#собеседование
ZGC (Z Garbage Collector)? Ответ:
Использует механизм colored pointers и load barriers. Работает параллельно с приложением, не останавливая его для полной сборки (concurrent).
Не требует больших тюнингов. Подходит для приложений реального времени, микросервисов с низкими SLI по задержкам.
Альтернатива — Shenandoah (от Red Hat), похожий по идеологии.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История технологии сегодня — 24 июля
ℹ️ Кто родился в этот день
Шарль Эми́ль Пика́р (фр. Charles Émile Picard; 24 июля 1856, Париж — 11 декабря 1941, там же) — французский математик. Известен фундаментальными результатами в области математического анализа. Его учебник анализа (Traité d’Analyse) долгое время считался классическим. Внёс существенный вклад также в теорию дифференциальных уравнений, теорию функций, топологию, теорию групп. Для линейных дифференциальных уравнений разработал аналог теории Галуа. Часть его трудов посвящены истории и философии математики.
🌐 Знаковые события
1987 — IBM выпустила операционную систему DOS version 3.3.
#Biography #Birth_Date #Events #24июля
Шарль Эми́ль Пика́р (фр. Charles Émile Picard; 24 июля 1856, Париж — 11 декабря 1941, там же) — французский математик. Известен фундаментальными результатами в области математического анализа. Его учебник анализа (Traité d’Analyse) долгое время считался классическим. Внёс существенный вклад также в теорию дифференциальных уравнений, теорию функций, топологию, теорию групп. Для линейных дифференциальных уравнений разработал аналог теории Галуа. Часть его трудов посвящены истории и философии математики.
1987 — IBM выпустила операционную систему DOS version 3.3.
#Biography #Birth_Date #Events #24июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #064]
Тема:
Проблема:
Это удобно для кэшей или метаданных, привязанных к объектам. Однако внутри
Решение: При использовании
Если архитектура требует, чтобы значение знало о ключе, следует хранить значение в виде
Альтернативно, можно использовать
Объяснение: В
Сборщик мусора определяет достижимость объектов: если от корней GC (например, статические поля, локальные переменные) нет цепочки сильных ссылок к ключу, ключ считается слабодостижимым. Однако если значение содержит сильную ссылку на ключ, то цепочка
Таким образом, ключ не является слабодостижимым, и запись не удаляется. Это классический случай утечки памяти, который трудно диагностировать, потому что
#Java #советы
Тема:
WeakHashMap хранит ключи через слабые ссылки, но значения — сильные. Значение может удерживать ключ, предотвращая удаление.Проблема:
WeakHashMap предназначена для хранения записей, ключи которых могут быть собраны сборщиком мусора (GC), если на них нет сильных ссылок. Это удобно для кэшей или метаданных, привязанных к объектам. Однако внутри
WeakHashMap значения хранятся через сильные ссылки (обычные ссылки). Если значение содержит ссылку на свой ключ (прямо или косвенно), то образуется цикл: ключ держит значение через map, а значение держит ключ через свою ссылку. В результате ни ключ, ни значение не становятся достижимыми только через слабую ссылку, и запись никогда не удаляется из WeakHashMap. Это приводит к утечке памяти, так как объекты остаются живыми, несмотря на то, что внешние ссылки на ключ отсутствуют. Разработчики часто не учитывают этот нюанс и используют WeakHashMap для кэширования, полагая, что оно автоматически очищается, а затем сталкиваются с неожиданным ростом памяти.Решение: При использовании
WeakHashMap необходимо гарантировать, что значение не содержит ссылки на ключ. Если архитектура требует, чтобы значение знало о ключе, следует хранить значение в виде
WeakReference или использовать отдельную структуру. Альтернативно, можно использовать
WeakHashMap только для простых типов ключей (например, строк), которые не хранятся в значениях. Для сложных кэшей лучше применять библиотеки типа Caffeine или Guava Cache, которые предоставляют более гибкие стратегии очистки и не страдают от этой проблемы. В случае, если значение обязательно должно ссылаться на ключ, можно использовать java.lang.ref.WeakReference для значения и хранить его в WeakHashMap, но тогда нужно самостоятельно обрабатывать удаление.import java.util.WeakHashMap;
import java.lang.ref.WeakReference;
public class WeakHashMapPitfall {
static class Key { /* ... */ }
static class Value {
private final Key key; // ссылка на ключ!
Value(Key key) { this.key = key; }
}
public static void main(String[] args) {
//Антипаттерн: значение ссылается на ключ
WeakHashMap<Key, Value> map = new WeakHashMap<>();
Key key = new Key();
map.put(key, new Value(key)); // значение хранит ссылку на ключ
key = null; // убираем внешнюю ссылку
System.gc(); // попытка сборки
// Запись не будет удалена, так как Value держит Key
System.out.println("Size: " + map.size()); // 1 — утечка!
//Решение: не хранить ссылку на ключ в значении
static class SafeValue {
// без ссылки на Key
}
WeakHashMap<Key, SafeValue> safeMap = new WeakHashMap<>();
Key key2 = new Key();
safeMap.put(key2, new SafeValue());
key2 = null;
System.gc();
// Запись будет удалена
System.out.println("Safe size: " + safeMap.size()); // 0
// Альтернатива: использовать WeakReference для значения
WeakHashMap<Key, WeakReference<Value>> refMap = new WeakHashMap<>();
Key key3 = new Key();
refMap.put(key3, new WeakReference<>(new Value(key3))); // всё равно Value ссылается на Key
// это не решает проблему, если Value ссылается на Key
}
}
Объяснение: В
WeakHashMap запись удаляется, когда ключ становится доступным только через слабую ссылку (т.е. нет сильных ссылок на ключ, кроме самой карты). Сборщик мусора определяет достижимость объектов: если от корней GC (например, статические поля, локальные переменные) нет цепочки сильных ссылок к ключу, ключ считается слабодостижимым. Однако если значение содержит сильную ссылку на ключ, то цепочка
map -> значение -> ключ создает сильную достижимость ключа (через значение). Таким образом, ключ не является слабодостижимым, и запись не удаляется. Это классический случай утечки памяти, который трудно диагностировать, потому что
WeakHashMap работает как ожидается для большинства случаев. #Java #советы
👍3
Что выведет код?
#Tasks
public class Task240726 {
static class Key {
String id;
Key(String id) { this.id = id; }
@Override
public int hashCode() { return id.hashCode(); }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
return id.equals(key.id);
}
}
static class Value {
Key ref;
Value(Key ref) { this.ref = ref; }
}
public static void main(String[] args) throws InterruptedException {
WeakHashMap<Key, Value> map = new WeakHashMap<>();
Key key1 = new Key("one");
Key key2 = new Key("two");
map.put(key1, new Value(key1));
map.put(key2, new Value(null));
key1 = null;
key2 = null;
System.gc();
Thread.sleep(1000);
System.out.println(map.size());
System.out.println(map.containsKey(new Key("one")));
System.out.println(map.containsKey(new Key("two")));
}
}#Tasks
👍2
👍2
Что такое 🤓
Ответ:
Начиная с Java 17 (preview с Java 17, финал в Java 21), switch может сопоставлять не только константы, но и типы объектов с автоматическим приведением.
Пример:
String result = switch (obj)
{
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
case null -> "null"; default -> "Unknown";
};.
Также поддерживаются охранные условия (guards): case Point p when p.x() > 0 -> ....
Это делает switch полноценным инструментом для работы с иерархиями типов, уменьшая количество instanceof и приведений.
#собеседование
switch с паттернами (Pattern Matching for Switch)? Ответ:
Пример:
String result = switch (obj)
{
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
case null -> "null"; default -> "Unknown";
};.
Также поддерживаются охранные условия (guards): case Point p when p.x() > 0 -> ....
Это делает switch полноценным инструментом для работы с иерархиями типов, уменьшая количество instanceof и приведений.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
История технологии сегодня — 25 июля
ℹ️ Кто родился в этот день
Джон Баннистер Гуденаф (англ. John Bannister Goodenough; 25 июля 1922, Йена, Тюрингия[вд] — 25 июня 2023, Остин) — американский учёный, специалист в области физики и химии твёрдого тела и материаловедения. Известен своим вкладом в разработку первых литий-ионных аккумуляторов.
🌐 Знаковые события
1907 — русский физик Борис Розинг подал заявку на патент на «Способ электрической передачи изображений на расстояние», то есть телевидение.
#Biography #Birth_Date #Events #25июля
Джон Баннистер Гуденаф (англ. John Bannister Goodenough; 25 июля 1922, Йена, Тюрингия[вд] — 25 июня 2023, Остин) — американский учёный, специалист в области физики и химии твёрдого тела и материаловедения. Известен своим вкладом в разработку первых литий-ионных аккумуляторов.
1907 — русский физик Борис Розинг подал заявку на патент на «Способ электрической передачи изображений на расстояние», то есть телевидение.
#Biography #Birth_Date #Events #25июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
История технологии сегодня — 26 июля
ℹ️ Кто родился в этот день
Джеймс Эфрэйм Лавлок (англ. James Ephraim Lovelock; 26 июля 1919, Летчуэрт, Хартфордшир — 26 июля 2022[3], Эбботсбери[вд], Юго-Западная Англия) — британский учёный, независимый исследователь, биофизик, химик, футуролог. Приобрёл известность как создатель Гипотезы Геи (согласно которой планета Земля функционирует как суперорганизм), а также как активный сторонник использования атомной энергии.
Исаак Бэббитт (англ. Isaac Babbitt; 26 июля 1799, Тонтон, Массачусетс, США — 26 мая 1862, Сомервилл, Массачусетс, США) — американский изобретатель. В 1839 году он изобрел подшипник из металлического сплава на основе олова с низким коэффициентом трения. Этот металл был назван по его имени баббитом, и до сих пор широко используется в подшипниках.
🌐 Знаковые события
1925 — впервые поднялся в воздух самолёт «К-1» конструкции К. А. Калинина — первый отечественный пассажирский самолёт, рекомендованный к серийному выпуску.
1989 — 24-летнему американскому студенту Роберту Моррису предъявляется обвинение в создании и запуске компьютерного вируса. Моррис становится первым создателем вируса, подвергнутым уголовному преследованию.
#Biography #Birth_Date #Events #26июля
Джеймс Эфрэйм Лавлок (англ. James Ephraim Lovelock; 26 июля 1919, Летчуэрт, Хартфордшир — 26 июля 2022[3], Эбботсбери[вд], Юго-Западная Англия) — британский учёный, независимый исследователь, биофизик, химик, футуролог. Приобрёл известность как создатель Гипотезы Геи (согласно которой планета Земля функционирует как суперорганизм), а также как активный сторонник использования атомной энергии.
Исаак Бэббитт (англ. Isaac Babbitt; 26 июля 1799, Тонтон, Массачусетс, США — 26 мая 1862, Сомервилл, Массачусетс, США) — американский изобретатель. В 1839 году он изобрел подшипник из металлического сплава на основе олова с низким коэффициентом трения. Этот металл был назван по его имени баббитом, и до сих пор широко используется в подшипниках.
1925 — впервые поднялся в воздух самолёт «К-1» конструкции К. А. Калинина — первый отечественный пассажирский самолёт, рекомендованный к серийному выпуску.
1989 — 24-летнему американскому студенту Роберту Моррису предъявляется обвинение в создании и запуске компьютерного вируса. Моррис становится первым создателем вируса, подвергнутым уголовному преследованию.
#Biography #Birth_Date #Events #26июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Как отдыхать от кода, чтобы не потерять к нему интерес.
Было время, когда я изучая программирование и готовясь к собеседованиям, просиживал за изучением Java по 12-15 часов в день. Это был цейтнот и время поджимало, поэтому я не жалел себя.
И прекрасно помню, как вставая от компа, я понимал, что вообще не соображаю. Мозг, как онемевший орган, переставал реагировать на внешние раздражители.
Учиться в таком режиме было тяжело, но быстро. А ощущение отупения накапливалось и с каждым днем было все сложнее заставить себя учиться. Все сложнее было воспринимать. и что важнее - осознавать что-то новое.
Тогда я и понял, что отдых для мозга не менее важен, чем для мышц при напряженной физической работе.
Вот мои советы:
1. Программирование — это умственный стресс и нагрузка. Значит, отдых должен быть противоположным.
Многие думают: «Отдохну, полежу, сериальчик посмотрю». Это может сработать разок, но по сути — это просто пассивная стимуляция. Усталый мозг не отключается, он продолжает переваривать входящие, только уже из Netflix.
В психологии есть понятие когнитивной разрядки (cognitive offloading) — это когда мозг сознательно уходит от абстрактных нагрузок к чему-то телесному и простому.
Поэтому, будут крайне полезны:
🔵 Регулярные пешие прогулки, без телефона в руках.
🔵 Физнагрузки (бег, плавание, даже уборка) — вообще мастхев для программистов.
Я в свою очередь стараюсь заниматься хотя-бы через день. Ничего лучше так не прочищает мозг, чем 5-километровая пробежка по парку😉 .
2. Отдых не должен быть побегом. Он должен быть паузой.
Плохо, когда однажды перенагрузив мозг, ты вновь пытаешься это повторить, а он сопротивляется. Ты прямо-таки можешь ощущать его сопротивление, нежелание осознавать прочитанное.
И ты пытаешься сбежать от программирования в какие-то рандомные развлечения. Это может вызвать ненужные тебе переживания, самоосуждение, тут и до "темного попутчика" (о котором мы говорили в статье) недалеко.
Поэтому правильный, а что немаловажно, своевременный отдых — это контекстный отдых, когда ты всё ещё как бы рядом с ИТ, но без задачи что-то выдать.
Примеры:
🔵 Чтение биографий учёных, разработчиков — это вдохновляет и не грузит.
🔵 Хобби: пайка электроники, музыка, геймдев или гейминг — лёгкое смежное времяпрепровождение.
🔵 Визуальные виды деятельности: рисование, моделирование, pixel art.
В психологии это описывается как ресторативная активность (restorative experiences) — действия, которые возвращают ощущение контроля и удовольствия от процесса.
3. Мини-проекты без цели.
Порой, когда ты перегружен "сложным" программированием и чувствуешь, что уже не вывозишь, полезно переключиться на "развлекательное" программирование - написать что-то простое и веселое.
Попробуй написать:
🔵 Телеграм - бота, который будет постить твои рандомные мысли.
🔵 Код, сортирующий папки по настроению (буквально по цветам или дням недели).
🔵 Программу, делающую любую глупость, но красиво ☺️ .
Это и есть "игра с собой" (self-determined play) — процесс, в котором мотивация рождается не из цели, а из самого процесса. В котором ты отдыхаешь, но при этом не теряешь связи с программированием и получаешь удовольствие от процесса.
4. Общение в среде программирования.
Иногда, после высокой умственной нагрузки, достаточно оставаться рядом с темой. Расслабиться и послушать подкасты, посмотреть чужие лайвкодинги.
А можно найти сообщество (похожее на наше☺️ ) и программистов, с которыми пообсуждать околопрограммерские вопросы.
Это вызывает ощущение сопричастности.
В когнитивной психологии это называется пассивное обучение (incidental learning) — когда ты усваиваешь что-то из обсуждения, даже не замечая.
А это в свою очередь, поддерживает интерес и мотивацию (Deci & Ryan, 2000).
А как вы боретесь с усталостью? Пишите в комментариях!💪
#motivation
Было время, когда я изучая программирование и готовясь к собеседованиям, просиживал за изучением Java по 12-15 часов в день. Это был цейтнот и время поджимало, поэтому я не жалел себя.
И прекрасно помню, как вставая от компа, я понимал, что вообще не соображаю. Мозг, как онемевший орган, переставал реагировать на внешние раздражители.
Учиться в таком режиме было тяжело, но быстро. А ощущение отупения накапливалось и с каждым днем было все сложнее заставить себя учиться. Все сложнее было воспринимать. и что важнее - осознавать что-то новое.
Тогда я и понял, что отдых для мозга не менее важен, чем для мышц при напряженной физической работе.
Вот мои советы:
1. Программирование — это умственный стресс и нагрузка. Значит, отдых должен быть противоположным.
Многие думают: «Отдохну, полежу, сериальчик посмотрю». Это может сработать разок, но по сути — это просто пассивная стимуляция. Усталый мозг не отключается, он продолжает переваривать входящие, только уже из Netflix.
В психологии есть понятие когнитивной разрядки (cognitive offloading) — это когда мозг сознательно уходит от абстрактных нагрузок к чему-то телесному и простому.
Поэтому, будут крайне полезны:
Я в свою очередь стараюсь заниматься хотя-бы через день. Ничего лучше так не прочищает мозг, чем 5-километровая пробежка по парку
2. Отдых не должен быть побегом. Он должен быть паузой.
Плохо, когда однажды перенагрузив мозг, ты вновь пытаешься это повторить, а он сопротивляется. Ты прямо-таки можешь ощущать его сопротивление, нежелание осознавать прочитанное.
И ты пытаешься сбежать от программирования в какие-то рандомные развлечения. Это может вызвать ненужные тебе переживания, самоосуждение, тут и до "темного попутчика" (о котором мы говорили в статье) недалеко.
Поэтому правильный, а что немаловажно, своевременный отдых — это контекстный отдых, когда ты всё ещё как бы рядом с ИТ, но без задачи что-то выдать.
Примеры:
В психологии это описывается как ресторативная активность (restorative experiences) — действия, которые возвращают ощущение контроля и удовольствия от процесса.
И твое «Хочу программировать» останется внутри даже в перерыве от него, ведь ты не перенапрягаешься.
3. Мини-проекты без цели.
Порой, когда ты перегружен "сложным" программированием и чувствуешь, что уже не вывозишь, полезно переключиться на "развлекательное" программирование - написать что-то простое и веселое.
Попробуй написать:
Это и есть "игра с собой" (self-determined play) — процесс, в котором мотивация рождается не из цели, а из самого процесса. В котором ты отдыхаешь, но при этом не теряешь связи с программированием и получаешь удовольствие от процесса.
4. Общение в среде программирования.
Иногда, после высокой умственной нагрузки, достаточно оставаться рядом с темой. Расслабиться и послушать подкасты, посмотреть чужие лайвкодинги.
А можно найти сообщество (похожее на наше
Это вызывает ощущение сопричастности.
В когнитивной психологии это называется пассивное обучение (incidental learning) — когда ты усваиваешь что-то из обсуждения, даже не замечая.
А это в свою очередь, поддерживает интерес и мотивацию (Deci & Ryan, 2000).
Эффективный отдых — это не пассивность, а активная смена когнитивной нагрузки. Хороший отдых питает тот же источник интереса, что и программирование, но делает это без давления.
А как вы боретесь с усталостью? Пишите в комментариях!
#motivation
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥1
История технологии сегодня — 27 июля
ℹ️ Кто родился в этот день
Сэр Демис Хассабис (родился 27 июля 1976 г.) — британский исследователь в области искусственного интеллекта (ИИ) и предприниматель. Он является генеральным директором и соучредителем Google DeepMind и Isomorphic Labs, а также советником правительства Великобритании по вопросам ИИ. В 2024 году Хассабис и Джон М. Джампер были совместно удостоены Нобелевской премии по химии за свой вклад в исследования ИИ в области предсказания структуры белков.
🌐 Знаковые события
1985 — первый полёт спортивно-пилотажного акробатического самолёта СУ-26М, в конструкции которого широко применены композитные материалы.
#Biography #Birth_Date #Events #27июля
Сэр Демис Хассабис (родился 27 июля 1976 г.) — британский исследователь в области искусственного интеллекта (ИИ) и предприниматель. Он является генеральным директором и соучредителем Google DeepMind и Isomorphic Labs, а также советником правительства Великобритании по вопросам ИИ. В 2024 году Хассабис и Джон М. Джампер были совместно удостоены Нобелевской премии по химии за свой вклад в исследования ИИ в области предсказания структуры белков.
1985 — первый полёт спортивно-пилотажного акробатического самолёта СУ-26М, в конструкции которого широко применены композитные материалы.
#Biography #Birth_Date #Events #27июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)
Глава 2. Современный NIO.2 (java.nio.file)
SeekableByteChannel — произвольный доступ к файлу. Символические и жёсткие ссылки
До этого момента мы работали с файлами через высокоуровневые абстракции: читали целиком, писали целиком, копировали, перемещали. Но файловая система — это в первую очередь последовательность байтов на диске, и иногда нужно читать или писать не с начала, а с произвольной позиции.
Для этого в NIO.2 существует интерфейс
Вторая часть посвящена ссылкам в файловой системе — механизму, который позволяет одним данным соответствовать несколько имён. Символические и жёсткие ссылки — фундаментальная концепция Unix-файловых систем, и их непонимание часто приводит к багам при обходе деревьев, подсчёте размеров и резервном копировании.
Часть 1. SeekableByteChannel
SeekableByteChannel — это интерфейс в пакете
Канал (channel) в Java NIO — это абстракция для соединения с сущностью, способной выполнять операции ввода-вывода: файл, сокет, пайп. Каналы работают с буферами (ByteBuffer), а не с потоками байтов. Это позволяет использовать прямой (direct) ввод-вывод, минуя промежуточное копирование в кучу JVM.
Интерфейс
Открытие канала: Files.newByteChannel()
Метод
StandardOpenOption — режимы открытия
READ — открыть для чтения. Файл должен существовать. Соответствует флагу
WRITE — открыть для записи. Файл должен существовать. Соответствует флагу
APPEND — все операции записи производятся в конец файла, независимо от текущей позиции. Соответствует флагу
CREATE — создать файл, если он не существует. Соответствует
CREATE_NEW — создать файл, но выбросить исключение, если он уже существует. Соответствует
TRUNCATE_EXISTING — если файл существует и открыт для записи, обрезать его размер до 0. Соответствует
DELETE_ON_CLOSE — удалить файл при закрытии канала. Полезно для временных файлов.
SYNC — каждая операция записи синхронизирует данные и метаданные с диском. Соответствует
DSYNC — синхронизирует только данные, не метаданные. Соответствует
SPARSE — создать разреженный файл (sparse file), если файловая система поддерживает.
Разреженный файл (sparse file) — это файл, в котором последовательности нулевых байтов не занимают место на диске. Файловая система хранит только информацию о "дырах" (holes), а не сами нули. При чтении таких областей возвращаются нулевые байты. Разреженные файлы экономят дисковое пространство при работе с большими файлами, большая часть которых пуста.
Durability (стойкость) — свойство системы хранения, гарантирующее, что записанные данные сохранятся даже при сбое питания. SYNC и DSYNC принудительно сбрасывают данные из кэша страниц ОС на физическое устройство хранения.
Чтение и запись с произвольной позиции
ByteBuffer — это класс в Java NIO, представляющий блок памяти, через который производится ввод-вывод. Буфер имеет три ключевых свойства: capacity (ёмкость), limit (предел) и position (текущая позиция). Метод flip() переключает буфер из режима записи (write) в режим чтения (read): устанавливает limit = position, position = 0.
#Java #для_новичков #beginner #IO #NIO #SeekableByteChannel
Глава 2. Современный NIO.2 (java.nio.file)
SeekableByteChannel — произвольный доступ к файлу. Символические и жёсткие ссылки
До этого момента мы работали с файлами через высокоуровневые абстракции: читали целиком, писали целиком, копировали, перемещали. Но файловая система — это в первую очередь последовательность байтов на диске, и иногда нужно читать или писать не с начала, а с произвольной позиции.
Для этого в NIO.2 существует интерфейс
SeekableByteChannel. Он объединяет возможности чтения, записи и позиционирования в единый канал, заменяя устаревший RandomAccessFile.Вторая часть посвящена ссылкам в файловой системе — механизму, который позволяет одним данным соответствовать несколько имён. Символические и жёсткие ссылки — фундаментальная концепция Unix-файловых систем, и их непонимание часто приводит к багам при обходе деревьев, подсчёте размеров и резервном копировании.
Часть 1. SeekableByteChannel
SeekableByteChannel — это интерфейс в пакете
java.nio.channels, расширяющий ByteChannel. ByteChannel в свою очередь объединяет ReadableByteChannel и WritableByteChannel, то есть поддерживает и чтение, и запись. Добавление приставки "Seekable" означает, что канал позволяет изменять текущую позицию чтения/записи — "перематывать" указатель в произвольное место файла.Канал (channel) в Java NIO — это абстракция для соединения с сущностью, способной выполнять операции ввода-вывода: файл, сокет, пайп. Каналы работают с буферами (ByteBuffer), а не с потоками байтов. Это позволяет использовать прямой (direct) ввод-вывод, минуя промежуточное копирование в кучу JVM.
Интерфейс
SeekableByteChannel декларирует три ключевых метода, отсутствующих в обычном ByteChannel:position() — возвращает текущую позицию в файле.position(long newPosition) — устанавливает новую позицию.size() — возвращает текущий размер файла в байтах.Открытие канала: Files.newByteChannel()
Метод
Files.newByteChannel(Path path, OpenOption... options) возвращает SeekableByteChannel для указанного пути. Это фабричный метод, который делегирует создание канала текущему FileSystemProvider.import java.nio.file.*;
import java.nio.channels.SeekableByteChannel;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ChannelOpener {
public void openForRead(Path file) throws IOException {
// Открываем только для чтения
try (SeekableByteChannel channel = Files.newByteChannel(file, StandardOpenOption.READ)) {
System.out.println("Размер файла: " + channel.size());
System.out.println("Текущая позиция: " + channel.position());
}
}
public void openForWrite(Path file) throws IOException {
// Открываем для чтения и записи, создаём если нет, обрезаем если есть
try (SeekableByteChannel channel = Files.newByteChannel(file,
StandardOpenOption.READ,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING)) {
// Записываем данные
ByteBuffer buffer = ByteBuffer.wrap("Hello, World".getBytes(StandardCharsets.UTF_8));
channel.write(buffer);
}
}
}
StandardOpenOption — режимы открытия
StandardOpenOption — это enum, определяющий, как именно открывать или создавать файл. Каждая опция соответствует флагу системного вызова open() (POSIX):READ — открыть для чтения. Файл должен существовать. Соответствует флагу
O_RDONLY.WRITE — открыть для записи. Файл должен существовать. Соответствует флагу
O_WRONLY (в комбинации с READ даёт O_RDWR).APPEND — все операции записи производятся в конец файла, независимо от текущей позиции. Соответствует флагу
O_APPEND. Позиция после записи не изменяется (остаётся там, где была до записи).CREATE — создать файл, если он не существует. Соответствует
O_CREAT.CREATE_NEW — создать файл, но выбросить исключение, если он уже существует. Соответствует
O_CREAT | O_EXCL. Атомарная операция.TRUNCATE_EXISTING — если файл существует и открыт для записи, обрезать его размер до 0. Соответствует
O_TRUNC.DELETE_ON_CLOSE — удалить файл при закрытии канала. Полезно для временных файлов.
SYNC — каждая операция записи синхронизирует данные и метаданные с диском. Соответствует
O_SYNC. Сильно снижает производительность, но гарантирует durability.DSYNC — синхронизирует только данные, не метаданные. Соответствует
O_DSYNC.SPARSE — создать разреженный файл (sparse file), если файловая система поддерживает.
Разреженный файл (sparse file) — это файл, в котором последовательности нулевых байтов не занимают место на диске. Файловая система хранит только информацию о "дырах" (holes), а не сами нули. При чтении таких областей возвращаются нулевые байты. Разреженные файлы экономят дисковое пространство при работе с большими файлами, большая часть которых пуста.
Durability (стойкость) — свойство системы хранения, гарантирующее, что записанные данные сохранятся даже при сбое питания. SYNC и DSYNC принудительно сбрасывают данные из кэша страниц ОС на физическое устройство хранения.
Чтение и запись с произвольной позиции
public class RandomAccessExample {
public void readFromPosition(Path file, long position, int length) throws IOException {
try (SeekableByteChannel channel = Files.newByteChannel(file, StandardOpenOption.READ)) {
// Устанавливаем позицию
channel.position(position);
ByteBuffer buffer = ByteBuffer.allocate(length);
int bytesRead = channel.read(buffer);
buffer.flip(); // переключаем буфер из режима записи в режим чтения
byte[] data = new byte[bytesRead];
buffer.get(data);
System.out.println("Прочитано с позиции " + position + ": " + new String(data));
}
}
public void writeAtPosition(Path file, long position, String data) throws IOException {
try (SeekableByteChannel channel = Files.newByteChannel(file,
StandardOpenOption.READ, StandardOpenOption.WRITE)) {
channel.position(position);
ByteBuffer buffer = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8));
channel.write(buffer);
// После записи позиция сдвинулась на количество записанных байт
System.out.println("Новая позиция: " + channel.position());
}
}
public void appendToFile(Path file, String data) throws IOException {
// Способ 1: через APPEND опцию
try (SeekableByteChannel channel = Files.newByteChannel(file,
StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
ByteBuffer buffer = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8));
channel.write(buffer);
}
// Способ 2: через position(size()) — ручное позиционирование в конец
try (SeekableByteChannel channel = Files.newByteChannel(file,
StandardOpenOption.READ, StandardOpenOption.WRITE)) {
channel.position(channel.size()); // переходим в конец
ByteBuffer buffer = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8));
channel.write(buffer);
}
}
}ByteBuffer — это класс в Java NIO, представляющий блок памяти, через который производится ввод-вывод. Буфер имеет три ключевых свойства: capacity (ёмкость), limit (предел) и position (текущая позиция). Метод flip() переключает буфер из режима записи (write) в режим чтения (read): устанавливает limit = position, position = 0.
#Java #для_новичков #beginner #IO #NIO #SeekableByteChannel
👍4
Сравнение с RandomAccessFile
Класс
Однако у него есть архитектурные недостатки:
Не работает с Path:
Не поддерживает символические ссылки корректно:
Не интегрирован с NIO:
Менее гибкая модель опций:
Не поддерживает кастомные FileSystem:
FileChannel — это конкретная реализация
Часть 2. Символические и жёсткие ссылки
В файловых системах Unix имя файла — это не сам файл, а ссылка на структуру данных, хранящую метаданные и указатели на блоки. Эта структура называется inode. Директория — это просто список пар "имя — номер inode". Ссылка — это запись в этом списке.
inode (index node) — структура данных в файловой системе Unix, содержащая всё, кроме имени файла и его содержимого: тип файла, права доступа, владелец, группа, размер, временные метки, счётчик ссылок и указатели на блоки данных. Номер inode уникален в пределах файловой системы (раздела диска).
Жёсткая ссылка (hard link)
Жёсткая ссылка — это дополнительное имя для существующего файла, указывающее на тот же inode.
Все жёсткие ссылки на файл равноправны: нет "оригинала" и "копий". Каждая жёсткая ссылка увеличивает счётчик ссылок (link count) в inode на 1.
Когда файл удаляется через
Ограничения жёстких ссылок:
Нельзя создать на директорию (за исключением системных
Нельзя создать между разными файловыми системами (разделами диска), так как inode уникален только в пределах одной ФС.
Нельзя создать на несуществующий файл.
Символическая ссылка (symbolic link, symlink)
Символическая ссылка — это специальный файл, содержащий путь (строку) к другому файлу или директории. В отличие от жёсткой ссылки, символическая ссылка имеет свой собственный inode и не влияет на счётчик ссылок целевого файла. Она может указывать на несуществующий объект (висячая ссылка, dangling symlink), на директорию, на файл в другой файловой системе или даже на саму себя.
Висячая ссылка (dangling symlink) — символическая ссылка, целевой объект которой был удалён или перемещён. При попытке доступа к такой ссылке системный вызов вернёт ENOENT (No such file or directory).
Символические ссылки могут быть абсолютными (начинаются с /, указывают на фиксированный путь в файловой системе) или относительными (указывают относительно директории, в которой находится ссылка).
Относительные ссылки предпочтительны, так как сохраняют работоспособность при перемещении дерева.
#Java #для_новичков #beginner #IO #NIO #SeekableByteChannel
Класс
java.io.RandomAccessFile существует с Java 1.0 и предоставляет аналогичный функционал. Однако у него есть архитектурные недостатки:
Не работает с Path:
RandomAccessFile принимает только String или File, не Path. Для использования с NIO.2 требуется конвертация.Не поддерживает символические ссылки корректно:
RandomAccessFile всегда следует по ссылкам, нет опции NOFOLLOW_LINKS.Не интегрирован с NIO:
RandomAccessFile работает со своими методами read(), write(), не с ByteBuffer. Хотя с Java 1.4 у него появился метод getChannel(), возвращающий FileChannel.Менее гибкая модель опций:
RandomAccessFile имеет только два режима — "r" (read) и "rw" (read-write). Нет поддержки APPEND, CREATE_NEW, SYNC и других флагов.Не поддерживает кастомные FileSystem:
RandomAccessFile работает только с дефолтной файловой системой. SeekableByteChannel через FileSystemProvider может работать с ZIP, сетевыми ФС и т.д.// Старый способ (RandomAccessFile)
try (RandomAccessFile raf = new RandomAccessFile("data.bin", "rw")) {
raf.seek(100); // позиционирование
raf.write("data".getBytes());
}
// Новый способ (SeekableByteChannel)
try (SeekableByteChannel channel = Files.newByteChannel(Paths.get("data.bin"),
StandardOpenOption.READ, StandardOpenOption.WRITE)) {
channel.position(100);
channel.write(ByteBuffer.wrap("data".getBytes()));
}
FileChannel — это конкретная реализация
SeekableByteChannel в Java, работающая с файлами локальной файловой системы. Он предоставляет дополнительные методы: map() (memory-mapped files), transferTo()/transferFrom() (zero-copy), lock() (файловые блокировки), force() (синхронизация с диском).Часть 2. Символические и жёсткие ссылки
В файловых системах Unix имя файла — это не сам файл, а ссылка на структуру данных, хранящую метаданные и указатели на блоки. Эта структура называется inode. Директория — это просто список пар "имя — номер inode". Ссылка — это запись в этом списке.
inode (index node) — структура данных в файловой системе Unix, содержащая всё, кроме имени файла и его содержимого: тип файла, права доступа, владелец, группа, размер, временные метки, счётчик ссылок и указатели на блоки данных. Номер inode уникален в пределах файловой системы (раздела диска).
Жёсткая ссылка (hard link)
Жёсткая ссылка — это дополнительное имя для существующего файла, указывающее на тот же inode.
Все жёсткие ссылки на файл равноправны: нет "оригинала" и "копий". Каждая жёсткая ссылка увеличивает счётчик ссылок (link count) в inode на 1.
Когда файл удаляется через
unlink() (системный вызов, лежащий в основе Files.delete()), ядро уменьшает счётчик ссылок. Если счётчик достигает 0 и нет открытых файловых дескрипторов на этот inode, блоки данных помечаются как свободные. Если счётчик 0, но дескрипторы открыты, файл становится "невидимым" в файловой системе, но продолжает существовать до закрытия всех дескрипторов.Ограничения жёстких ссылок:
Нельзя создать на директорию (за исключением системных
. и ..). Это предотвращает циклы в файловой системе.Нельзя создать между разными файловыми системами (разделами диска), так как inode уникален только в пределах одной ФС.
Нельзя создать на несуществующий файл.
import java.nio.file.*;
import java.io.IOException;
public class HardLinkManager {
public void createHardLink(Path existingFile, Path newLink) throws IOException {
// Проверяем, что исходный файл существует и на той же файловой системе
if (!Files.isRegularFile(existingFile)) {
throw new IllegalArgumentException("Исходный файл не существует или не является обычным файлом");
}
// createLink создаёт жёсткую ссылку
Files.createLink(newLink, existingFile);
// Проверяем: оба пути указывают на один inode
Object key1 = Files.readAttributes(existingFile, BasicFileAttributes.class).fileKey();
Object key2 = Files.readAttributes(newLink, BasicFileAttributes.class).fileKey();
if (key1 != null && key1.equals(key2)) {
System.out.println("Жёсткая ссылка создана. Оба пути указывают на inode " + key1);
}
}
}
Files.createLink(Path link, Path existing) выполняет системный вызов link() (POSIX), который создаёт новую запись в директории, указывающую на тот же inode, что и existing. Это атомарная операция на уровне файловой системы.Символическая ссылка (symbolic link, symlink)
Символическая ссылка — это специальный файл, содержащий путь (строку) к другому файлу или директории. В отличие от жёсткой ссылки, символическая ссылка имеет свой собственный inode и не влияет на счётчик ссылок целевого файла. Она может указывать на несуществующий объект (висячая ссылка, dangling symlink), на директорию, на файл в другой файловой системе или даже на саму себя.
Висячая ссылка (dangling symlink) — символическая ссылка, целевой объект которой был удалён или перемещён. При попытке доступа к такой ссылке системный вызов вернёт ENOENT (No such file or directory).
Символические ссылки могут быть абсолютными (начинаются с /, указывают на фиксированный путь в файловой системе) или относительными (указывают относительно директории, в которой находится ссылка).
Относительные ссылки предпочтительны, так как сохраняют работоспособность при перемещении дерева.
public class SymlinkManager {
public void createSymlink(Path link, Path target) throws IOException {
// Создаём символическую ссылку
// target может быть абсолютным или относительным
Files.createSymbolicLink(link, target);
}
public void createRelativeSymlink(Path linkDir, Path targetFile) throws IOException {
// Строим относительный путь от linkDir до targetFile
Path relativeTarget = linkDir.relativize(targetFile);
Path link = linkDir.resolve("shortcut");
Files.createSymbolicLink(link, relativeTarget);
}
public void inspectSymlink(Path link) throws IOException {
if (!Files.isSymbolicLink(link)) {
System.out.println("Это не символическая ссылка");
return;
}
// Читаем целевой путь, хранящийся в ссылке
Path target = Files.readSymbolicLink(link);
System.out.println("Ссылка указывает на: " + target);
// Проверяем, существует ли целевой объект
if (Files.exists(link)) {
System.out.println("Целевой объект существует");
// Читаем атрибуты целевого объекта (следуем по ссылке)
BasicFileAttributes attrs = Files.readAttributes(link, BasicFileAttributes.class);
System.out.println("Тип цели: " + (attrs.isDirectory() ? "директория" : "файл"));
} else {
System.out.println("Висячая ссылка — цель не существует");
}
// Читаем атрибуты самой ссылки (не следуем по ней)
BasicFileAttributes linkAttrs = Files.readAttributes(
link, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS
);
System.out.println("Размер самой ссылки: " + linkAttrs.size() + " байт (длина пути)");
}
}Files.readSymbolicLink(Path link) возвращает Path, содержащий строку, записанную в символической ссылке. Это не резолвит путь — просто читает содержимое ссылки. Размер символической ссылки (возвращаемый Files.size()) равен длине строки пути в байтах, а не размеру целевого файла.#Java #для_новичков #beginner #IO #NIO #SeekableByteChannel
👍4