java中,clone方法用于复制对象,是一种创建对象的方式。另一种创建对象的方法就是使用new操作符。
new操作符的大致流程是先根据new后的类型确定需要分配多大的内存空间,然后调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象就创建完毕了,然后返回它的地址。
【java|java中的 clone方法】clone方法的大致流程与new操作符类似,第一步是分配内存,大小与调用clone方法对象的内存相同,然后将使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建。
Person p1 = new Person(1,"p1");
Person p2 = (Person) p.clone();
p1和p2是两个不同的对象。但Person中有两个成员变量,age和name。age是int类型,name是String类型。实现如下:
public class Person implements Cloneable{private int age ;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}public Person() {}public int getAge() {
return age;
}public String getName() {
return name;
}@Override
protected Object clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
age是基本数据类型, 那么对它的拷贝就是直接将一个4字节的整数值拷贝。
name是String类型的, 它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式:
1. 直接将源对象中的name的引用值拷贝给新对象的name字段;
2. 根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。
这两种拷贝方式分别叫做浅拷贝和深拷贝。
Object中默认的clone方法,是浅拷贝的。如果要在clone对象的时候进行深拷贝,就要实现Clonable接口。覆盖clone方法中,除了调用父类中的clone方法得到一个新的对象,还要将该类中的引用变量也clone出来。
以代码为例:
class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;
}@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}}class Head {
public Face face;
public Head() {}
public Head(Face face){this.face = face;
}}public static void main(String[] args) throws CloneNotSupportedException {Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " +(body.head == body1.head));
}
以上代码中, 有两个主要的类, 分别为Body和Face, 在Body类中, 组合了一个Face对象。当对Body对象进行clone时, 它组合的Face对象只进行浅拷贝。
如果要使Body对象在clone时进行深拷贝, 那么就要在Body的clone方法中,将源对象引用的Head对象也clone一份。
class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;
}@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody =(Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}}class Head implements Cloneable{
public Face face;
public Head() {}
public Head(Face face){this.face = face;
}@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
} public static void main(String[] args) throws CloneNotSupportedException {Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " +(body.head == body1.head));
}
body和body1内的head引用指向了不同的Head对象, 也就是说在clone Body对象的同时, 也拷贝了它所引用的Head对象, 进行了深拷贝。
因此,如果在拷贝一个对象时,要想让这个拷贝的对象和源对象完全彼此独立,那么在引用链上的每一级对象都要被显式的拷贝。所以创建彻底的深拷贝是非常麻烦的,尤其是在引用关系非常复杂的情况下, 或者在引用链的某一级上引用了一个第三方的对象, 而这个对象没有实现clone方法, 那么在它之后的所有引用的对象都是被共享的。
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)