全部博文(121)
分类: Java
2008-07-04 16:17:57
一、 关于serialVersionUID:
1. 通常要声明一个序列化版本号,诸如: "private static final long
serialVersionUID = 7526472295622776147L; "
2. 在新的版本中不更改serialVersionUID的值,如果更改将会引起与旧序列化对象的不兼容.
3. 新的序列化类是否可以读出老的序列化对象取决于所作更改的性质.
二、关于不兼容的更改:
1. 对类中某个变量或方法的删除:这样会便得老序列化版本的类在读新序
列化版本的对象时会把一些新版本的值设为默认值来,这样做反而会削弱老版本完成反序列化的能力.
2. 将类的层次颠倒:这样会便得数据在数据流中的序列错误.
3. 更改非静态的field为静态的.或将nontransient 改为transient:对于默认生成的序列化,这种做法等同于删除这个field.这样做这个数据将不会被写入数据流中.当删除一个field的时候,老序列化版本将会初始化一个默认的值,这样会引起不可想象的错误.
4. 更改原来field的数据类型:
5. 更改readObject或者writeObject类,以使得它不再能读或写默认的field,或者更改它们去读/写原来版本中不可以读/写的.
6.
将可序列化类改为Externalizable
类,反之亦然.
7. 将non-enum类改为enum类.
8. 将Serializabe或者Externalizabe去掉.
9. 增加一些可以产生新对象的writeReplace或者readResolve方法.
三、关于readObject 和writeObject:
1. 实现readObject和writeObject的方法必须首先调默认方法.
2. readObject方法必须在Serializable类中实现,在反序列化结束后进行反序列化对象的检验.
3. static 或transient域是不会进行默认的序列化的.
四、需要注意的几点:
1. 内部类尽可能少地被序列化.
2. 容器类应该经常以Hashtable 形式出现而不是大的哈希表数据结构.Hashtable这种形式通过K,V对的存储来实现序列化.
3. 对象能包含其它的对象,而这其它的对象又可以包含另外的对象。JAVA serialization能够自动的处理嵌套的对象。对于一个对象的简单的域,writeObject()直接将值写入流。而,当遇到一个对象域时,writeObject()被再次调用,如果这个对象内嵌另一个对象,那么,writeObject() 又被调用,直到对象能被直接写入流为止。程序员所需要做的是将对象传入ObjectOutputStream 的writeObject() 方法,剩下的将又系统自动完成。
4. 实现Serializable回导致发布的API难以更改,并且使得package-private和private
这两个本来封装的较好的咚咚也不能得到保障了
5. Serializable会为每个类生成一个序列号,生成依据是类名、类实现的接口名、
public和protected方法,所以只要你一不小心改了一个已经publish的API,并且没有自己定义一个long类型的叫做serialVersionUID的field,哪怕只是添加一个getXX,就会让你读原来的序列化到文件中的东西读不出.
6. 不用构造函数用Serializable就可以构造对象,看起来不大合理,这被称为
extralinguistic mechanism,所以当实现Serializable时应该注意维持构造函数中所维
持的那些不变状态
7. 设计用来被继承的类时,尽量不实现Serializable,用来被继承的interface也不要
继承Serializable。但是如果父类不实现Serializable接口,子类很难实现它,特别是
对于父类没有可以访问的不含参数的构造函数的时候。所以,一旦你决定不实现
Serializable接口并且类被用来继承的时候记得提供一个无参数的构造函数.
8. 不管你选择什么序列化形式,声明一个显式的UID:
private static final long serialVersionUID = randomLongValue;
关于嵌套类序列化的一个例子:
import java.io. * ;
class tree implements java.io.Serializable {
public tree left;
public tree right;
public int id;
public int level;
private static int count = 0 ;
public tree( int depth) {
id = count ++ ;
level = depth;
if (depth > 0 ) {
left = new tree(depth - 1 );
right = new tree(depth - 1 );
}
}
public void print( int levels) {
for ( int i = 0 ; i < level; i ++ )
System.out.print( " " );
System.out.println( " node " + id);
if (level <= levels && left != null )
left.print(levels);
if (level <= levels && right != null )
right.print(levels);
}
public static void main (String argv[]) {
try {
/**/ /* 创建一个文件写入序列化树。 */
FileOutputStream ostream = new FileOutputStream( " tree.tmp " );
/**/ /* 创建输出流 */
ObjectOutputStream p = new ObjectOutputStream(ostream);
/**/ /* 创建一个二层的树。 */
tree base = new tree( 2 );
p.writeObject(base); // 将树写入流中。
p.writeObject( " LiLy is 惠止南国 " );
p.flush();
ostream.close(); // 关闭文件。
/**/ /* 打开文件并设置成从中读取对象。 */
FileInputStream istream = new FileInputStream( " tree.tmp " );
ObjectInputStream q = new ObjectInputStream(istream);
/**/ /* 读取树对象,以及所有子树 */
tree new_tree = (tree)q.readObject();
new_tree.print( 2 ); // 打印出树形结构的最上面 2级
String name = (String)q.readObject();
System.out.println( " \n " + name);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
最后结果如下:
node 0
node 1
node 2
node 3
node 4
node 5
node 6
LiLy is 惠止南国
可以看到,在序列化的时候,writeObject与readObject之间的先后顺序。readObject将最先write的object read出来。用数据结构的术语来讲就姑且称之为先进先出吧!
在序列化时,有几点要注意的:
1:当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量。
2:如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。
3:如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个SerializableException。我们可以将这个引用标记为transient,那么对象仍然可以序列化
______________________________________________________________