Java对象什么时候可以序列化但不能克隆?

Java对象什么时候可以序列化但不能克隆?,java,serialization,serializable,deep-copy,cloneable,Java,Serialization,Serializable,Deep Copy,Cloneable,如果Java类实现了Serializable接口,但没有公共的clone()方法,通常可以创建如下深度副本: class CloneHelper { @SuppressWarnings("unchecked") public static <T extends Serializable> T clone(T obj) { try { ByteArrayOutputStream baos = new ByteArrayOutput

如果Java类实现了
Serializable
接口,但没有公共的
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);
        }
    }
}
类克隆助手{
@抑制警告(“未选中”)
公共静态T克隆(T obj){
试一试{
ByteArrayOutputStream bas=新的ByteArrayOutputStream();
ObjectOutputStream oos=新的ObjectOutputStream(BAS);
oos.writeObject(obj);
oos.close();
byte[]bytes=baos.toByteArray();
ByteArrayInputStream bais=新的ByteArrayInputStream(字节);
ObjectInputStream ois=新ObjectInputStream(BAI);
T copy=(T)ois.readObject();
ois.close();
返回副本;
}捕获(ClassNotFoundException ex){
//不应该发生
抛出新错误(ex);
}捕获(IOEX异常){
//可能是T的自定义序列化方法中的错误
抛出新的运行时异常(ex);
}
}
}
我经常遇到像这样的第三方库类,并求助于上面这样的黑客。我甚至偶尔扩展了
ObjectOutputStream
,以使副本更浅。除了效率低下(编码/解码速度慢,临时序列化图可能会消耗大量内存)之外,它从来不会导致严重的问题

如果使用这种技术不安全,那么该类可能不应该被声明为可序列化的

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



相关:

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


注意,
Cloneable
方法现在被广泛认为是不可靠的。更多信息请参见Joshua Bloch。特别是它没有
clone()
方法

Brian关于Cloneable的观点很好,但即使Cloneable工作正常,仍然有一些情况下,您可能希望对象可以序列化,但不能克隆


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

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

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


另外,我不确定clone()是否“坏了”,也不确定它是否很难正确实现。拷贝构造函数的使用更为自然,也更为正确。

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

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


再次强调,边缘情况,但您需要注意的是。

可序列化的最大问题之一是它们不容易变为不可变。 序列化迫使您在这里做出妥协


我很想在对象图中创建复制构造函数。这只是有点繁重的工作要做

我刚刚想到了另一个案例——当它是一个案例时


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

这是非常正确的。事实上,我的回答是为了解决上述问题,但显然没有做到这一点