java克隆对象并修改属性需先获得独立副本:浅克隆用clone()(需实现cloneable),但引用类型仍共享;深克隆需手动复制引用字段或用序列化;现代推荐用record+with式构造或builder模式。

Java 中克隆对象并修改其属性,核心在于先获得原对象的独立副本(避免引用共享),再对副本进行修改。直接赋值(=)只是复制引用,改的是同一个对象;必须通过真正克隆才能实现“复制后独立修改”。
用 clone() 方法(浅克隆)
这是最常用的方式,但要求类实现 Cloneable 接口,并重写 clone() 方法:
- 在类中声明
implements Cloneable - 重写
public Object clone(),调用super.clone() - 注意:默认是浅克隆 —— 基本类型和字符串会被复制,但引用类型仍指向原对象中的同一实例
示例:
public class Person implements Cloneable {
private String name;
private int age;
private Address address; // 引用类型
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅克隆
}
}
// 使用
Person p1 = new Person("张三", 25, new Address("北京"));
Person p2 = (Person) p1.clone();
p2.setName("李四"); // ✅ 独立修改 name
p2.getAddress().setCity("上海"); // ❌ p1 的 address 也被改了(浅克隆问题)
手动深克隆(推荐用于含引用字段的场景)
当对象包含可变引用字段(如 List、Map、自定义对象等)时,需手动实现深克隆,确保所有层级都新建实例:
- 在
clone()方法中,对每个引用字段调用其自身的clone()或新建对象并复制内容 - 若引用类型未实现
Cloneable,可用构造器或静态工厂方法复制
改进上面的 Person.clone():
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
@Override
public Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
if (this.address != null) {
cloned.address = new Address(this.address.getCity()); // 深拷贝 address
}
return cloned;
}
用序列化方式实现通用深克隆
适用于不便于修改源码、或嵌套结构复杂的情况。原理是将对象序列化为字节流再反序列化,天然实现深克隆:
- 要求所有字段所属类都实现
Serializable - 简单可靠,但有性能开销,且不能序列化 transient 字段或不可序列化资源(如 Socket、Thread)
工具方法示例:
public static <t extends serializable> T deepClone(T obj) {
try (ByteArrayInputStream bis = new ByteArrayInputStream(
new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(obj);
// 实际需完整写出序列化/反序列化逻辑
);
ObjectInputStream ois = new ObjectInputStream(bis)) {
return (T) ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
</t>
更稳妥的做法是使用 Apache Commons Lang 的 SerializationUtils.clone()。
现代替代方案:用记录类(Record)+ 构造新实例
Java 14+ 引入 record,天然不可变。要“克隆并修改”,直接用 with-style 构造(需配合 Lombok 或手动写 builder):
- Lombok 的
@With可为字段生成withXxx()方法,返回新实例 - 或自己写 builder,调用
new Person.Builder().copyFrom(p1).name("李四").build()
这种方式语义清晰、线程安全、无克隆陷阱,越来越成为首选。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










