Рубрики
Без рубрики

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

Узнайте об интерфейсах выходного потока Java и общих методах

Автор оригинала: Kumar Chandrakant.

1. Обзор

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

Мы рассмотрим, что означают такие слова, как “вывод” и “поток”, более подробно по ходу дела.

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

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

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

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

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

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

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

2.2. Потоки

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

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

3. Интерфейсы выходного потока

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

3.1. Закрывается

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

3.2. Автоклавируемый

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

Более подробную информацию о try-with-resource можно найти здесь .

3.3. Смываемый

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

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

4. Методы в потоке вывода

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

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

4.1. запись(int b)

Мы можем использовать этот метод для записи одного определенного байта в выходной поток . Поскольку аргумент “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. запись(байт[] b, int off, длина int)

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

Он может записать количество байтов “длины” из массива байтов, указанное аргументом, начинающимся со смещения, определяемого параметром “off”, в выходной поток :

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. запись(байт[] b)

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

Это имеет тот же эффект, что и вызов write(b, 0, b.length) :

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 .

Мы не будем вдаваться в подробности этих подклассов.

5.1. Поток вывода файлов

Как следует из названия, FileOutputStream является OutputStream для записи данных в Файл . 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 , PrintStream .

5.4. ObjectOutputStream

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

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

5.5. PipedOutputStream

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

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

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

В приведенном выше случае, если мы вызовем этот метод с данными как “Привет”, “Мир!”, это приведет к тому, что данные будут записаны в файл только при выходе кода из блока 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 .