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()来进行保护。

许多Windows客户最近报告说,当他们尝试启动程序或游戏时,它会抛出一个错误,指出KernelBase.dll文件丢失或找不到。当正在运行的程序意外崩溃时也会报告此错误。目前,用户不确定要采取什么步骤来解决此问题。他们甚至尝试多次重启Windows系统,效果相同。导致此问题的原因可能有很多,可能是因为系统数据文件损坏、Windows系统过时等。在分析了以上所有要点之后,我们提出了一组修复程序,可以帮助用户修复此问题并顺利使用系统。如果您也遇到同样的问题,请继续阅读本文以了解更多信息。解决方

在您的计算机上首次安装Windows操作系统时,会自动创建ntdll.dll文件。基本上,ntdll.dll是与Windows操作系统相关联的文件,位于WindowsSystem32目录中。如果您看到这个文件的属性,它的文件描述为NT层DLL,它包含Windows操作系统正常工作所需的所有主要NT内核函数。该文件大部分时间在后台运行,但有时它可能会崩溃或无法正确执行操作。通常,当多个应用程序/程序尝试同时访问该文件时,就会发生这种情况。许多Windows用户报告说,当他们访问的

浏览器兼容性设置的方法有选择合适的HTML和CSS标准、使用CSS前缀和Reset、使用浏览器兼容性前缀库、检测浏览器特性、使用浏览器兼容性工具和进行跨浏览器测试。详细介绍:1、选择合适的HTML和CSS标准,在编写HTML和CSS代码时,应尽量遵循W3C的标准,不同浏览器对标准的支持程度会有所不同,但遵循标准可以最大程度地提高兼容性;2、使用CSS前缀等等。

在iOS17.2中,Apple允许您自定义iPhone用于默认通知的提示音。对于不喜欢Apple在iOS17中引入的“反弹”基调的人来说,这是个好消息。在以前的iOS版本中,Apple允许用户选择铃声和文本铃声,以及电子邮件、日历和提醒警报的自定义警报声音。其他任何抛出通知的内容都使用默认声音,并且无法更改它。在iOS17中,Apple随后将原来的默认警报声音从“Tri-tone”更改为“Rebound”。许多用户不喜欢这种变化,有些人认为新声音更难听到。因此,iOS17.2中将默认声音更改为您

无线投影您的显示器可能是一个福音,尤其是当您尝试与更多人共享内容时。大多数现代电视都支持某种无线投影,笔记本电脑也是如此。但是你如何开始呢?让我们来了解一下!Windows 11 是否原生支持投影屏幕?是也不是,Windows 11 原生使用 Miracast 进行无线显示投影,同时通过 Chromium 浏览器支持 Chromecast。另一方面,电视与 Miracast 或 Chromecast 捆绑在一起。这使得找到合适的兼容性并让一切在您的设备上正常工作变得困难重重。值得庆幸的是,大多数

微软6月24号正式公布了win11系统,可以看到用户界面、开始菜单等和Windows10X中发现的非常相似。有的朋友在使用预览版的时候发现用的不习惯,想要改win10系统开使用,那么我们要如何操作呢,下面我们就来看看win11改win10系统教程,一起来学习一下吧。1、第一步是从Windows11打开新设置。在这里,您需要转到图像中显示的系统设置。2、在系统设置下,选择“恢复”选项。在这里,您将能够看到“以前版本的窗口”选项。您还可以在它旁边看到一个“返回”按钮,单击此按钮。3、您可以指定要返回

在这里,我们将看到C和C++之间的一些不兼容性。一些可以使用C编译器编译的C代码,在C++编译器中无法编译。并且会返回错误。我们可以使用一种语法来定义函数,该语法在参数列表之后可选择指定参数类型。示例#include<stdio.h>voidmy_function(x,y)intx;inty;{//NotvalidinC++ printf("x=%d,y=%d",x,y);}intmain(){ &n

如何进行平稳的PHP5.6到PHP7.4升级,避免兼容性困扰?随着PHP技术的不断发展,PHP7.4已成为主流的PHP版本,但许多项目仍然停留在较旧的版本,如PHP5.6。升级到PHP7.4可以带来更高的性能、更多的特性和更好的安全性。然而,由于PHP5.6和PHP7.4之间的一些不兼容之处,升级过程可能会带来一些困扰。本文将介绍如何进行平稳的PH


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

SecLists
SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

ホットトピック



