Потоки вывода, OutputStream

Стандартная библиотека Java имеет весьма развитые средства вывода данных. Все возможности вывода данных сосредоточены в пакете java.io.

Существуют две параллельные иерархии классов вывода : OutputStream и Writer. Класс Writer введен в последних версиях Java.

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

Иерархия OutputStream

Поток Stream- это абстрактное значение источника или приёмника данных, которые способны обрабатывать информацию. Есть два типа потоков: байтовые и символьные. В некоторых ситуациях символьные потоки более эффективны, чем байтовые. Классы, производные от классов OutputStream или Writer, имеют методы с именами write() для записи одиночных байтов или массива байтов (отвечают за вывод данных).

Выходной поток OutputStream

Класс OutputStream - это абстрактный класс, определяющий байтовый потоковый вывода.

Наследники данного класса определяют куда направлять данные: в массив байтов, в файл или канал. Из массива байт можно создать текстовую строку String.

Методы класса OutputStream :

  • void write(int b) записывает один байт в выходной поток. Аргумент этого метода имеет тип int, что позволяет вызывать write, передавая ему выражение, при этом не нужно выполнять приведение его типа к byte.
  • void write(byte b[]) записывает в выходной поток весь указанный массив байтов.
  • void write(byte b[], int off, int len) записывает в поток часть массива len байтов, начиная с элемента b[off].
  • void flush() очищает любые выходные буферы, завершая операцию вывода.
  • void close() закрывает выходной поток. Последующие попытки записи в этот поток будут возбуждать IOException.

Класс ByteArrayOutputStream

Класс ByteArrayOutputStream представляет поток вывода, использующий массив байтов в качестве места вывода. Чтобы создать объект данного класса, можно использовать один из его конструкторов :

ByteArrayOutputStream()
ByteArrayOutputStream(int size)

Первый конструктор создает массив данных для хранения байтов длиной в 32 байта, а второй конструктор создает массив длиной size.

Примеры использования класса ByteArrayOutputStream :

import java.io.ByteArrayOutputStream;

public class TestBOS
{
    public static void main(String[] args)
    {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        String text = "Hello World!";
        byte[] buffer = text.getBytes();
        try{
            bos.write(buffer);
        } catch(Exception e) {
            System.out.println(e.getMessage());
        }
        // Преобразование массива байтов в строку
        System.out.println(bos.toString());
         
        // Вывод в консоль по символьно
        byte[] array = bos.toByteArray();
        for (byte b: array) {
            System.out.print((char)b);
        }
        System.out.println();
    }
}

В классе ByteArrayOutputStream метод write записывает в поток некоторые данные (массив байтов). Этот массив байтов записывается в объекте ByteArrayOutputStream в защищенное поле buf, которое представляет также массив байтов (protected byte[] buf). Так как метод write может вызвать исключение, то вызов этого метода помещается в блок try..catch.

Используя методы toString() и toByteArray(), можно получить массив байтов buf в виде текста или непосредственно в виде массива байт.

С помощью метода writeTo можно перенаправить массив байт в другой поток. Данный метод в качестве параметра принимает объект OutputStream, в который производится запись массива байт :

Для ByteArrayOutputStream не надо явным образом закрывать поток с помощью метода close.

Класс FileOutputStream

Класс FileOutputStream создаёт объект класса OutputStream, который можно использовать для записи байтов в файл. Это основной класс для работы с файлами. Создание нового объекта не зависит от того, существует ли заданный файл или нет. Если файл отсутствует, то будет создан новый файл. В случае попытки открытия файла, доступного только для чтения, будет вызвано исключение.

FileOutputStream имеет следующий конструкторы:

public FileOutputStream(File file) throws FileNotFoundException

public FileOutputStream(String name) throws FileNotFoundException

public FileOutputStream(String name, boolean append) throws FileNotFoundException

Смысл конструкторов последнего понятен из их описания. Но имеется несколько нюансов :

  • При открытии файла на запись, если файл не существует, то он будет создан.
  • Если файл существует, то он будет полностью обновлен. Т.е. если открыть и сразу закрыть файл, то содержимое файла будет уничтожено; реальный файл на диске станет нулевой длины.
  • Исключением для предыдущего правила является последний из конструкторов. Если третьему параметру append присвоить значение true, то можно будет дописывать в конец файла.

Какой-либо дополнительной функциональности по сравнению с базовым классом FileOutputStream не добавляет.

ByteArrayOutputStream bos = new ByteArrayOutputStream();
String text = "Hello Wolrd!";
byte[] buffer = text.getBytes();
try {
    bos.write(buffer);
} catch(Exception e){
    System.out.println(e.getMessage());
}

try {
    FileOutputStream fos = new FileOutputStream("hello.txt");
    bos.writeTo(fos);
} catch(IOException e){
    System.out.println(e.getMessage());
}

Класс BufferedOutputStream

Класс BufferedOutputStream создает буфер для потоков вывода. Этот буфер накапливает выводимые байты без постоянного обращения к устройству. И когда буфер заполнен, производится запись данных.

import java.io.*;
...
String text = "Hello world!"; // строка для записи
FileOutputStream fos = new FileOutputStream("file.txt");
try { 
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    // Переводим текст в байты
    byte[] buffer = text.getBytes();
    bos.write(buffer, 0, buffer.length);
} catch(IOException e) {
    System.out.println(e.getMessage());
} 

Класс BufferedOutputStream в конструкторе принимает в качестве параметра объект OutputStream - в примере это файловый поток вывода FileOutputStream.

BufferedOutputStream не добавляет много новой функциональности, он просто оптимизирует действие потока выводаи его следует использовать для организации более эффективного буферизованного вывода в поток.

Класс DataOutputStream

Класс DataOutputStream позволяет писать данные в поток через интерфейс DataOutput, который определяет методы, преобразующие элементарные значения в форму последовательности байтов. Такие потоки облегчают сохранение в файле двоичных данных.

Для записи каждого из примитивных типов предназначен свой метод класса DataOutputStream:

  • writeByte(int value) - записывает в поток 1 байт
  • writeChar(int value) - записывает 2х-байтовое значение char
  • writeInt(int value) - записывает в поток целочисленное значение int
  • writeShort(int v) - записывает в поток значение short
  • writeFloat(float value) - записывает в поток 4-байтовое значение float
  • writeDouble(double value) - записывает в поток 8-байтовое значение double
  • writeBoolean(boolean value) - записывает в поток булевое однобайтовое значение
  • writeLong(long value) - записывает в поток значение long
  • writeUTF(String value) - записывает в поток строку в кодировке UTF-8
import java.io.*;
...
FileOutputStream fos = new FileOutputStream("c://data.bin");
// запись в файл
try (DataOutputStream dos = new DataOutputStream(fos)) {
    // записываем значения
    dos.writeUTF("Киса Воробьянинов");
    dos.writeInt(30);
    dos.writeDouble(20.58);
    dos.writeBoolean(falss);
    System.out.println("Запись в файл выполнена");
} catch(IOException e){
    System.out.println(e.getMessage());
}

Класс PrintStream

PrintStream является именно тем классом, который используется для вывода информации в консоль. Когда мы с помощью вызова System.out.println() пишем в консоль некоторую информацию, то тем самым используется PrintStream, так как переменная out класса System представляет объект класса PrintStream, а метод println() - это метод класса PrintStream.

Но PrintStream можно использовать для записи информации в поток вывода. Например, запишем информацию в файл:

import java.io.*;
...
String text = "Hello, World!"; // строка для записи
FileOutputStream fos = new FileOutputStream("C:/data.txt");
try {
    PrintStream printStream = new PrintStream(fos));
    printStream.println(text);
    System.out.println("Запись в файл выполнена");
} catch(IOException e) {
    System.out.println(e.getMessage());
} 

В данном примере используется конструктор PrintStream, который в качестве параметра принимает поток вывода FileOutputStream. Можно было бы также использовать конструктор с указанием названия файла для записи: PrintStream (String filename).

С помощью метода println() производится запись информации в выходной поток - то есть в объект FileOutputStream. В случае с выводом на консоль с помощью System.out.println() в качестве потока вывода выступает консоль.

Для вывода информации в выходной поток PrintStream использует следующие методы:

println(): вывод строковой информации с переводом строки
print(): вывод строковой информации без перевода строки
printf(): форматированный вывод

Следующий код показывает возможности использования форматированного вывода класса PrintStream :

int i = 15;
printStream.printf("Квадрат числа %d равен %d \n", i, i*i);

Класс ObjectOutputStream

Класс ObjectOutputStream используется для сериализации объектов в поток. Сериализация представляет процесс записи состояния объекта в поток, соответственно процесс извлечения или восстановления состояния объекта из потока называется десериализацией. Сериализация очень удобна, когда идет работа со сложными объектами.

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

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

МетодОписание
void close() закрывает поток
void flush() сбрасывает содержимое буфера в выходной поток и очищает его
void write(byte[] buf) записывает в поток массив байтов
void write(int val) записывает в поток один младший байт из val
void writeBoolean(boolean val) записывает в поток значение boolean
void writeByte(int val) записывает в поток один младший байт из val
void writeChar(int val) записывает в поток значение типа char, представленное целочисленным значением
void writeDouble(double val) записывает в поток значение типа double
void writeFloat(float val) записывает в поток значение типа float
void writeInt(int val) записывает целочисленное значение
void writeLong(long val) записывает значение типа long
void writeShort(int val) записывает значение типа short
void writeUTF(String str) записывает в поток строку в кодировке UTF-8
void writeObject(Object obj) записывает в поток отдельный объект

Представленные методы охватывают весь спектр данных, которые можно сериализовать.

Пример использования класса ObjectOutputStream :

import java.io.*;
 
class Person implements Serializable
{
    private static final long serialVersionUID = 1L;
	
    public String  name   ;
    public int     age    ;
    public double  height ;
    public boolean married;
     
    Person(String name, int age, double height, boolean married)
    {
        this.name    = name;
        this.age     = age;
        this.height  = height;
        this.married = married;
    }
}

public class Example
{
    public static void main(String[] args)
    {
        FileOutputStream fos = new FileOutputStream("c:/data/persons.dat");
        try {
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            Person person = new Person("Остап Бендер", 35, 175, false);
            oos.writeObject (person);
        }
        catch(Exception e){
            System.out.println(e.getMessage());
        }
    }
}

Необходимо принимать во внимание, что сериализовать можно только те объекты, которые реализуют интерфейс Serializable.

Класс PipedOutputStream

Пакет java.io содержит класс PipedOutputStream, который может быть подключен к PipedInputStream, используемый для установления связи между двумя каналами. Данные в PipedOutputStream передаются в потоке Thread, который отправляет их в подключенный PipedInputStream, где данные также читаются, но в другом потоке.

То есть, класс PipedOutputStream предназначен для передачи информации между программами через каналы (pipes).

Наиболее часто используемые методы класса PipedOutputStream :

  • void write(int b) - запись байта в канал
  • void write(byte[] bytes, int off, int len) - запись определенного количества len байт начиная со смещения off массив bytes
  • connect(PipedInputStream pis) - установление связи в каналом ввода pis
  • close() - закрытие канала
  • flush() - сброс данных в канал

Все методы класса могут вызвать исключение IOException.

Пример использования класса PipedOutputStream :

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class Example
{
    public static void main(String[] args)
    {
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream  pis = new PipedInputStream();

        byte[] bytes = "Hello, World!".getBytes();
        try {
            // Установление связи между "трубами"
            pos.connect(pis);
            // Запись данных в PipedOutputStream
            for (int i = 0; i < bytes.length; i++)
                pos.write(bytes[i]);
            // Чтение данных из PipedInputStream
            int c;
            while((c = pis.read() ) != -1) {
                System.out.print((char) c);
            }              
        } catch (IOException ioe) {
			System.out.println(ioe);
        }
    }
}
Наверх
  Рейтинг@Mail.ru