1. clone() 方法的核心基础
(1)方法定义(Object 类中)
clone() 是 Object 类的protected 本地方法,源码简化如下:
protected native Object clone() throws CloneNotSupportedException;
native:底层由C/C++实现,直接操作内存,拷贝效率高;protected:默认仅允许同包类/子类访问,若想让外部类调用,需重写为public;CloneNotSupportedException:若类未实现Cloneable接口,调用clone()会抛出此异常。
(2)核心规则
clone()创建的是新对象(堆中新内存地址),而非引用拷贝;- 要调用
clone(),类必须实现Cloneable接口(标记接口,无任何方法,仅表示“该类允许克隆”); - 默认实现是浅拷贝(关键!后续重点讲)。
2. 使用 clone() 的完整步骤
步骤1:实现 Cloneable 接口
// 标记该类允许克隆
public class User implements Cloneable {
private String name;
private int age;
// 引用类型字段(用于演示深浅拷贝)
private Address address;
// 构造器、getter/setter 省略
public User(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 步骤2:重写 clone() 方法(改为public,处理异常)
@Override
public Object clone() throws CloneNotSupportedException {
// 调用父类(Object)的 clone() 方法,完成基础拷贝
return super.clone();
}
}
// 引用类型辅助类
class Address {
private String city;
public Address(String city) {
this.city = city;
}
// getter/setter 省略
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
}
步骤3:调用 clone() 方法
public class CloneDemo {
public static void main(String[] args) {
try {
// 创建原对象
Address addr = new Address("北京");
User user1 = new User("张三", 20, addr);
// 克隆新对象
User user2 = (User) user1.clone();
// 验证:新对象和原对象地址不同(是新对象)
System.out.println(user1 == user2); // false
// 验证:基础类型字段值相同
System.out.println(user1.getAge() == user2.getAge()); // true
System.out.println(user1.getName().equals(user2.getName())); // true
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
3. 核心难点:浅拷贝 vs 深拷贝
这是 clone() 最易踩坑的点,先明确两者的核心区别:
| 类型 | 拷贝规则 | 风险点 |
|---|---|---|
| 浅拷贝(默认) | 1. 基础类型字段:拷贝值本身 2. 引用类型字段:拷贝地址引用(新旧对象指向同一个引用对象) | 修改新对象的引用字段,会影响原对象 |
| 深拷贝 | 1. 基础类型字段:拷贝值 2. 引用类型字段:创建新的引用对象(新旧对象引用字段指向不同内存) | 无上述风险,拷贝更彻底 |
(1)浅拷贝的问题演示
接上面的示例,修改新对象的引用字段:
public class ShallowCloneProblem {
public static void main(String[] args) {
try {
Address addr = new Address("北京");
User user1 = new User("张三", 20, addr);
User user2 = (User) user1.clone();
// 修改新对象的引用字段(地址)
user2.getAddress().setCity("上海");
// 原对象的地址也被修改了!(浅拷贝导致)
System.out.println(user1.getAddress().getCity()); // 输出:上海
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
(2)实现深拷贝的两种方式
方式1:重写 clone() 时,手动拷贝引用字段
让引用类型也实现 Cloneable 并重写 clone(),然后在主类的 clone() 中手动拷贝:
// 1. Address 实现 Cloneable 并重写 clone()
class Address implements Cloneable {
private String city;
public Address(String city) { this.city = city; }
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
// getter/setter 省略
}
// 2. User 类重写 clone(),手动拷贝引用字段
public class User implements Cloneable {
private String name;
private int age;
private Address address;
@Override
public Object clone() throws CloneNotSupportedException {
// 第一步:拷贝当前对象的基础字段(浅拷贝)
User cloneUser = (User) super.clone();
// 第二步:手动拷贝引用字段(深拷贝核心)
cloneUser.address = (Address) this.address.clone();
return cloneUser;
}
// 构造器、getter/setter 省略
}
方式2:通过序列化实现深拷贝(更通用)
无需让每个引用类都实现 Cloneable,适合引用层级深的场景:
import java.io.*;
public class User implements Serializable { // 实现Serializable接口
private String name;
private int age;
private Address address; // Address也需实现Serializable
// 深拷贝方法(序列化+反序列化)
public User deepClone() throws IOException, ClassNotFoundException {
// 1. 序列化:将对象写入字节流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 2. 反序列化:从字节流重建对象(全新的引用)
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (User) ois.readObject();
}
// 构造器、getter/setter 省略
}
// Address 需实现 Serializable
class Address implements Serializable {
private String city;
public Address(String city) { this.city = city; }
// getter/setter 省略
}
// 测试深拷贝
public class DeepCloneTest {
public static void main(String[] args) {
try {
Address addr = new Address("北京");
User user1 = new User("张三", 20, addr);
User user2 = user1.deepClone();
user2.getAddress().setCity("上海");
// 原对象地址不变(深拷贝成功)
System.out.println(user1.getAddress().getCity()); // 输出:北京
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. clone() 的优缺点
优点
- 底层是 native 方法,拷贝效率高(浅拷贝场景);
- 无需手动写大量赋值代码,简化对象拷贝逻辑。
缺点
- 默认是浅拷贝,深拷贝需手动处理,层级深时繁琐;
- 必须实现
Cloneable接口,否则抛异常(接口无方法,设计不优雅); clone()返回值是Object,需强制类型转换,易出错;- 无法拷贝
final引用字段(final 字段指向的对象无法被重新赋值)。
5. 替代方案:避免使用 clone()
在实际开发中,更多人选择以下更优雅的方式替代 clone():
- 手动编写拷贝方法(拷贝构造器):
public class User {
private String name;
private int age;
private Address address;
// 拷贝构造器
public User(User original) {
this.name = original.name;
this.age = original.age;
// 深拷贝:新建Address对象
this.address = new Address(original.address.getCity());
}
}
- 使用第三方库(如 Apache Commons Lang 的
SerializationUtils、Gson/Jackson 序列化):
// Apache Commons Lang 实现深拷贝
User user2 = (User) SerializationUtils.clone(user1);
总结
Object.clone()是 native 方法,需实现Cloneable接口才能调用,默认是浅拷贝;- 浅拷贝仅拷贝基础类型值和引用地址,修改引用字段会影响原对象,深拷贝需手动处理引用字段;
- 实际开发中,推荐用拷贝构造器或序列化实现深拷贝,替代原生
clone()(更易维护)。
简单记:clone() 核心是“内存级拷贝”,浅拷贝够用就用,需要彻底拷贝就做深拷贝,或直接用更优雅的替代方案。
