Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
билеты по java (нет 16 и 23) .doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
111.62 Кб
Скачать
  1. Потоки ввода и вывода

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

Поток данных позволяет получать или передавать данные. Большинство потоков имеют направление – одни позволяют записывать данные, отправляя их (output streams), другие читают данные с какого-либо источника (input streams).

Иерархия i\o классов содержит достаточно много версий реализации потоков, применяемых в разных случаях для ускорения процессов передачи данных. Однако в корне иерархии здесь стоят всего 2 асбрактных класса InputStream и OutputStream.

Рассмотрим основные методы этих классов, которые наследуются в реализациях и которые всегда можно вызывать у объектов такого типа:

InputStream:

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

int read(byte[ ] byteArray) – почти то же самое, только результат чтения сохраняется в указанный байтовый массив-буффер. При этом чтение происходит уже не по одному байту, а поток пытается считать число байт, равное размеру массива, возвращая значение фактически считанных байт. Обратите внимание, что если в качестве параметра подать null, получится ошибка времени выполнения.

OutputStream:

void write(int b) – записывает подаваемый параметр-бат в поток. Обратите внимание, что подаётся значение типа int, но фактически запишется byte, при этом все старшие разряды, недоступные для хранения в типе byte, просто отбрасываются.

void write(byte[ ] b) – метод пишет подаваемый массив в поток.

void flush() – при вызове метода, все неотправленные байты находящиеся «внутри» потока принудительно отсылаются. Дело в том, что отправлять данные по одному байту, даже если так их предполагалось писать в поток, часто бывает неэкономно и в разных реализациях у потока присутствует внутренний буфер. При вызове методов типа write() данные могут попадать туда, а записываться, когда буфер заполняется. При этом закрыв поток в какой-то момент, мы не можем быть уверены, что все данные были отосланы, для этого и следует вызывать этот метод.

void close() – метод закрывает поток и освобождает ресурсы связанные с ним. Часто бывает, что пока покток открыт (особенно, если поток открыт для записи), нельзя обращаться к тому ресурсу, в который происходит запись (например, файл), для этого поток надо закрывать после использования. Однако, стоит учесть, что закрытый поток не может быть открыт заново, для продолжения записи нужно создавать новый. Метод close() присутствует и у класса InputStream, однако там его вызов не столь принципиален, т.к. чтение, как правило, не препятствует записи и доступу к ресурсу-источнику.

  1. Сериализация

Процедура преобразования объектов в последовательность байт получила название - сериализация (serialization), обратное действие, – то есть воссоздание объекта из последовательности байт – десериализация.

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

Для представления объектов в виде последовательности байт определены унаследованные от DataInput и DataOutput интерфейсы ObjectInput и ObjectOutput, соответственно. В java.io имеются реализации этих интерфейсов – классы ObjectInputStream и ObjectOutputStream.

Эти классы используют стандартный механизм сериализации, который предлагает JVM. Для того, чтобы объект мог быть сериализован, класс, от которого он порожден, должен реализовывать интерфейс java.io.Serializable. В этом интерфейсе не определен ни один метод. Он нужен лишь для указания, что объекты класса могут участвовать в сериализации. При попытке сериализовать объект, не имеющий такого интерфейса, будет брошен java.io.NotSerializableException.

Чтобы начать сериализацию объекта, нужен выходной поток OutputStream, в который и будет записываться сгенерированная последовательность байт. Этот поток передается в конструктор ObjectOutputStream. Затем вызовом метода writeObject() объект сериализуется и записывается в выходной поток.

Итак, сериализация объекта заключается в сохранении и восстановлении состояния объекта.

С помощью DataOutput интерфейса можно легко сохранить значения всех доступных полей (будем для простоты считать, что они все примитивного типа). Однако в большинстве случаев в родительских классах могут быть объявлены недоступные нам поля (например, private). Тем не менее, такие поля, как правило, играют важную роль в определении состояния объекта, так как они могут влиять на результат работы унаследованных методов.

Для исключения поля объекта из сериализации его необходимо объявить с модификатором transient.

Особого внимания требуют статические поля. Поскольку они принадлежат классу, а не объекту, они не участвуют в сериализации. При восстановлении объект будет работать с таким значением static-поля, которое уже установлено для его класса в этой JVM.