Вязовик Н.А.
Шрифт:
Если же процедура сериализации в корне отличается от стандартной, то для таких классов предназначен альтернативный интерфейс java.io.Externalizable.
При использовании этого интерфейса в поток автоматически записывается только идентификация класса. Сохранить и восстановить всю информацию о состоянии экземпляра должен сам класс. Для этого в нем должны быть объявлены методы writeExternal и readExternal интерфейса Externalizable. Эти методы должны обеспечить сохранение состояния, описываемого полями самого класса и его суперкласса.
При восстановлении Externalizable -объекта экземпляр создается путем вызова конструктора без аргументов, после чего вызывается метод readExternal.
Метод writeExternal имеет сигнатуру:
void writeExternal(ObjectOutput out)
throws IOException;
Для сохранения состояния вызываются методы ObjectOutput, с помощью которых можно записать как примитивные, так и объектные значения. Для корректной работы в соответствующем методе
void readExternal(ObjectInput in)
throws IOException,ClassNotFoundException;
эти значения должны быть считаны в том же самом порядке.
Классы Reader и Writer и их наследники
Рассмотренные классы – наследники InputStream и OutputStream – работают с байтовыми данными. Если с их помощью записывать или считывать текст, то сначала необходимо сопоставить каждому символу его числовой код. Такое соответствие называется кодировкой.
Известно, что Java использует кодировку Unicode, в которой символы представляются двухбайтовым кодом. Байтовые потоки зачастую работают с текстом упрощенно – они просто отбрасывают старший байт каждого символа. В реальных же приложениях могут использовать различные кодировки (даже для русского языка их существует несколько). Поэтому в версии Java 1.1 появился дополнительный набор классов, основывающийся на типах Reader и Writer. Их иерархия представлена на рис. 15.2.
Эта иерархия очень схожа с аналогичной для байтовых потоков InputStream и OutputStream. Главное отличие между ними – Reader и Writer работают с потоком символов ( char ). Только чтение массива символов в Reader описывается методом read(char[]), а запись в Writer – write(char[]).
В таблице 15.1 приведены соответствия классов для байтовых и символьных потоков.
Рис. 15.2. Иерархия классов Reader и Writer.
Таблица 15.1. Соответствие классов для байтовых и символьных потоков.
| Байтовый поток | Символьный поток |
|---|---|
| InputStream | Reader |
| OutputStream | Writer |
| ByteArrayInputStream | CharArrayReader |
| ByteArrayOutputStream | CharArrayWriter |
| Нет аналога | InputStreamReader |
| Нет аналога | OutputStreamWriter |
| FileInputStream | FileReader |
| FileOutputStream | FileWriter |
| FilterInputStream | FilterReader |
| FilterOutputStream | FilterWriter |
| BufferedInputStream | BufferedReader |
| BufferedOutputStream | BufferedWriter |
| PrintStream | PrintWriter |
| DataInputStream | Нет аналога |
| DataOutputStream | Нет аналога |
| ObjectInputStream | Нет аналога |
| ObjectOutputStream | Нет аналога |
| PipedInputStream | PipedReader |
| PipedOutputStream | PipedWriter |
| StringBufferInputStream | StringReader |
| Нет аналога | StringWriter |
| LineNumberInputStream | LineNumberReader |
| PushBackInputStream | PushBackReader |
| SequenceInputStream | Нет аналога |
Как видно из таблицы, различия крайне незначительны и предсказуемы.
Например, конечно же, отсутствует преобразование в символьное представление примитивных типов Java и объектов ( DataInput/Output, ObjectInput/Output ). Добавлены классы-мосты, преобразующие символьные потоки в байтовые: InputStreamReader и OutputStreamWriter. Именно на их основе реализованы FileReader и FileWriter. Метод available класса InputStream в классе Reader отсутствует, он заменен методом ready, возвращающим булевое значение, – готов ли поток к считыванию (то есть будет ли считывание произведено без блокирования).
В остальном же использование символьных потоков идентично работе с байтовыми потоками. Так, программный код для записи символьных данных в файл будет выглядеть примерно следующим образом:
String fileName = "d:\\file.txt";
//Строка, которая будет записана в файл
String data = "Some data to be written and read.\n";
try {
FileWriter fw = new FileWriter(fileName);
BufferedWriter bw = new BufferedWriter(fw);
System.out.println("Write some data to file: " + fileName);
// Несколько раз записать строку
for(int i=(int)(Math.random*10);--i>=0;)
bw.write(data);
bw.close;
// Считываем результат
FileReader fr = new FileReader(fileName);
BufferedReader br = new BufferedReader(fr);
String s = null;
int count = 0;
System.out.println("Read data from file: " + fileName);
// Считывать данные, отображая на экран
while((s=br.readLine)!=null)
System.out.println("row " + ++count + " read:" + s);