`
wezly
  • 浏览: 485235 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

序列化中serialVersionUID的使用

阅读更多

先来看一个例子: 
定义一个bean: 

Java代码 
  1. public class Serial implements Serializable {  
  2.     int id;  
  3.     String name;  
  4.     public Serial(int id, String name) {  
  5.         this.id = id;  
  6.         this.name = name;  
  7.     }  
  8.     public String toString() {  
  9.         return "DATA: " + id + " " +name;  
  10.   
  11.     }  
  12. }  

序列化操作: 
Java代码 
  1. public static void main(String[] args) {  
  2.         Serial serial=new Serial(1,"hrbeu");  
  3.         System.out.println("object serial:"+serial);   
  4.         try{  
  5.             FileOutputStream fos=new FileOutputStream("serialTest.txt");   
  6.             ObjectOutputStream oos=new ObjectOutputStream(fos);   
  7.             oos.writeObject(serial);   
  8.             oos.flush();   
  9.             oos.close();   
  10.         }catch(Exception e){  
  11.             System.out.println("Exception:"+e);   
  12.         }  
  13.     }  

反序列化操作: 
Java代码 
  1. public static void main(String[] args) {  
  2.          try{   
  3.              Serial object2;   
  4.              FileInputStream fis=new FileInputStream("serialTest.txt");   
  5.              ObjectInputStream ois=new ObjectInputStream(fis);   
  6.              object2=(Serial)ois.readObject();   
  7.              ois.close();   
  8.              System.out.println("object deserial:"+object2);   
  9.             }catch(Exception e){   
  10.                 System.out.println("Exception:"+e);   
  11.             }   
  12.     }  

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

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

接下来在定义的bean中显示的声明UID,如下: 
Java代码 
  1. 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序列化的方法,则可根据以上分析的三点进行准备。对于这样的情况使用对象序列化方法还是可以应付的。对于一个字处理软件的文档的兼容性要求颇高,一般情况下的策略都是要求良好的向下兼容性,和尽可能的向上兼容性。则一般不会使用对象序列化技术,一个精心设计的文档结构,更能解决问题。 

分享到:
评论

相关推荐

    java序列化和serialVersionUID的使用方法实例

    Java 序列化和 serialVersionUID 的使用方法实例 Java 序列化是指将 Java 对象转换为二进制流的过程,以便在网络中传输或持久化到数据库或文件系统中。序列化的作用是将 Java 对象的状态保存起来,以便下次使用时...

    java 对象的序列化与反序列化

    Java对象的序列化和反序列化是Java编程中一项重要的技术,主要用于将对象的状态转换为字节流,以便存储或在网络上传输。这一过程对于理解Java的IO操作、持久化数据以及实现分布式通信等场景非常关键。 首先,我们来...

    详述IntelliJ IDEA 中自动生成 serialVersionUID 的方法(图文)

    在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体(类)的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。 在 ...

    java序列化(Serializable)的作用和反序列化.doc

    下面是一个简单的序列化示例代码,展示了如何将一个`Box`对象序列化并存储到文件中,然后再从文件中读取出来: ```java package com.hotye.dchaoxiong.serializabletest; import java.io.FileInputStream; import ...

    序列化版本号serialVersionUID的作用_动力节点Java学院整理

    序列化版本号serialVersionUID的作用_动力节点Java学院整理.

    Java序列化

    - Java允许使用 `writeObject()` 和 `readObject()` 方法来自定义序列化和反序列化的行为,这两个方法需要在类中声明为`private`,并由`java.io.Serializable` 接口的实现类提供。 7. **序列化安全性** - 序列化...

    java序列化实现演示

    在给定的链接"Java序列化机制(2)- serialVersionUID 实验"中,博主通过一个实验详细解释了`serialVersionUID`的作用和重要性。实验可能包括以下步骤: 1. 创建一个实现`Serializable`接口的简单类,并运行序列化...

    java serializable 序列化与反序列化

    2. **如何实现反序列化**:使用`ObjectInputStream`的`readObject()`方法从流中读取对象: ```java try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myfile.ser"))) { MyObject obj = ...

    序列化与反序列化Demo

    序列化与反序列化是计算机科学中的重要概念,特别是在数据存储、网络通信和持久化对象等领域。简单来说,序列化是将对象的状态转换为可存储或传输的数据格式的过程,而反序列化则是将这种数据格式恢复为原来的对象...

    Java中serialVersionUID的解释

    也就是说,当我们序列化一个对象时,serialVersionUID 会被保存下来,并在反序列化时被用来检查对象的版本是否相同。 那么,serialVersionUID 有两种生成方式:一种是默认的 1L,例如:private static final long ...

    IDEA中实体类实现了序列化接口

    1. **版本控制**:当类的结构发生变化时,`serialVersionUID`会发生变化,这样可以避免由于类的结构变化导致的序列化和反序列化过程中的不一致。 2. **兼容性检测**:在进行序列化和反序列化操作时,系统会比较序列...

    Java序列化_Java序列化结构_

    Java序列化是Java平台中的一种持久化机制,它允许对象的状态被转换成字节流,以便存储、网络传输或在不同时间点恢复。这个过程被称为序列化,而反向操作称为反序列化。序列化在许多场景下都非常有用,比如在分布式...

    java对象序列化和反序列化

    - 使用`ObjectInputStream`的`readObject()`方法读取序列化的字节流,会根据字节流中的信息创建和初始化对象。 3. **潜在问题**: - 安全风险:恶意构造的序列化数据可能导致代码执行,因此在反序列化时要确保...

    java序列化(Serializable)的作用和反序列化

    1. **文件保存**:当需要将内存中的对象状态保存到文件中或数据库中时,序列化非常有用。 2. **网络通信**:在网络上传输对象时,序列化能够将对象转换为字节流形式,便于在网络间传输。 3. **远程方法调用(RMI)**:...

    java程序,序列化和反序列化操作对文件的运用

    为了避免这个问题,可以使用版本ID(serialVersionUID)来确保序列化兼容性。 ```java // 在类定义中添加版本ID private static final long serialVersionUID = 1L; ``` 文件的序列化和反序列化操作对于数据持久化...

    Java对象序列化标准最新版

    在序列化过程中,可以使用反射机制来访问类的可序列化字段。这允许`ObjectOutputStream`和`ObjectInputStream`类动态地处理不同类型的对象。 **1.8 `ObjectOutput`接口** `ObjectOutput`接口定义了将对象序列化的...

    Java对象的序列化和反序列化实践

    在Java编程语言中,对象的序列化和反序列化是两个关键的概念,它们允许我们将对象的状态转换为可存储或传输的格式,然后再恢复为原始对象。这个过程对于数据持久化、网络传输以及跨进程通信等场景非常有用。下面将...

    Java 文件 序列化 读写

    - 使用`ObjectInputStream`类的`readObject()`方法从文件中反序列化对象。例如: ```java FileInputStream fis = new FileInputStream("object.ser"); ObjectInputStream ois = new ObjectInputStream(fis); My...

    对象的序列化和反序列化

    在实际应用中,我们还可以使用`transient`关键字来标记那些不需要序列化的字段,因为这些字段可能包含敏感信息或者不应该被持久化的状态。同时,`volatile`字段在序列化和反序列化过程中可能会失去其特性,因此需要...

Global site tag (gtag.js) - Google Analytics