轻松搞定“深浅克隆”这件事
对象的复制
当需要2个对象时,可以使用 new 出2个对象,或者通过赋值的方式,复制出另一个对象。
User user1 = new User("Tom");
User user2 = new User("Jerry");
// 或者使用复制的方式
User user3 = user1;
user1.name = "Johne"
System.out.println(zoo1);
System.out.println(zoo2);
System.out.println(zoo1);
new 两个对象方式的过于麻烦,如果使用复制的方式,如果修改user1的值时你会发现user3 的值也被修改了。
深克隆和浅克隆
Java 中在 java.lang.Object 中提供了克隆的方法,可以很好的解决以上的问题。克隆划分两种:深克隆和浅克隆。他们都将把原对象的成员变量都复制给克隆对象。重要的是对象中值类型和引用类型克隆的区别
- 浅克隆:值类型复制给克隆对象,引用类型是将引用地址复制给克隆对象的。对象中的引用类型的成员还是指向同一个。
- 深克隆:无论值类型还是引用类型都复制给克隆对象,所以引用类型成员的克隆就是将引用对象地址以及地址所指向的值也都复制给克隆对象了。
Object clone() 方法
可以自己打开源码可以看下 clone() 的方法:
@IntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
源码中有 clone 方法的注释,说明了 clone 方法的三条约定:
- 所有对象 ,x.clone() != x 。因为 clone 的对象和原对象不是同一个
- 所有对象 , x.clone().getClass == x.getClass() 。因为它们类型是一样的
- 所有对象,x.clone().equal(x) 是 true。因为 x.clone() 是个 浅克隆,所以引用对象值还是相同的
Arrays.copyOf() 方法
数组是引用类型,它也提供了克隆 copyOf 方法,简单写个例子测试下,你就知道它是个浅克隆。
User[] o1 = {new User("Java")};
User[] o2 = Arrays.copyOf(o1, o1.length);
o1[0].setName("Golang");
System.out.println("o1:" + o1[0].getName());
System.out.println("o2:" + o2[0].getName());
// 输出结果
o1:Golang
o2:Golang
在 Java 语言中要实现克隆则需要实现 Cloneable 接口,并重写 Object 类中的 clone() 方法。
实现浅克隆
浅克隆的实现步骤:
- 对象类需要实现 Cloneable接口
- 覆盖Object类的clone()方法(覆盖clone()方法,访问修饰符设为public,默认是protected,但是如果所有类都在同一个包下protected是可以访问的);
- 在clone()方法中调用super.clone();
public class User implements Cloneable {
private Role role;
@Override
public User clone() throws CloneNotSupportedException {
return (Role)super.clone()
}
}
public class Role{
...
}
实现深克隆
深克隆的实现只需要重写 clone 方法,前提是所有涉及的对象都需要实现 Cloneable 的接口。然后再需要支持的深克隆的对象将引用类型的值也进行复制给克隆对象。
public class User implements Cloneable {
private Role role;
@Override
public User clone() throws CloneNotSupportedException {
User user = (User) super.clone();
user.setRole(this.User.role.clone())
return user;
}
}
public class Role implements Cloneable {
...
@Override
public Role clone() throws CloneNotSupportedException {
return (Role)super.clone()
}
}
其他方式实现深克隆
深克隆就是将所有对象成员也都复制给克隆对象。其成员对象是引用类型时只需重新定义一个新的应用类型对象。所以还有其他方式可以实现深克隆
构造方法实现克隆
使用构造方法带新的对象的方式实现一个深克隆
public class User {
private Role role;
public User(){}
public User(Role role){
}
}
public class TestExample{
public static void main(String[] args){
User user1= new User()
user1.role = new Role()
User user2 = new User(new Role()) // 通过构造方法实现深克隆
}
}
字节流实现深克隆
通过 JDK 自带的字节流实现深克隆的方式,实现步骤:
- 将原对象写入到内存的字节流,
- 再从字节流中读出刚刚存储的信息,来作为一个新的对象返回
public class User {
private Role role;
public User(){}
}
public class TestExample{
public static void main(String[] args){
User user1= new User()
user1.role = new Role()
// 通过字节流实现克隆
User p2 = (User) StreamClone.clone(user1);
}
}
其他第三方工具实现深克隆
- Apache Commons Lang 实现深克隆
- 通过 JSON 工具类
整个克隆的设计就是这么简单,实现克隆需要两个主要的步骤,一是 实现 Cloneable 空接口,二是重写 Object 的 clone() 方法再调用父类的克隆方法 (super.clone())。
参考资料:
转载自:https://juejin.cn/post/7137234370674294814