
浅拷贝仅复制引用而非对象本身,因此对可变对象的修改会反映在原对象上;但 string 因其不可变性无法体现该副作用,需改用 stringbuilder 等可变类型才能清晰验证。
浅拷贝仅复制引用而非对象本身,因此对可变对象的修改会反映在原对象上;但 string 因其不可变性无法体现该副作用,需改用 stringbuilder 等可变类型才能清晰验证。
在 Java 中,浅拷贝(shallow copy)是指新对象与其原始对象共享内部引用类型字段的内存地址——即“复制引用,不复制实例”。这意味着:若被引用的对象是可变的(mutable),那么通过任一引用对其状态的修改,都会影响另一方;但若被引用的是不可变对象(如 String),则赋值操作(=)只会改变引用指向,而不会触发共享状态的变更,从而掩盖浅拷贝的典型副作用。
为什么原代码中没出现预期的副作用?
你定义的 Joo 类中 name 字段是 String 类型:
public String name;
虽然 Joo b = new Joo(a) 执行的是浅拷贝(this.name = a.name),确实让 a.name 和 b.name 指向同一个字符串对象 "Bernt",但当你执行:
b.name = "Ernie";
这并非“修改字符串内容”,而是让 b.name 重新指向一个全新的字符串对象 "Ernie"。由于 String 是不可变类(immutable),它没有提供任何修改自身内容的方法(如 setCharAt() 或 append()),所有看似“修改”的操作(如 substring()、toUpperCase())均返回新实例。因此,a.name 仍指向 "Bernt",毫无影响——这不是浅拷贝失效,而是 String 的设计特性天然规避了共享状态风险。
✅ 正确演示浅拷贝副作用:使用可变对象
要真实观察浅拷贝引发的副作用,必须使用可变引用类型,例如 StringBuilder、ArrayList 或自定义可变类。以下是修正后的可运行示例:
public class Joo {
public StringBuilder name;
public Joo(String a) {
this.name = new StringBuilder(a); // 创建新 StringBuilder 实例
}
// 浅拷贝构造器:直接复用引用
public Joo(Joo a) {
this.name = a.name; // 关键:未 new,仅赋值引用
}
public static void main(String[] args) {
Joo a = new Joo("Bernt");
Joo b = new Joo(a);
System.out.println("a: " + a.name); // a: Bernt
System.out.println("b: " + b.name); // b: Bernt
b.name.append(" (modified)"); // ✅ 修改共享对象的状态(非重新赋值!)
System.out.println("a: " + a.name); // a: Bernt (modified)
System.out.println("b: " + b.name); // b: Bernt (modified)
}
}
输出结果:
a: Bernt b: Bernt a: Bernt (modified) b: Bernt (modified)
可见,b.name.append(...) 直接改变了底层 StringBuilder 对象的内容,由于 a.name 与 b.name 指向同一实例,修改同步体现。
⚠️ 注意事项与最佳实践
-
浅拷贝 ≠ 安全拷贝:仅当所有字段均为基本类型或不可变引用类型(如
String,LocalDateTime)时,浅拷贝才等价于逻辑上的“安全副本”。 -
深拷贝适用场景:若对象包含可变引用(如
List<t></t>、Date、自定义 POJO),且需完全隔离修改影响,应实现深拷贝(可通过序列化、第三方库如 Apache Commons Lang 的SerializationUtils.clone(),或手动递归复制)。 -
构造器命名建议:为避免歧义,可将浅拷贝构造器明确命名为
JooShallowCopy(Joo src),深拷贝构造器为JooDeepCopy(Joo src)。 -
现代替代方案:考虑使用不可变数据结构(如
ImmutableList)或 record 类(Java 14+)从设计上消除共享状态风险。
总之,理解浅拷贝的关键在于区分“引用重定向”(=)与“对象状态变更”(如 append(), add())。只有后者才能暴露引用共享的本质——而这正是可变类型存在的意义,也是深入掌握 Java 对象模型的必经之路。










