Перейти к основному содержимому

Ввод-вывод и файлы в Java

Разработчику Архитектору

Ввод-вывод и файлы

Ввод-вывод (I/O) в Java строится на потоках: последовательная передача байтов или символов между программой и источником (файл, сеть, консоль). Современный API файловой системы — NIO.2 (java.nio.file, с Java 7).

Исключения при I/O: обработка исключений. Строки и кодировки: строки.


Два уровня API

УровеньПакетыНазначение
Классический I/Ojava.ioInputStream, OutputStream, Reader, Writer
NIO / NIO.2java.nio, java.nio.fileБуферы, каналы, Path, Files

В новом коде для файлов на диске начинайте с Path + Files, для сети и бинарных протоколов — потоки или NIO channels.


Path и Files (NIO.2)

Path — неизменяемый путь (замена устаревшему java.io.File для логики путей):

Path config = Path.of("app", "config", "settings.properties");
Path absolute = config.toAbsolutePath().normalize();

boolean exists = Files.exists(config);
boolean isDir = Files.isDirectory(config);
long size = Files.size(config);

Чтение и запись целиком (малые и средние файлы):

Path file = Path.of("notes.txt");

List<String> lines = Files.readAllLines(file, StandardCharsets.UTF_8);
String text = Files.readString(file, StandardCharsets.UTF_8);

Files.writeString(file, "новая строка\n", StandardCharsets.UTF_8,
StandardOpenOption.CREATE, StandardOpenOption.APPEND);

Поток строк (большие файлы, ленивая обработка):

try (Stream<String> lines = Files.lines(file, StandardCharsets.UTF_8)) {
long count = lines.filter(l -> !l.isBlank()).count();
}

Копирование и перемещение:

Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.move(oldPath, newPath);
Files.delete(path);
Files.createDirectories(Path.of("data", "cache"));

Байтовые и символьные потоки

ТипКлассыЕдиница
БайтовыйInputStream, OutputStreambyte
СимвольныйReader, Writerchar (кодировка)

Символьные потоки оборачивают байтовые с указанием charset:

try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);
Writer writer = Files.newBufferedWriter(out, StandardCharsets.UTF_8)) {
char[] buf = new char[8192];
int n;
while ((n = reader.read(buf)) != -1) {
writer.write(buf, 0, n);
}
}

Буферизация (BufferedInputStream, BufferedReader) снижает число системных вызовов.


try-with-resources

Ресурсы, реализующие AutoCloseable, закрываются автоматически:

try (InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(target)) {
in.transferTo(out); // Java 9+
}

При исключении в блоке сначала выбрасывается основное, затем подавленное из close() — удобно для диагностики.

Связь с try-with-resources для исключений.


Сериализация объектов (кратко)

ObjectOutputStream / ObjectInputStream сохраняют граф объектов в бинарный поток. Класс должен реализовать Serializable (или Externalizable).

В современных системах чаще используют JSON/XML/Protobuf (см. стандартные библиотеки), а не Java Serialization — из соображений безопасности и совместимости версий.


Работа с каталогами

try (Stream<Path> walk = Files.walk(Path.of("logs"), 2)) {
walk.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".log"))
.forEach(System.out::println);
}

Files.list(dir) — только один уровень; walk — рекурсия с лимитом глубины.


Типичные ошибки

  1. Не указана кодировкаFileReader без charset использует платформенную; на Windows часто не UTF-8.
  2. Утечка дескрипторов — поток не закрыт (нет try-with-resources).
  3. Чтение гигантского файла в readAllLines — риск OutOfMemoryError; используйте Files.lines или буфер.
  4. Относительные пути — зависят от user.dir; для конфигов лучше явный базовый каталог или переменные окружения.
  5. Пути с пробеламиPath.of корректен; не склеивайте строки вручную через \.

I/O и многопоточность

Блокирующее чтение в одном потоке занимает поток ОС на всё время ожидания диска или сети. Для множества одновременных соединений смотрите асинхронность и NIO (Selector, неблокирующие каналы) или фреймворки уровня Spring WebFlux.


Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).