Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Васюткина Технология разработки програм java (Горячев).doc
Скачиваний:
140
Добавлен:
23.03.2016
Размер:
1.84 Mб
Скачать

Каналы обмена информацией. В пакете java.Io есть четыре класса Pipedxxx, организующих обмен информацией между потоками - Thread.

В одном подпроцессе-потоке — источнике информации — создается объект класса PipedWriter или PipedOutputStream, в который записывается информация методами write() этих классов.

В другом подпроцессе-потоке — приемнике информации — формируется объект класса PipedReader или PipedInputStream. Он связывается с объектом-источником с помощью конструктора или специальным методом connect(), и читает информацию методами read().

Источник и приемник можно создать и связать в обратном порядке.

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

Если надо организовать двусторонний обмен информацией, то создаются два канала.

В примере 6.4 метод run() класса Source генерирует информацию, для простоты просто целые числа k, и передает ее в канал методом pw.write(k). Метод run() класса Target читает информацию из канала методом pr.read(). Концы канала связываются с помощью конструктора класса Target.

Пример 6.4. Канал обмена информацией

class Target extends Thread {

private PipedReader pr;

Target(PipedWriter pw) {

try { pr = new PipedReader(pw); }

catch(IOException e) {

System.err.println("From Target(): " + e);

}

}

PipedReader getStream(){ return pr; }

public void run() {

while(true) {

try { System.out.println("Reading: " + pr.read()); }

catch(IOException e) {

System.out.println("The job's finished.");

System.exit(0);

}

}

}

}

class Source extends Thread {

private PipedWriter pw;

Source() { pw = new PipedWriter(); }

PipedWriter getStream() { return pw;}

public void run() {

for (int k = 0; k < 10; k++) {

try {

pw.write(k);

System.out.println("Writing: " + k);

}

catch(Exception e) {

System.err.println("From Source.run(): " + e) ;

}

}

}

}

public class Main {

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

Source s = new Source();

Target t = new Target(s.getStream());

s.start();

t.start();

}

}

Сериализация объектов. Методы классов ObjectInputStream и ObjectOutputStream позволяют прочитать из входного байтового потока или записать в выходной байтовый поток данные сложных типов — объекты, массивы, строки.

Процесс записи объекта в выходной поток получил название сериализации (serialization), а чтения объекта из входного потока и восстановления его в оперативной памяти — десериализации (deserialization).

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

class A implements Serializabie{...}

Для сериализации достаточно создать объект класса ObjectOutputStream, связав его с выходным потоком, и выводить в этот поток объекты методом writeObject().

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

Если в объекте присутствуют ссылки на другие объекты, то они тоже сериализуются, а в них могут быть ссылки на другие объекты, которые опять-таки сериализуются, и получается целое множество связанных между собой сериализуемых объектов. Метод writeObject() распознает две ссылки на один объект и выводит его в выходной поток только один раз. К тому же, он распознает ссылки, замкнутые в кольцо, и избегает зацикливания.

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

Десериализация происходит так же просто, как и сериализация. Нужно только соблюдать порядок чтения элементов потока.

В примере 6.5 мы создаем объект класса GregorianCalendar с текущей датой и временем, сериализуем его в файл date.ser, через три секунды десериализуем и сравниваем с текущим временем.

Пример 6.5. Сериализация объекта

import java.util.*;

class SerDatef {

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

GregorianCalendar d = new GregorianCalendar();

ObjectOutputStream oos = new ObjectOutputStream(

new FileOutputStream("date.ser"));

oos.writeObject(d);

oos.flush();

oos.close();

Thread.sleep(3000);

ObjectInputStream ois = new ObjectInputStream(

new FileInputStream("date.ser"));

GregorianCalendar oldDate = (GregorianCalendar)ois.readObject();

ois.close();

GregorianCalendar newDate = new GregorianCalendar();

System.out.println("Old time = " +

oldDate.get(Calendar.HOUR) +

":" + oldDate.get(Calendar.MINUTE) +

":" + oldDate.get(Calendar.SECOND) +

"\nNew time = " + newDate.get(Calendar.HOUR) +

":" + newDate.get(Calendar.MINUTE) +

":" + newDate.get(Calendar.SECOND));

}

}

Метод writeObject() не записывает в выходной поток поля, помеченные static и transient. Впрочем, это положение можно изменить, переопределив метод writeObject() или задав список сериализуемых полей.

Вообще процесс сериализации можно полностью настроить под свои нужды, переопределив методы ввода/вывода и воспользовавшись вспомогательными классами. Можно даже взять весь процесс на себя, реализовав не интерфейс Serializable, а интерфейс Externalizable, но тогда придется реализовать методы readExternal() и writeExternal(), выполняющие ввод/вывод.