1. Обзор
В этом руководстве мы подробно рассмотрим Java-класс OutputStream
. Выходной
поток
— это абстрактный класс. Он служит суперклассом для всех классов, представляющих выходной поток байтов.
Мы рассмотрим, что означают такие слова, как «выход» и «поток», более подробно по ходу дела.
2. Краткое введение в Java IO
OutputStream является частью Java IO API , который определяет классы, необходимые для выполнения операций ввода-вывода в Java. Все они упакованы в пространство имен java.io.
Это один из основных пакетов, доступных в Java, начиная с версии 1.0.
Начиная с Java 1.4, у нас также есть Java NIO, упакованный в пространство имен java.nio
, что позволяет выполнять неблокирующие операции ввода и вывода. Однако в этой статье мы сосредоточимся на ObjectStream
как части Java IO.
Подробности, связанные с Java IO и Java NIO, можно найти здесь .
2.1. Ввод и вывод
Java IO в основном предоставляет механизм для чтения данных из источника и записи данных в место назначения . Вход представляет собой источник, а выход представляет здесь пункт назначения.
Эти источники и места назначения могут быть любыми: от файлов, каналов до сетевых подключений.
2.2. Потоки
Java IO предоставляет концепцию потоков, которая в основном представляет собой непрерывный поток данных . Потоки могут поддерживать множество различных типов данных, таких как байты, символы, объекты и т. д.
Более того, подключение к источнику или месту назначения — это то, что представляет собой поток. Следовательно, они поступают либо как InputStream
, либо как OutputStream
соответственно.
3. Интерфейсы OutputStream
OutputStream
реализует набор интерфейсов, которые придают своим подклассам определенный характер. Давайте быстро пройдемся по ним.
3.1. Закрываемый
Интерфейс Closeable
предоставляет метод close()
, который обрабатывает закрытие источника или назначения данных. Каждая реализация OutputStream
должна предоставлять реализацию этого метода. Здесь они могут выполнять действия по высвобождению ресурсов.
3.2. AutoCloseable
Интерфейс AutoCloseable
также предоставляет метод close()
с поведением, сходным с таковым в Closeable
. Однако в этом случае метод close()
вызывается автоматически при выходе из блока try-with-resource.
Подробнее о try-with-resource можно прочитать здесь .
3.3. Смываемый
Интерфейс Flushable
предоставляет метод, называемый flush()
, который обрабатывает сброс данных в пункт назначения.
Конкретная реализация OutputStream
может выбрать буферизацию ранее записанных байтов для оптимизации, но вызов flush()
заставляет ее немедленно записать в место назначения .
4. Методы в OutputStream
OutputStream
имеет несколько методов, которые каждый реализующий класс должен реализовать для соответствующих типов данных.
Это помимо методов close()
и flush()
, которые он наследует от интерфейсов Closeable
и Flushable
.
4.1. написать (инт б)
Мы можем использовать этот метод для записи одного определенного байта в OutputStream
. Поскольку аргумент «int» состоит из четырех байтов, по контракту записывается только первый младший байт, а оставшиеся три старших байта игнорируются:
public static void fileOutputStreamByteSingle(String file, String data) throws IOException {
byte[] bytes = data.getBytes();
try (OutputStream out = new FileOutputStream(file)) {
out.write(bytes[6]);
}
}
Если мы вызовем этот метод с данными как «Hello World!», в результате мы получим файл со следующим текстом:
W
Это, как мы видим, седьмой символ строки с индексом sixth.
4.2. запись (byte[] b, int off, int length)
Эта перегруженная версия метода write()
предназначена для записи подпоследовательности массива байтов в OutputStream
.
Он может записывать число байтов «length» из массива байтов, как указано аргументом, начиная со смещения, определяемого «off», в OutputStream:
public static void fileOutputStreamByteSubSequence(
String file, String data) throws IOException {
byte[] bytes = data.getBytes();
try (OutputStream out = new FileOutputStream(file)) {
out.write(bytes, 6, 5);
}
}
Если мы теперь вызовем этот метод с теми же данными, что и раньше, мы получим следующий текст в нашем выходном файле:
World
Это подстрока наших данных, начинающаяся с индекса пять и состоящая из пяти символов.
4.3. написать (байт [] б)
Это еще одна перегруженная версия метода write()
, которая может записывать весь массив байтов, как указано аргументом в OutputStream
.
Это имеет тот же эффект, что и вызов write(b, 0, b.lengh)
:
public static void fileOutputStreamByteSequence(String file, String data) throws IOException {
byte[] bytes = data.getBytes();
try (OutputStream out = new FileOutputStream(file)) {
out.write(bytes);
}
}
Когда мы вызываем этот метод сейчас с теми же данными, у нас есть вся строка
в нашем выходном файле:
Hello World!
5. Прямые подклассы OutputStream
Теперь мы обсудим некоторые из прямых известных подклассов OutputStream
, которые индивидуально представляют определенный тип данных, для которого OutputStream
они определяют.
Они определяют свои собственные методы, кроме реализации унаследованных от OutputStream
.
Мы не будем вдаваться в подробности этих подклассов.
5.1. FileOutputStream
Как следует из названия, FileOutputStream
— это OutputStream
для записи данных в File
. FileOutputStream
, как и любой другой OutputStream
, может записывать поток необработанных байтов.
Мы уже рассмотрели различные методы в FileOutputStream
в предыдущем разделе.
5.2. ByteArrayOutputStream
ByteArrayOutputStream
— это реализация OutputStream
, которая может записывать данные в массив байтов . Буфер продолжает расти по мере того, как ByteArrayOutputStream
записывает в него данные.
Мы можем оставить начальный размер буфера по умолчанию равным 32 байтам или задать конкретный размер с помощью одного из доступных конструкторов.
Здесь важно отметить, что метод close()
практически не действует. Другие методы в ByteArrayOutputStream
можно безопасно вызывать даже после вызова функции close()
.
5.3. ФильтрВыходнойПоток
OutputStream в первую очередь записывает поток байтов в место назначения, но он также может преобразовывать данные перед этим. FilterOutputStream
представляет собой надкласс всех таких классов, которые выполняют определенное преобразование данных . FilterOutputStream
всегда создается с существующим OutputStream
.
Некоторыми примерами FilterOutputStream
являются BufferedOutputStream
, CheckedOutputStream
, CipherOutputStream
, DataOutputStream
, DeflaterOutputStream
, DigestOutputStream
, InflaterOutputStream
, PrintStream
.
5.4. ObjectOutputStream
ObjectOutputStream
может записывать примитивные типы данных и графики объектов Java в место назначения. Мы можем создать ObjectOutputStream
, используя существующий OutputStream
для записи в определенное место назначения, например в файл.
Обратите внимание, что объекты должны реализовывать Serializable
для ObjectOutputStream
, чтобы записывать их в место назначения. Вы можете найти более подробную информацию о сериализации Java здесь .
5.5. PipedOutputStream
PipedOutputStream полезен
для создания коммуникационного канала . PipedOutputStream
может записывать данные, которые может читать подключенный PipedInputStream .
PipedOutputStream
имеет конструктор для соединения с PipedInputStream
. В качестве альтернативы, мы можем сделать это позже, используя метод connect()
, предоставленный в PipedOutputStream
. ``
6. Буферизация выходного
потока
Операции ввода и вывода обычно связаны с относительно дорогостоящими операциями, такими как доступ к диску, сетевая активность и т. д. Выполнение этого часто может сделать программу менее эффективной.
У нас есть «буферизованные потоки» данных в Java для обработки этих сценариев. Вместо этого BufferedOutputStream
записывает данные в буфер, который реже сбрасывается в место назначения , когда буфер заполняется или вызывается метод flush()
.
BufferedOutputStream
расширяет рассмотренный ранее FilterOutputStream
и оборачивает существующий OutputStream
для записи в пункт назначения:
public static void bufferedOutputStream(
String file, String ...data) throws IOException {
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
for(String s : data) {
out.write(s.getBytes());
out.write(" ".getBytes());
}
}
}
Важно отметить, что каждый вызов write()
для каждого аргумента данных только записывает в буфер и не приводит к потенциально дорогостоящему вызову File.
В приведенном выше случае, если мы вызовем этот метод с данными как «Hello», «World!», это приведет к тому, что данные будут записаны в файл только тогда, когда код выйдет из блока try-with-resources, который вызывает метод close ()
в BufferedOutputStream
.
В результате получается выходной файл со следующим текстом:
Hello World!
7. Написание текста с помощью OutputStreamWriter
Поток байтов, как обсуждалось ранее, представляет необработанные данные, которые могут быть набором текстовых символов. Теперь мы можем получить массив символов и сами выполнить преобразование в массив байтов:
byte[] bytes = data.getBytes();
Java предоставляет удобные классы для преодоления этого разрыва. В случае OutputStream
этим классом является OutputStreamWriter
. OutputStreamWriter
оборачивает OutputStream
и может напрямую записывать символы в нужное место назначения .
Мы также можем дополнительно предоставить OutputStreamWriter
набор символов для кодирования:
public static void outputStreamWriter(String file, String data) throws IOException {
try (OutputStream out = new FileOutputStream(file);
Writer writer = new OutputStreamWriter(out,"UTF-8")) {
writer.write(data);
}
}
Теперь, как мы видим, нам не нужно выполнять преобразование массива символов в массив байтов перед использованием FileOutputStream.
OutputStreamWriter
делает это удобно для нас .
Неудивительно, что когда мы вызываем вышеуказанный метод с такими данными, как «Hello World!», это приводит к файлу с текстом в виде:
Hello World!
8. Заключение
В этой статье мы обсудили абстрактный класс Java OutputStream
. Мы рассмотрели интерфейсы, которые он реализует, и методы, которые он предоставляет.
Затем мы обсудили некоторые из подклассов OutputStream,
доступных в Java. Наконец-то мы поговорили о буферизации и символьных потоках.
Как всегда, код примеров доступен на GitHub .