论坛首页 Java企业应用论坛

serialVersionUID的使用

浏览 3997 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-09-12  
先来看一个例子:
定义一个bean:
public class Serial implements Serializable {
	int id;
	String name;
	public Serial(int id, String name) {
		this.id = id;
		this.name = name;
	}
	public String toString() {
		return "DATA: " + id + " " +name;

	}
}

序列化操作:
public static void main(String[] args) {
		Serial serial=new Serial(1,"hrbeu");
		System.out.println("object serial:"+serial); 
		try{
			FileOutputStream fos=new FileOutputStream("serialTest.txt"); 
	        ObjectOutputStream oos=new ObjectOutputStream(fos); 
	        oos.writeObject(serial); 
	        oos.flush(); 
	        oos.close(); 
		}catch(Exception e){
			System.out.println("Exception:"+e); 
		}
	}

反序列化操作:
public static void main(String[] args) {
		 try{ 
			 Serial object2; 
	         FileInputStream fis=new FileInputStream("serialTest.txt"); 
	         ObjectInputStream ois=new ObjectInputStream(fis); 
	         object2=(Serial)ois.readObject(); 
	         ois.close(); 
	         System.out.println("object deserial:"+object2); 
	        }catch(Exception e){ 
	            System.out.println("Exception:"+e); 
	        } 
	}

运行程序,则反序列化成功。
现在改动一下bean,在定义的bean中添加一个公有方法(非私有就可以):
public void todo(){}//没什么意义的方法

接下来在老版本的序列化的结果上反序列化就会出错:
Exception:java.io.InvalidClassException: com.serializable.test.Serial; local class incompatible: stream classdesc serialVersionUID = 5087256472645325817, local class serialVersionUID = 6553118832574415117

接下来在定义的bean中显示的声明UID,如下:
private static final long serialVersionUID = 6553118832574415117L;

再次重新执行上面的步骤,则反序列化成功。

引用
兼容也就是版本控制,java通过一个名为UID(stream unique identifier)来控制,这个UID是隐式的,它通过类名,方法名等诸多因素经过计算而得,理论上是一一映射的关系,也就是唯一的。如果UID不一样的话,就无法实现反序列化了,并且将会得到InvalidClassException。

有时候你的类增加了一些无关紧要的非私有方法,而逻辑字段并不改变的时候,你希望老版本和新版本保持兼容性,则需要显式的声名UID来实现。
以下内容来自网络:
====================================================================
如何保持向上兼容性:

      向上兼容性是指老的版本能够读取新的版本序列化的数据流。常常出现在我们的服务器的数据更新了,仍然希望老的客户端能够支持反序列化新的数据流,直到其更新到新的版本。可以说,这是半自动的事情。

      跟一般的讲,因为在java中serialVersionUID是唯一控制着能否反序列化成功的标志,只要这个值不一样,就无法反序列化成功。但只要这个值相同,无论如何都将反序列化,在这个过程中,对于向上兼容性,新数据流中的多余的内容将会被忽略;对于向下兼容性而言,旧的数据流中所包含的所有内容都将会被恢复,新版本的类中没有涉及到的部分将保持默认值。利用这一特性,可以说,只要我们认为的保持serialVersionUID不变,向上兼容性是自动实现的。

      当然,一但我们将新版本中的老的内容拿掉,情况就不同了,即使UID保持不变,会引发异常。正是因为这一点,我们要牢记一个类一旦实现了序列化又要保持向上下兼容性,就不可以随随便便的修改了!!!

      测试也证明了这一点,有兴趣的读者可以自己试一试。

如何保持向下兼容性:

         一如上文所指出的,你会想当然的认为只要保持serialVersionUID不变,向下兼容性是自动实现的。但实际上,向下兼容要复杂一些。这是因为,我们必须要对那些没有初始化的字段负责。要保证它们能被使用。

      所以必须要利用
      private void readObject(java.io.ObjectInputStream in)
      throws IOException, ClassNotFoundException{
         in.defaultReadObject();//先反序列化对象
         if(ver=5552){//以前的版本5552
             …初始化其他字段
          }else if(ver=5550){//以前的版本5550
            …初始化其他字段
          }else{//太老的版本不支持
          throw new InvalidClassException();
      }
}
      细心的读者会注意到要保证in.defaultReadObject();能够顺利执行,就必须要求serialVersionUID保持一致,所以这里的ver不能够利用serialVersionUID了。这里的ver是一个我们预先安插好的final long ver=xxxx;并且它不能够被transient修饰。所以保持向下的兼容性至少有三点要求:

           1.serialVersionUID保持一致
           2.预先安插好我们自己的版本识别标志的final long ver=xxxx;
           3.保证初始化所有的域

讨论一下兼容性策略:

         到这里我们可以看到要保持向下的兼容性很麻烦。而且随着版本数目的增加。维护会变得困难而繁琐。讨论什么样的程序应该使用怎么样的兼容性序列化策略已经超出本文的范畴,但是对于一个游戏的存盘功能,和对于一个字处理软件的文档的兼容性的要求肯定不同。对于rpg游戏的存盘功能,一般要求能够保持向下兼容,这里如果使用java序列化的方法,则可根据以上分析的三点进行准备。对于这样的情况使用对象序列化方法还是可以应付的。对于一个字处理软件的文档的兼容性要求颇高,一般情况下的策略都是要求良好的向下兼容性,和尽可能的向上兼容性。则一般不会使用对象序列化技术,一个精心设计的文档结构,更能解决问题。

   发表时间:2011-08-21  
不错,这篇文章确实不错
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics