Chinaunix首页 | 论坛 | 博客
  • 博客访问: 14432186
  • 博文数量: 5645
  • 博客积分: 9880
  • 博客等级: 中将
  • 技术积分: 68081
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-28 13:35
文章分类

全部博文(5645)

文章存档

2008年(5645)

我的朋友

分类:

2008-04-28 21:43:18

下载本文示例代码
  首先要了解java默认的序列化行为,java将一切关于对象的信息都保存了下了,也就是说,有些时候那些不需要保存的也被保存了下来。一般情况下,我们仅仅需要保存逻辑数据就可以了。不需要保存的数据我们可以用关键字transient标出。   以下是一个例子: import java.io.*; public class Serial implements Serializable {  int company_id;  String company_addr;  transient boolean company_flag; }   则company_flag字段将不会参与序列化与反序列化,但同时你也增加了为他初始值的责任。这也是序列化常常导致的问题之一。因为序列化相当于一个只接受数据流的public构造函数,这种对象构造方法是语言之外的。但他仍然是一种形式上的构造函数。如若你的类不能够通过其他方面来保证初始化,则你需要额外的提供readObject方法,首先正常的反序列化,然后对transient标示的字段进行初始化。   在不适合的时候,使用java默认的序列化行为可能会带来速度上的影响,最糟糕的情况是,可能导致溢出。在某些数据结构的实现中,经常会充斥着各种的循环引用,而java的默认序列化行为,并不了解你的对象结构,其结果就是java试图通过一种昂贵的“图遍历”来保存对象状态。可想而知,不但慢而且可能溢出。这时候你就要提供自己的readObject,来代替默认的行为。   兼容性问题   兼容性历来是复杂而麻烦的问题。   不要兼容性:   首先来看看如果我们的目的是不要兼容性,应该注意哪些。不要兼容性的场合很多,比如war3每当版本升级就不能够读取以前的replays。   兼容也就是版本控制,java通过一个名为UID(stream unique identifier)来控制,这个UID是隐式的,它通过类名,方法名等诸多因素经过计算而得,理论上是一一映射的关系,也就是唯一的。如果UID不一样的话,就无法实现反序列化了,并且将会得到InvalidClassException。   当我们要人为的产生一个新的版本(实现并没有改动),而抛弃以前的版本的话,可以通过显式的声名UID来实现: private static final long serialVersionUID=????;   你可以编造一个版本号,但注意不要重复。这样在反序列化的时候老版本将得到InvalidClassException,我们可以在老版本的地方捕捉这个异常,并提示用户升级的新的版本。   当改动不大时,保持兼容性(向下兼容性的一个特例):   有时候你的类增加了一些无关紧要的非私有方法,而逻辑字段并不改变的时候,你当然希望老版本和新版本保持兼容性,方法同样是通过显式的声名UID来实现。下面我们验证一下。   老版本: import java.io.*; public class Serial implements Serializable {  int company_id;  String company_addr;  public Serial1(int company_id, String company_addr) {   this.company_id = company_id;   this.company_addr = company_addr;  }  public String toString() {   return "DATA: " company_id " "   company_addr;  } }  新版本 import java.io.*; public class Serial implements Serializable {  int company_id;  String company_addr;  public Serial1(int company_id, String company_addr) {   this.company_id = company_id;   this.company_addr = company_addr;  }  public String toString() {   return "DATA: " company_id " " company_addr;  }  public void todo(){}//无关紧要的方法 }   首先将老版本序列化,然后用新版本读出,发生错误: java.io.InvalidClassException: Serial.Serial1; local class incompatible: stream classdesc serialVersionUID = 762508508425139227, local class serialVersionUID = 1187169935661445676   接下来我们加入显式的声名UID: private static final long serialVersionUID=762508508425139227l;   再次运行,顺利地产生新对象 DATA: 1001 com1   如何保持向上兼容性:   向上兼容性是指老的版本能够读取新的版本序列化的数据流。常常出现在我们的服务器的数据更新了,仍然希望老的客户端能够支持反序列化新的数据流,直到其更新到新的版本。可以说,这是半自动的事情。   跟一般的讲,因为在java中serialVersionUID是唯一控制着能否反序列化成功的标志,只要这个值不一样,就无法反序列化成功。但只要这个值相同,无论如何都将反序列化,在这个过程中,对于向上兼容性,新数据流中的多余的内容将会被忽略;对于向下兼容性而言,旧的数据流中所包含的所有内容都将会被恢复,新版本的类中没有涉及到的部分将保持默认值。利用这一特性,可以说,只要我们认为的保持serialVersionUID不变,向上兼容性是自动实现的。   当然,一但我们将新版本中的老的内容拿掉,情况就不同了,即使UID保持不变,会引发异常。正是因为这一点,我们要牢记一个类一旦实现了序列化又要保持向上下兼容性,就不可以随随便便的修改了!!!   测试也证明了这一点,有兴趣的读者可以自己试一试。 共2页。 1 2 :   首先要了解java默认的序列化行为,java将一切关于对象的信息都保存了下了,也就是说,有些时候那些不需要保存的也被保存了下来。一般情况下,我们仅仅需要保存逻辑数据就可以了。不需要保存的数据我们可以用关键字transient标出。   以下是一个例子: import java.io.*; public class Serial implements Serializable {  int company_id;  String company_addr;  transient boolean company_flag; }   则company_flag字段将不会参与序列化与反序列化,但同时你也增加了为他初始值的责任。这也是序列化常常导致的问题之一。因为序列化相当于一个只接受数据流的public构造函数,这种对象构造方法是语言之外的。但他仍然是一种形式上的构造函数。如若你的类不能够通过其他方面来保证初始化,则你需要额外的提供readObject方法,首先正常的反序列化,然后对transient标示的字段进行初始化。   在不适合的时候,使用java默认的序列化行为可能会带来速度上的影响,最糟糕的情况是,可能导致溢出。在某些数据结构的实现中,经常会充斥着各种的循环引用,而java的默认序列化行为,并不了解你的对象结构,其结果就是java试图通过一种昂贵的“图遍历”来保存对象状态。可想而知,不但慢而且可能溢出。这时候你就要提供自己的readObject,来代替默认的行为。   兼容性问题   兼容性历来是复杂而麻烦的问题。   不要兼容性:   首先来看看如果我们的目的是不要兼容性,应该注意哪些。不要兼容性的场合很多,比如war3每当版本升级就不能够读取以前的replays。   兼容也就是版本控制,java通过一个名为UID(stream unique identifier)来控制,这个UID是隐式的,它通过类名,方法名等诸多因素经过计算而得,理论上是一一映射的关系,也就是唯一的。如果UID不一样的话,就无法实现反序列化了,并且将会得到InvalidClassException。   当我们要人为的产生一个新的版本(实现并没有改动),而抛弃以前的版本的话,可以通过显式的声名UID来实现: private static final long serialVersionUID=????;   你可以编造一个版本号,但注意不要重复。这样在反序列化的时候老版本将得到InvalidClassException,我们可以在老版本的地方捕捉这个异常,并提示用户升级的新的版本。   当改动不大时,保持兼容性(向下兼容性的一个特例):   有时候你的类增加了一些无关紧要的非私有方法,而逻辑字段并不改变的时候,你当然希望老版本和新版本保持兼容性,方法同样是通过显式的声名UID来实现。下面我们验证一下。   老版本: import java.io.*; public class Serial implements Serializable {  int company_id;  String company_addr;  public Serial1(int company_id, String company_addr) {   this.company_id = company_id;   this.company_addr = company_addr;  }  public String toString() {   return "DATA: " company_id " "   company_addr;  } }  新版本 import java.io.*; public class Serial implements Serializable {  int company_id;  String company_addr;  public Serial1(int company_id, String company_addr) {   this.company_id = company_id;   this.company_addr = company_addr;  }  public String toString() {   return "DATA: " company_id " " company_addr;  }  public void todo(){}//无关紧要的方法 }   首先将老版本序列化,然后用新版本读出,发生错误: java.io.InvalidClassException: Serial.Serial1; local class incompatible: stream classdesc serialVersionUID = 762508508425139227, local class serialVersionUID = 1187169935661445676   接下来我们加入显式的声名UID: private static final long serialVersionUID=762508508425139227l;   再次运行,顺利地产生新对象 DATA: 1001 com1   如何保持向上兼容性:   向上兼容性是指老的版本能够读取新的版本序列化的数据流。常常出现在我们的服务器的数据更新了,仍然希望老的客户端能够支持反序列化新的数据流,直到其更新到新的版本。可以说,这是半自动的事情。   跟一般的讲,因为在java中serialVersionUID是唯一控制着能否反序列化成功的标志,只要这个值不一样,就无法反序列化成功。但只要这个值相同,无论如何都将反序列化,在这个过程中,对于向上兼容性,新数据流中的多余的内容将会被忽略;对于向下兼容性而言,旧的数据流中所包含的所有内容都将会被恢复,新版本的类中没有涉及到的部分将保持默认值。利用这一特性,可以说,只要我们认为的保持serialVersionUID不变,向上兼容性是自动实现的。   当然,一但我们将新版本中的老的内容拿掉,情况就不同了,即使UID保持不变,会引发异常。正是因为这一点,我们要牢记一个类一旦实现了序列化又要保持向上下兼容性,就不可以随随便便的修改了!!!   测试也证明了这一点,有兴趣的读者可以自己试一试。 共2页。 1 2 : 下载本文示例代码


谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化谈谈J2SE中的序列化之接受默认序列化
阅读(66) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~