操作“耐久性”
一个较量诱人的想法是用序列化技能生存措施的一些状态信息,从而将措施利便地规复到以前的状态。但在详细实现以前,有些问题是必需办理的。假如两个工具都有指向第三个工具的句柄,该如何对这两个工具序列化呢?假如从两个工具序列化后的状态规复它们,第三个工具的句柄只会呈此刻一个工具身上吗?假如将这两个工具序列化成独立的文件,然后在代码的差异部门从头装配它们,又会获得什么功效呢?
下面这个例子对上述问题举办了很好的说明:
//: MyWorld.java import java.io.*; import java.util.*; class House implements Serializable {} class Animal implements Serializable { String name; House preferredHouse; Animal(String nm, House h) { name = nm; preferredHouse = h; } public String toString() { return name + "[" + super.toString() + "], " + preferredHouse + "\n"; } } public class MyWorld { public static void main(String[] args) { House house = new House(); Vector animals = new Vector(); animals.addElement( new Animal("Bosco the dog", house)); animals.addElement( new Animal("Ralph the hamster", house)); animals.addElement( new Animal("Fronk the cat", house)); System.out.println("animals: " + animals); try { ByteArrayOutputStream buf1 = new ByteArrayOutputStream(); ObjectOutputStream o1 = new ObjectOutputStream(buf1); o1.writeObject(animals); o1.writeObject(animals); // Write a 2nd set // Write to a different stream: ByteArrayOutputStream buf2 = new ByteArrayOutputStream(); ObjectOutputStream o2 = new ObjectOutputStream(buf2); o2.writeObject(animals); // Now get them back: ObjectInputStream in1 = new ObjectInputStream( new ByteArrayInputStream( buf1.toByteArray())); ObjectInputStream in2 = new ObjectInputStream( new ByteArrayInputStream( buf2.toByteArray())); Vector animals1 = (Vector)in1.readObject(); Vector animals2 = (Vector)in1.readObject(); Vector animals3 = (Vector)in2.readObject(); System.out.println("animals1: " + animals1); System.out.println("animals2: " + animals2); System.out.println("animals3: " + animals3); } catch(Exception e) { e.printStackTrace(); } } } ///:~
这里一件有趣的工作是也许是能针对一个字节数组应用工具的序列化,从而实现对任何Serializable(可序列化)工具的一个“全面复制”(全面复制意味着复制的是整个工具网,而不只是根基工具和它的句柄)。复制问题将在第12章举办全面报告。
Animal工具包括了范例为House的字段。在main()中,会建设这些Animal的一个Vector,并对其序列化两次,别离送入两个差异的数据流内。这些数据从头装配并打印出来后,可看到下面这样的功效(工具在每次运行时城市处在差异的内存位置,所以每次运行的功效有区别):
animals: [Bosco the dog[[email protected]], [email protected] , Ralph the hamster[[email protected]], [email protected] , Fronk the cat[[email protected]], [email protected] ] animals1: [Bosco the dog[[email protected]], [email protected] , Ralph the hamster[[email protected]], [email protected] , Fronk the cat[[email protected]], [email protected] ] animals2: [Bosco the dog[[email protected]], [email protected] , Ralph the hamster[[email protected]], [email protected] , Fronk the cat[[email protected]], [email protected] ] animals3: [Bosco the dog[[email protected]], [email protected] , Ralph the hamster[[email protected]], [email protected] , Fronk the cat[[email protected]], [email protected] ]
虽然,我们但愿装配好的工具有与本来差异的地点。但留意在animals1和animals2中呈现了沟通的地点,个中包罗共享的、对House工具的引用。在另一方面,当animals3规复今后,系统没有步伐知道另一个流内的工具是第一个流内工具的化身,所以会发生一个完全差异的工具网。
只要将所有对象都序列化到单唯一个数据流里,就能规复得到与以前写入时完全一样的工具网,不会不慎造成工具的反复。虽然,在写第一个和最后一个工具的时间之间,可改变工具的状态,但那必需由我们明晰采纳操纵——序列化时,工具会回收它们其时的任何状态(包罗它们与其他工具的毗连干系)写入。
若想生存系统状态,最安详的做法是看成一种“微观”操纵序列化。假如序列化了某些对象,再去做其他一些事情,再来序列化更多的对象,以此类推,那么最终将无法安详地生存系统状态。相反,应将组成系统状态的所有工具都置入单个荟萃内,并在一次操纵里完成谁人荟萃的写入。这样一来,同样只需一次要领挪用,即可乐陋习复之。
下面这个例子是一套假想的计较机帮助设计(CAD)系统,对这一要领举办了很好的演示。另外,它还为我们引入了static字段的问题——如寄望联机文档,就会发明Class是“Serializable”(可序列化)的,所以只需简朴地序列化Class工具,就能实现static字段的生存。这无论如何都是一种明智的做法。
//: CADState.java // Saving and restoring the state of a // pretend CAD system. import java.io.*; import java.util.*; abstract class Shape implements Serializable { public static final int RED = 1, BLUE = 2, GREEN = 3; private int xPos, yPos, dimension; private static Random r = new Random(); private static int counter = 0; abstract public void setColor(int newColor); abstract public int getColor(); public Shape(int xVal, int yVal, int dim) { xPos = xVal; yPos = yVal; dimension = dim; } public String toString() { return getClass().toString() + " color[" + getColor() + "] xPos[" + xPos + "] yPos[" + yPos + "] dim[" + dimension + "]\n"; } public static Shape randomFactory() { int xVal = r.nextInt() % 100; int yVal = r.nextInt() % 100; int dim = r.nextInt() % 100; switch(counter++ % 3) { default: case 0: return new Circle(xVal, yVal, dim); case 1: return new Square(xVal, yVal, dim); case 2: return new Line(xVal, yVal, dim); } } } class Circle extends Shape { private static int color = RED; public Circle(int xVal, int yVal, int dim) { super(xVal, yVal, dim); } public void setColor(int newColor) { color = newColor; } public int getColor() { return color; } } class Square extends Shape { private static int color; public Square(int xVal, int yVal, int dim) { super(xVal, yVal, dim); color = RED; } public void setColor(int newColor) { color = newColor; } public int getColor() { return color; } } class Line extends Shape { private static int color = RED; public static void serializeStaticState(ObjectOutputStream os) throws IOException { os.writeInt(color); } public static void deserializeStaticState(ObjectInputStream os) throws IOException { color = os.readInt(); } public Line(int xVal, int yVal, int dim) { super(xVal, yVal, dim); } public void setColor(int newColor) { color = newColor; } public int getColor() { return color; } } public class CADState { public static void main(String[] args) throws Exception { Vector shapeTypes, shapes; if(args.length == 0) { shapeTypes = new Vector(); shapes = new Vector(); // Add handles to the class objects: shapeTypes.addElement(Circle.class); shapeTypes.addElement(Square.class); shapeTypes.addElement(Line.class); // Make some shapes: for(int i = 0; i < 10; i++) shapes.addElement(Shape.randomFactory()); // Set all the static colors to GREEN: for(int i = 0; i < 10; i++) ((Shape)shapes.elementAt(i)) .setColor(Shape.GREEN); // Save the state vector: ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("CADState.out")); out.writeObject(shapeTypes); Line.serializeStaticState(out); out.writeObject(shapes); } else { // There's a command-line argument ObjectInputStream in = new ObjectInputStream( new FileInputStream(args[0])); // Read in the same order they were written: shapeTypes = (Vector)in.readObject(); Line.deserializeStaticState(in); shapes = (Vector)in.readObject(); } // Display the shapes: System.out.println(shapes); } } ///:~
#p#分页标题#e#
Shape(几许形状)类“实现了可序列化”(implements Serializable),所以从Shape担任的任何对象也城市自动“可序列化”。每个Shape都包括了数据,并且每个衍生的Shape类都包括了一个非凡的static字段,用于抉择所有那些范例的Shape的颜色(如将一个static字段置入基本类,功效只会发生一个字段,因为static字段未在衍生类中复制)。可对基本类中的要领举办包围处理惩罚,以便为差异的范例配置颜色(static要领不会动态绑定,所以这些都是普通的要领)。每次挪用randomFactory()要领时,它城市建设一个差异的Shape(Shape值回收随机值)。
Circle(圆)和Square(矩形)属于对Shape的直接扩展;独一的不同是Circle在界说时会初始化颜色,而Square在构建器中初始化。Line(直线)的问题将留到今后接头。
在main()中,一个Vector用于容纳Class工具,而另一个用于容纳形状。若不提供相应的呼吁行参数,就会建设shapeTypes Vector,并添加Class工具。然后建设shapes Vector,并添加Shape工具。接下来,所有static color值城市设成GREEN,并且所有对象城市序列化到文件CADState.out。
若提供了一个呼吁行参数(假设CADState.out),便会打开谁人文件,并用它规复措施的状态。无论在哪种环境下,功效发生的Shape的Vector城市打印出来。下面列出它某一次运行的功效:
>java CADState [class Circle color[3] xPos[-51] yPos[-99] dim[38] , class Square color[3] xPos[2] yPos[61] dim[-46] , class Line color[3] xPos[51] yPos[73] dim[64] , class Circle color[3] xPos[-70] yPos[1] dim[16] , class Square color[3] xPos[3] yPos[94] dim[-36] , class Line color[3] xPos[-84] yPos[-21] dim[-35] , class Circle color[3] xPos[-75] yPos[-43] dim[22] , class Square color[3] xPos[81] yPos[30] dim[-45] , class Line color[3] xPos[-29] yPos[92] dim[17] , class Circle color[3] xPos[17] yPos[90] dim[-76] ] >java CADState CADState.out [class Circle color[1] xPos[-51] yPos[-99] dim[38] , class Square color[0] xPos[2] yPos[61] dim[-46] , class Line color[3] xPos[51] yPos[73] dim[64] , class Circle color[1] xPos[-70] yPos[1] dim[16] , class Square color[0] xPos[3] yPos[94] dim[-36] , class Line color[3] xPos[-84] yPos[-21] dim[-35] , class Circle color[1] xPos[-75] yPos[-43] dim[22] , class Square color[0] xPos[81] yPos[30] dim[-45] , class Line color[3] xPos[-29] yPos[92] dim[17] , class Circle color[1] xPos[17] yPos[90] dim[-76] ]
#p#分页标题#e#
从中可以看出,xPos,yPos以及dim的值都已乐成生存和规复出来。但在获取static信息时却呈现了问题。所有“3”都已进入,但没有正常地出来。Circle有一个1值(界说为RED),而Square有一个0值(记着,它们是在构建器里初始化的)。看上去好像static基础没有获得初始化!实情正是如此——尽量类Class是“可以序列化的”,但却不能按我们但愿的事情。所以如果想序列化static值,必需亲自动手。
这正是Line中的serializeStaticState()和deserializeStaticState()两个static要领的用途。可以看到,这两个要领都是作为存储和规复历程的一部门明晰挪用的(留意写入序列化文件和从中读回的顺序不能改变)。所觉得了使CADState.java正确运行起来,必需回收下述三种要领之一:
(1) 为几许形状添加一个serializeStaticState()和deserializeStaticState()。
(2) 删除Vector shapeTypes以及与之有关的所有代码
(3) 在几许形状内添加对新序列化和除掉序列化静态要领的挪用
要留意的另一个问题是安详,因为序列化处理惩罚也会将private数据生存下来。若有需要保密的字段,应将其标志成transient。但在这之后,必需设计一种安详的信息生存要领。这样一来,一旦需要规复,就可以重设那些private变量。