I/O ·
‹ Предыдущий Следующий ›
⏱ 5 минут чтения Обновлено: 2026-10-31

Потоки ввода-вывода в Java (InputStream, OutputStream). Практические Задания

Попробуйте записать в файл букву 'я' через FileOutputStream и прочитать её обратно — вместо неё вы получите латинскую O, и никакого исключения не будет:

try (OutputStream out = new FileOutputStream("a.txt")) {
    out.write('я');                        // write(int) берёт только младший байт: 0x044F -> 0x4F
}
try (InputStream in = new FileInputStream("a.txt")) {
    System.out.println((char) in.read());  // Вывод: O
}

Задания по потокам ввода-вывода в Java закрепляют модуль «Java I/O»: вы перепишете копирование файлов на try-with-resources, скопируете текстовый файл построчно через BufferedReader и BufferedWriter, рекурсивно обойдёте каталог с помощью класса File и запишете отчёты в файл через PrintWriter в двух сквозных проектах — «Отчёт о сотрудниках» и «Интернет-магазин».

Что повторить перед решением

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

Урок Что понадобится в задачах
Типы байтовых потоков FileInputStream и FileOutputStream, чтение до -1
Конструкция try-with-resources Автоматическое закрытие одного и нескольких потоков вместо finally
Классы символьных потоков BufferedReader, BufferedWriter, FileReader, FileWriter, PrintWriter
Использование класса File listFiles(), isDirectory() для рекурсивного обхода каталога
Использование потоков данных Общая схема: открыть поток, читать или писать, закрыть

Класс FileCopy

Перепишите класс FileCopy, который копирует файл побайтно:

package io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy {
    public static void main(String[] args) throws IOException {
        FileInputStream fileIn = null;
        FileOutputStream fileOut = null;

        try {
            fileIn = new FileInputStream("src/io/file.txt");
            fileOut = new FileOutputStream("src/io/copied_file.txt");

            int a;
            while ((a = fileIn.read()) != -1) {
                fileOut.write(a);
            }
        } finally {
            if (fileIn != null) {
                fileIn.close();
            }
            if (fileOut != null) {
                fileOut.close();
            }
        }
    }
}
  1. Замените ручное закрытие потоков в блоке finally на try-with-resources.
  2. Добавьте блок catch для обработки IOException и уберите throws IOException из сигнатуры метода main.

Подсказка

Оба потока объявите в одних скобках через точку с запятой: try (FileInputStream fileIn = new FileInputStream(...); FileOutputStream fileOut = new FileOutputStream(...)). Они закроются автоматически в обратном порядке, даже если при копировании возникнет исключение, поэтому проверки на null и блок finally больше не нужны.

Класс FileInputOutputStreamDemo

Перепишите класс FileInputOutputStreamDemo:

package io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class FileInputOutputStreamDemo {
    public static void main(String[] args) {
        try {
            char[] symbols = {'a', 'b', 'c'};
            OutputStream output = new FileOutputStream("a.txt");
            for (int i = 0; i < symbols.length; i++) {
                // Запись каждого символа в текстовый файл
                output.write(symbols[i]);
            }
            output.close();

            InputStream input = new FileInputStream("a.txt");
            int size = input.available();

            for (int i = 0; i < size; i++) {
                // Чтение текстового файла посимвольно
                System.out.print((char) input.read() + " ");
            }
            input.close();
        } catch (IOException e) {
            System.out.print("Exception");
        }
    }
}
  1. Перепишите код с использованием try-with-resources для OutputStream и InputStream.
  2. Замените цикл записи for (int i = 0; i < symbols.length; i++) на цикл for-each.
  3. Записывайте файл a.txt в каталог src/io: путь к файлу — "src/io/a.txt".

Ожидаемый вывод программы: a b c

Неочевидный момент

Удобнее записать и прочитать файл в двух последовательных блоках try-with-resources: тогда поток записи гарантированно закрыт до начала чтения. Цикл чтения на for-each не переводится, а available() не обязан возвращать полный объём данных — надёжнее читать до конца потока: while ((b = input.read()) != -1).

Копирование файла в другой файл

  1. Напишите класс, который построчно копирует содержимое одного текстового файла в другой.
  2. Для чтения используйте BufferedReader поверх FileReader, для записи — BufferedWriter поверх FileWriter.
  3. Закрывайте потоки с помощью try-with-resources.

Нюанс с readLine()

readLine() возвращает строку без символа перевода строки, а в конце файла — null. После каждой записанной строки вызывайте newLine(), иначе весь текст склеится в одну строку. Чтобы кириллица не исказилась, укажите кодировку явно: new FileReader(file, StandardCharsets.UTF_8) (Java 11+).

Запись отчёта о сотрудниках в файл

Отчёт о сотрудниках · часть 4 из 4 · ← часть 3

  1. Добавьте в класс Employee метод, который записывает отчёт о сотрудниках в файл.
  2. Используйте класс PrintWriter.

С чего начать

У PrintWriter есть те же методы print(), println() и printf(), что и у System.out: если отчёт уже выводится на консоль, достаточно направить вывод в файл. Методы записи PrintWriter не выбрасывают IOException — после записи проверьте checkError().

Рекурсивный вывод содержимого каталога

  1. Напишите метод, который рекурсивно обходит заданный каталог и выводит информацию обо всех файлах и каталогах, находящихся в нём и во всех его подкаталогах.
  2. Используйте класс File и рекурсию.

Подсказка

Передавайте в метод File и уровень вложенности: для каждого элемента listFiles(), который является каталогом, вызывайте тот же метод с уровнем на единицу больше, а отступ в выводе стройте по уровню. listFiles() возвращает null, если путь не является каталогом или к нему нет доступа, — без проверки получите NullPointerException.

Запись чека в файл через PrintWriter

Интернет-магазин · часть 8 из 13 · ← часть 7 · часть 9 →

Часть 1, часть 2, часть 3, часть 4, часть 5, часть 6, часть 7.

  1. После оформления покупки запишите в файл отчёт о содержимом корзины.
  2. Для записи и форматирования отчёта используйте класс PrintWriter.

Как оформить отчёт

Откройте PrintWriter в try-with-resources, чтобы содержимое буфера гарантированно попало в файл. Каждую позицию корзины выводите через printf() с фиксированной шириной колонок, например "%-20s %5d %10.2f%n", а в конце — итоговую сумму.

На чём чаще всего ошибаются

Чаще всего решения по вводу-выводу ломаются молча: программа отрабатывает без исключений, но файл оказывается пустым, обрезанным или с искажённой кириллицей. Причина почти всегда в неверно выбранном потоке, незакрытом буфере или кодировке по умолчанию. Ниже — типичные промахи и способы их исправить.

Ошибка Что произойдёт Как исправить
outputStream.write('я') Записывается только младший байт, при чтении получается O Для текста используйте Writer с явной кодировкой: new FileWriter(file, StandardCharsets.UTF_8)
Размер файла через input.available() Метод не обязан возвращать весь объём данных; для сетевых потоков часто возвращает 0 Читайте до конца потока: while ((b = in.read()) != -1)
BufferedWriter или PrintWriter без close() Данные остаются в буфере, файл пустой или обрезан Открывайте поток в try-with-resources
Закрытие нескольких потоков подряд в finally Если первый close() выбросит IOException, второй поток не закроется Объявите оба потока в одном try-with-resources
Нет проверки результата PrintWriter Ошибка записи (например, переполнен диск) не выбрасывает IOException — отчёт теряется молча После записи проверьте checkError()
for (File f : dir.listFiles()) без проверки NullPointerException, если путь не каталог или к нему нет доступа Проверьте результат listFiles() на null
new FileWriter("report.txt") без кодировки До Java 17 включительно используется кодировка платформы (в русской Windows — windows-1251), с Java 18 (JEP 400) — UTF-8; файл открывается по-разному на разных машинах Указывайте StandardCharsets.UTF_8 явно (конструкторы с Charset есть с Java 11)

Проверь свой код

Перед отправкой решения сверьтесь с рекомендациями по оформлению кода: переменные потоков называйте по смыслу (reader, writer, а не a и b), не оставляйте пустых блоков catch и не объявляйте throws IOException у main, если по условию исключение нужно обработать.

Часто задаваемые вопросы

Какие потоки выбрать для копирования файла: байтовые или символьные?

Байтовые (InputStream/OutputStream) копируют любой файл — картинку, архив, PDF — без изменений. Символьные (Reader/Writer) нужны, когда файл обрабатывается как текст: построчно, с учётом кодировки. Двоичный файл через Reader копировать нельзя — байты будут испорчены декодированием. Подробнее — в уроках Типы байтовых потоков и Классы символьных потоков.

Нужен ли блок finally, если используется try-with-resources?

Для закрытия потоков — нет: ресурсы закрываются автоматически в порядке, обратном объявлению, даже если в блоке try возникло исключение. Если при этом close() тоже выбросит исключение, оно не потеряется, а будет добавлено к основному и доступно через e.getSuppressed(). finally оставляют только для действий, не связанных с закрытием ресурсов.

Как скопировать файл в современной Java без ручного цикла?

Одной строкой: Files.copy(Path.of("src/io/file.txt"), Path.of("src/io/copy.txt"), StandardCopyOption.REPLACE_EXISTING). Files.copy появился в Java 7, Path.of — в Java 11. В задачах страницы цикл пишется вручную намеренно: так видно, как работают read(), буферы и закрытие потоков.

Почему программа не находит файл или создаёт его не там, где ожидалось?

Относительный путь вроде "src/io/a.txt" отсчитывается от рабочего каталога процесса, а не от папки с классом. Узнать его можно через System.getProperty("user.dir"); в IntelliJ IDEA по умолчанию это корень проекта. Если каталога src/io нет, FileOutputStream выбросит FileNotFoundException — сам каталог он не создаёт.

Комментарии

unknown Feb 1, 2023
Решение к Задаче №2 https://www.online-java.com/aA8tU4hE16
unknown Feb 1, 2023
Решение к Задаче №3, не уверен, что такое, но в целом очень даже подходит https://www.online-java.com/GLmAlNDUjM

Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.