Java 通过序列化进行深度克隆时,通过引用克隆某些实例字段

Java 通过序列化进行深度克隆时,通过引用克隆某些实例字段,java,serialization,clone,Java,Serialization,Clone,我正在使用序列化深入克隆一个大型Java类,正如所建议的那样。在相应的问题中,我解释了为什么需要以这种方式进行克隆,这突出了不同深度克隆技术在结果上的一个重要差异,即在克隆中保留共享引用,在我的例子中,这是必须的。简而言之,如果在原始的两个字段中指向同一个对象,那么在深度克隆中,这些字段不应指向两个不同的新对象,而应指向同一个新对象。通过序列化的深度克隆实现了这一点 由于该技术要求对树中的类进行的唯一更改是使所有类实现可序列化,因此我不会在树的每个类中编写“clone”方法。所以我不是在每个类中

我正在使用序列化深入克隆一个大型Java类,正如所建议的那样。在相应的问题中,我解释了为什么需要以这种方式进行克隆,这突出了不同深度克隆技术在结果上的一个重要差异,即在克隆中保留共享引用,在我的例子中,这是必须的。简而言之,如果在原始的两个字段中指向同一个对象,那么在深度克隆中,这些字段不应指向两个不同的新对象,而应指向同一个新对象。通过序列化的深度克隆实现了这一点

由于该技术要求对树中的类进行的唯一更改是使所有类实现可序列化,因此我不会在树的每个类中编写“clone”方法。所以我不是在每个类中编写代码来克隆每个字段。但是我仍然想从克隆过程中排除一些字段,我通过在不想克隆的字段声明中添加
transient
修饰符来做到这一点。这些字段在克隆中将为
null

现在我有一个不同的需要。我需要能够说,某个字段确实需要克隆,但不是深度克隆:只需复制引用;让克隆中的该字段指向与原始字段中相同的对象

因此,我想知道如何使序列化通过复制引用而不是序列化来克隆特定字段,就像对其他字段进行反序列化一样。这是我的问题


否则,我能想到的唯一解决方案是在树的每个类中实现一个“clone”方法(不一定是
Object.clone()
),并在每个“clone”方法中显式分配每个字段,对某些字段使用序列化,并复制对其他字段的引用。但是除了由于要克隆的类有很多字段而需要做大量工作之外,我还担心这样我将不再在主对象的树中保留共享引用,因为我将分别克隆每个字段,因此,如果树中的两个字段指向同一个对象,那么在克隆每个字段时,这一事实将不为人所知,因此序列化将不可能使它们指向同一个新对象。

我想演示一下我在评论中提到的复制私有临时字段的想法。在这里,我对它进行了进一步的开发,引入了一个getter,使它更清楚地表明引用可以从对象中取出。注意,getter是私有的,也就是说,只能从类内部调用它,这就足够了。代码已经编译好了

public class MyClonable {

    private transient List<String> fieldRequiringShallowCopy;

    /** getter to be used by cloning */
    private List<String> getFieldRequiringShallowCopy() {
        return fieldRequiringShallowCopy;
    }

    /**
     * Call this method after cloning by serialization to copy transient field.
     * @param original The object that this object was cloned from.
     */
    public void finishClone(MyClonable original) {
        this.fieldRequiringShallowCopy = original.getFieldRequiringShallowCopy();
    }

}
公共类MyClonable{
私有瞬态列表字段要求浅拷贝;
/**克隆要使用的getter*/
私有列表getFieldRequiringShallowCopy(){
返回字段需要浅拷贝;
}
/**
*通过序列化克隆后调用此方法以复制临时字段。
*@param original克隆此对象的对象。
*/
public void finishClone(MyClonable原件){
this.fieldrequireringshallowcopy=original.getfieldrequireringshallowcopy();
}
}
编辑:要回答您在注释中的问题,如果需要浅层复制的字段嵌套在大对象中更深,则需要从外部调用并委托给包含该字段的子对象:

public class BigClonable {

    private Part enclosedThing;

    private Part getEnclosedThing() {
        return enclosedThing;
    }

    /**
     * Call this method after cloning by serialization to copy transient field.
     * @param original The object that this object was cloned from.
     */
    public void finishClone(BigClonable original) {
        // delegate to part object
        enclosedThing.finishClone(original.getEnclosedThing());
    }

}

public class Part {

    private transient List<String> fieldRequiringShallowCopy;

    /** getter to be used by cloning */
    private List<String> getFieldRequiringShallowCopy() {
        return fieldRequiringShallowCopy;
    }

    public void finishClone(Part original) {
        this.fieldRequiringShallowCopy = original.getFieldRequiringShallowCopy();
    }

}
公共类BigClonable{
封闭的私人部分;
私有部分getEnclosedThing(){
归还所附物品;
}
/**
*通过序列化克隆后调用此方法以复制临时字段。
*@param original克隆此对象的对象。
*/
public void finishClone(BigClonable原件){
//委托给零件对象
enclosedThing.finishClone(original.getEnclosedThing());
}
}
公共课部分{
私有瞬态列表字段要求浅拷贝;
/**克隆要使用的getter*/
私有列表getFieldRequiringShallowCopy(){
返回字段需要浅拷贝;
}
公共作废完成克隆(部分原件){
this.fieldrequireringshallowcopy=original.getfieldrequireringshallowcopy();
}
}

此技术可用于任何嵌套级别。您需要在每个级别上使用委托方法,我也喜欢在每个级别上使用私有getter(和/或setter)。

一个想法是声明该字段是暂时的,然后通过序列化在克隆后复制引用。我还认为枚举的序列化考虑到每个枚举只能有一个实例,因此如果您可以将字段引用的类型设置为en enum?@OleV.V。如果理解正确,我仍然会创建整个根对象的深层克隆,但随后会将其部分
瞬态
字段重新指定给与原始对象相同的引用。一个问题是,某些字段可能不是
公共的
,或者带有setter。我不想深度克隆某些字段的另一个原因是,它们是大列表,我不需要深度克隆,因此我想将它们从深度克隆中排除,以提高性能。
Enum
这一点很有趣,我可以创建一些字段
Enum
,但对于其他一些字段,这将是一个太多的hack,会使代码太不清晰。@OleV.V。很抱歉,我误解了您的部分想法,因此我在评论中提到的关于性能的缺点并不存在,因为您的意思是使这些字段变为临时字段,并在克隆后分配它们,这样它们就不会得到深度克隆。实际上,我正在研究是否可以从克隆主对象的位置访问所有这些字段,以便在克隆后将这些字段设置为原始引用;这实际上可能解决了我的问题。在缺少公共getter的情况下,请注意私有字段只是cl