`
paddy.w
  • 浏览: 497484 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Understand the Serializable and serialVersionUID

    博客分类:
  • Java
阅读更多
        看了一篇简单介绍serialVersionUID的文章,在这里结合javaAPI文档和自己的理解说一下Serializable和serialVersionUID。

        一、Serializable

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

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

        二、serialVersionUID

        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在序列化和反序列化的过程中必须匹配。

        缺省serialVersionUID。
        如果不显式的指定一个serialVersionUID,JVM会根据自有的算法来生成一个缺省的serialVersionUID。
        这个缺省的serialVersionUID的计算对类的细节高度敏感,并且会因为不同的JVM而发生变化。在不同的JVM之间进行反序列化会导致InvalidClassExceptions异常。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics