Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 97 98 99 100 101 102 103 104 105 ... 132

Добавление атрибутов и интерфейсов

«Наслаивание» дополнительных объектов для получения новых свойств и функций у определенных объектов называется надстройкой, или декоратором22. В библиотеке ввода/вывода Java постоянно требуется совмещение нескольких свойств, поэтому в ней и используются надстройки. Существование в библиотеке ввода/вывода Java классов-фильтров объясняется тем, что абстрактный класс фильтра является базвовым классом для всех существующих надстроек. (Надстройка должна включать в себя интерфейс «декорируемого» объекта, но расширение этого интерфейса также не запрещено и практикуется некоторыми классами-«фильтрами».)

Однако у такого подхода есть свои недостатки. Надстройки предоставляют дополнительную гибкость при написании программы (можно легко совмещать различные атрибуты), но при этом код получается излишне сложным. Некоторое неудобство библиотеки ввода/вывода Java объясняется тем, что для получения желаемого объекта приходится создавать много дополнительных объектов — «ядро» ввода/вывода и несколько надстроек.

Интерфейс для надстроек предоставляют классы FilterlnputStream (для входных потоков) и FilterOutputStream (для выходных потоков). Это абстрактные классы, производные от основных базовых классов библиотеки ввода/вывода InputStream и OutputStream. Наследование от этих классов — основное требование к классу-надстройке (поскольку таким образом обеспечивается общий интерфейс для «наслаиваемых» объектов).

Чтение из InputStream с использованием FilterlnputStream

Классы, производные от FilterlnputStream (табл. 16.3), выполняют две различные миссии. DatalnputStream позволяет читать из потока различные типы простейших данных и строки. (Все методы этого класса начинаются с префикса read — например, readByte(), readFloat() и т. п.) Этот класс, вместе с «парным» классом DataOutputStream, предоставляет возможность направленной передачи простейших данных посредством потоков. Место доставки определяется классами, описанными в табл. 16.1.

Другие классы изменяют внутренние механизмы входного потока: применение буферизации, подсчет количества прочитанных строк (что позволяет запросить или задать номер строки), возможность возврата в поток только что прочитанных символов. Вероятно, последние два класса создавались в основном для построения компиляторов (то есть их добавили в библиотеку в процессе написания компилятора Java), и прока в повседневном программировании от них немного.

Ввод данных почти всегда осуществляется с буферизацией, вне зависимости от присоединенного устройства ввода/вывода, поэтому имеет смысл сделать отдельный класс (или просто специальный метод) не для буферизованного ввода данных, а, наоборот, для прямого.

Таблица 16.3. Разновидности надстроек FilterlnputStream

Класс

Назначение

Аргументы конструктора, порядок применения

DatalnputStream

Используется в сочетании с классом DataOutputStream и позволяет читать примитивы

Входной поток InputStream. Содержит все необходимое для чтения всех простейших типов

Аргументы конструктора, порядок применения

Назначение

Класс

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

BufferedlnputStream

LineNumberlnputStream

PushbacklnputStream

Следит за количеством считанных из потока строк; вы можете узнать их число, вызвав метод getLineNumber(), а также перейти к определенной строке с помощью метода setLineNumber(int) Имеет односимвольный буфер, который позволяет вернуть в поток только что прочитанный символ

Входной поток InputStream, размер буфера (необязательно). По существу, никакого интерфейса этот класс не предоставляет, он просто присоединяет к потоку буфер. Добавьте дополнительно объект, предоставляющий интерфейс Входной поток InputStream. Этот класс просто считывает строки, поэтому к нему стоит добавить еще одну, более полезную, надстройку

Входной поток InputStream. Обычно используется при сканировании файлов для компилятора. Скорее всего, вам он не понадобится

Запись в OutputStream с помощью FilterOutputStream

У класса DatalnputStream существует «парный» класс DataOutputStream (табл. 16.4), который позволяет форматировать и записывать в поток примитивы и строки таким образом, что на любой машине и на любой платформе их сможет прочитать и правильно обработать DatalnputStream. Все его методы начинаются с префикса write (запись): writeByte(), writeFloat() и т. п.

Класс PrintStream изначально был предназначен для вывода значений в формате, понятном для человека. В данном аспекте он отличается от класса DataOutputStream, потому что цель последнего — разместить данные в потоке так, чтобы DatalnputStream смог их правильно распознать.

Основные методы класса PrintStream — print() и println(), они перегружены для наиболее часто используемых типов. После вывода значения метод println() осуществляет перевод на новую строку, в то время как метод print() этого не делает.

На практике класс PrintStream довольно-таки неудобен, поскольку он перехватывает и скрывает все возбуждаемые исключения. (Приходится явно вызывать метод checkError(), который возвращает true, если во время исполнения какого-либо из методов класса произошла ошибка.) К тому же этот класс как следует не интернационализован и не выполняет перевод строки платформ-но-независимым способом (все эти проблемы решены в классе PrintWriter, описанном ниже).

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

Таблица 16.4. Разновидности надстроек FilterOutputStream

Аргументы конструктора, порядок применения

Класс

Назначение

Используется в сочетании с классом DatalnputStream, позволяет записывать в поток примитивы (целые, символы и т. д.) независимо от платформы При записи форматирует данные. Если класс DataOutputStream отвечает за хранение данных, то этот класс отвечает за отображение данных

DataOutputStream

PrintStream

BufferedOutputStream

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

Выходной поток OutputStream. Содержит все необходимое для записи простейших типов данных

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

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

Классы Reader и Writer

При выпуске Java версии 1.1 в библиотеке ввода/вывода были сделаны значительные изменения. Когда в первый раз видишь новые классы, основанные на базовых классах Reader и Writer, возникает мысль, что они пришли на замену классам InputStream и OutputStream. Тем не менее это не так. Хотя некоторые аспекты изначальной библиотеки ввода/вывода, основанной на потоках, объявлены устаревшими (и при их использовании компилятор выдаст соответствующее предупреждение), классы InputStream и OutputStream все еще предоставляют достаточно полезные возможности для проведения байт-ориентированного ввода/ вывода. Одновременно с этим классы Reader и Writer позволяют проводить операции символьно ориентированного ввода/вывода, в кодировке Юникод. Дополнительно:

1. В Java 1.1 в иерархию потоков, основанную на классах InputStream и OutputStream, были добавлены новые классы. Очевидно, эта иерархия не должна была уйти в прошлое.

2. В некоторых ситуациях для решения задачи используются как «байтовые», так и «символьные» классы. Для этого в библиотеке появились классы-адаптеры: InputStreamReader конвертирует InputStream в Reader, a Output-StreamWriter трансформирует OutputStream в Writer.

Основной причиной появления иерархий классов Reader и Writer стала интернационализация. Старая библиотека ввода/вывода поддерживала только 8-битовые символы и зачастую неверно обращалась с 16-битовыми символами Юникода. Именно благодаря символам Юникода возможна интернационализация программ (простейший тип Java char (символ) также основан на Юникоде), поэтому новые классы отвечают за их правильное использование в операциях ввода/вывода. Вдобавок новые средства спроектированы так, что работают быстрее старых классов.

Источники и приемники данных

Практически у всех изначальных потоковых классов имеются соответствующие классы Reader и Writer со схожими функциями, однако работающие с символами Юникода. Впрочем, во многих ситуациях правильным (а зачастую и единственным) выбором становятся классы, ориентированные на прием и посылку байтов; в особенности это относится к библиотекам сжатия данных java.utiLzip. Поэтому лучше всего будет такая тактика: пытаться использовать классы Reader и Writer где только возможно. Обнаружить место, где эти классы неприменимы, будет нетрудно — компилятор выдаст вам сообщение об ошибке.

1 ... 97 98 99 100 101 102 103 104 105 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий