Классы Java для работы с потоками.
Программист, создающий автономное приложение Java, может работать с потоками нескольких типов:
1 стандартные потоки ввода и вывода;
2 потоки, связанные с локальными файлами;
3 потоки, связанные с файлами в оперативной памяти;
4 потоки, связанные с удаленными файлами
Рассмотрим кратко классы, связанные с потоками.
Стандартные потоки
Для работы со стандартными потоками в классе System имеется три статических объекта: System.in, System.out и System.err. По своему назначению эти потоки больше всего напоминают стандартные потоки ввода, вывода и вывода сообщений об ошибках операционной системы MS-DOS.
Поток System.in связан с клавиатурой, поток System.out и System.err - с консолью приложения Java.
Базовые классы для работы с файлами и потоками
Количество классов, созданных для работы с файлами, достаточно велико, чтобы привести начинающего программиста в растерянность. Прежде чем мы займемся конкретными классами и приведем примеры приложений, работающих с потоками и файлами, рассмотрим иерархию классов, предназначенных для орагнизации ввода и вывода.
Все основные классы, интересующие нас в этой главе, произошли от класса Object (рис. 1.3.1)
Рис. 1.3.1.Основные классы для работы с файлами и потоками
Класс InputStream
Класс InputStream является базовым для большого количества классов, на основе которых создаются потоки ввода. Именно производные классы применяются программистами, так как в них имеются намного более мощные методы, чем в классе InputStream. Эти методы позволяют работать с потоком ввода не на уровне отдельных байт, а на уровне объектов различных классов, например, класса String и других.
Класс OutputStream
Аналогично, класс OutputStream служит в качестве базового для различных классов, имеющих отношение к потокам вывода.
Класс RandomAccesFile
С помощью класса RandomAccesFile можно организовать работу с файлами в режиме прямого доступа, когда программа указывает смещение и размер блока данных, над которым выполняется операция ввода или вывода. Заметим, кстати, что классы InputStream и OutputStream также можно использовать для обращения к файлам в режиме прямого доступа.
Класс File
Класс File предназначен для работы с оглавлениями каталогов. С помощью этого класса можно получить список файлов и каталогов, расположенных в заданном каталоге, создать или удалить каталог, переименовать файл или каталог, а также выполнить некоторые другие операции.
Класс FileDescriptor
C помощью класса FileDescriptor вы можете проверить идентификатор открытого файла.
Класс StreamTokenizer
Очень удобен класс StreamTokenizer. Он позволяет организовать выделение из входного потока данных элементов, отделенных друг от друга заданными разделителями, такими, например, как запятая, пробел, символы возврата каретки и перевода строки.
Производные от класса InputStream
От класса InputStream производится много других классов, как это показано на рис. 1.3.2.
Рис. 1.3.2. Классы, производные от класса InputStream
Класс FilterInputStream
Класс FilterInputStream, который происходит непосредственно от класса InputStream, является абстрактным классом, на базе которого созданы классы BufferedInputStream, DataInputStream, LineNumberInputStream и PushBackInputStream. Непосредственно класс FilterInputStream не используется в приложениях Java, так как, во-первых, он является абстрактным и предназначен для переопределения методов базового класса InputStream, а во-вторых, наиболее полезные методы для работы с потоками ввода имеются в классах, созданных на базе класса FilterInputStream.
Класс BufferedInputStream
Буферизация операций ввода и вывода в большинстве случаев значительно ускоряет работу приложений, так как при ее использовании сокращается количество обращений к системе для обмена данными с внешними устройствами.
Класс BufferedInputStream может быть использован приложениями Java для организации буферизованных потоков ввода. Заметим, что конструкторы этого класса в качестве параметра получают ссылку на объект класса InputStream. Таким образом, вы не можете просто создать объект класса BufferedInputStream, не создав перед этим объекта класса InputStream.
Класс DataInputStream
Составляя программы на языке программирования С, вы были вынуждены работать с потоками на уровне байт или, в лучшем случае, на уровне текстовых строк. Однако часто возникает необходимость записывать в потоки данных и читать оттуда объекты других типов, например, целые числа и числа типа double, числа в формате с плавающей десятичной точкой, массивы байт и символов и так далее.
Класс DataInputStream содержит методы, позволяющие извлекать из входного потока данные в перечисленных выше форматах или, как говорят, выполнять форматированный ввод данных. Он также реализует интерфейс DataInput, служащий для этой же цели. Поэтому класс DataInputStream очень удобен и часто применяется в приложениях для работы с потоками ввода.
Так же как и конструктор класса BufferedInputStream, конструктор класса DataInputStream должен получить через свои параметр ссылку на объект класса InputStream.
Класс LineNumberInputStream
С помощью класса LineNumberInputStream можно работать с текстовыми потоками, состоящими из отдельных строк, разделенных символами возврата каретки \r и перехода на следующую строку \n. Методы этого класса позволяют следить за нумерацией строк в таких потоках.
Класс PushBackInputStream
Класс PushBackInputStream позволяет возвратить в поток ввода только что прочитанный оттуда символ, с тем чтобы после этого данный символ можно было прочитать снова.
Класс ByteArrayInputStream
При необходимости можно создать в приложениях Java входной поток данных не на базе локального или удаленного файла, а на базе массива, расположенного в оперативной памяти. Класс ByteArrayInputStream предназначен именно для этого - передаете конструктору класса ссылку на массив, и получаете входной поток данных, связанный с этим массивом.
Потоки в оперативной памяти могут быть использованы для временного хранения данных. Заметим, что, так как аплеты Java не могут обращаться к локальным файлам, для создания временных файлов можно использовать потоки в оперативной памяти на базе класса ByteArrayInputStream. Другую возможность предоставляет класс StringBufferInputStream, рассмотренный ниже.
Класс StringBufferInputStream
Класс StringBufferInputStream позволяет создавать потоки ввода на базе строк класса String, используя при этом только младшие байты хранящихся в такой строке символов. Этот класс может служить дополнением для класса ByteArrayInputStream, который также предназначен для создания потоков на базе данных из оперативной памяти.
Класс FileInputStream
Этот класс позволяет создать поток ввода на базе класса File или FileDescriptor.
Класс PipedInputStream
С помощью классов PipedInputStream и PipedOutputStream можно организовать двухстороннюю передачу данных между двумя одновременно работающими задачами мультизадачного аплета.
Класс SequenceInputStream
Класс SequenceInputStream позволяет объединить несколько входных потоков в один поток. Если в процессе чтения будет достигнут конец первого потока такого объединения, в дальнейшем чтение будет выполняться из второго потока и так далее.
Производные от класса OutputStream
Класс OutputStream предназначен для создания потоков вывода. Приложения, как правило, непосредственно не используют этот класс для операций вывода, так же как и класс InputStream для операций ввода. Вместо этого применяются классы, иерархия которых показана на рис. 2.4.3.
Рис. 1.3.3. Классы, производные от класса OutputtStream
Класс FilterOutputStream
Абстрактный класс FilterOutputStream служит прослойкой между классом OutputStream и классами BufferedOutputStream, DataOutputStream, а также PrintStream. Он выполняет роль, аналогичную роли рассмотренного ранее класса FilterIntputStream.
Класс BufferedOutputStream
Класс BufferedOutputStream предназначен для создания буферизованных потоков вывода. Как мы уже говорили, буферизация ускоряет работу приложений с потоками.
Класс DataOutputStream
С помощью класса DataOutputStream приложения Java могут выполнять форматированный вывод данных. Для ввода форматированных данных вы должны создать входной поток с использованием класса DataInputStream, о котором мы уже говорили. Класс DataOutputStream реализует интерфейс DataOutput.
Класс PrintStream
Потоки, созданные с использованием класса PrintStream, предназначены для форматного вывода данных различных типов с целью их визуального представления в виде текстовой строки. Аналогичная операция в языке программирования С++ выполнялась функцией printf.
Класс ByteArrayOutputStream
С помощью класса ByteArrayOutputStream можно создать поток вывода в оперативной памяти.
Класс FileOutputStream
Этот класс позволяет создать поток вывода на базе класса File или FileDescriptor.
Класс PipedOutputStream
Как я уже говорили, классы PipedInputStream и PipedOutputStream предназначены для организации двухсторонней передачи данных между двумя одновременно работающими задачами мультизадачного аплета.
1.4. Программирование на основе классов Java
Базовым элементом объектно-ориентированного программирования в языке Java является класс. В этой главе я попытался создать свои собственные классы, работать с экземплярами этих классов. Классы в Java не обязательно должны содержать метод main. Единственное назначение этого метода — указать интерпретатору Java, откуда надо начинать выполнение программы. Для того, чтобы создать класс, достаточно иметь исходный файл, в котором будет присутствовать ключевое слово class, и вслед за ним — допустимый идентификатор и пара фигурных скобок для его тела.
class Point {
}
Имя исходного файла Java должно соответствовать имени хранящегося в нем класса. Регистр букв важен и в имени класса, и в имени файла.
Класс — это шаблон для создания объекта. Класс определяет структуру объекта и его методы, образующие функциональный интерфейс. В процессе выполнения Java-программы система использует определения классов для создания представителей классов. Представители являются реальными объектами. Термины “представитель”, “экземпляр” и “объект” взаимозаменяемы. Ниже приведена общая форма определения класса.
class имя_класса extends имя_суперкласса { type переменная1_объекта:
type переменная2_объекта:
type переменнаяN_объекта:
type имяметода1(список_параметров) { тело метода;
}
type имяметода2(список_параметров) { тело метода;
}
type имя методаМ(список_параметров) { тело метода;
}
}
Ключевое слово extends указывает на то, что “имя_класса” — это подкласс класса “имя_суперкласса”. Во главе классовой иерархии Java стоит единственный ее встроенный класс — Object. Если вы хотите создать подкласс непосредственно этого класса, ключевое слово extends и следующее за ним имя суперкласса можно опустить — транслятор включит их в ваше определение автоматически. Примером может служить класс Point, приведенный ранее.
Переменные представителей (instance variables)
Данные инкапсулируются в класс путем объявления переменных между открывающей и закрывающей фигурными скобками, выделяющими в определении класса его тело. Эти переменные объявляются точно так же, как объявлялись локальные переменные. Единственное отличие состоит в том, что их надо объявлять вне методов, в том числе вне метода main. Ниже приведен фрагмент кода, в котором объявлен класс Point с двумя переменными типа int.
class Point { int х, у;
}
В качестве типа для переменных объектов можно использовать как любой из простых типов, так и классовые типы. Скоро мы добавим к приведенному выше классу метод main, чтобы его можно было запустить из командной строки и создать несколько объектов.
Оператор new
Оператор new создает экземпляр указанного класса и возвращает ссылку на вновь созданный объект. Ниже приведен пример создания и присваивание переменной р экземпляра класса Point.
Point р = new Point();
В Java можно создавать несколько ссылок на один и тот же объект. Приведенная ниже программа создает два различных объекта класса Point и в каждый из них заносит свои собственные значения. Оператор точка используется для доступа к переменным и методам объекта.
class TwoPoints {
public static void main(String args[]) {
Point p1 = new Point();
Point p2 = new Point();
p1.x = 10;
p1.y = 20;
р2.х = 42;
р2.у = 99;
System.out.println("x = " + p1.x + " у = " + p1.y);
System.out.println("x = " + р2.х + " у = " + р2.у);
} }
В этом примере вновь было использовано класс Point, было создано два объекта этого класса, и их переменным х и у присвоены различные значения. Таким образом, мы продемонстрировали, что переменные различных объектов независимы на самом деле.
Замечание
Поскольку при запуске интерпретатора мы указали в командной строке не класс Point, а класс TwoPoints, метод main класса Point был полностью проигнорирован. Добавим в класс Point метод main и, тем самым, получим законченную программу.
class Point { int х, у;
public static void main(String args[]) {
Point p = new Point();
р.х = 10;
p.у = 20;
System.out.println("x = " + р.х + " у = " + p.y);} }