1.1.3.1.4 序列化再探讨 从以上技术的讨论中我们不难体会到,序列化是Java之所以能够出色地实现其鼓吹的两大卖点??分布式(distributed)和跨平台(OS independent)的一个重要基础。TIJ(即“Thinking in Java”)谈到I/O系统时,把序列化称为“lightweight persistence”??“轻量级的持久化”,这确实很有意思。
: c$ ^7 a7 m1 A ★为什么叫做“序列”化?/ L4 T- {* u5 r( S2 r. H
开场白里我说更习惯于把“Serialization”称为“序列化”而不是“串行化”,这是有原因的。介绍这个原因之前先回顾一些计算机基本的知识,我们知道现代计算机的内存空间都是线性编址的(什么是“线性”知道吧,就是一个元素只有一个唯一的“前驱”和唯一的“后继”,当然头尾元素是个例外;对于地址来说,它的下一个地址当然不可能有两个,否则就乱套了),“地址”这个概念推广到数据结构,就相当于“指针”,这个在本科低年级大概就知道了。注意了,既然是线性的,那“地址”就可以看作是内存空间的“序号”,说明它的组织是有顺序的,“序号”或者说“序列号”正是“Serialization”机制的一种体现。为什么这么说呢?譬如我们有两个对象a和b,分别是类A和B的实例,它们都是可序列化的,而A和B都有一个类型为C的属性,根据前面我们说过的原则,C当然也必须是可序列化的。
$ x. i1 k. L9 @9 ?: s; z 1. import java.io.*;4 g/ t x* j- @* y
2. ...5 T" W0 H0 S0 l
3. class A implements Serializable
& D+ G' Y( C9 h6 V 4. {* `* g& k+ d( U' ^
5. C c;
+ y- C$ t/ ]2 P2 n- o% @0 F/ E( f 6. .... B$ k- O% {# v, c$ \% b( u" E
7. }0 Q9 u2 l1 x, e1 y1 L
8.; A9 \: n' {8 w& P; j" ` o; K, D
9. class B implements Serializable! Q0 Q: U% t9 T
10. {! o3 K' r9 }1 |+ k: a
11. C c;
: _# p- ^* S& C. T) E 12. ...
8 X; n2 {, Z; f4 }$ `0 j, O 13. }
" A6 @* F7 g7 R, @9 f; U4 z- ]( V 14.: t5 [. K4 s- {
15. class C implements Serializable
t" W, V2 E+ ]! R) j 16. {. b2 W6 d9 U6 a
17. ...4 V; W6 C+ z: b( j' F- d% t- C4 {- `
18. }
Z0 D, e1 F/ I3 N, n 19.
2 G* R: V- H+ I: G' a 20. A a;# O. e- R0 n- U3 j7 Q0 d: v# [
21. B b;. r) c/ v/ T3 |
22. C c1;2 m5 H" A! y1 e2 B( J. H7 n
23. ...
0 h$ x3 ^! |% _5 F- M7 S. t1 z 注意,这里我们在实例化a和b的时候,有意让他们的c属性使用同一个C类型对象的引用,譬如c1,那么请试想一下,但我们序列化a和b的时候,它们的c属性在外部字节流(当然可以不仅仅是文件)里保存的是一份拷贝还是两份拷贝呢?序列化在这里使用的是一种类似于“指针”的方案:它为每个被序列化的对象标上一个“序列号”(serial number),但序列化一个对象的时候,如果其某个属性对象是已经被序列化的,那么这里只向输出流写入该属性的序列号;从字节流恢复被序列化的对象时,也根据序列号找到对应的流来恢复。这就是“序列化”名称的由来!这里我们看到“序列化”和“指针”是极相似的,只不过“指针”是内存空间的地址链,而序列化用的是外部流中的“序列号链”。) \- R$ T; v0 ]* m/ D
使用“序列号”而不是内存地址来标识一个被序列化的对象,是因为从流中恢复对象到内存,其地址可能就未必是原来的地址了??我们需要的只是这些对象之间的引用关系,而不是死板的原始位置,这在RMI中就更是必要,在两台不同的机器之间传递对象(流),根本就不可能指望它们在两台机器上都具有相同的内存地址。 |