本篇文章通过学习JavaGuide:Java基础常见面试题总结(中) | JavaGuide
之后的自我学习总结!
- 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
- 深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
【一、基本概念】
● 浅拷贝 (Shallow Copy)
-
复制对象本身,但是对于字段中的引用类型,复制的是引用地址
-
新、旧对象共享同一个引用对象
● 深拷贝 (Deep Copy)
-
不仅复制本身,还要把所有引用对象都复制一份
-
新、旧对象完全独立,不共享内部状态
【二、Java clone() 机制】
● Object.clone()
-
Java 基类 Object 提供了 clone()
-
默认执行 浅拷贝
● 启用 clone() 需要
-
实现
Cloneable
接口 (标记接口,无方法) -
重写
clone()
方法 -
调用
super.clone()
进行复制
@Override
public ClassName clone() {
try {
return (ClassName) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
【三、浅拷贝 vs 深拷贝 实际区别】
● 浅拷贝
class Person implements Cloneable {
private Address address; // Address 是可变对象
@Override
public Person clone() {
return (Person) super.clone();
}
}
结果: 新、旧 Person 对象共用同一个 address
● 深拷贝
@Override
public Person clone() {
Person p = (Person) super.clone();
p.address = address.clone(); // 手动复制引用对象
return p;
}
结果: 新、旧对象独立的 address
【四、注意点】
如果对象的字段全部是
-
基本类型 (如 int, double)
-
不可变对象 (如 String)
则 super.clone()
通常够用,不需要深复制
⚠ 如果字段是可变对象
-
如 List, Map, 自定义对象
-
那么必须手动调用 clone()
【五、为什么不推荐 clone()?】
问题 | 说明 |
---|---|
API 设计失败 | Cloneable 是标记接口,clone 异常处理复杂 |
难以维护 | 多层引用对象需通通 clone |
不谈经濟 | 优化难,效率不高 |
推荐替代:
-
拷贝构造器 (Copy Constructor)
-
完整定制方法 (Builder 或完整 clone 手写)
-
应急场景:实现 Serializable 后对象序列化/反序列化
【六、小结】
区别 | 浅拷贝 | 深拷贝 |
引用对象复制 | 只复制地址 | 也复制对象本身 |
实现 | super.clone() | 手动 clone 字段 |
效果 | 新、旧共享 | 新、旧独立 |
风险 | 引用冒泡、数据互相影响 | 无风险,安全复制 |
如非必要,建议利用拷贝构造器或手动构造而非 clone()方法,避免隐怪 bug
下面看一个例子帮助理解:
public class copylearn {
public static void main(String[] args) {
Person person1 = new Person(new Address("广州"));
Person person1copy = person1.clone();
System.out.println(person1 == person1copy);
System.out.println(person1.getAddress() == person1copy.getAddress());
System.out.println(person1copy.getAddress().getName());
}
public static class Address implements Cloneable{
private String name;
public Address(String name) {
this.name = name;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Override
public Address clone(){
try{
return(Address) super.clone();
}catch(CloneNotSupportedException e){
throw new AssertionError();
}
}
}
public static class Person implements Cloneable{
private Address address;
public Person(Address address) {
this.address = address;
}
public Address getAddress() {return address;}
public void setAddress(Address address) {this.address = address;}
@Override
public Person clone(){
try{
Person person = (Person) super.clone(); //默认浅拷贝
person.setAddress(address.clone()); //深拷贝Address
return person;
}catch(CloneNotSupportedException e){
throw new AssertionError();
}
}
}
}
结果:
false
false
广州
如果把源代码中 //深拷贝Address 这行注释掉的话,那么结果自然就是flase,true了
这里放个图片帮助理解:
上面的代码只是用来学习和测试,如果后期需要用到拷贝的话,最好还是使用构造器方法,下面给出示例:
public static class Address {
private String name;
public Address(String name) {
this.name = name;
}
// ✅ 拷贝构造函数
public Address(Address other) {
this.name = other.name; // String 是不可变的,直接赋值安全
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
public static class Person {
private Address address;
public Person(Address address) {
this.address = address;
}
// ✅ 拷贝构造函数(深拷贝 Address)
public Person(Person other) {
this.address = new Address(other.address); // 创建新的地址对象
}
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
}
对比项 | clone() | 拷贝构造函数 |
---|---|---|
是否推荐 | ❌ 不推荐(设计缺陷) | ✅ 推荐(安全、明确) |
控制力 | 差(容易误共享) | 强(字段你自己决定) |
可读性 | 一般 | 高 |
错误隐患 | 多(必须记得实现 Cloneable + 异常处理) | 少(构造函数明确) |