通过自定义序列化模拟 java 对象外部化

Posted

技术标签:

【中文标题】通过自定义序列化模拟 java 对象外部化【英文标题】:simulating java object externalization via custom serialization 【发布时间】:2016-09-21 19:07:51 【问题描述】:

与序列化相比,外部化的主要好处是外部化只保留对象的一部分,而不是序列化的整个对象。但是我认为如果我们在 writeObject() 方法中不调用 ObjectOutputStreamdefaultWriteObject() 方法,我们可以通过自定义序列化来模拟外部化可序列化的类。所以不用调用defaultWriteObject()方法,只在writeObject()方法中持久化序列化类所需的实例变量,就可以实现外化的好处。

这是一个演示上述内容的示例:

package com.test;

import java.io.*;

public class Test 
    public static void main(String[] args) throws FileNotFoundException,     IOException, ClassNotFoundException 
        Dog dog = new Dog();
        System.out.println("before serialization: i = " + dog.i + ", j = " +     dog.j);

        FileOutputStream fos = new FileOutputStream("abc.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(dog);

        FileInputStream fis = new FileInputStream("abc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Dog dog2 = (Dog) ois.readObject();
        System.out.println("after deserialization: i = " + dog2.i + ", j = " +      dog2.j);

    

    public static class Dog implements Serializable 
        int i = 10;
        int j = 20;

        private void writeObject(ObjectOutputStream oos) throws IOException
            //oos.defaultWriteObject();
            System.out.println("In WriteObject");
            oos.writeInt(i);
        

        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException 
            //ois.defaultReadObject();
            System.out.println("In ReadObject");
            i = ois.readInt();
        
    

这段代码的输出是:

before serialization: i = 10, j = 20
In WriteObject
In ReadObject
after deserialization: i = 10, j = 0

如您所见,oos.defaultWriteObject()ois.defaultReadObject(); 已被注释,我们只保留和恢复实例变量 i

那么,我的假设是否正确,我们可以通过自定义序列化来模拟外部化概念?

【问题讨论】:

“与序列化相比,外部化的主要好处是外部化只保留对象的一部分,而不是像序列化那样保留整个对象” ...你在问什么?序列化的表单应该有足够的信息来保存和恢复对象的状态。 “对象的一部分”的概念在序列化的上下文中毫无意义。如果您指的是“自定义序列化表单”,那么内置的序列化工具提供了创建完全自定义序列化表单的能力。 @scottb 我已经用一个例子更新了我的问题,请查看。 【参考方案1】:

那么,我的假设是否正确,我们可以模拟外部化 通过自定义序列化的概念?

您的假设是正确的,即程序员有能力为他选择的类构造任何序列化形式。

Serializable 接口是一个标记接口,它向 Java 运行时环境发出信号,表明已为实现类启用了基于 Java 的序列化。如果您什么都不做,Java 运行时会调用默认的序列化工具,该工具会根据您的类的所有实例字段为您创建一个序列化表单。

类的最佳序列化形式是:

仅描述其实例的逻辑状态 不包含特定于实现的详细信息或元数据 写入和读取流式传输和恢复类实例所需的最少信息

例如,在您上面的代码中,如果 ij 都描述了您的对象的有意义的状态,那么不包含 j 的序列化表单将是有缺陷的,因为您将无法恢复对象在反序列化后恢复到有意义的状态。

但是,如果 i 描述了有意义的状态,但 j 是一个不属于对象逻辑状态的实现细节,那么最好从流中删除 j 以获得更优化的序列化形式。

虽然默认的序列化形式(由内置 Java 序列化工具发出)通常足以满足简单的值类,但更复杂的抽象包含不应成为其序列化形式的一部分的元数据和实现信息。

为了帮助程序员为他们的类设计最佳的序列化形式(如果默认形式不合适),Java 提供了两种广泛的机制来为对象生成最佳的序列化形式:

自定义序列化 Externalizable 接口

前一种策略允许程序员使用transient关键字修改内置Java序列化工具的行为,并挂钩到readObject()writeObject()readResolve()等方法。对于具有必须保护的不变量的不可变值类,特别推荐使用序列化代理。

后一种策略让程序员实现Externalizable 而不是SerializableExternalizable 本身扩展了Serializable)。 Externalizable 接口与Serializable 不同,它不是标记接口。它的方法在实现时旨在让程序员完全控制对象的序列化形式是如何发出和恢复的。

“外化相对于序列化的主要好处是外化只持久化对象的一部分,而不是整个对象 就像序列化的情况一样。 "

仅包含“对象的一部分”并且不包含重构对象在序列化之前存在的状态所需的所有信息的序列化形式是有缺陷的序列化形式。这种形式可能会在那些依赖序列化进行进程间通信的平台中引起问题。

【讨论】:

感谢您的详细回复。所以无论我们可以用外部化做什么,我们都可以用自定义序列化来实现,对吧?这意味着如果我们不想持久化整个对象,我们可以像使用自定义序列化一样使用外部化(以问题示例中所示的方式)

以上是关于通过自定义序列化模拟 java 对象外部化的主要内容,如果未能解决你的问题,请参考以下文章

Spring集成Java DSL:指定自定义反序列化器

post发送JSON数据(字符串、数组、字典、自定义对象)给服务器

ASP.NET WEB API 中自定义对象的序列化 [重复]

Java Jackson如何在自定义序列化程序中为对象使用默认序列化程序

Java Serializable接口(序列化)理解及自定义序列化

深入分析Java的序列化与反序列化