11.Иерархия классов ввода вывода. Символьные и байтовые потоки. Буферизованный ввод-ввывод. Примеры.

 

В JAVA существует множество различных классов ввода/вывода причем многие из них существуют в следствии исторической необходимости и эволюции языка.

Библиотеки ввода/вывода часто используют абстракцию потока, который представляется любым источником данных или представляется как объект, способный производить или принимать кусочки данных. Поток прячет детали того, что случается с данными внутри реального устройства ввода/вывода.

Библиотечные классы Java для ввода/вывода делятся на классы ввода и вывода, как вы можете увидеть, взглянув на иерархию Java классов в онлайн документации с помощью вашего Web броузера. При наследовании, все, что наследуется от классов InputStream или Reader, имеет основной метод, называемый read( ) для чтения единичного байта или массива байт. Точно так же, все, что наследуется от классов OutputStream или Writer, имеет основной метод, называемый write( ) для записи единичного байта или массива байт. Однако чаще всего вы не можете использовать эти методы; они существуют для того, чтобы другие классы могли использовать их — эти другие классы обеспечивают более полезные интерфейсы. Таким образом, вы редко будете создавать ваш объект потока, используя единственный класс, вместо этого вы будите располагать множеством объектом для обеспечения желаемой функциональности. Факт в том что вы создаете более, чем один объект для создания единственного результирующего потока, это главная причина, по которой потоки Java являются запутанными.

Полезно распределить классы по категориям, исходя из их функциональности. В Java 1.0 разработчики библиотеки начали с решения, что все классы, которые могут что-то делать с вводом, должны наследоваться от InputStream, а все классы, которые ассоциируются с выводом, должны наследоваться от OutputStream.

Работа InputStream состоит в представлении классов, которые производят ввод от различных источников. Источниками могут быть:

Массив байт.

Объект String.

Файл.

“Труба”, которая работает так же, как и физическая труба: вы помещаете вещи в один конец, а они выходят из другого.

Последовательность других потоков, так что вы можете собрать их вместе в единый поток.

Другие источники, такие как Internet соединение. (Это будет обсуждено в одной из следующих глав.)

Каждый из них имеет ассоциированный подкласс InputStream. Кроме того, FilterInputStream также имеет тип InputStream, для обеспечения базового класса для "декоративных" классов, которые присоединяют атрибуты или полезные интерфейсы для входного потока. Это будет обсуждаться дальше.

Типы OutputStream

Эта категория включает классы, которые решают, куда будет производиться вывод: в массив байт (но не String; возможно, вы можете создать его, используя массив байт), в файл, или в “трубу”.

Кроме того, FilterOutputStream обеспечивает базовый класс для "декорирования" классов, которые присоединяют атрибуты или полезные интерфейсы для выходного потока. Это будет обсуждаться позже.

Классы FilterInputStream совершают две значительные вещи. DataInputStream позволяет вам читать различные типы примитивных данных, наряду с объектами типа String. (Все методы начинаются со слова “read”, например: readByte( ), readFloat( ), и т.п.) Таким образом, наряду со своим компаньоном DataOutputStream, это позволяет вам перемещать примитивные данные из одного места в другое через поток. Эти “места” определяются классами в таблице 11-1.

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

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

Дополнением к DataInputStream является DataOutputStream, который форматирует каждый из примитивных типов и объекты String в поток, таким образом, которым любой DataInputStream на любой машине смог бы прочесть его. Все методы начинаются со слова “write”, например writeByte( ), writeFloat( ) и т.п.

Изначальное предназначение PrintStream было в печати всех примитивных типов данных и объектов String в удобочитаемом формате. Он отличается от DataOutputStream, чья цель состоит в помещении элементов данных в поток таким способом, чтобы DataInputStream мог без труда реконструировать их.

Двумя важнейшими методами PrintStream являются print( ) и println( ), которые перегружены для печати всех различных типов. Различия между print( ) и println( ) в том, что последний метод добавляет символ новой строки, когда завершен вывод.

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

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

Когда вы видите классы Reader и Writer, вы сначала можете подумать (как и я), что они предназначены для замены классов InputStream и OutputStream. Но не в этом случае. Хотя некоторые аспекты начальной библиотеки потоков устарели и были заменены (если вы используете их, вы должны получать предупреждение компилятора), классы InputStream и OutputStream все еще обеспечивают ценную функциональность в форме байт-ориентированных систем ввода/вывода, в то время как классы Reader и Writer обеспечивают Unicode-совместимый, символьно ориентированный ввод/вывод. Кроме того:

Java 1.1 добавил новые классы в иерархию InputStream и OutputStream, так что, очевидно, что эти классы не заменены.

Иногда возникают ситуации, когда вы должны использовать классы из “byte” иерархии в комбинации с классами в “символьной” иерархии. Чтобы выполнить это, существуют классы - “мосты”: InputStreamReader преобразует InputStream к Reader, и OutputStreamWriter преобразует OutputStream к Writer.

Наиболее важная причина во введении иерархии Reader и Writer состоит в интернационализации. Старая иерархия потоков ввода/вывода поддерживает только 8-битные байтовые потоки и не обрабатывает 16 битные Unicode символы. Так как Unicode используется для интернационализации (и родной тип char в Java - это 16-bit Unicode), иерархия Reader и Writer были добавлены для поддержки Unicode и всех операций ввода/вывода. Кроме того, новые библиотеки были разработаны для ускорения операций по сравнению со старыми.

12.Использование маркеров Tokenizer при вводе. Примеры.

 

Tokenizing - это процесс разбивания последовательности символов на последовательность значащих элементов (“tokens”), которые являются кусочками текста, разделенных чем-либо по вашему выбору. Например, ваши значащие элементы могут быть словами, разделенными пробелом и пунктуацией. Есть два класса, обеспечиваемых стандартной библиотекой Java, которые могут использоваться для токенизации: StreamTokenizer и StringTokenizer.

StreamTokenizer

Хотя StreamTokenizer не наследуется от InputStream или OutputStream, он работает только с объектами InputStream, так что он по праву принадлежит библиотеке ввода/вывода

Представление слов в сортированном виде проще выполнить при хранении данных в TreeMap, который автоматически организует ключи в сортированном порядке (смотрите Главу 9). Когда вы получите набор ключей, используя keySet( ), они также будут отсортированы.

Для открытия файла используется FileReader, а для деления файла на слова, создается StreamTokenizer из FileReader, помещенного в BufferedReader. Для StreamTokenizer, существует стандартный список разделителей, и вы можете добавить еще с помощью нескольких методов. Здесь используется ordinaryChar( ) для того, чтобы сказать: “Этот символ не является тем, чем я интересуюсь”, так что синтаксический анализатор не будет включать его, как часть любого слова, которые он создает. Например, фраза st.ordinaryChar('.') означает, что точка не будет включаться, как часть анализируемого слова. Вы можете найти более подробную информацию в HTML документации по JDK на java.sun.com.

StringTokenizer

Хотя он не является частью библиотеки ввода/вывода, StringTokenizer имеет во многом сходную функциональность, что и описанный здесь StreamTokenizer.

StringTokenizer возвращает значащие элементы из строки по одной. Эти значащие элементы являются последовательностью символов, разделенных символами табуляции, пробелами и символами перевода строки. Таким образом, значащими элементами строки “Куда делась моя кошка?” являются “Куда”, “делась”, “моя” и “кошка?”. Как и в случае StreamTokenizer, вы можете настроить StringTokenizer, чтобы он разбивал ввод любым способом, который вам нужен, но с помощью StringTokenizer вы можете сделать это, передав второй аргумент в конструктор, который имеет тип String и является разделителем, который вы хотите использовать. В общем, если вам нужна большая изощренность, используйте StreamTokenizer.

Вы запрашиваете у объекта StringTokenizer следующий значащий элемент строки, используя метод nextToken( ), который возвращает либо следующий значащий элемент, либо пустую строку, которая указывает, что более элементов не осталось

13. Примитивные типы данных и их классы-оболочки. Основные методы работы. Инициализация. Значения NaN и Inf.

 

Есть группа типов, имеющих особое обращение; вы можете думать о них, как о “примитивных” типах, которые вы достаточно часто используете в вашем программировании. Причина специального использования в том, что создание объектов с помощью new —особенно маленьких, простые переменных — не очень существенно, поскольку new помещает объекты в кучу. Для этих типов Java возвращается к подходу, принятому в C и C++. Так что, вместо создания переменной с использованием new, “автоматические” переменные создаются не по ссылке. Переменная хранит значение, и оно помещается в стек, так как это более эффективно.

Java определяет размер каждого примитивного типа. Размеры не меняются при переходе от одной архитектуры машины к другой, как это сделано во многих языках. Этот размер инвариантен - это причина того, что программирование на Java так переносимо.

Примитивный тип

Размер

Минимум

Максимум

Тип оболочки

Boolean

Boolean

Char

16-бит

Unicode 0

Unicode 216- 1

Character

Byte

8-bit

-128

+127

Byte

Short

16-bit

-215

+215 — 1

Short

Int

32-bit

-231

+231 — 1

Integer

Long

64-bit

-263

+263—1

Long

Float

32-bit

IEEE754

IEEE754

Float

Double

64-bit

IEEE754

IEEE754

Double

Void

Void

 

Все числовые типы знаковые, так что не ищите беззнаковые типы.

Размер boolean типов точно не определено; только указано, что они способны принимать литерные значения true или false.

Примитивные типы данных также имеют классы “оболочки” для них. Это означает, что если вы хотите создать не примитивный объект в куче для представления примитивного типа, вы используете ассоциированную оболочку. Например:

char c = 'x';

Character C = new Character(c);

Или вы также моги использовать:

Character C = new Character('x');

Обоснования для этого действия будет дано в последующих главах.

                  Числа высокой точности

Java включает два класса для работы с высокоточной арифметикой: BigInteger и BigDecimal. Хотя они приблизительно попадают в ту же категорию, что и классы “оболочки”, ни один из них не имеет примитивного аналога.

Оба класса имеют методы, обеспечивающие аналогичные операции, которые вы выполняете для примитивных типов. Так что с классами BigInteger или BigDecimal вы можете делать все, что вы можете делать с int или float, только вы должны использовать вызов методов вместо операторов. Также, так как это более закручено, операции выполняются медленнее. Вы меняете скорость на точность.

BigInteger поддерживают целые числа произвольной точности. Это означает, что вы можете точно представить значение целого числа любого размера без потерь любой информации во время операций.

BigDecimal для чисел с фиксированной точкой произвольной точности; вы можете использовать это, например, для точных денежных расчетов.

Обратитесь к вашей онлайн документации, чтобы узнать более детально относительно конструкторов и методов, которые вы можете вызывать для этих двух классов.

 

Hosted by uCoz