1. Класс OutputStream
С потоками ввода мы только что разобрались. Настало время поговорить о потоках вывода.
Класс OutputStream
является классом-родителем для всех классов, которые поддерживают байтовый вывод. Это абстрактный класс, который сам ничего не делает: для этого у него есть классы-наследники на все случаи жизни.
Сложновато звучит. Если попроще, этот класс оперирует байтами, а не, например, символами или другими типами данных. А то, что он абстрактный, значит, что мы обычно используем не его, а один из его классов-наследников. Например, FileOutputStream
и ему подобные.
Но вернемся к классу OutputStream
. У этого класса есть методы, которые обязаны реализовывать все его классы-наследники. Вот основные из них:
Методы | Описание |
---|---|
|
Записывает один байт (не int ) в поток. |
|
Записывает массив байт в поток |
|
Записывает часть массива байт в поток |
|
Записывает в поток все данные, которые хранятся в буфере |
|
Закрывает поток |
При создании объекта класса-наследника InputStream
обычно указывается объект-источник, из которого InputStream
читает данные. При создании объекта класса-наследника OutputStream
также обычно указывается целевой объект или целевой поток, в который будут записываться данные.
Вкратце пройдемся по всем методам класса OutputStream
:
Метод write(int b)
Этот метод записывает в поток вывода один байт (не int
). Переданное значение приводится к типу байт, три первые байта отбрасываются.
Метод write(byte[] buffer)
Записывает в поток вывода переданный массив байтов. Все.
Метод write(byte[] buffer, int offset, int length)
Записывает в поток вывода часть переданного массива байтов. Переменная offset задает номер первого элемента массива, length
— длина записываемого фрагмента.
Метод flush()
Метод flush()
используется, чтобы принудительно записать в целевой поток данные, которые могут кэшироваться в текущем потоке. Актуально при использовании буферизации и/или нескольких объектах потоков, организованных в цепочку.
Метод close()
Закрывает поток и освобождает все системные ресурсы, связанные с этим потоком. Метод close()
можно не вызывать, если вы используете try-with-resources
.
Пример — копирование файла
Код | Примечание |
---|---|
|
InputStream для чтения из файлаOutputStream для записи в файлБуфер, в который мы будем считывать данные Пока данные есть в потоке Считываем данные в буфер Записываем данные из буфера во второй поток |
2. Класс Writer
Класс Writer
— это полный аналог класса OutputStream
, и снова только с одним отличием: он работает с символами, char
, вместо байт.
Это абстрактный класс: объекты класса Writer
создать нельзя. Его основная цель — быть единым классом-родителем для сотен классов-наследников и задать для них общие методы работы с символьными потоками.
Методы класса Writer
(и всех его классов-наследников):
Методы | Описание |
---|---|
|
Записывает один символ (не int ) в поток. |
|
Записывает массив символов в поток |
|
Записывает часть массива символов в поток |
|
Записывает строку в поток |
|
Записывает часть строки в поток |
|
Записывает в поток все данные, которые хранятся в буфере |
|
Закрывает поток |
Методы очень похожи на методы класса OutputStream
, только работают с символами вместо байт.
Краткое описание методов:
Метод write(int b)
Этот метод записывает в поток вывода один символ char
(не int
). Переданное значение приводится к типу char
, два первых байта отбрасываются.
Метод write(char[] buffer)
Записывает в поток вывода переданный массив символов.
Метод write(char[] buffer, int offset, int length)
Записывает в поток вывода часть переданного массива символов. Переменная offset
задает номер первого элемента массива, length
— длина записываемого фрагмента.
Метод write(String str)
Записывает в поток вывода переданную строку.
Метод write(String str, int offset, int length)
Записывает в поток вывода часть переданной строки: строку преобразуют в массив символов. Переменная offset
задает номер первого элемента массива, length
— длина записываемого фрагмента.
Метод flush()
Метод flush()
используется, чтобы принудительно записать в целевой поток данные, которые могут кэшироваться в текущем потоке. Актуально при использовании буферизации и/или нескольких объектах потоков, организованных в цепочку.
Метод close()
Закрывает поток и освобождает все системные ресурсы, связанные с этим потоком. Метод close()
можно не вызывать, если вы используете try-with-resources
.
Пример программы, которая копирует текстовый файл:
Код | Примечание |
---|---|
|
Reader для чтения из файлаWriter для записи в файлБуфер, в который будем считывать данные Пока данные есть в потоке Читаем данные в буфер Записываем данные из буфера во второй поток |
Класс StringWriter
Есть еще один интересный класс-наследник от класса Writer
— это StringWriter
. В нем находится изменяемая строка — объект StringBuffer
. И каждый раз, когда вы что-то «пишете» в объект StringWriter
, текст просто добавляется во внутренний буфер.
Пример:
Код | Примечание |
---|---|
|
Создается целевой символьный поток StringWriter Строка пишется в буфер внутри StringWriter Строка пишется в буфер внутри StringWriter Преобразовываем содержимое объекта к строке |
В данном случае класс StringWriter
— это, по сути, обертка над классом StringBuffer
, однако класс StringWriter
— это наследник класса-потока Writer
, и он может использоваться в цепочках из объектов-потоков. Довольно полезное свойство на практике.
3. Класс PrintStream
Классы потокового вывода тоже можно организовывать в цепочки с использованием потоков-посредников, которые записывают данные в переданный им целевой поток. Общая картинка взаимодействия этих потоков выглядит так:
Самый интересный и многофункциональный из всех промежуточных потоков вывода — PrintStream
. У него несколько десятков методов и аж целых 12 конструкторов.
Класс PrintStream
унаследован от класса FilterOutputStream
, а тот унаследован от OutputStream
. Поэтому класс PrintStream
имеет все методы классов-родителей и плюс свои. Вот самые интересные из них:
Методы | Описание |
---|---|
|
Преобразует переданной объект в строку и выводит в целевой поток. |
|
Преобразует переданный объект в строку и выводит в целевой поток. Добавляет в конце символ переноса строки |
|
Выводит в целевой поток символ переноса строки |
|
Конструирует и выводит строку на основе строки шаблона и переданных аргументов, по аналогии с методом String.format() |
А где же несколько десятков методов, спросите вы?
Все дело в том, что у него много вариантов метода print()
и println()
с разными аргументами. Их вполне можно свести к этой таблице.
Мы даже не будем разбирать эти методы, т.к. вы их и так уже хорошо знаете. Догадываетесь, к чему я клоню?
Помните команду System.out.println()
? А ведь ее можно записать в две строки:
Код | Вывод на экран |
---|---|
|
|
Наша любимая команда System.out.println()
— это вызов метода println()
у статической переменной out
класса System
. А тип у этой переменной — PrintStream
.
Уже много уровней вы почти в каждой задаче вызываете методы класса PrintStream
и даже не догадываетесь об этом!
Практическое использование
Есть в Java один интересный класс — ByteArrayOutputStream
, который представляет из себя динамически увеличивающийся массив байт, унаследованный от OutputStream
.
Объект ByteArrayOutputStream
и объект PrintStream
можно выстроить в такую цепочку:
Код | Описание |
---|---|
|
Создали в памяти буфер для записи Обернули буфер в объект PrintStream Записывает данные как в консоль Преобразовываем массив в строку! Вывод на экран:
|
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ