按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
return (int)(Math。random() * 10);
}
private Data'' d = {
new Data(r()); new Data(r()); new Data(r())
};
private Worm next;
private char c;
// Value of i == number of segments
Worm(int i; char x) {
System。out。println(〃 Worm constructor: 〃 + i);
c = x;
if(……i 》 0)
next = new Worm(i; (char)(x + 1));
}
Worm() {
System。out。println(〃Default constructor〃);
}
316
…………………………………………………………Page 318……………………………………………………………
public String toString() {
String s = 〃:〃 + c + 〃(〃;
for(int i = 0; i 《 d。length; i++)
s += d'i'。toString();
s += 〃)〃;
if(next != null)
s += next。toString();
return s;
}
public static void main(String'' args) {
Worm w = new Worm(6; 'a');
System。out。println(〃w = 〃 + w);
try {
ObjectOutputStream out =
new ObjectOutputStream(
new FileOutputStream(〃worm。out〃));
out。writeObject(〃Worm storage〃);
out。writeObject(w);
out。close(); // Also flushes output
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream(〃worm。out〃));
String s = (String)in。readObject();
Worm w2 = (Worm)in。readObject();
System。out。println(s + 〃; w2 = 〃 + w2);
} catch(Exception e) {
e。printStackTrace();
}
try {
ByteArrayOutputStream bout =
new ByteArrayOutputStream();
ObjectOutputStream out =
new ObjectOutputStream(bout);
out。writeObject(〃Worm storage〃);
out。writeObject(w);
out。flush();
ObjectInputStream in =
new ObjectInputStream(
new ByteArrayInputStream(
bout。toByteArray()));
String s = (String)in。readObject();
Worm w3 = (Worm)in。readObject();
System。out。println(s + 〃; w3 = 〃 + w3);
} catch(Exception e) {
e。printStackTrace();
}
}
} ///:~
更有趣的是,Worm 内的Data 对象数组是用随机数字初始化的(这样便不用怀疑编译器保留了某种原始信
息)。每个 Worm 段都用一个 Char 标记。这个 Char 是在重复生成链接的Worm 列表时自动产生的。创建一个
Worm 时,需告诉构建器希望它有多长。为产生下一个句柄(next ),它总是用减去 1 的长度来调用Worm 构
317
…………………………………………………………Page 319……………………………………………………………
建器。最后一个next 句柄则保持为null (空),表示已抵达Worm 的尾部。
上面的所有操作都是为了加深事情的复杂程度,加大对象序列化的难度。然而,真正的序列化过程却是非常
简单的。一旦从另外某个流里创建了ObjectOutputStream ,writeObject()就会序列化对象。注意也可以为
一个String 调用 writeObject() 。亦可使用与DataOutputStream 相同的方法写入所有基本数据类型(它们
有相同的接口)。
有两个单独的try 块看起来是类似的。第一个读写的是文件,而另一个读写的是一个 ByteArray (字节数
组)。可利用对任何DataInputStream 或者DataOutputStream 的序列化来读写特定的对象;正如在关于连网
的那一章会讲到的那样,这些对象甚至包括网络。一次循环后的输出结果如下:
Worm constructor: 6
Worm constructor: 5
Worm constructor: 4
Worm constructor: 3
Worm constructor: 2
Worm constructor: 1
w = :a(262):b(100):c(396):d(480):e(316):f(398)
Worm storage; w2 = :a(262):b(100):c(396):d(480):e(316):f(398)
Worm storage; w3 = :a(262):b(100):c(396):d(480):e(316):f(398)
可以看出,装配回原状的对象确实包含了原来那个对象里包含的所有链接。
注意在对一个Serializable (可序列化)对象进行重新装配的过程中,不会调用任何构建器(甚至默认构建
器)。整个对象都是通过从 InputStream 中取得数据恢复的。
作为Java 1。1 特性的一种,我们注意到对象的序列化并不属于新的 Reader 和 Writer 层次结构的一部分,而
是沿用老式的 InputStream 和OutputStream 结构。所以在一些特殊的场合下,不得不混合使用两种类型的层
次结构。
10。9。1 寻找类
读者或许会奇怪为什么需要一个对象从它的序列化状态中恢复。举个例子来说,假定我们序列化一个对象,
并通过网络将其作为文件传送给另一台机器。此时,位于另一台机器的程序可以只用文件目录来重新构造这
个对象吗?
回答这个问题的最好方法就是做一个实验。下面这个文件位于本章的子目录下:
//: Alien。java
// A serializable class
import java。io。*;
public class Alien implements Serializable {
} ///:~
用于创建和序列化一个 Alien 对象的文件位于相同的目录下:
//: FreezeAlien。java
// Create a serialized output file
import java。io。*;
public class FreezeAlien {
public static void main(String'' args)
throws Exception {
ObjectOutput out =
new ObjectOutputStream(
new FileOutputStream(〃file。x〃));
Alien zorcon = new Alien();
318
…………………………………………………………Page 320……………………………………………………………
out。writeObject(zorcon);
}
} ///:~
该程序并不是捕获和控制违例,而是将违例简单、直接地传递到main()外部,这样便能在命令行报告它们。
程序编译并运行后,将结果产生的 file。x 复制到名为 xfiles 的子目录,代码如下:
//: ThawAlien。java
// Try to recover a serialized file without the
// class of object that's stored in that file。
package c10。xfiles;
import java。io。*;
public class ThawAlien {
public static void main(String'' args)
throws Exception {
ObjectInputStream in =
new ObjectInputStream(
new FileInputStream(〃file。x〃));
Object mystery = in。readObject();
System。out。println(
mystery。getClass()。toString());
}
} ///:~
该程序能打开文件,并成功读取mystery 对象中的内容。然而,一旦尝试查找与对象有关的任何资料——这
要求Alien 的Class 对象——Java 虚拟机(JVM)便找不到Alien。class (除非它正好在类路