среда, 1 августа 2012 г.

module: io streams

OutputStream


    OutputStream - это абстракция байтового потока вывода, т.е. способа писать/выводить байты. Возможны и другие абстракции, но именно такой класс был выбран для java.io.*.

    Абстрактный класс OutputStream имеет 5 методов: три для записи - write(int)write(byte[])write(byte[], int, int), и два для завершения - flush(), close().

    Надо понимать, что есть контракт класса OutputStream, а есть контракт конкретного наследника. Они могут не совпадать, но контракты наследников не могут противоречить контракту предка! Т.е. код, написанный для работы с предком, должен корректно работать с любым потомком.

    В общем случае, предполагается, что у потока вывода есть два состояние - "готов к записи" и "закрыт" (по факту, может быть всего единственное состояние, как у ByteArrayOutputStream - "Closing a ByteArrayOutputStream has no effect", flush() - унаследован от OutputStream, тоже ничего не делает). У потока вывода нет состояния "новый", это выражается в том, что нет методов init()/open()/connect()/start()/... Сразу же после создания, поток готов к записи. После вызова close() - поток закрыт. Невозможно повторно открыть закрытый поток.

    Общая практика завершения работы с потоком вывода заключается в вызове flush() + close(). Для некоторых потоков эти методы могут ничего не делать, как у ByteArrayOutputStream. Но никто еще не был уволен за вызов этой комбинации. close() стоит вызывать в finally секции, в close() могут освобождаться важные системные ресурсы (FileOutputStream - может вызывать освобождение ресурсов операционной системы, связанных с файлом).

    "Пишущие" методы
    Запись одного байта производится методом write(int). По ряду причин (TODO: каких?) сигнатура метода имеет параметр типа int, а не типа byte. Тут происходит нетривиальное (TODO: какое?) преобразование - байт лежит в диапазоне [-128,127], а преобразуется в значение int в диапазоне [0, 255].
    Запись всего массива байтов производится методом write(byte[]).
    Запись диапазона из массива байтов производится методом write(byte[],  int, int). Обратите внимание: второй аргумент - это индекс левого конца, но третий аргумент - это длина диапазона, а не индекс правого конца.
    Заметьте, что запись массива и диапазона - безусловны (т.е. либо будут записаны все данные, либо будет инициирована исключительная ситуация - обе ситуации могут произойти после задержки/"залипания"/блокирования метода). Чтение из OutputStream (read(byte[]), read(byte[], int, int)) может не заполнить весь массив данными.


    "Завершающий" методы
    flush() - производит "сбрасывание" данных, если таковые "застряли" в потоке вывода. Основной пример "застревания" - буферизация. Можно вызывать много раз подряд. После вызова поток вывода готов к продолжению записи данных.
    close() - производит "закрытие" потока вывода. 


   УПРОЩЕННАЯ ВЕРСИЯ:
package java.io;

public class OutputStream {

    public void write(int b) throws IOException;

    public void write(byte b[]) throws IOException;

    public void write(byte b[], int off, int len) throws IOException;

    public void flush() throws IOException {}

    public void close() throws IOException {}
}

    ПОЛНАЯ ВЕРСИЯ:
package java.io;

public abstract class OutputStream implements Closeable, Flushable {

    public abstract void write(int b) throws IOException;

    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    public void write(byte b[], int off, int len) throws IOException {
        ...
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }

    public void flush() throws IOException {}

    public void close() throws IOException {}
}

    Лабораторные

    io.streams.remove_zero
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

public class BAOSTest {
    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream buff = new ByteArrayOutputStream();
        buff.write(0);
        buff.write(1);
        buff.write(2);
        byte[] arr = buff.toByteArray();
        System.out.println(Arrays.toString(arr));
    }
}

    Написать программу, которая вычитывает файл и записывает файл, удаляя все байты равные 0.
    а) добавьте в текущую программу корректное завершение работы c потоками(flush(), close()) + гарантированные вызов close() ОБОИХ потоков даже в случае IOException.
    б) добавьте буферизацию (BufferedInputStream + BufferedOutputStream) в чтение из и запись в файл. Сравните время работы без буфера и с буфером размером 1, 2, 4, 8, ..., 1024 байта.
    с) кроме буферизации добавьте чтение в массив (read(byte[]) или read(byte[], int, int)) + запись массивом (write(byte[] или write(byte[], int, int))).
    P.S. Для заметного ускорения проводите тестирование на большом (более 1Мб).