JAVA序列化,官方说明有误??

FengPrince 2012-09-22 01:56:50
即使设置了serialiVersionUID,在.class文件升级后可能导致不能反序列化。
以下是官方的兼容性修改和不兼容性修改,个人实际测试了一下,感觉不是很对啊!!!
Incompatible Changes

Incompatible changes to classes are those changes for which the guarantee of interoperability cannot be maintained. The incompatible changes that may occur while evolving a class are:

Deleting fields - If a field is deleted in a class, the stream written will not contain its value. When the stream is read by an earlier class, the value of the field will be set to the default value because no value is available in the stream. However, this default value may adversely impair the ability of the earlier version to fulfill its contract.
Moving classes up or down the hierarchy - This cannot be allowed since the data in the stream appears in the wrong sequence.
Changing a nonstatic field to static or a nontransient field to transient - When relying on default serialization, this change is equivalent to deleting a field from the class. This version of the class will not write that data to the stream, so it will not be available to be read by earlier versions of the class. As when deleting a field, the field of the earlier version will be initialized to the default value, which can cause the class to fail in unexpected ways.
Changing the declared type of a primitive field - Each version of the class writes the data with its declared type. Earlier versions of the class attempting to read the field will fail because the type of the data in the stream does not match the type of the field.
Changing the writeObject or readObject method so that it no longer writes or reads the default field data or changing it so that it attempts to write it or read it when the previous version did not. The default field data must consistently either appear or not appear in the stream.
Changing a class from Serializable to Externalizable or vice versa is an incompatible change since the stream will contain data that is incompatible with the implementation of the available class.
Changing a class from a non-enum type to an enum type or vice versa since the stream will contain data that is incompatible with the implementation of the available class.
Removing either Serializable or Externalizable is an incompatible change since when written it will no longer supply the fields needed by older versions of the class.
Adding the writeReplace or readResolve method to a class is incompatible if the behavior would produce an object that is incompatible with any older version of the class.


Compatible Changes

The compatible changes to a class are handled as follows:

Adding fields - When the class being reconstituted has a field that does not occur in the stream, that field in the object will be initialized to the default value for its type. If class-specific initialization is needed, the class may provide a readObject method that can initialize the field to nondefault values.
Adding classes - The stream will contain the type hierarchy of each object in the stream. Comparing this hierarchy in the stream with the current class can detect additional classes. Since there is no information in the stream from which to initialize the object, the class's fields will be initialized to the default values.
Removing classes - Comparing the class hierarchy in the stream with that of the current class can detect that a class has been deleted. In this case, the fields and objects corresponding to that class are read from the stream. Primitive fields are discarded, but the objects referenced by the deleted class are created, since they may be referred to later in the stream. They will be garbage-collected when the stream is garbage-collected or reset.
Adding writeObject/readObject methods - If the version reading the stream has these methods then readObject is expected, as usual, to read the required data written to the stream by the default serialization. It should call defaultReadObject first before reading any optional data. The writeObject method is expected as usual to call defaultWriteObject to write the required data and then may write optional data.
Removing writeObject/readObject methods - If the class reading the stream does not have these methods, the required data will be read by default serialization, and the optional data will be discarded.
Adding java.io.Serializable - This is equivalent to adding types. There will be no values in the stream for this class so its fields will be initialized to default values. The support for subclassing nonserializable classes requires that the class's supertype have a no-arg constructor and the class itself will be initialized to default values. If the no-arg constructor is not available, the InvalidClassException is thrown.
Changing the access to a field - The access modifiers public, package, protected, and private have no effect on the ability of serialization to assign values to the fields.
Changing a field from static to nonstatic or transient to nontransient - When relying on default serialization to compute the serializable fields, this change is equivalent to adding a field to the class. The new field will be written to the stream but earlier classes will ignore the value since serialization will not assign values to static or transient fields.
...全文
166 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
raistlic 2012-09-24
  • 打赏
  • 举报
回复
甚至某一次“一模一样”都不能算成功,要各种情况每次都一模一样才算成功兼容。
raistlic 2012-09-24
  • 打赏
  • 举报
回复
Deleting fields - If a field is deleted in a class, the stream written will not contain its value. When the stream is read by an earlier class, the value of the field will be set to the default value because no value is available in the stream. However, this default value may adversely impair the ability of the earlier version to fulfill its contract.

删除fields - 如果一个field被删除了,那么写出的(新的对象)流将不会包含它的值。当这个流被一个更早版本(更改之前)的类读入时,该field的值将被设置为默认值(0, null……)因为流中没有包含它可用的值。然而,这个默认值可能会损害这个更早版本(的对象),使其不能满足(它本来满足的)协议。

运行没有报错,不能说明对象“反序列化成功”,反序列化得到的对象跟原来没改的时候一模一样才算反序列化成功。
FengPrince 2012-09-24
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

另外,使用默认的序列化格式总是不太好,多数情况下,序列化格式应该反复斟酌,自己定义。
[/Quote]
嗯,谢谢提醒。。。
FengPrince 2012-09-24
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 的回复:]

引用 9 楼 的回复:
那么当删除fields时,如果使用新的类来反序列化旧的对象,由于序列化流的fields比.class所需的fields多,可以将多的丢弃,那么这种情况也是兼容的。


没错,我就是这么理解的。

当“序列化流的fields比.class所需的fields多”时,
只要反序列化过程中调用了 objectInputStream.defaultReadObjec……
[/Quote]
呵呵,你的理解应该是正确的,感觉标准说得比较概括,自己看的时候也没有去细想。

在CSDN上问问题,感觉很少有人回答我,非常感谢你,哈哈。。。
raistlic 2012-09-24
  • 打赏
  • 举报
回复
另外,使用默认的序列化格式总是不太好,多数情况下,序列化格式应该反复斟酌,自己定义。
raistlic 2012-09-24
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 的回复:]
那么当删除fields时,如果使用新的类来反序列化旧的对象,由于序列化流的fields比.class所需的fields多,可以将多的丢弃,那么这种情况也是兼容的。
[/Quote]

没错,我就是这么理解的。

当“序列化流的fields比.class所需的fields多”时,
只要反序列化过程中调用了 objectInputStream.defaultReadObject(); 多出的field会被忽略掉,
所以,“序列化流的fields比.class所需的fields多”时,总是没有问题的。

如果你汇总一下:

删除field时,
新class读旧的序列化流,没有问题
旧class读新的序列化流,有问题

添加field时,
新class读旧的序列化流,可以通过readObject()方法克服,没有问题
旧class读新的序列化流,没有问题

综合一下,删除field有问题,添加field没有问题。

FengPrince 2012-09-24
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 的回复:]

引用 1 楼 的回复:
而且我个人认为兼容应该有两种情况:
(1)新的class类兼容以前已经序列化的对象;
(2)新类的序列化对象使用旧版本的class文件来反序列化;
如果是第一种情况,删除一个field对第二种情况来说相当于添加一个field;
如果是第一种情况,添加一个field对第二种情况来说相当于删除一个field。


你说你看得懂,为什么对原文的描述视而不见呢?……
[/Quote]
首先,真的十分谢谢你耐心回答我的问题。。。

对于添加fields,用新的类来反序列化旧的对象时,即使不在新类中的readObject方法中为新加的fields指定值,JVM也会自动默认初始化,这一点与删除fields时使用旧的类来反序列化新对象一样,都是JVM为序列化流所缺少的那些fields指定默认值。
我的着眼点就在这里,认为两者使用的是同一个实现机制:序列化流没有类所需要的fields时JVM都为其指定默认址。所以一个兼容一个不兼容让我觉得困惑。

你的意思应该是:JVM实现机制并不是重点,重点是当前使用的.class所需要的fields必须能够被合理初始化。当序列化流的fields比.class所需的fields多时,可以将多的丢弃;但当比.class少时,必须要为少的那些fields显式指定值,这个时候,对于添加fields来说,可以通过新类的readObject来做,而对于删除fields来说,旧的类已经无法实现了,所以添加是兼容的,删除是不兼容的。。。

那么当删除fields时,如果使用新的类来反序列化旧的对象,由于序列化流的fields比.class所需的fields多,可以将多的丢弃,那么这种情况也是兼容的。

也就是说删除fields并不总是不兼容的,标准给出的只是不兼容的情况。同理,将non-static变为static也是一样的,它们都有兼容的可能性。

不知道我这种理解对吗??
期待您的回复,谢谢。。。。
raistlic 2012-09-24
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]
而且我个人认为兼容应该有两种情况:
(1)新的class类兼容以前已经序列化的对象;
(2)新类的序列化对象使用旧版本的class文件来反序列化;
如果是第一种情况,删除一个field对第二种情况来说相当于添加一个field;
如果是第一种情况,添加一个field对第二种情况来说相当于删除一个field。
[/Quote]

你说你看得懂,为什么对原文的描述视而不见呢?

里面已经说了不兼容的原因是: 用旧类反序列化新对象可能会因为被删掉的field被赋默认值而导致违反协议。

添加那一段也说明了:

既然你添加field,你当然知道旧的对象流读进来会没有那个field,所以你在readObject()方法里给新的field赋合适的值,就解决问题。
If class-specific initialization is needed, the class may provide a readObject method that can initialize the field to nondefault values
FengPrince 2012-09-24
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]

Deleting fields - If a field is deleted in a class, the stream written will not contain its value. When the stream is read by an earlier class, the value of the field will be set to the default valu……
[/Quote]
没有必要给我翻译,我看得懂英文。

既然是这个逻辑,那么Adding fields呢,怎么就是兼容的呢??
当使用新的class文件来反序列化旧的对象时,旧的对象也没有包含它需要用的值啊?不是照样默认初始化了(0,false,null)??
我前面说过,删除和添加就是一个逆过程,就看你从哪个角度来看了。
删除fields,使用旧的.class文件来反序列化新的对象与添加fields,使用新的.class文件来反序列化旧的对象,这两者有差别吗??都是反序列化流中的fields比类所要求的fields少了,既然少了就默认初始化呗!
可为什么添加fields是兼容的,删除fields就变成了不兼容的??

还有你说的一模一样,几乎是不可能的,只要是修改了non-static和non-transient fields都不可能导致一模一样,除非是修改了方法之类的东西。但标准中明确将adding fields作为兼容性修改!
FengPrince 2012-09-22
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 的回复:]

如果你显示赋值序列id,则兼容不兼容是根据序列id进行判断,如果不是,则根据当前类结构等诸多信息计算一个id用于判断是否兼容。
[/Quote]
老大,你都没看我的帖子啊!!
第一句话说得很明白,即使显式设置了id也不一定就是兼容的!!
为啥呢 2012-09-22
  • 打赏
  • 举报
回复
如果你显示赋值序列id,则兼容不兼容是根据序列id进行判断,如果不是,则根据当前类结构等诸多信息计算一个id用于判断是否兼容。
FengPrince 2012-09-22
  • 打赏
  • 举报
回复
自己顶。。。
FengPrince 2012-09-22
  • 打赏
  • 举报
回复
比方说,官方认为删除一个field是不兼容的,添加一个field是兼容的。
我测试了一下删除一下field没有出现不兼容啊??

而且我个人认为兼容应该有两种情况:
(1)新的class类兼容以前已经序列化的对象;
(2)新类的序列化对象使用旧版本的class文件来反序列化;
如果是第一种情况,删除一个field对第二种情况来说相当于添加一个field;
如果是第一种情况,添加一个field对第二种情况来说相当于删除一个field。
也就是说,从不同角度而言,添加与删除是互补互逆的,为什么一个导致兼容,另一个就不导致兼容呢??
而且我运行的实际情况(分4种情况测试)是,添加与删除都是兼容的。

还比如说将non-static field变成static是不兼容的,反过来是兼容的。
我测试的结果是
non-static field变成static
(1)新的class类兼容以前已经序列化的对象: 兼容的;
(2)新类的序列化对象使用旧版本的class文件来反序列化:不兼容
static field变成non-static
(1)新的class类兼容以前已经序列化的对象: 兼容的;
(2)新类的序列化对象使用旧版本的class文件来反序列化:不兼容
可以看出,无论是non-static改成static还是static改成non-static,都有兼容和不兼容的情况!!

我不知道,是否我对兼容性理解有误!!
反正想不明白官方说的意思,求高手不吝赐教,多谢!!
串联谐振双有源桥(SR-DAB)DC-DC变换器闭环仿真研究内容概要:本文围绕串联谐振双有源桥(SR-DAB)DC-DC变换器的闭环仿真展开研究,重点探讨其在电力电子系统中的动态响应特性与控制策略。通过对SR-DAB变换器建立数学模型,并结合Matlab/Simulink进行闭环仿真,分析系统在不同工作条件下的电压、电流波形及功率传输效率,验证控制算法的有效性与稳定性。研究涵盖系统建模、控制器设计(如PI控制)、软开关实现条件、抗干扰能力以及动态调节性能,旨在提升变换器在新能源、储能、数据中心供电等场景中的可靠性和能效水平。; 适合人群:具备电力电子、自动控制理论基础,从事新能源、微电网、电力系统仿真等相关领域的科研人员与工程技术人员。; 使用场景及目标:①用于高校及科研机构开展DC-DC变换器控制策略的教学与实验;②为工业界在高效率隔离型功率变换系统的设计与优化提供仿真依据和技术支撑;③服务于数据中心、电动汽车、可再生能源并网等场景下的电源系统研发。; 阅读建议:建议读者结合Matlab代码实践仿真流程,重点关注系统建模与控制器参数整定部分,通过调整负载、输入电压等条件观察系统响应,深入理解闭环控制对变换器性能的影响机制。
内容概要:本文介绍了FastReport VCL/FMX/LCL 2026.2.4版本的更新内容,重点涵盖核心引擎、图形处理、用户界面及导出功能的优化与修复。主要更新包括修复了Developer Express兼容性问题、TfrxSVGGraphic在TImage中的显示问题、PDF/A导出合规性、EMF图像旋转异常,以及ReportTree拖拽功能等问题。同时,部分模块新增了HTMLTags对XLSX导出的支持、内置右键菜单和新图标,并改进了FastCube组件的元素调整体验。此外,还修复了CPP脚本中Result参数名的Bug以及自定义过滤器中OR操作失效的问题。; 适合人群:使用Delphi或Lazarus进行开发,且涉及报表设计与数据展示功能的中高级开发者;尤其适用于需要集成FastReport控件到企业级应用中的技术人员。; 使用场景及目标:①提升报表系统在不同平台(VCL/FMX/LCL)下的稳定性和兼容性;②满足PDF/A标准的文档归档需求;③优化用户交互体验,如拖拽操作与元素缩放;④增强脚本支持与数据集字段识别能力,确保开发效率与功能完整性。; 阅读建议:建议结合官方更新链接和实际项目中使用的FastReport模块对照查看,重点关注自身所用组件(如VCL、FMX或Lazarus版)的具体修复项,及时升级以规避已知问题并利用新特性优化报表功能。

62,621

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧