【问题标题】:Serialized public static final object not == to itself when deserialized反序列化时,序列化的公共静态最终对象不 == 自身
【发布时间】:2018-04-06 23:53:54
【问题描述】:

我创建了一个需要可序列化的树数据结构,但每棵树都共享一个公共根对象,这是一个公共静态最终常量。

我遇到的问题如下所示。在我序列化 ROOT 对象后,反序列化会创建一个新的 Tree 对象,而不是返回对原始常量的引用。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;

public class Main {

    public static final class Tree implements Serializable {

        public final ArrayList<Tree> branches = new ArrayList<>();
    }

    public static final Tree ROOT = new Tree();

    public static void main(String[] args) throws Exception {
        File file = new File("C:/Users/Stephen/Desktop/temp.bin");
        ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
        oout.writeObject(ROOT);
        oout.writeObject(ROOT);
        oout.close();
        ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
        Tree t1 = (Tree) oin.readObject();
        Tree t2 = (Tree) oin.readObject();
        oin.close();
        System.out.println(ROOT == t1); // false
        System.out.println(t1 == t2); // true
    }
}

第一个打印语句输出“false”,但我希望它是“true”。

【问题讨论】:

  • 由于一些 cmets 提到了这一点:是的,我确实知道 == 和 #equals(Object) 之间的区别。我想要前者。从 ObjectInputStream 读取对象时,我希望它返回对已序列化的完全相同的公共静态最终 ROOT 对象的引用,而不是不同的 Tree 对象,即使该不同的对象#equals to ROOT。
  • 进一步澄清:我理解为什么这段代码输出“假”。我有 15 年以上的 Java 开发经验,不需要关于语言基础的建议。我正在寻找解决这个问题的方法。我需要一种方法来识别现有的公共静态最终对象何时被反序列化,而不是创建一个新对象,而是返回对原始对象的引用。我的第一次尝试是编写一个自定义的反序列化方法(如此处所述:oracle.com/technetwork/articles/java/javaserial-1536170.html),但这种方法似乎会自动创建一个新对象。
  • 您是否研究过 readResolve 和 writeResolve?我想他们可能会做你想做的事。 (不过,要做到正确并不是超级简单;Effective Java 有一个关于它的两个部分,iirc)
  • 具体来说,我推荐看Currency类的实现。
  • 当您以简化的方式编写问题时,不会自动显示您 15 年的经验。人们会自动假设更简单的问题,因为 SO 充满了它们。您必须小心不要发生这种情况:stackoverflow.com/questions/49533897/…(它有 -2 / +2 分,因为人们误解了这个问题)。

标签: java serialization


【解决方案1】:

感谢@yshavit 的评论,我能够找到自己问题的答案。诀窍是在反序列化时使用 #readResolve() 方法。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;

public class Main {

    public static final class Tree implements Serializable {

        public final ArrayList<Tree> branches = new ArrayList<>();
        private final boolean isRoot = ROOT == null;

        private Object readResolve() throws ObjectStreamException {
            if(isRoot)
                return ROOT;
            else
                return this;
        }
    }

    public static final Tree ROOT = new Tree();

    public static void main(String[] args) throws Exception {
        File file = new File("C:/Users/Stephen/Desktop/temp.bin");
        ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
        oout.writeObject(ROOT);
        oout.writeObject(ROOT);
        oout.close();
        ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
        Tree t1 = (Tree) oin.readObject();
        Tree t2 = (Tree) oin.readObject();
        oin.close();
        System.out.println(ROOT == t1); // true
        System.out.println(t1 == t2); // true
    }
}

【讨论】:

  • 对不起,今天才看到你对我的评论的回复。很高兴你成功了!
【解决方案2】:

== 在 java 中比较内存位置,当且仅当它相同时,对象才被认为是相等的。话虽如此,Java 中的平等取决于用户。当且仅当它们是相同的对象(存储在相同的内存位置)或您是否有一组规则(该对象的哪些属性应该相同以认为对象相等)时,您是否希望对象被视为相等?

在您的情况下,您需要检查 ROOTroot 对象的内容及其属性的内容。== 将始终比较内存位置,并且对于不同的对象(ROOT 和根目录)总是不同的在这种情况下)

【讨论】:

  • 我想要前者。我知道 == 和 #equals(Object) 之间的区别,我希望这些对象彼此 ==。
  • @sgware == 永远不会返回 true,因为比较堆上的 2 个不同对象。这将帮助您更好地理解。stackoverflow.com/questions/29051809/…
  • 我已经明白了。我的问题是不理解 == 是如何工作的。我的问题是找到一种方法来确保当一个公共静态最终常量被反序列化时,它被反序列化为本身,而不是一个新对象。我怀疑该解决方案将涉及编写自定义序列化和反序列化方法,但这样做已经假定创建一个新对象并且似乎没有提供一种方法来返回对现有对象而不是新对象的引用。
【解决方案3】:

root == ROOT 不会返回内存位置吗?您需要比较根 ArrayList 是否与 ROOT ArrayList 相同

【讨论】:

  • 是的,它正在检查内存位置,因为我希望它们相同。可悲的是,它们的 ArrayList 也不是彼此之间的 ==。
  • @sgware 只记得 java 是按值传递而不是按引用传递,因此在说 oout.writeObject(ROOT); 时需要明确询问其内存位置;
猜你喜欢
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 2015-03-19
  • 2020-06-07
  • 2013-12-04
相关资源
最近更新 更多