Руководство по Java OutputStream

1. Обзор

В этом руководстве мы подробно рассмотрим класс Java OutputStream . OutputStream - абстрактный класс. Это служит суперклассом для всех классов, представляющих выходной поток байтов.

Мы рассмотрим, что означают эти слова, такие как «вывод» и «поток», более подробно, по мере продвижения вперед.

2. Краткое введение в Java IO

  • OutputStream является частью API Java IO ** , который определяет классы, необходимые для выполнения операций ввода-вывода в Java. Все они упакованы в пространство имен java.io . Это один из основных пакетов, доступных в Java начиная с версии 1.0.

Начиная с Java 1.4, у нас также есть Java NIO, упакованный в пространство имен java.nio , которое позволяет неблокировать операции ввода и вывода. Тем не менее, нашей основной темой для этой статьи является ObjectStream как часть Java IO.

Подробности, связанные с Java IO и Java NIO, можно найти по адресу here .

2.1. Вход и выход

Java IO в основном предоставляет механизм для чтения данных из источника и записи данных в место назначения . Ввод представляет источник, в то время как выход представляет здесь назначение.

Этими источниками и назначениями могут быть что угодно, от Файлов, Каналов до Сетевых подключений

2.2. Streams

Java IO предоставляет концепцию потоков, которая в основном представляет непрерывный поток данных . Потоки могут поддерживать множество различных типов данных, таких как байты, символы, объекты и т. Д.

Более того, соединение с источником или адресатом - это то, что представляет поток. Следовательно, они могут быть либо InputStream , либо OutputStream соответственно.

3. Интерфейсы OutputStream

OutputStream реализует набор интерфейсов, которые предоставляют некоторый отдельный символ своим подклассам Давайте пройдемся по ним быстро.

3.1. Closeable

Интерфейс Closeable предоставляет метод с именем _close () which , который обрабатывает закрытие источника или места назначения данных. Каждая реализация OutputStream_ должна обеспечивать реализацию этого метода. Здесь они могут выполнять действия по освобождению ресурсов.

3.2. AutoCloseable

Интерфейс AutoCloseable также предоставляет метод с именем close () с поведением, аналогичным тому, которое есть в Closeable . Однако в этом случае метод close () автоматически вызывается при выходе из блока try-with-resource.

Более подробную информацию о попытке с ресурсом можно найти по адресу here .

3.3. Flushable

Интерфейс Flushable предоставляет метод с именем flush () , который обрабатывает сброс данных в пункт назначения.

Конкретная реализация OutputStream может предпочесть буферизацию ранее записанных байтов для оптимизации, но вызов flush () заставляет ее немедленно записать в место назначения .

4. Методы в OutputStream

OutputStream имеет несколько методов, которые каждый реализующий класс должен реализовать для своих соответствующих типов данных.

Они отличаются от методов close () и flush () , которые он наследует от интерфейсов Closeable и Flushable .

4.1. write (int b)

Мы можем использовать этот метод для записи одного конкретного байта в 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

Это, как мы видим, является седьмым символом строки, индексированной шестой.

4.2. write (byte[]b, int off, int length)

Эта перегруженная версия метода write () предназначена для записи подпоследовательности байтового массива в OutputStream .

Он может записывать число «длины» байтов из байтового массива, как указано аргументом, начинающимся со смещения, определенного как «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 (byte[]b)

Это еще одна перегруженная версия метода 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);
    }
}

Когда мы вызываем этот метод сейчас с теми же данными, у нас есть весь String в нашем выходном файле:

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. FilterOutputStream

OutputStream в первую очередь записывает поток байтов в место назначения, но он также может преобразовывать данные перед этим. FilterOutputStream представляет суперкласс всех таких классов, которые выполняют конкретное преобразование данных . FilterOutputStream всегда создается с существующим OutputStream .

Некоторые примеры FilterOutputStream : BufferedOutputStream , CheckedOutputStream , CipherOutputStream , DataOutputStream , DeflaterOutputStream , DigestOutputStream , InflaterOutputStream , __Pr.

5.4. ObjectOutputStream

ObjectOutputStream может записывать примитивные типы данных и графики объектов Java в место назначения. Мы можем создать ObjectOutputStream , используя существующий OutputStream для записи в конкретное место назначения, например File.

Обратите внимание, что объектам необходимо реализовать Serializable для ObjectOutputStream , чтобы записать их в место назначения. Вы можете найти более подробную информацию о сериализации Java here .

5.5. PipedOutputStream

PipedOutputStream полезен для создания канала связи .

PipedOutputStream может записывать данные, которые может прочитать связанный PipedInputStream .

PipedOutputStream имеет конструктор для соединения с PipedInputStream . В качестве альтернативы, мы можем сделать это позже, используя метод, предоставленный в PipedOutputStream , называемый connect () .

6. OutputStream Буферизация

Операции ввода и вывода обычно включают относительно дорогие операции, такие как доступ к диску, сетевая активность и т. Д. Выполнение этого часто может сделать программу менее эффективной.

У нас есть «буферизованные потоки» данных в 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 () для каждого аргумента данных записывает только в буфер и не приводит к потенциально дорогостоящему вызову файла.

В приведенном выше случае, если мы вызовем этот метод с данными как «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. Мы наконец поговорили о буферизации и потоках символов.

Как всегда, код для примеров доступен по адресу over на GitHub .