Потоки ввода/вывода
Программы, написанные нами в предыдущих главах, воспринимали информацию только из параметров командной строки и графических компонентов, а результаты выводили на консоль или в графические компоненты. Однако во многих случаях требуется выводить результаты на принтер, в файл, базу данных или передавать по сети. Исходные данные тоже часто приходится загружать из файла, базы данных или из сети.
Для того чтобы отвлечься от особенностей конкретных устройств ввода/вывода, в Java употребляется понятие потока (stream). Считается, что в программу идет входной поток (input stream) символов Unicode или просто байтов, воспринимаемый в программе методами read(). Из программы методами write () или print (), println() выводится выходной поток (output stream) символов или байтов. При этом неважно, куда направлен поток: на консоль, на принтер, в файл или в сеть, методы write () и print () ничего об этом не знают.
Можно представить себе поток как трубу, по которой в одном направлении последовательно "текут" символы или байты, один за другим. Методы read (), write (), print (), println () взаимодействуют с одним концом трубы, другой конец соединяется с источником или приемником данных конструкторами классов, в которых реализованы эти методы.
Конечно, полное игнорирование особенностей устройств ввода/вывода сильно замедляет передачу информации. Поэтому в Java все-таки выделяется файловый ввод/вывод, вывод на печать, сетевой поток.
Три потока определены в классе system статическими полями in, out и err. Их можно использовать без всяких дополнительных определений, что мы и делали на протяжении всей книги. Они называются соответственно стандартным вводом (stdin), стандартным выводом (stdout) и стандартным выводом сообщений (stderr). Эти стандартные потоки могут быть соединены с разными конкретными устройствами ввода и вывода.
Потоки out и err – это экземпляры класса Printstream, организующего выходной поток байтов. Эти экземпляры выводят информацию на консоль методами print (), println () и write (), которых в классе Printstream имеется около двадцати для разных типов аргументов.
Поток err предназначен для вывода системных сообщений программы: трассировки, сообщений об ошибках или, просто, о выполнении каких-то этапов программы. Такие сведения обычно заносятся в специальные журналы, log-файлы, а не выводятся на консоль. В Java есть средства переназначения потока, например, с консоли в файл.
Поток in – это экземпляр класса inputstream. Он назначен на клавиатурный ввод с консоли методами read(). Класс inputstream абстрактный, поэтому реально используется какой-то из его подклассов.
Понятие потока оказалось настолько удобным и облегчающим программирование ввода/вывода, что в Java предусмотрена возможность создания потоков, направляющих символы или байты не на внешнее устройство, а в массив или из массива, т. е. связывающих программу с областью оперативной памяти. Более того, можно создать поток, связанный со строкой типа string, находящейся, опять-таки, в оперативной памяти. Кроме того, можно создать канал (pipe) обмена информацией между подпроцессами.
Еще один вид потока – поток байтов, составляющих объект Java. Его можно направить в файл или передать по сети,'а потом восстановить в оперативной памяти. Эта операция называется сериализацией (serialization) объектов.
Методы организации потоков собраны в классы пакета java.io.
Кроме классов, организующих поток, в пакет java.io входят классы с методами преобразования потока, например, можно преобразовать поток байтов, образующих целые числа, в поток этих чисел.
Еще одна возможность, предоставляемая классами пакета java.io, – слить несколько потоков в один поток.
Итак, в Java есть целых четыре иерархии классов для создания, преобразования и слияния потоков. Во главе иерархии четыре класса, непосредственно расширяющих класс object:
- Reader – абстрактный класс, в котором собраны самые общие методы символьного ввода;
- writer – абстрактный класс, в котором собраны самые общие методы символьного вывода;
- inputstream – абстрактный класс с общими методами байтового ввода;
- Outputstream – абстрактный класс с общими методами байтового вывода.
Классы входных потоков Reader и inputstream определяют по три метода ввода:
- read () – возвращает один символ или байт, взятый из входного потока, в виде целого значения типа int; если поток уже закончился, возвращает -1;
- read (chart] buf) – заполняет заранее определенный массив buf символами из входного потока; в классе inputstream массив типа bytet] и заполняется он байтами; метод возвращает фактическое число взятых из потока элементов или -1, если поток уже закончился;
- read (char[] buf, int offset, int len) – заполняет часть символьного или байтового массива buf, начиная с индекса offset, число взятых из потока элементов равно len; метод возвращает фактическое число взятых из потока элементов или -1.
Эти методы выбрасывают IOException, если произошла ошибка ввода/вывода.