Абстрактный класс InputStream 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Абстрактный класс InputStream



Ввод/вывод

 

Функции ввода-вывода информации, реализованные в составе стандартного пакета java.io, определены в терминах потоков данных (streams). Потоки данных – это упорядоченные последовательности данных, которым соответствует определённый источник (для потоков ввода) или получатель (для потоков вывода). Большинство типов потоков данных поддерживает методы определённых базовых интерфейсов и абстрактных классов с некоторыми дополнениями.

Пакет java.io охватывает определения типов двух основных разновидностей – символьных потоков и байтовых потоков. Под символьными потоками понимают последовательности 16-битовых символов Unicode, а каждому байту отвечает порция данных длиной 8 бит. Ввод-вывод может быть либо текстовым, либо бинарным. Функции текстового ввода-вывода способны обращаться с потоками символов, поддающихся восприятию человеком (например, с исходными текстами программ), а средства обработки бинарных данных имеют дело с информацией, хранящейся в двоичном виде (например, с битовыми представлениями графических изображений).

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

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

Классы и интерфейсы из состава пакета java.io можно условно разделить на пять обширных групп:

1) типы общего назначения, служащие для построения различных типов байтовых и символьных потоков: потоки ввода и вывода, чтения и записи, а также классы, позволяющие выполнять взаимные преобразования между ними

2) классы, определяющие разновидности потоков: фильтрованные потоки, буферизованные потоки, канальные потоки, а также специальные подвиды таких потоков, такие как поток чтения строк с сохранением нумерации и лексический анализатор потока чтения

3) специализированные потоковые классы и интерфейсы для ввода и вывода значений простых типов и строк

4) классы и интерфейсы для обработки файловых данных в стиле, не зависящем от особенностей платформы

5) классы и интерфейсы, поддерживающие механизм сериализации объектов

Объекты класса IOException используются многими методами ввода-вывода в качестве сигнала о возникновении исключительной ситуации

 

Байтовые поток

 

Существует ряд особенностей, присущих всем байтовым потокам данных: например, все они поддерживают механизм открытия и закрытия. Поток открывается при создании объекта класса и, оставаясь открытым, позволяет осуществлять операции чтения и записи данных. Поток закрывается при вызове метода close() объекта. Закрытие потока служит цели высвобождения системных ресурсов, которые используются потоком. Если поток явно не закрывается, он продолжает существовать и расходовать системные ресурсы. Потоки следует закрывать сразу после завершения их использования.

 

Символьные потоки

 

Абстрактными классами, обеспечивающими чтение и запись символьных потоков, являются Reader и Writer. В каждом из них предусмотрены методы, схожие с «собратьями» из состава классов, представляющих байтовые потоки.

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

 

Абстрактный класс Reader

 

Абстрактный класс Reader обеспечивает поддержку символьного потока чтения аналогично тому, как это делает InputStream, реализующий модель байтового потока ввода, и методы Reader схожи с теми, которые объявлены в составе InputStream.

· public int read() throws IOException
Считывает один символ данных и возвращает его в виде целого числа из диапазона от 0 до 65535. Если символов, готовых для чтения, не существует ввиду достижения конца потока, возвращается значение -1. Метод выполняет блокировку до тех пор, пока имеются доступные для чтения символы, не достигнут конец потока либо не выброшено исключение

· public abstract int read(char[] buf, int offset, int count)
throws IOException
Считывает символы и сохраняет их в части массива buf, максимальное количество символов, подлежащих чтению, определяется значением count (если 0 – чтение не производится и возвращается 0). Символы заносятся в элементы массива, начиная с buf[offset] и до buf[offset+count-1]. Метод возвращает количество фактически считанных символов. Если ввиду достижения конца потока не считан ни один символ, возвращается -1. Если первый символ не может быть считан по причине, не связанной с достижением конца потока (например, поток уже закрыт), выбрасывается исключение типа IOException

· public int read(char[] buf) throws IOException
Метод аналогичен предыдущему при условии read(buf, 0, buf.length)

· public long skip(long count) throws IOException
Пропускает, самое большее, count символов либо часть последовательности символов до конца потока. Возвращает количество фактически пропущенных символов. Значение count не должно быть отрицательным

· public boolean ready() throws IOException
Возвращает true, если поток готов для чтения данных, т.е. в нём существует хотя бы один доступный символ. Результат, равный false, не свидетельствует о том, что очередной вызов read приведёт к возникновению блокировки, поскольку в промежутке между обращениями к ready и read в поток может поступить очередная порция данных

· public abstract void close() throws IOException
Закрывает ранее открытый поток чтения. Метод используется для высвобождения ресурсов, связанных с потоком. Любые попытки обращения к закрытому потоку приводят к выбрасыванию исключения типа IOException, но повторное закрытие потока эффектов не вызывает

 

При реализации Reader требуется, чтобы производный класс обеспечил практическое воплощение варианта метода read(), осуществляющего чтение данных в массив символов, и версии метода close(). Во многих случаях, однако, производительность операций может быть улучшена за счёт переопределения и других методов.

 

 

Абстрактный класс Writer

 

Абстрактный класс Writer обеспечивает поддержку символьного потока записи аналогично тому, как это делает OutputStream, реализующий модель байтового потока вывода, и многие методы Writer схожи с теми, которые объявлены в составе OutputStream, однако, помимо этого, в классе Writer предусмотрены некоторые другие полезные версии метода write.

public void write(int ch) throws IOException
Записывает ch в виде символа. Символ передаётся как значение int, но записывается только 16 битов последнего

public abstract void write(char[] buf, int offset, int count) throws IOException
Записывает count символов массива buf, начиная с элемента buf[offset]

public void write(char[] buf) throws IOException
Метод аналогичен предыдущему при условии write(buf, 0, buf.length)

public void write(String str, int offset, int count) throws IOException
Записывает count символов строки str, начиная с символа str.charAt(offset)

public void write(String str) throws IOException
Метод аналогичен предыдущему при условии write(str, 0, str.length())

public abstract void flush() throws IOException
Осуществляет сброс потока. Единственный вызов flush приводит к сбросу всех буферов в цепочке взаимосвязанных потоков. Если поток не относится к категории буферизированных, никакие действия не выполняются

public abstract void close() throws IOException
Закрывает ранее открытый поток записи, выполняя при необходимости его сброс. Метод используется для высвобождения ресурсов, связанных с потоком. Любые попытки обращения к закрытому потоку приводят к выбрасыванию исключения типа IOException, но повторное закрытие потока эффектов не вызывает

 

 

Классы, производные от Writer, обязаны обеспечить реализацию варианта метода write(), связанного с записью символов из части массива, а также методов close() и flush(). Все остальные методы Writer основаны на этих трёх. Это отличает Writer от OutputStream, в котором в качестве базового метода вывода предусмотрен вариант write(), осуществляющий вывод одного байта, а для методов close() и flush() предложены реализации по умолчанию. Производительность операций может быть улучшена за счёт переопределения в производных классах и других методов

 

Потоки Filter

 

Классы семейства фильтрованных потоков Filter – FilterInputStream, FilterOutputStream, FilterReader и FilterWriter – позволяют объединять потоки в цепочки для получения составных потоков, обладающих расширенным набором функций.

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

Позволяется связывать в единую цепочку любое количество байтовых (символьных) Filter-потоков ввода (чтения). Источником исходных данных может быть поток, не относящийся к семейству потоков Filter. Подобным образом могут соединяться в цепочки и Filter-потоки вывода (записи). Все потоки вывода (записи) в цепочке, начиная с первого и заканчивая предпоследним, должны относиться к классу Filter, но последним может быть объект любого потокового класса вывода (записи).

Не все классы семейства Filter в действительности осуществляют изменение данных. Некоторые просто привносят дополнительные черты поведения, а другие предлагают новые интерфейсы взаимодействия с потоками.

 

Потоки Buffered

 

Классы семейства буферизованных потоков Buffered – BufferedInputStream, BufferedOutputStream, BufferedReader и BufferedWriter – осуществляют буферизацию данных, позволяющую избежать необходимости обращения к источнику (получателю) данных при выполнении каждой отдельной операции read() или write(). Эти классы часто используются в сочетании с потоками семейства File: доступ к данным на диске выполняется намного медленнее, чем к информации в буфере памяти, и средства буферизации помогают снизить потребность в обращениях к диску.

Когда метод read() потока Buffered, вызываемый для ввода (чтения) данных, обнаруживает, что буфер потока пуст, он вызывает одноимённый метод потока источника, заполняет буфер настолько большой порцией данных, насколько это возможно и возвращает запрошенные данные из буфера. При очередных обращениях к методу read() объекта Buffered данные будут извлекаться из буфера, пока его содержимое не исчерпается, и в этот момент объекту вновь придётся вызвать read() потока-источника.

Процесс повторяется до тех пор, пока не иссякнет источник данных.

Buffered – потоки вывода (записи) ведут себя аналогичным образом. Когда write() заполняет буфер данных, вызывается одноимённый метод потока-получателя, освобождающий буфер. Такой механизм буферизации помогает превратить последовательность запросов на вывод (запись) небольших порций данных, адресуемых объекту Buffered, в единственный вызов метода write() потока-получателя.

 

Потоки Piped

 

Канальные потоки, определяемые классами семейства Piped - PipedInputStream, PipedOutputStream, PipedReader и PipedWriter – используются в виде пар ввода-вывода (записи-чтения). Данные, переданные в поток вывода (записи), служат источником для потока ввода (чтения). Каналы реализуют механизм обмена данными между различными потоками вычислений.

Единственный безопасный способ обращения с потоками данных Piped связан с использованием двух потоков вычислений: один из них осуществляет вывод данных, а другой – их ввод. Когда буфер канала полностью заполняется, попытка вывода данных в него приводит к блокированию соответствующего потока вычислений. Если операции вывода и ввода выполняются одним потоком вычислений, тот будет блокирован постоянно. Поток вычислений, осуществляющий ввод, блокируется, если буфер канала пуст.

Чтобы избежать опасности «вечного» блокирования одного потока вычислений, когда его «собрат» на другом конце канала прекращает работу, каждый канал отслеживает подлинность и работоспособность потоков вычислений, обращавшихся к нему с целью вывода и ввода данных последними. Канал, прежде чем блокировать текущий поток вычислений, проверяет, «жив» ли поток на другом конце канала. Если обнаруживается, что работа противоположного потока вычислений прекращена, текущий поток генерирует исключение типа IOException.

Потоки Piped должны быть связаны друг с другом. Это можно сделать при создании потоков, передав конструктору потока PipedReader в качестве аргумента ссылку на поток PipedWriter (и наоборот, порядок несущественен) или позднее, используя метод connect().

При попытках использования потоков Piped до их соединения или связывания ранее соединённых потоков выбрасывается исключение типа IOException.

 

Байтовые потоки ByteArray

 

Массивы байтов, размещённые в оперативной памяти, могут выступать в роли источника или получателя данных при работе с потоками семейства ByteArray. Объект класса ByteArrayInputStream использует массив типа byte в качестве источника данных. В составе класса ByteArrayOutputStream предусмотрены средства динамического наращивания объёма массива типа byte, получающего выводимые данные.

 

Потоки Print

 

Классы семейства Print – PrintStream и PrintWriter – содержат объявление ряда методов, которые упрощают задачу вывода (записи) в поток значений простых типов и объектов в удобочитаемом текстовом формате. В потоковых классах Print реализованы перегруженные версии методов print() и println() для вывода следующих значений типов – char, char[], int, long, float, double, Object, String, boolean. Эти методы гораздо удобнее для применения, нежели обычные методы потокового вывода (записи) write(). Метод println() добавляет в конец порции выводимых данных, переданной в виде параметра, признак завершения строки (без параметров просто завершает текущую строку).

Каждый из потоков Print действует как поток Filter, и поэтому в процессе вывода данные могут быть подвержены дополнительной фильтрации. Класс PrintStream работает с байтовыми потоками, а PrintWriter – с символьными. Поскольку чаще возникает потребность в записи символов, в обычных ситуациях следует пользоваться средствами класса PrintWriter.

Одна из важных характеристик поведения потоков Print связана с тем, что ни один из реализованных в них методов вывода (записи) не выбрасывает исключения типа IOException. Если при передаче данных «внутреннему» потоку возникает ошибка, методы завершают выполнение нормальным образом. Проверить наличие ошибки можно с помощью вызова метода checkError(), возвращающего результат типа boolean.

 

Класс StreamTokenizer

 

Задачи лексического анализа потока данных относятся к числу традиционных и в составе пакета java.io представлен класс StreamTokenizer, позволяющий решать некоторые из них. Поток разбивается на лексемы с помощью объекта StreamTokenizer, конструктор которого принимает в качестве параметра объект типа Reader, выполняющий функцию источника данных; объект StreamTokenizer действует в соответствии с заданными параметрами сканирования данных. На каждой итерации цикла сканирования вызывается метод nextToken(), который возвращает очередную считанную из потока лексему и информацию о её типе, присваивая эти данные полям объекта StreamTokenizer.

Класс StreamTokenizer ориентирован преимущественно на анализ текстов, представляющих код, написанный на каком-либо языке программирования; класс нельзя отнести к числу инструментов лексического анализа общего назначения.

Когда метод nextToken распознаёт лексему, он возвращает её тип в виде значения и присваивает последнее полю ttype.

Существует четыре типа лексем, воспринимаемых методом nextToken():

TT_ WORD; обнаружено слово; оно сохраняется в поле sval типа String

TT_ NUMBER; обнаружено число; оно сохраняется в поле nval типа double; распознаются только десятичные числа с плавающей запятой (с десятичной точкой или без таковой) в нормальной нотации (анализатор не распознает ни 3.4е79 как число с плавающей запятой, ни 0xffff как шестнадцатеричное число)

TT_ EOL; обнаружен признак завершения строки

TT_ EOF; достигнут конец файла

Подразумевается, что текст, подлежащий анализу, состоит из байтов, значение которых относятся к диапазону от \u0000 до \u00FF, - корректность распознавания символов Unicode, не принадлежащих указанному интервалу, не гарантируется. Поток ввода включает специальные и обычные символы. К категории специальных относятся те символы, которые анализатор трактует специальным образом, а именно: символы пробела, символы, обозначающие числа и слова, и т.д. Любой другой символ воспринимается как обычный. Когда очередным символом потока является обычный символ, в качестве его типа возвращается значение этого символа.

Пример 3

static double sumStream(Reader in) throws IOException {

StreamTokenizer nums = new StreamTokenizer(in);

double result = 0.0;

while (nums.nextToken()!= StreamTokenizer.TT_EOF) {

     if (nums.ttype == StreamTokenizer.TT_NUMBER)

          result += nums.nval;

}

return result;

}

 

Байтовые потоки Data

 

В интерфейсах DataInput и DataOutput определены методы потокового ввода-вывода данных простых типов, а классы DataInputStream и DataOutputStream обеспечивают реализацию интерфейсов, предлагаемую по умолчанию. Интерфейсы, предусматривающие потоковый ввод и вывод бинарных данных, обладают почти одинаковой структурой.

Ниже перечислены методы семейств read и write, обеспечивающих ввод и вывод данных каждого из простых типов:

readBoolean (writeBoolean), readChar (writeChar), readByte (writeByte), readShort (writeShort), readInt (writeInt), readLong (writeLong), readFloat (writeFloat), readDouble (writeDouble), readUTF (writeUTF)

Методы интерфейса DataInput обычно реагируют на событие достижения конца потока, выбрасывая исключение типа EOFException, производного от IOException.

 

Для каждого интерфейса Data существует определённый поток Data. Кроме того, имеется класс RandomAccessFile, который реализует одновременно оба интерфейса Data – ввода и вывода. Каждый класс Data является расширением соответствующего класса Filter, так что потоки Data могут использоваться в целях фильтрации данных других потоков. В составе каждого класса Data есть конструкторы, в качестве параметра принимающие ссылки на другой подходящий поток ввода или вывода.

Примеры реализации соответствующих методов приведены в примере 4.

 

Пример 4

public static void writeData(double[] data, String file) throws IOException {
     OutputStream fout = new FileOutputStream(file);

         DataOutputStream out = new DataOutputStream(fout);

         out.writeInt(data.length);

        for (int i =0; i < data.length; i++)

             out.writeDouble(data[i]);

       out.close();

}

 

public static double[] readData(String file) throws IOException {
        InputStream fin = new FileInputStream(file);

          DataInputStream in = new DataInputStream(fin);

             double[] data = new double[in.readInt()];

            for (int i =0; i < data.length; i++)

                data[i] = in.readDouble();

            in.close();

            return data;

}

 

Класс File

 

Класс File реализует ряд полезных средств для работы с файлами и директориями: создание, проверка атрибутов, удаление, переименование. В составе класса File также объявлены методы, которые позволяют создавать временные файлы, используемые, например, для хранения промежуточных результатов и удаляемые при завершении работы программы.

 

Потоки File

 

Потоковые классы семейства File – FileInputStream, FileOutputStream, FileReader и FileWriter – позволяют трактовать файл как поток, предназначенный для ввода(чтения) или вывода(записи) данных. В составе каждого из типов предусмотрены три конструктора, принимающие в качестве параметра одно из следующих значений:

• строку String, задающую имя файла

• объект класса File, указывающий на файл

• объект FileDescriptor

Объект FileDescriptor служит для представления сущности, описывающей открытый файл и определяемой особенностями применяемой операционной системы. Если при использовании байтового или символьного потоков ввода (чтения), обнаруживается, что файл не существует, выбрасывается исключение типа FileNotFoundException. При доступе к файлу во всех случаях проверяется наличие соответствующих полномочий: если необходимые права на обращение к файлу отсутствуют, генерируется исключение типа SecurityException. Два первых конструктора при построении байтового или символьного потоков вывода(записи) предусматривают создание файла, если такого не существует, при наличии файла его содержимое усекается.

 

 

Сериализация объектов. Подготовка классов к сериализации. Порядок сериализации и десериализации. Настройка механизма сериализации. Контроль версий объектов.

 

Сериализация объектов

 

Одной из существенных возможностей многих реальных приложений является их способность преобразовывать объекты в байтовые потоки, которые могут передаваться в пределах сети - например, с целью использования в рамках технологии удалённого вызова метода, - сохраняться на диске в виде файлов, а затем при необходимости восстанавливаться в форме «живых» объектов. Процесс преобразования содержимого объекта в поток байтов принято называть сериализацией объекта, а обратное преобразование – восстановление объекта из данных потока – десериализацией.

Потоки Object – ObjectOutputStream и ObjectInputStream - позволяют осуществлять сериализацию и десериализацию объектов. ObjectInputStream и ObjectOutputStream, помимо данных стандартных классов (простых типов, строк и их массивов) позволяют вводить и выводить графы объектов.

Под термином граф объекта имеется в виду, что когда содержимое объекта выводится в поток ObjectOutputStream средствами методов writeObject(), в потоки сохраняются наборы байтов, представляющие и текущий объект, и все другие объекты, на который тот ссылается. Поскольку данные об объекте, подвергшимся сериализации, представляются в форме байтов, в семействе потоков Object отсутствуют символьные разновидности Reader и Writer. Результатом десериализации последовательности байтов, представляющих ранее сериализованный объект, из потока ObjectInputStream с помощью методов readObject служит граф объекта, равнозначный исходному.

Пример 5

// Сериализация

LinkedList ls = new LinkedList();

FileOutputStream fileOut = new FileOutputStream("list");

ObjectOutputStream out = new ObjectOutputStream(fileOut);

out.writeObject(ls);

 

// Десериализация

FileInputStream fileIn = new FileInputStream("list");

ObjectInputStream in = new ObjectInputStream(fileIn);

LinkedList ls = (LinkedList) in.readObject();

 

Подготовка классов к сериализации

 

Необходимым условием возможности осуществления сериализации объектов класса средствами потока ObjectOutputStream является реализация классом интерфейса-маркера Serializable. Поддержка классом интерфейса Serializable свидетельствует о том, что объекты класса готовы к сериализации.

По умолчанию процесс сериализации заключается в сериализации каждого поля объекта, которое не обозначено как transient или static. Данные простых типов и строки сохраняются с помощью того же механизма, какой поддерживается потоками DataOutputStream, а сериализация объектов выполняется средствами метода writeObject().

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

Схема десериализации, предлагаемая по умолчанию, предусматривает считывание из потока байтовых данных, сохранённых в процессе сериализации. Статические поля класса остаются в неприкосновенности – в ходе загрузки класса будут выполнены все обычные процедуры инициализации, и статические поля получат требуемые исходные значения. Каждому полю transient в восстановленном объекте присваивается значение по умолчанию, соответствующее типу этого поля.

Иногда встречаются классы, которые сами по себе допускают возможность сериализации, но их отдельные элементы – нет. Любая попытка осуществления сериализации объекта, не поддерживающего эту возможность, приводит к выбрасыванию исключения типа NotSerializableException.

 

Контроль версий объектов

 

Реализация классов со временем изменяется. Если подобное происходит в промежутке времени между сериализацией и десериализацией объекта этого класса, поток ObjectInputStream способен обнаружить факт внесения изменений. При сохранении объекта вместе с ним записывается уникальный идентификатор номера версии – 64-битовое значение типа long. По умолчанию идентификатор создаётся в виде хеш-кода, построенного на основе информации об именах класса, его членов и базовых интерфейсов. Изменение таких данных служит сигналом о возможной несовместимости версий класса.

При вводе данных об объекте из потока ObjectInputStream считывается также и идентификатор номера версии. Затем предпринимается попытка загрузки соответствующего класса. Если требуемый класс не найден либо идентификатор загруженного класса не совпадает с тем, который считан из потока, метод readObject() выбрасывает исключение типа InvalidClassException.

Если успешно загружены все нужные классы и выявлено совпадение всех идентификаторов, объект, как принято считать, может быть подвергнут десериализации. Таким образом, получается, что любое, даже самое незначительное изменение в структуре класса способно привести к несовместимости версий.

Но во многих случаях модификация классов носит отнюдь не радикальный характер. Существует возможность сохранить совместимость класса, снабжённого дополнительными несущественными нововведениями, с данными ранее сериализованных объектов. Для этого следует принудительно объявить в составе класса специальное поле, содержащее значение идентификатора номера версии, например:

private static final long serialVersionUID =...;

Конкретное значение идентификатора рассчитывается средствами используемой среды разработки Java. Ничто не мешает использовать в качестве идентификатора и любое произвольное значение, если оно фиксируется в самой первой версии класса, но это не самое лучшее решение, так как вы почти наверняка не сможете столь же тщательно сгенерировать идентификатор, как это делают стандартные инструменты и обеспечить его уникальность, чтобы предотвратить возможные конфликты с хеш-кодами других классов.

Теперь, когда поток ObjectInputStream находит описание требуемого класса и сопоставляет его идентификатор с тем, который сохранён вместе с сериализованными данными, проблема несовместимости не возникнет, даже если реализация класса на самом деле подверглась изменению.

 

Ввод/вывод

 

Функции ввода-вывода информации, реализованные в составе стандартного пакета java.io, определены в терминах потоков данных (streams). Потоки данных – это упорядоченные последовательности данных, которым соответствует определённый источник (для потоков ввода) или получатель (для потоков вывода). Большинство типов потоков данных поддерживает методы определённых базовых интерфейсов и абстрактных классов с некоторыми дополнениями.

Пакет java.io охватывает определения типов двух основных разновидностей – символьных потоков и байтовых потоков. Под символьными потоками понимают последовательности 16-битовых символов Unicode, а каждому байту отвечает порция данных длиной 8 бит. Ввод-вывод может быть либо текстовым, либо бинарным. Функции текстового ввода-вывода способны обращаться с потоками символов, поддающихся восприятию человеком (например, с исходными текстами программ), а средства обработки бинарных данных имеют дело с информацией, хранящейся в двоичном виде (например, с битовыми представлениями графических изображений).

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

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

Классы и интерфейсы из состава пакета java.io можно условно разделить на пять обширных групп:

1) типы общего назначения, служащие для построения различных типов байтовых и символьных потоков: потоки ввода и вывода, чтения и записи, а также классы, позволяющие выполнять взаимные преобразования между ними

2) классы, определяющие разновидности потоков: фильтрованные потоки, буферизованные потоки, канальные потоки, а также специальные подвиды таких потоков, такие как поток чтения строк с сохранением нумерации и лексический анализатор потока чтения

3) специализированные потоковые классы и интерфейсы для ввода и вывода значений простых типов и строк

4) классы и интерфейсы для обработки файловых данных в стиле, не зависящем от особенностей платформы

5) классы и интерфейсы, поддерживающие механизм сериализации объектов

Объекты класса IOException используются многими методами ввода-вывода в качестве сигнала о возникновении исключительной ситуации

 

Байтовые поток

 

Существует ряд особенностей, присущих всем байтовым потокам данных: например, все они поддерживают механизм открытия и закрытия. Поток открывается при создании объекта класса и, оставаясь открытым, позволяет осуществлять операции чтения и записи данных. Поток закрывается при вызове метода close() объекта. Закрытие потока служит цели высвобождения системных ресурсов, которые используются потоком. Если поток явно не закрывается, он продолжает существовать и расходовать системные ресурсы. Потоки следует закрывать сразу после завершения их использования.

 

Абстрактный класс InputStream

 

В составе абстрактного класса InputStream объявлены методы, обеспечивающие выполнение функций ввода байтовых данных из определённого источника. InputStream является базовым классом по отношению к потокам ввода, определённых в пакете java. io, и обладает методами, перечисленными ниже.

public abstract int read() throws IOException
Вводит один байт данных и возвращает его в виде целого числа из диапазона от 0 до 255, иными словами, байтовое значение трактуется как целое без знака. Если байтов, готовых для ввода, не существует ввиду достижения конца потока, возвращается значение -1

public int read(byte[] buf, int offset, int count) throws IOException
Вводит байты данных и сохраняет их в части массива buf, максимальное количество байтов, подлежащих вводу, определяется значением count (если 0 – ввод не производится и возвращается 0). Байты заносятся в элементы массива, начиная с buf[offset] и до buf[offset+count-1]. Метод возвращает количество фактически введённых байтов. Если ввиду достижения конца потока не введён ни один байт, возвращается -1. Если первый байт не может быть введён по причине, не связанной с достижением конца потока (например, поток уже закрыт), выбрасывается исключение типа IOException

public int read(byte[] buf) throws IOException
Метод аналогичен предыдущему при условии read(buf, 0, buf.length)

public long skip(long count) throws IOException
Пропускает, самое большее, count байтов либо часть последовательности байтов до конца потока. Возвращает количество фактически пропущенных байтов. Если значение count отрицательно, операция не выполняется

public int available() throws IOException
Возвращает количество байтов, которые могут быть введены или пропущены, не вызывая блокировки. Реализация метода, предлагаемая по умолчанию, возвращает значение 0

public void close() throws IOException
Закрывает ранее открытый поток ввода. Метод используется для высвобождения ресурсов, связанных с потоком. Обращение к закрытому потоку приводит к выбрасыванию исключения типа IOException, но повторное закрытие потока эффектов не вызывает. В реализации метода, предлагаемой по умолчанию, никакие действия не предусмотрены

При реализации InputStream требуется, чтобы производный класс обеспечил практическое воплощение варианта метода read(), осуществляющего ввод единственного байта, поскольку остальные версии read() основываются на первой. Во многих случаях, однако, производительность операций может быть улучшена за счёт переопределения и других методов. Исходные версии методов available() и close() нуждаются в переопределении в соответствии с потребностями конкретных типов потоков.

Пример 1

import java.io.*;

class CountBytes {

public static void main(String[] args) throws IOException {

     InputStream in;

     if (args.length == 0)

          in = System.in;

     else

          in = new FileInputStream(args[0]);

     int total = 0;

     while (in.read()!= -1)

          total++;

    System.out.println(total + " байтов");

}}

 



Поделиться:


Последнее изменение этой страницы: 2021-05-11; просмотров: 187; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.144.252.140 (0.144 с.)