Потоки ввода-вывода (I/O streams) — одна из базовых концепций Java, появившаяся с ранних версий языка. Эта концепция изначально была разработана с целью абстрагирования процессов чтения и записи данных: поток может быть связан с файлом, сетью или даже консолью — для кода это выглядит одинаково.
Проблема возникает, когда разработчик неправильно управляет потоками, забывает закрывать их или путает разные типы потоков (символьные и байтовые), что часто приводит к утечкам ресурсов, искажению данных или ошибкам на этапе выполнения.
Решение — грамотное понимание и применение иерархии потоков ввода-вывода (InputStream/OutputStream для байтов, Reader/Writer для символов), а также обязательное закрытие потоков после завершения работы, предпочтительно через try-with-resources начиная с Java 7.
Пример кода чтения и записи файла:
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt")); BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) { String line; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } }
Ключевые особенности:
Что произойдёт, если не закрыть поток ввода-вывода в Java?
Потоки занимаются низкоуровневыми ресурсами ОС, и незакрытие потока может привести к утечкам памяти, блокировке файлов, сбоям в работе приложения или даже исчерпанию дескрипторов файлов на уровне ОС.
Можно ли использовать один и тот же OutputStream для записи в разные файлы?
Нет, классический OutputStream жестко ассоциирован с одним источником/приёмником данных. Для разных файлов — разные объекты OutputStream.
В чем разница между PrintWriter и BufferedWriter? Когда какой использовать?
PrintWriter специализируется на дополнении вывода форматированными методами печати, например println(), и умеет работать с автосбросом. BufferedWriter же преимущественно увеличивает производительность за счёт буферизации. В большинстве прикладных задач PrintWriter предпочтительнее для текстового вывода, но если требуется просто быстро писать символы или строки без форматирования — BufferedWriter подойдет лучше.
Разработчик читает данные из большого файла при помощи FileInputStream, не использует буферизацию и забывает закрыть поток.
Плюсы:
Минусы:
Используется связка BufferedReader с try-with-resources, чтение и обработка идут пакетами по строкам, поток автоматически закрывается.
Плюсы:
Минусы: