Java如何返回接收对象的独立拷贝并存储到其他位置
你代码里的PObject other = obj只是做了引用赋值,没有创建任何新对象。对于引用类型来说,变量存储的是堆内存中对象的地址,直接用等号赋值只会复制地址值,不会分配新的内存空间,所以obj和other本质是同一个对象的两个别名,打印出来的内存哈希码自然完全一致。
要拿到独立的对象副本,核心是必须创建全新的PObject实例,再把原对象的属性值复制到新实例上,常用的实现方式有以下几种:
1. 拷贝构造函数(最推荐,可控性最高)
给PObject类添加专门的拷贝构造方法,在构造逻辑里逐字段复制原对象的值:
public class PObject { // 类的属性示例 public int X { get; set; } public int Y { get; set; } public string Tag { get; set; } public Collider Collider { get; set; } // 引用类型成员 // 普通无参构造 public PObject() { } // 拷贝构造 public PObject(PObject source) { // 值类型成员直接赋值即可 X = source.X; Y = source.Y; // 不可变引用类型(比如string)可以直接赋值 Tag = source.Tag; // 可变引用类型成员如果需要完全独立,也要对应做拷贝,避免共享实例 Collider = new Collider(source.Collider); } }
对应的Instantiate方法修改为:
public static PObject Instantiate(PObject obj) { PObject other = new PObject(obj); // 这里真正分配了新的内存创建实例 objects.Add(other); return other; }
这种方式没有黑盒逻辑,哪些字段要拷贝、引用成员要不要做深拷贝完全由你控制,几乎不会出隐式问题。
2. 实现克隆接口
你也可以实现对应语言的原生克隆能力:C#实现ICloneable接口,Java重写根类Object的clone()方法。
注意:大部分语言默认的克隆实现是浅拷贝——如果类里有可变引用类型的成员,浅拷贝只会复制成员的引用,两个对象的引用成员还是指向同一个实例,修改其中一个会影响另一个。需要完全独立的副本时,必须手动给所有可变引用成员也实现拷贝逻辑,也就是做深拷贝。
3. 序列化反序列化实现深拷贝
如果类的嵌套结构很复杂,手写逐字段拷贝工作量太大,可以用序列化反序列化的方案:把原对象序列化到内存流,再反序列化为新实例。这种方式会自动递归拷贝所有嵌套成员,生成完全独立的深拷贝对象,不需要手动给每个层级写拷贝逻辑,只需要给PObject以及所有嵌套的成员类型标记为可序列化即可。
修改实现后再打印两个对象,你会看到@后缀的哈希值不同,说明两个对象分别占用独立的内存空间,修改其中一个的属性不会对另一个造成影响。
内容的提问来源于stack exchange,提问作者Tiago Barros

