简介
clone
在Java语言中,clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象和源对象相同。
需求
有一辆新车,在出厂时新车中已经包含了一些高级配置,此时,顾客感觉这车性价比比较高,想要买两辆相同配置的车,也就是说虽然说另一辆车跟这个车的配置相同,但是并不是同一辆车,(两辆车具有不同的轮胎,既然引出了深克隆与浅克隆的问题:浅克隆,虽然是两辆不同的车,但是却使用相同的轮胎,这是属于浅克隆,也就是不彻底的克隆,另外就是深克隆,完全变成两辆不同的车)要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。
比较Java中创建对象的两种方式
- 使用new关键字创建新对象
new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
- 使用clone方法来复制对象
而clone方法第一步就是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
总结:new关键字通过调用构造函数进行初始化,clone函数通过使用源对象中各个实例变量来对相应的实例变量进行实例化。
复制引用实例
这里我们使用Person类来作为需要进行复制引用的对象。
package dx.com.one.JavaExercixe;
/**
* Created by dx on 2017/3/17.
*/
class Person implements Cloneable {
String name;
int age;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
复制操作:
public class CloneTest {
public static void main(String[] args) throws Exception {
//复制引用
System.out.println("=====复制引用=======");
Person p1 = new Person("dx", 12);
System.out.println(p1.toString());
Person p2 = p1;
System.out.println(p2.toString());
p2.setName("wwj");
System.out.println("p2改变姓名");
System.out.println(p1.toString());
}
}
结果:
=======复制引用=============
Person{name='dx', age=12}
Person{name='dx', age=12}
p2改变姓名
Person{name='wwj', age=12}
分析:
当我们将P1的引用赋值给P2时,将P2的名字进行修改,发现P1的名字也发生了变化。(引用赋值)
克隆引用实例
public static void main(String[] args) throws Exception {
Person p1 = new Person("dx", 12);
System.out.println("=====================克隆引用============================");
Person p3 = (Person) p1.clone();
System.out.println(p3.toString());
p3.setName("wwj");
System.out.println(p3.toString());
System.out.println(p1.toString());
}
结果:
=====================克隆引用============================
Person{name='dx', age=12}
Person{name='wwj', age=12}
Person{name='dx', age=12}
分析:
当我们改变P3的name值时,P1的值并没有改变。(对象克隆)
深克隆、浅克隆
- 差别
当被克隆对象的成员变量类型是java的基本类型时(外加String类型),只要实现如上简单的clone(称影子clone)就可以。但是成员变量是数组或复杂类型时,就必须实现深度clone。
我们这里将利用Father与Son实现深拷贝与浅拷贝。
- Father
class Father implements Cloneable {
private String name;
private int age;
private Son son;
public Father(String name, int age, Son son) {
this.age = age;
this.name = name;
this.son = son;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Son getSon() {
return son;
}
public void setSon(Son son){
this.son = son;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- son
class Son implements Cloneable{
private String name;
private int age;
public Son(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 运行实例
public static void main(String[] args) {
Son son = new Son("dx", 12);
Father father = new Father("dxb", 12, son);
Father cloneFather = null;
try {
cloneFather = (Father)father.clone();
}catch (Exception exception)
{
exception.printStackTrace();
}
System.out.println(father.toString());
System.out.println(cloneFather.toString());
System.out.println(father.getSon().toString());
System.out.println(cloneFather.getSon().toString());
}
- 结果
dx.com.one.JavaExercixe.Father@677327b6
dx.com.one.JavaExercixe.Father@14ae5a5
dx.com.one.JavaExercixe.Son@7f31245a
dx.com.one.JavaExercixe.Son@7f31245a
- 分析
我们看到Father是不同的对象而Son是同一对象,并没有从根本上分成两个对象。称为浅克隆。
- 深克隆
对Father中进行改变:
protected Object clone() throws CloneNotSupportedException {
Father father = (Father)super.clone();
father.setSon((Son) father.getSon().clone());
return father;
}
- 运行实例
public static void main(String[] args) {
Son son = new Son("dx", 12);
Father father = new Father("dxb", 12, son);
Father cloneFather = null;
try {
cloneFather = (Father)father.clone();
}catch (Exception exception)
{
exception.printStackTrace();
}
System.out.println(father.toString());
System.out.println(cloneFather.toString());
System.out.println(father.getSon().toString());
System.out.println(cloneFather.getSon().toString());
}
- 结果
dx.com.one.JavaExercixe.Father@677327b6
dx.com.one.JavaExercixe.Father@14ae5a5
dx.com.one.JavaExercixe.Son@7f31245a
dx.com.one.JavaExercixe.Son@6d6f6e28
- 分析
可知对Father进行克隆时,对Son也进行了克隆,完全分为两个对象。(深克隆)