首页 数据库 mysql教程 J2SE中的序默认序列化_MySQL

J2SE中的序默认序列化_MySQL

Jun 01, 2016 pm 02:06 PM
兼容性 序列 我们 版本 默认

J2SE

  要保存的也被保存了下来。一般情况下,我们仅仅需要保存逻辑数据就可以了。不需要保存的数据我们可以用关键字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保持不变,会引发异常。正是因为这一点,我们要牢记一个类一旦实现了序列化又要保持向上下兼容性,就不可以随随便便的修改了!!!

  测试也证明了这一点,有兴趣的读者可以自己试一试。
   如何保持向下兼容性:

  一如上文所指出的,你会想当然的认为只要保持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序列化的方法,则可根据以上分析的三点进行准备。对于这样的情况使用对象序列化方法还是可以应付的。对于一个字处理软件的文档的兼容性要求颇高,一般情况下的策略都是要求良好的向下兼容性,和尽可能的向上兼容性。则一般不会使用对象序列化技术,一个精心设计的文档结构,更能解决问题。

  数据一致性问题、约束问题

  要知道序列化是另一种形式上的“public构造函数”,但他仅仅构造起对象,而不作任何的检查,这样人很不舒服,所以必要的检查是必须的,这利用了readObject()

  private void readObject(java.io.ObjectInputStream in)
  throws IOException, ClassNotFoundException{
  in.defaultReadObject();//先反序列化对象
  …进行检查与初始化
  }

  出于结构化的考虑,通常使用一个名为initialize的函数,负责检查与初始化,如果失败抛出异常。要保持检查与初始化是很容易被忘记的,这常常导致问题。另一个问题在于当父类没有加入readObject()的时候,子类很容易忘记要调用对应的initialize函数。这仿佛回到了当初为什么要引入构造函数的问题,原因就是防止子类忘记调用初始化函数引发各种问题。所以,如果要保持数据一致性,一定要加入readObject()。

  安全问题

  安全性的话题超出了本文的范畴,但是你应该要知道,有可能一个攻击者会对你的类准备一个恶意的数据流企图生成一个错误的类。当你需要确保你的对象数据安全的话,你一般可以利用上面的方法来检查,并初始化,但对于某些引用不好检查。解决方法就是对重要的部件进行保护性拷贝。这里推荐一个好方法,它不用保护性拷贝个别的域,而是直接保护性拷贝整个对象。这就是:

  Object readResolve() throws ObjectStreamException;

  这个方法的用途就是,他会紧接着readObject()调用。它将会利用返回的对象代替原来反序列化的对象。也就是原来readObject()反序列化的对象将会被立即的丢弃。

  Object readResolve() throws ObjectStreamException{
  return new Serial2(this.xxx1,this.xxx2);// xxx1、xxx2是刚刚反序列化得来的,这是一种保护性拷贝
  }

  这样的话虽然在时间上有所浪费,但是对于特别的重要而安全的类,可以使用这种方法。如果数据一致性问题、约束问题通过逐一检查来解决很麻烦,也可以利用这种方法,但要考虑好成本,和注意下面的局限性。 利用readResolve()有一个明显的缺点,就是当父类实现了readResolve(),子类将变得无丛下手。如果一个保护的或者是公有的父类的readResolve()存在,并且子类也没有改写它,将会使得子类反序列化的时候最终得到一个父类的对象,这既不是我们要得结果,也不容易发现这种错误。而让子类重写readResolve()无疑是一个负担。也就是说对于要继承的类而言,实现readResolve()来保护类不是一个好方法。我们只能利用第一种方法写一个保护性的readObject()。

  所以我的建议是:一般情况下,只有对于final的类采用readResolve()来进行保护。

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Linux下更新curl版本教程! Linux下更新curl版本教程! Mar 07, 2024 am 08:30 AM

在Linux下更新curl版本,您可以按照以下步骤进行操作:检查当前curl版本:首先,您需要确定当前系统中安装的curl版本。打开终端,并执行以下命令:curl--version该命令将显示当前curl的版本信息。确认可用的curl版本:在更新curl之前,您需要确定可用的最新版本。您可以访问curl的官方网站(curl.haxx.se)或相关的软件源,查找最新版本的curl。下载curl源代码:使用curl或浏览器,下载您选择的curl版本的源代码文件(通常为.tar.gz或.tar.bz2

更新pip版本的简单步骤:1分钟内完成 更新pip版本的简单步骤:1分钟内完成 Jan 27, 2024 am 09:45 AM

一分钟搞定:如何更新pip版本,需要具体代码示例随着Python的快速发展,pip成为了Python包管理的标准工具。然而,随着时间的推移,pip版本也在不断更新,为了能够使用最新的功能和修复可能的安全漏洞,更新pip版本是非常重要的。本文将介绍如何在一分钟内快速更新pip,并提供具体的代码示例。首先,我们需要打开命令行窗口。在Windows系统中,可以使用

查看麒麟操作系统版本和内核版本 查看麒麟操作系统版本和内核版本 Feb 21, 2024 pm 07:04 PM

查看麒麟操作系统版本和内核版本在麒麟操作系统中,了解如何查看系统版本和内核版本是进行系统管理和维护的基础。查看麒麟操作系统版本方法一:使用/etc/.kyinfo文件要查看麒麟操作系统的版本,您可以查看/etc/.kyinfo文件。此文件包含了操作系统的版本信息。执行以下命令:cat/etc/.kyinfo此命令将显示操作系统的详细版本信息。方法二:使用/etc/issue文件另一个查看操作系统版本的方法是通过查看/etc/issue文件。这个文件同样提供了版本信息,但可能不如.kyinfo文件

解读PHP版本NTS的含义与区别 解读PHP版本NTS的含义与区别 Mar 27, 2024 am 11:48 AM

PHP版本NTS的含义与区别PHP是一种流行的服务器端脚本语言,广泛应用于Web开发领域。PHP有两种主要的版本:ThreadSafe(TS)和Non-ThreadSafe(NTS)。在PHP的官方网站上,我们可以看到两个不同的PHP下载版本,分别是PHPNTS和PHPTS。那么,PHP版本NTS是什么意思?它和TS版本有什么区别呢?接下来,

如何轻松查看Oracle的安装版本 如何轻松查看Oracle的安装版本 Mar 07, 2024 am 11:27 AM

如何轻松查看Oracle的安装版本,需要具体代码示例作为一款被广泛应用于企业级数据库管理系统的软件,Oracle数据库具有许多版本和不同的安装方式。在日常工作中,我们经常需要查看Oracle数据库的安装版本,以便进行相应的操作和维护。本文将介绍如何轻松地查看Oracle的安装版本,并给出具体的代码示例。方法一:通过SQL查询在Oracle数据库中,我们可以通

大模型一对一战斗75万轮,GPT-4夺冠,Llama 3位列第五 大模型一对一战斗75万轮,GPT-4夺冠,Llama 3位列第五 Apr 23, 2024 pm 03:28 PM

关于Llama3,又有测试结果新鲜出炉——大模型评测社区LMSYS发布了一份大模型排行榜单,Llama3位列第五,英文单项与GPT-4并列第一。图片不同于其他Benchmark,这份榜单的依据是模型一对一battle,由全网测评者自行命题并打分。最终,Llama3取得了榜单中的第五名,排在前面的是GPT-4的三个不同版本,以及Claude3超大杯Opus。而在英文单项榜单中,Llama3反超了Claude,与GPT-4打成了平手。对于这一结果,Meta的首席科学家LeCun十分高兴,转发了推文并

如何在 Windows 轻松安装 Python,两种方法任你选 如何在 Windows 轻松安装 Python,两种方法任你选 Feb 18, 2024 pm 04:57 PM

Python为什么这么火?原因很简单:它易学易用,而且上手快,适合初学者。不论是什么项目,只要涉及到编程,Python往往是我的首选。而且,不必拘泥于Linux系统,Windows也可以很好地支持。在我的Windows11操作系统上已经安装了Python,我常常使用它来编写程序和运行代码。安装Python在Windows上非常简单,接下来我将分享两种常用的安装方法。选择适合的Python版本Python提供了多个版本,选择合适的版本非常重要,这关系到程序的兼容性和功能:目前版本有Python2和

dp接口怎么看1.2还是1.4 dp接口怎么看1.2还是1.4 Feb 06, 2024 am 10:27 AM

DP接口是电脑里面一个重要的接口线,有很多用户在使用电脑的时候,想要知道怎么样可以查看DP接口是1.2还是1.4的,其实只需要在GPU-Z中就可以看。dp接口怎么看1.2还是1.4:1、首先在GPU-Z中选择“Advanced”。2、再看“Advanced”下面“General”中的“Monitor1”,可以看“LinkRate(current)”和“Lanes(current)”这两项。3、最后如果显示8.1Gbps×4,就是DP1.3版本以上,一般都是DP1.4,如果是5.4Gbps×4,则

See all articles