. В пакете java.io есть четыре класса pipedxxx, облегчающие передачу данных между подпроцессами.
В одном подпроцессе — источнике информации — создается объект класса PipedWriter+ или PipedOutputStream, в который записывается информация методами write () этих классов.
В другом.подпроцессе —приемнике информации — формируется объект класса PipedReader или PipedInputStream. Он связывается с объектом-источником с помощью конструктора или специальным методом connect (), и читает информацию методами read ().
Источник и приемник можно создать и связать в обратном порядке.
Так создается однонаправленный канал (pipe) информации. На самом деле это некоторая область оперативной памяти, к которой организован совместный доступ двух или более подпроцессов. Доступ синхронизируется, записывающие процессы не могут помешать чтению.
Если надо организовать двусторонний обмен информацией, то создаются два канала.
В листинге 18.5 метод run о класса source генерирует информацию, для простоты просто целые числа k, и передает £е в канал методом pw. write (k). Метод run() класса Target читает информацию из канала методом pr.read(). Концы канала связываются с помощью конструктора класса Target. На Рисунок 18.6 видна последовательность записи и чтения информации.
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);
|
}
}
}
class PipedPrWr{
public static void main(String[] args){
Source s = new Source();
Target t = new Target(s.getStream());
s.start();
t.start();
}
}
Сериализация объектов
Методы классов ObjectInputStream и ObjectOutputStream позволяют прочитать из входного байтового потока или записать в выходной байтовый поток данные сложных типов — объекты, массивы, строки — подобно тому, как методы классов DataInputStream и DataOutputStream читают и записывают данные простых типов.
Классы Objeetxxx содержат методы как для чтений, так и записи полей объектов и элементов массивов.
Процесс записи объекта в выходной поток получил название сериализации (serialization), а чтения объекта из входного потока и восстановления его в оперативной памяти — десериализации(deserialization).
Сериализация объекта нарушает его безопасность, так как объект в потоке может быть доступен для внешних программ (доступность имеется и для скрытых и защищенных полей объекта)
Сериализации можно подвергнуть не каждый объект, а только тот, который реализует интерфейс Serializable. Этот интерфейс не содержит ни полей, ни методов. Реализовать в нем нечего. По сути дела запись
class A implements Serializable{...}
это только пометка, разрешающая сериализацию класса А.
Как всегда в Java, процесс сериализации максимально автоматизирован. Достаточно создать объект класса ObjectOutputStream, связав его с выходным потоком, и выводить в этот поток объекты методом writeObject():
MyClass me = new MyClass("abc", -12, 5.67e-5);
int[] arr = {10, 20, 30};
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("myobjects.ser"));
oos.writeObject(me);
oos.writeObject(arr);
oos.writeObject("Some string");
oos.writeObject (new Date());
oos.flush();
В выходной поток выводятся все нестатические поля объекта, независимо от прав доступа к ним, а также сведения о классе этого объекта, необходимые для его правильного восстановления при десериализации. Байт-коды методов (функции и процедуры) класса не сериализуются.
|
Если в объекте присутствуют ссылки на другие объекты, то они тоже сериализуются, а в них могут быть ссылки на другие объекты, которые опять-таки сериализуются, и получается целое множество связанных между собой сериализуемых объектов. Метод writeObject() распознает две ссылки на один объект и выводит его в выходной поток только один раз. К тому же, он распознает ссылки, замкнутые в кольцо, и избегает зацикливания.
Все классы объектов, входящих в такое сериализуемое множество, а также все их внутренние классы, должны реализовать интерфейс Serializable, в противном случае будет выброшено исключение класса NotserializableException и процесс сериализации прервется. Многие классы J2SDK реализуют этот интерфейс (все потомки таких классов наследуют реализацию). Например, класс java.awt.Component реализует интерфейс Serializable, значит, все графические компоненты можно сериализовать. Не реализуют этот интерфейс обычно классы, тесно связанные с выполнением программ, например, java.awt.Toolkit. Состояние экземпляров таких классов нет смысла сохранять или передавать по сети. Не реализуют интерфейс Serializable и классы, содержащие внутренние сведения Java "для служебного пользования".
Десериализация происходит так же просто, как и сериализация:
ObjectlnputStream ois = new ObjectInputStream(
new FilelnputStream("myobjects.ser"));
MyClass mcl = (MyClass)ois.readObject();
int[] a = (int[]) ois.readObject();
String s = (String) ois.readObject();
|
Date d = (Date) ois.readObject();
Нужно только соблюдать порядок чтения элементов потока. Например:
import java.io.*;
import java.util.*;
public class SerDate {
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));
}
}
Если не нужно сериализовать какое-то поле, то достаточно пометить его служебным словом transient, например:
transient MyClass me = new MyClass("abc", -12, 5.67e-5);
Метод writeObject() не записывает в выходной поток поля, помеченные static и transient. Впрочем, это положение можно изменить, переопределив метод writeObject() или задав список сериализуемых полей.
Вообще процесс сериализации можно полностью настроить под свои нужды, переопределив методы ввода/вывода и воспользовавшись вспомогательными классами. Можно даже взять весь процесс на себя, реализовав не интерфейс Serializable, а интерфейс Externalizable, но тогда придется реализовать методы readExternal () и writeExternal(), выполняющие ввод/вывод.
Печать в Java
Поскольку принтер — устройство графическое, вывод на печать очень похож на вывод графических объектов на экран.
В графическом компоненте кроме графического контекста — объекта класса Graphics, создается еще "печатный контекст". Это тоже объект класса Graphics, но реализующий интерфейс printGraphics и полученный из другого источника — объекта класса PrintJob, входящего в пакет java.awt. Сам же этот объект создается с помощью класса Toolkit пакета java.awt. На практике это выглядит так:
PrintJob pj = getToolkit().get,PrintJob (this, "Job Title", null);
Graphics pg = pj.getGraphics();
Метод getPrintJob () сначала выводит на экран стандартное окно "Печать" (Print) операционной системы. Когда пользователь выберет в этом окне параметры печати и начнет печать кнопкой ОК, создается объект pj. Если пользователь отказывается от печати при помощи кнопки Отмена (Cancel), то метод возвращает null.
В классе Toolkit два метода getPrintJob ():
getPrintJob(Frame frame, String jobTitle, JobAttributes jobAttr,
PageAttributes pageAttr)
getPrintJob(Frame frame, String jobTitle, Properties prop)
Аргумент frame указывает на окно верхнего уровня, управляющее печатью. Этот аргумент не может быть null. Строка jobTitle задает заголовок задания, который не печатается, и может быть равна null. Аргумент prop зависит от реализации системы печати, часто это просто null, в данном случае задаются стандартные параметры печати.
Аргумент jobAttr задает параметры печати.
Аргумент pageAttr задает параметры страницы. Класс pageProperties содержит пять подклассов со статическими константами, которые и задают параметры страницы и используются в конструкторе класса. Если для печати достаточно стандартных параметров, то можно воспользоваться конструктором по умолчанию.
После того как "печатный контекст" — объект pg класса Graphics — определен, можно вызывать метод print(pg) или printAll(pg) класса Component. Этот метод устанавливает связь с принтером по умолчанию и вызывает метод paint (pg). На печать выводится все то, что задано этим методом.
Например, чтобы распечатать текстовый файл, надо в процессе ввода разбить его текст на строки и в методе paint (pg) вывести строки методом pg.drawstring() так же, как выводят на экран. При этом следует учесть, что в "печатном контексте" нет шрифта по умолчанию, всегда надо устанавливать шрифт методом pg.setFont ().
После выполнения всех методов print() применяется метод pg.dispose(), вызывающий прогон страницы, и метод pj.end(), заканчивающий печать.
Например:
import java.awt.*;
import java.awt.event.*;
class PrintTest extends Frame{
PrintTest(String s){
super (s);
setSize(400, 400);
setVisible(true);
}
public void simplePrint (){
PrintJob pj =getToolkit().getPrintJob(this, "JobTitle", null);
if (pj!= null){
Graphics pg = pj.getGraphics();
if (pg!= null){
print(pg);
pg.dispose();
}
else System. err. println("Graphics's null");
pj.end();
}
else System. err. println("Job's null");
}
public void paint(Graphics g){
g.setFont(new Font("Serif", Font. ITALIC, 30));
g.setColor(Color. black);
g.drawArc(0, 100, 200, 200, 0, 360);
g.drawString("Страница 1", 100, 100);
}
public static void main(String[] args){
PrintTest pt = new PrintTest(" Простой гфимер печати");
pt.simplePrint();
pt.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent ev){
System. exit (0);
}
});
}
}