Java 对象什么时候可序列化但不可克隆才有意义?

发布于 2024-08-19 04:53:26 字数 1373 浏览 1 评论 0原文

如果一个Java类实现了Serialized接口但没有公共clone()方法,通常可以像这样创建深拷贝:

class CloneHelper {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();
            byte[] bytes = baos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            T copy = (T) ois.readObject();
            ois.close();
            return copy;
        } catch (ClassNotFoundException ex) {
            // Shouldn't happen
            throw new Error(ex);
        } catch (IOException ex) {
            // Probably a bug in T's custom serialization methods
            throw new RuntimeException(ex);
        }
    }
}

我经常遇到第三种情况 :像这样的派对库课程,并诉诸像上面那样的黑客。有时我什至扩展了 ObjectOutputStream 以使副本更浅。除了效率低下之外,它从来不会造成严重的问题(编码/解码速度慢,临时序列化图可能会消耗大量内存。)

并且如果使用此技术不安全,则可能不应该声明该类可序列化

所以我想知道的是,如果您的类是可序列化的,那么什么可能会阻止您定义公共的clone()方法(使用Cloneable< /code> 接口还是复制构造函数?)


相关:在 Java 中复制对象

If a Java class implements the Serializable interface but does not have a public clone() method, it is usually possible to create a deep copy like this:

class CloneHelper {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();
            byte[] bytes = baos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            T copy = (T) ois.readObject();
            ois.close();
            return copy;
        } catch (ClassNotFoundException ex) {
            // Shouldn't happen
            throw new Error(ex);
        } catch (IOException ex) {
            // Probably a bug in T's custom serialization methods
            throw new RuntimeException(ex);
        }
    }
}

I often encounter third-party library classes like this and resort to hacks like the one above. I've even extended ObjectOutputStream on occasion to make the copy shallower. It's never caused serious problems, other than being inefficient (slow to encode/decode and the temporary serialization graphs can consume a lot of memory.)

And if it is not safe to use this technique, the class probably should not have been declared Serializable.

So what I would like to know is, if your class is Serializable, what might prevent you from defining a public clone() method (using either the Cloneable interface or a copy constructor?)


Related: Copy an object in Java

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

百善笑为先 2024-08-26 04:53:26

我更愿意使用复制构造函数而不是使用上述机制。您可以更精细地定义要深层或浅层复制的内容,并使对象的复制与对象的序列化区分开来。复制构造函数可以(例如)允许两个对象共享对主对象的引用,但这可能不适合序列化并通过网络传输的对象。

请注意,Cloneable 方法现在被广泛认为已损坏。请参阅 Joshua Bloch 的这篇文章了解更多信息。特别是它没有 clone() 方法!

I would prefer to use a copy constructor rather than use the above mechanism. You can more finely define what's to be deeply or shallowly copied, and make the copying of an object distinct from the serialisation of an object. A copy constructor may (for example) allow two objects to share a reference to a master object, whereas this may not be appropriate for an object serialised and transmitted across the network.

Note that the Cloneable method is widely regarded now as broken. See this article with Joshua Bloch for more information. In particular it doesn't have a clone() method!

月牙弯弯 2024-08-26 04:53:26

Brian 关于 Cloneable 的观点非常好,但即使 Cloneable 工作正常,仍然存在您可能希望对象可序列化但不可克隆的情况。

如果一个对象在进程范围之外具有唯一的标识,例如数据库记录的内存中表示,那么您不希望它是可克隆的,因为这相当于创建具有相同属性的新记录,包括标识 -相关属性,例如数据库密钥,这几乎从来都不是正确的事情。同时,出于稳定性或其他原因,您可能会将一个系统分成多个进程,因此您可能有一个进程与数据库通信并生成这些“ENTITY”对象(有关更多信息,请参阅 Eric Evans 的“领域驱动设计”)有关在数据支持的应用程序中维护对象标识一致性的信息),但单独的进程可以使用这些对象来执行业务逻辑操作。实体对象需要可序列化才能从一个进程传递到另一个进程。

Brian's point about Cloneable is very good, but even if Cloneable worked right, there are still instances where you might want an object to be serializeable but not cloneable.

If an object has a unique identity outside the scope of the process, like an in-memory representation of a database record, you don't want it to be cloneable because that is equivalent to making a new record with identical attributes, including identity-related attributes like the database key, which is almost never the right thing. At the same time, you may have a system broken into multiple processes for stability or other reasons, so you may have one process talking to the database and generating these "ENTITY" objects (See "Domain-Driven Design" by Eric Evans for more info on maintaining object identity coherence in a data-backed application), but a separate process may use these objects to perform business logic operations. The entity object would need to be serializable for it to be passed from one process to another.

素罗衫 2024-08-26 04:53:26

我认为可序列化和可克隆接口应该用于完全不同的目的。如果你有一个复杂的类,那么实现它们中的每一个就不是那么容易了。所以一般情况下这取决于目的。

I think Serializable and Cloneable interfaces should be used for absolutely different purposes. And if you have a complex class then implementing each of them is not so easy. So in general case it depends on the purposes.

妖妓 2024-08-26 04:53:26

嗯,您是说序列化机制是间接“克隆”对象的一种方法。这当然不是它的主要功能。它通常用于让程序通过网络传输对象,或存储并稍后读取它们。您可能期望以这种方式使用对象,并实现 Serialized,而不期望代码在本地克隆对象,并且不实现 Cloneable。

代码通过序列化解决这个问题的事实表明,代码正在以作者不希望的方式使用对象,这可能是作者或调用者的“错误”,但这并不意味着通常可序列化和可克隆一起走。

另外,我不确定clone()是否“损坏”,以及正确实现起来是否困难。恕我直言,复制构造函数更容易使用并且正确。

Well, you're saying the serialization mechanism is one way to "clone" objects indirectly. That is of course not its primary function. It's usually used to let programs transmit objects across a network, or store and later read them. You may expect an object to be used this way, and implement Serializable, while not expecting code to clone objects locally, and not implement Cloneable.

The fact that code is working around this via serialization suggests the code is using an object in a way the author didn't intend, which could be either the author or caller's "fault", but it doesn't imply that in general Serializable and Cloneable go together.

Separately, I am not sure clone() is "broken" as much as tricky to implement correctly. A copy constructor is more natural to use and get right IMHO.

完美的未来在梦里 2024-08-26 04:53:26

Serialized 的最大问题之一是它们不能轻易地变得不可变。
序列化迫使你在这里做出妥协。

我很想在对象图中创建复制构造函数。只是要做一些更繁琐的工作。

One of the biggest problems with Serializable is that they can't be easily made immutable.
Serialization forces you to make a compromise here.

I'd be tempted to create copy constructors down the object graph. Its just a bit more grunt work to do.

不乱于心 2024-08-26 04:53:26

这让我觉得有点危险,因为序列化存在许多陷阱(尽管其中大多数不太可能,但如果我在 3d 方库中序列化对象,我仍然想测试它们)。不太可能是一个常见问题,但有可能有一个对象,其状态的一部分是易失性变量,该变量可能是克隆操作的一部分(并不是说这是一个好的设计,只是说它是可能的),并且这样的字段不会在序列化/反序列化过程中被复制。我想到的另一个问题是枚举、常量,以及如果您在反序列化期间不处理它们,则可能会获得这些内容的多个副本。

再说一次,边缘情况,但你需要注意。

That strikes me as sort of dangerous, as there are a number of pitfalls to serializing, (although most of them are unlikely, I'd still want to test for them if I was serializing objects in 3d party libraries). Not likely to be a common issue, but it's possible to have an object with a volatile variable as part of it's state that might be part of a cloning operation (not that this is a good design, just that it's possible), and such a field would not get copied in a serialization/deserialization process. Another issue that comes to mind is enums, constants, and the possibility of getting multiple copies of such things if you don't deal with them during deserialization.

Again, edge cases, but something you'd want to watch out for.

不…忘初心 2024-08-26 04:53:26

我只是想到了另一种情况 - 当它是 enum< /a>.

或者更一般地说,当您的类实现 <代码>readResolve。这意味着您从 readObject 返回的对象与从流中读取的对象不同,因此它不一定是最初写入流的对象的副本。

I just thought of another case - When it is an enum.

Or more generally, when your class implements readResolve. This means that the object you get back from readObject is not the same one that was read from the stream, and hence it is not necessarily a copy of the object that was originally written to the stream.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文