Understand the Serializable and serialVersionUID

看了一篇简单介绍serialVersionUID的文章,在这里结合javaAPI文档和自己的理解说一下Serializable和serialVersionUID。

[size=medium][color=darkblue]一、Serializable[/color][/size]

在JavaAPI中已经将这个问题说的很明白了,下面只是简单的概括一下。

1、Serializable是一个标志性接口,没有任何成员变量和方法。需要序列化一个类时只需要声明实现这个接口即可。
2、如果类A中组合了类B的对象的化,序列化类A对象的同时会序列化类B对象,前提是类A和类B都实现了Serializable接口。
3、如果类B继承类A,那么序列化类B分为下面两中情况:
(1)类A没有实现Serializable接口,而类B实现了。那么根据JavaAPI中的说明,如果需要序列化类B,需要保证类A和类B都有无参的构造方法,如果无法默认构造,则必须显式声明。没有无参构造方法的话,在运行时会报异常。
[color=red]用这种方式试了一下,虽然可以序列化类A继承来的属性(public、protected、包访问),但是不能正确读取,读出来是null,类B自身特有的属性没问题。有待进一步验证。[/color]
(2)类A实现了Serializable接口(类B实不实现无所谓,因为继承类A)。通过类B给继承于类A的变量和自身特有的变量赋值,并进行序列化。序列化成功,并可以正确读取。

[size=medium][color=darkblue]二、serialVersionUID[/color][/size]

serialVersionUID用于可序列化类的版本控制。如果不显式的声明一个serialVersionUID,JVM会基于这个序列化类的不同方面特征自动生成一个。
具体的可以参见JavaAPI文档的Serializable接口说明。

下面用一个例子来说明serialVersionUID的版本控制作用。

下面的类实现了Serializable接口,并且自定义了serialVersionUID=1L
public class Address implements Serializable{

private static final long serialVersionUID = 1L;

String street;
String country;

public void setStreet(String street){
this.street = street;
}

public void setCountry(String country){
this.country = country;
}

public String getStreet(){
return this.street;
}

public String getCountry(){
return this.country;
}

@Override
public String toString() {
return new StringBuffer(" Street : ")
.append(this.street)
.append(" Country : ")
.append(this.country).toString();
}
}


WriteObject类将Address类的对象写入磁盘文件address.ser。
public class WriteObject{

public static void main (String args[]) {

Address address = new Address();
address.setStreet("wall street");
address.setCountry("united state");

try{

FileOutputStream fout = new FileOutputStream("c:\\address.ser");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(address);
oos.close();
System.out.println("Done");

}catch(Exception ex){
ex.printStackTrace();
}
}
}


ReadObject类读取保存了Address对象的文件address.ser
public class ReadObject{

public static void main (String args[]) {

Address address;

try{

FileInputStream fin = new FileInputStream("c:\\address.ser");
ObjectInputStream ois = new ObjectInputStream(fin);
address = (Address) ois.readObject();
ois.close();

System.out.println(address);

}catch(Exception ex){
ex.printStackTrace();
}
}
}


测试类
public class SerialSample {

public static void main(String[] args) {
WriteObject wo=new WriteObject();
ReadObject ro=new ReadObject();
wo.write();
ro.read();
}
}


运行测试类,控制台的显示可以知道对象被序列化存入磁盘文件并正确的读出。下面在Address类中修改serialVersionUID=2L。注释掉测试类中wo.write()这一句并再次执行。控制台出现下面的错误:
java.io.InvalidClassException: *.*.*.Address; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2


这是因为序列化的时候serialVersionUID=1L,修改之后我们把一个serialVersionUID=2L的声明引用指向了反序列化后的serialVersionUID=1L的对象。JVM发现了不匹配,所以报错。
这也说明了serialVersionUID在序列化和反序列化的过程中必须匹配。

[size=medium][color=darkblue]缺省serialVersionUID。[/color][/size]
如果不显式的指定一个serialVersionUID,JVM会根据自有的算法来生成一个缺省的serialVersionUID。
这个缺省的serialVersionUID的计算对类的细节高度敏感,并且会因为不同的JVM而发生变化。在不同的JVM之间进行反序列化会导致InvalidClassExceptions异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值