友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第59部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
—只能认为这是序列化机制玩的一个把戏。
在任何情况下,接口中的定义的任何东西都会自动具有public 属性,所以假若writeObject()和
readObject()必须为 private,那么它们不能成为接口(interface)的一部分。但由于我们准确地加上了签
名,所以最终的效果实际与实现一个接口是相同的。
看起来似乎我们调用ObjectOutputStream。writeObject()的时候,我们传递给它的Serializable 对象似乎
会被检查是否实现了自己的writeObject() 。若答案是肯定的是,便会跳过常规的序列化过程,并调用
writeObject() 。readObject()也会遇到同样的情况。
还存在另一个问题。在我们的writeObject() 内部,可以调用defaultWriteObject(),从而决定采取默认的
writeObject()行动。类似地,在 readObject()内部,可以调用defaultReadObject()。下面这个简单的例子
演示了如何对一个 Serializable 对象的存储与恢复进行控制:
//: SerialCtl。java
// Controlling serialization by adding your own
324
…………………………………………………………Page 326……………………………………………………………
// writeObject() and readObject() methods。
import java。io。*;
public class SerialCtl implements Serializable {
String a;
transient String b;
public SerialCtl(String aa; String bb) {
a = 〃Not Transient: 〃 + aa;
b = 〃Transient: 〃 + bb;
}
public String toString() {
return a + 〃n〃 + b;
}
private void
writeObject(ObjectOutputStream stream)
throws IOException {
stream。defaultWriteObject();
stream。writeObject(b);
}
private void
readObject(ObjectInputStream stream)
throws IOException; ClassNotFoundException {
stream。defaultReadObject();
b = (String)stream。readObject();
}
public static void main(String'' args) {
SerialCtl sc =
new SerialCtl(〃Test1〃; 〃Test2〃);
System。out。println(〃Before: n〃 + sc);
ByteArrayOutputStream buf =
new ByteArrayOutputStream();
try {
ObjectOutputStream o =
new ObjectOutputStream(buf);
o。writeObject(sc);
// Now get it back:
ObjectInputStream in =
new ObjectInputStream(
new ByteArrayInputStream(
buf。toByteArray()));
SerialCtl sc2 = (SerialCtl)in。readObject();
System。out。println(〃After:n〃 + sc2);
} catch(Exception e) {
e。printStackTrace();
}
}
} ///:~
在这个例子中,一个String 保持原始状态,其他设为transient (临时),以便证明非临时字段会被
defaultWriteObject()方法自动保存,而 transient 字段必须在程序中明确保存和恢复。字段是在构建器内
部初始化的,而不是在定义的时候,这证明了它们不会在重新装配的时候被某些自动化机制初始化。
若准备通过默认机制写入对象的非 transient 部分,那么必须调用defaultWriteObject(),令其作为
325
…………………………………………………………Page 327……………………………………………………………
writeObject() 中的第一个操作;并调用defaultReadObject(),令其作为 readObject()的第一个操作。这些
都是不常见的调用方法。举个例子来说,当我们为一个ObjectOutputStream 调用defaultWriteObject()的
时候,而且没有为其传递参数,就需要采取这种操作,使其知道对象的句柄以及如何写入所有非transient
的部分。这种做法非常不便。
transient 对象的存储与恢复采用了我们更熟悉的代码。现在考虑一下会发生一些什么事情。在 main()中会
创建一个SerialCtl 对象,随后会序列化到一个ObjectOutputStream 里(注意这种情况下使用的是一个缓冲
区,而非文件——与ObjectOutputStream 完全一致)。正式的序列化操作是在下面这行代码里发生的:
o。writeObject(sc);
其中,writeObject()方法必须核查 sc,判断它是否有自己的writeObject()方法(不是检查它的接口——它
根本就没有,也不是检查类的类型,而是利用反射方法实际搜索方法)。若答案是肯定的,就使用那个方
法。类似的情况也会在 readObject()上发生。或许这是解决问题唯一实际的方法,但确实显得有些古怪。
3。 版本问题
有时候可能想改变一个可序列化的类的版本(比如原始类的对象可能保存在数据库中)。尽管这种做法得到
了支持,但一般只应在非常特殊的情况下才用它。此外,它要求操作者对背后的原理有一个比较深的认识,
而我们在这里还不想达到这种深度。JDK 1。1 的HTML 文档对这一主题进行了非常全面的论述(可从Sun 公司
下载,但可能也成了Java 开发包联机文档的一部分)。
10。9。3 利用“持久性”
一个比较诱人的想法是用序列化技术保存程序的一些状态信息,从而将程序方便地恢复到以前的状态。但在
具体实现以前,有些问题是必须解决的。如果两个对象都有指向第三个对象的句柄,该如何对这两个对象序
列化呢?如果从两个对象序列化后的状态恢复它们,第三个对象的句柄只会出现在一个对象身上吗?如果将
这两个对象序列化成独立的文件,然后在代码的不同部分重新装配它们,又会得到什么结果呢?
下面这个例子对上述问题进行了很好的说明:
//: 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));
326
…………………………………………………………Page 328……………………………………………………………
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'Animal@1cc76c'; House@1cc769
; Ralph the hamster'Animal@1cc76d'; House@1cc769
; Fronk the cat'Animal@1cc76e'; House@1cc769
'
animals1: 'Bosco the dog'Animal@1cca0c'; House@1cca16
; Ralph the hamster'Animal@1cca17'; House@1cca16
; Fronk the cat'Animal@1cca1b'; House@1cca16
327
…………………………………………………………Page 329……………………………………………………………
'
animals2: 'Bosco the dog'Animal@1cca0c'; House@1cca16
; Ralph the hamster'Animal@1cca17'; House@1cca16
; Fronk the cat'Animal@1cca1b'; House@1cca16
'
animals3: 'Bosco the dog'Animal@1cca52'; House@1cca5c
; Ralph the hamster'Animal@1cca5d'; House@1cca5c
; Fronk the cat'Animal@1cca61'; House@1cca5c
'
当然,我们希望装配好的对象有与原来不同的地址。但注意在 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;
328
…………………………………………………………Page 330……………………………………………………………
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);
329
…………………………………………………………Page 331……………………………………………………………
}
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 mand…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);
}
} ///:~
Shape (几何形状)类“实现了可序列化”(implements Serializable),所以从Shape 继承的任何东西也
都会自动“可序列化”。每个 Shape 都包含了数据,而且每个衍生的Shape 类都包含了一个特殊的 static 字
段,用于决定所有那些类型的 Shape 的颜色(如将一个 static字段置入基础类,结果只会产生一个字段,因
为 static 字段未在衍生类中复制)。可对基础类中的方法进行覆盖处理,以便为不同的类型设置颜色
330
…………………………………………………………Page 332……………………………………………………………
(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 都会打印出来。下面列出它某一次运行的结果:
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!