在Java的Object类中,有一个方法名为clone(),直译过来就是克隆,核心概念就是复制对象并返回一个新的对象。
protected native Object clone() throws CloneNotSupportedException;
二、如何进行对象克隆?
(1)在要实现克隆的对象类中实现Cloneable接口。
为啥?Cloneable接口为标记接口(标记接口为用户标记实现该接口的类具有该接口标记的功能,常见的标记接口有Serializable、Cloneable、RandomAccess),如果没有实现该接口,在调用clone方法时就会抛出CloneNotSupportException异常。
(2)在类中重写Object的clone方法。
为啥?重写是为了扩大访问权限,如果不重写,因Object的clone方法的修饰符是protected,除了与Object同包(java.lang)和直接子类能访问,其他类无权访问。并且默认Object的clone表现出来的是浅拷贝,如果要实现深拷贝,也是需要重写该方法的。
三、测试(浅克隆)
按照前面官方文档提到的,clone通常是一个浅拷贝,如果要做到深拷贝,需要对复制对象中的对象引用进行修改,换句话说就是浅拷贝的效果就是引用例行的属性无法完全复制,被克隆对象和克隆对象中的该引用类型的属性指向同一个引用,并不是完全独立无关的。
举例:
创建一个User类,其中包含一个引用类型的属性cp:
public class User implements Cloneable{
private String name;
private double height;
private double length;
private int gender;
private Couple cp;
// 省略构造方法,getter,setter和toString()
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
创建Couple类
public class Couple {
private String name;
private int gender;
// 省略构造方法和getter,setter和toString()
}
编写测试:
public static void main(String[] args) throws CloneNotSupportedException {
Couple cp = new Couple("张小小",0);
User user = new User("老王",180.0,18.0, 1, cp);
System.out.println("克隆之前的老王===================");
System.out.println(user);
// 克隆老王
User newUser = user.clone();
System.out.println("克隆出来的新老王=================");
System.out.println(newUser);
// 改变老王cp信息的值及个人信息
user.setName("老王爱花姑娘");
user.setGender(0);
cp.setGender(1);
System.out.println("发生变化之后的旧老王================");
System.out.println(user);
System.out.println("发生变化之后的新老王================");
System.out.println(newUser);
}
测试结果:
浅拷贝的情况下,原被克隆对象发生变化后,克隆对象的基本数据类型和不可变引用数据类型(String)的数据未发生影响,而cp字段为可变的应用类型,可以观察到克隆对象的内容随着被克隆对象的变化发生了同样的变化,说明两个对象的cp属性字段可能指向同一个引用,才会造成这样的结局。
四、深拷贝(深克隆)
以上章节中的浅拷贝的效果往往达不到我们的要求,因为在实际使用时,我们肯定是希望新拷贝出来的对象不受原对象的影响,否则咱们做出拷贝的意义何在?(我就碰到过因为对象被同事插进来的代码导致对象发生了变更,代码出现BUG的问题,后面是使用的深拷贝才消除同事的代码对该对象的影响)那么如何实现对象的深拷贝呢?列出以下几种常见的方式:
(1)clone函数的嵌套调用
既然引用类型无法被完全克隆,那么我们可以考虑在引用类型所在的类也实现Cloneable接口,在外层User类的clone方法调用属性的克隆方法。
Couple类实现Cloneable接口,重写Object的clone方法。
public class Couple implements Cloneable{
private String name;
private int gender;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
User类修改clone方法,在clone方法中调用Couple类的clone方法。
@Override
protected User clone() throws CloneNotSupportedException {
User user = (User) super.clone();
user.cp = (Couple) this.cp.clone();
return user;
}
同样的测试,查看测试结果:
以上我们看到已经达到深度拷贝的效果了,但是这种嵌套调用clone()方法存在问题:
如果有属性是数组类型呢?
官方文档明确说明虽然针对所有数组类型都认为是已经实现了Cloneable接口,但是实际克隆的时候可能仍然表现出浅拷贝。如果这一点不注意,在重写clone方法嵌套调用时未能正确调用clone,依然会出现浅拷贝的问题。
本文分享自 MoziInnovations 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!