学习目标:
1、了解对象克隆的概念
2、掌握重写clone实现对象的克隆
3、掌握深度克隆和浅克隆的概念
学习过程:
一、对象克隆简介
1、什么是对象的克隆
在java面向对象的编程当中,要复制引用类型的对象,就必须克隆这些对象。克隆对象,就是为新的对象分配空间,并进行对象的复制,并将原始对象的内容一一复制到新的对象空间去。
我们在编码过程经常会碰到将一个对象传递给另一个对象,java中对于基本型变量采用的是值传递,而对于对象比如JavaBean传递时采用的是引用传递,而很多时候对于对象传递我们也希望能够象值传递一样,使得传递之前和之后有不同的内存空间,这就是对象的克隆:一个新的对象和一个新的对象数据空间。
2、赋值不能实现克隆。
实现克隆需要通过调用可用的clone方法。如果是值类型的实例,那么“=”赋值运算符就可以将源对象的状态逐字节地复制到目标对象中。但是对象是对象空间的引用,所以赋值“=”方式只能是复制对象引用,是不会复制对象空间的。我们看看下面的代码:
(1)先构造一个Father类,实现代码如下;
1 2 3 4 5 6 7 8 9 10 11 12 | public class Father { private String name; public String getName() { return name; } public void setName(String name) { this .name = name; } } |
(2)新建一个Run类,构造一个father1对象,并为其属性name赋值“刘邦”,然后把father1对象赋值给另外一个对象father2,打印father2的name属性,和father1一样,但是如果我们修改father2对象的name属性,这时候father1对象的name属性也会发生改变,也就是father1对象和father2对象指向的对象空间是同一个,并没有克隆。实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Run { public static void main(String[] args) { Father father1 = new Father(); father1.setName( "刘邦" ); Father father2 = father1; System.out.println( "father2的name=" +father2.getName()); //修改father2的name属性 father2.setName( "李世民" ); System.out.println( "====修改后====" ); System.out.println( "father2的name=" +father2.getName()); //father1的name属性 也改变了。 System.out.println( "father1的name=" +father1.getName()); } } |
运行结果如下:
![attcontent/f4172a5f-db53-4d4a-b162-274c83aa1afa.png](http://112.74.183.95//images/attcontent/f4172a5f-db53-4d4a-b162-274c83aa1afa.png)
也就是说赋值仅仅只是对象引用的复制,不是空间的复制,图示如下:
![attcontent/eff9fb18-4bf8-413b-9e62-8a8c83f46880.png](http://112.74.183.95//images/attcontent/eff9fb18-4bf8-413b-9e62-8a8c83f46880.png)
也就是说对象克隆失败了。那如何才能真正做到对象的克隆呢?
二、对象的克隆的基本步骤
要实现对象的克隆我们需要完成按照以下几个步骤,然后调用clone()方法实现克隆。
1、可以利用Object类的clone()方法。实现Cloneable接口。
2、在派生类中覆盖基类的clone(),最好修改访问修饰符为public。
2、在派生类的clone()方法中,调用super.clone()。
示例代码如下:
1、让Father类实现Cloneable接口,并重写clone()方法。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //1、实现Cloneable 接口 public class Father implements Cloneable { private String name; public String getName() { return name; } public void setName(String name) { this .name = name; } @Override public Father clone() { try { Object object = super .clone(); Father father = (Father) object; return father; } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null ; } } |
2、修改原来的Run类的main方法,通过调用clone()方法完成克隆。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Run { public static void main(String[] args) { Father father1 = new Father(); father1.setName( "刘邦" ); Father father2 = father1.clone(); //通过调用clone()方法 System.out.println( "father2的name=" +father2.getName()); //修改father2的name属性 father2.setName( "李世民" ); System.out.println( "====修改后====" ); System.out.println( "father2的name=" +father2.getName()); //father1的name属性 也改变了。 System.out.println( "father1的name=" +father1.getName()); } } |
唯一修改的代码就是把赋值改成调用clone()方法,这时候再次运行就会修改了father2对象的name属性,不会对father1的name属性有任何影响,因为现在有两个对象引用,指向两个不同的对象空间。运行结果如下:
![attcontent/69604af7-c7fe-483d-ab6a-cbb68f1eaeb9.png](http://112.74.183.95//images/attcontent/69604af7-c7fe-483d-ab6a-cbb68f1eaeb9.png)
三、深度克隆
如果Father类的属性是一个对象,那么该对象并不会克隆的,如果你想要也克隆一份,那么该对象属性的实现类也需要实现克隆,我们称为深度克隆。
1、测试Father的属性是对象能否克隆成功。
比如我们添加一个Child类。实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Child { private String name; public String getName() { return name; } public void setName(String name) { this .name = name; } } |
把这个Child类作为Father类的一个属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | //1、实现Cloneable 接口 public class Father implements Cloneable { private String name; private Child child; public Child getChild() { return child; } public void setChild(Child child) { this .child = child; } public String getName() { return name; } public void setName(String name) { this .name = name; } @Override public Father clone() { try { Object object = super .clone(); Father father = (Father) object; return father; } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null ; } } |
修改Run方法,测试Child有没有克隆。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class Run { public static void main(String[] args) { Father father1 = new Father(); father1.setName( "刘邦" ); //设置Child属性。 Child child= new Child(); child.setName( "刘三儿" ); father1.setChild(child); Father father2 = father1.clone(); //通过调用clone()方法 System.out.println( "father2的name=" +father2.getName()); System.out.println( "father2的child的name=" +father2.getChild().getName()); //修改father2的name属性 father2.setName( "李世民" ); System.out.println( "====修改后====" ); System.out.println( "father2的name=" +father2.getName()); System.out.println( "father1的name=" +father1.getName()); System.out.println( "=====Child======" ); father2.getChild().setName( "李四" ); System.out.println( "father2的child的name=" +father2.getChild().getName()); System.out.println( "father1的child的name=" +father1.getChild().getName()); } } |
运行结果如下:
![attcontent/22c01bf8-0215-4eeb-8337-6a6d40e94a12.png](http://112.74.183.95//images/attcontent/22c01bf8-0215-4eeb-8337-6a6d40e94a12.png)
也就是说Child并没有克隆。
2、实现深度克隆。
让Child也可克隆:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class Child implements Cloneable { private String name; public String getName() { return name; } public void setName(String name) { this .name = name; } @Override public Child clone() { try { Object object = super .clone(); Child child = (Child) object; return child; } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null ; } } |
修改Father类的clone()方法,在克隆Father的时候同时把Child也克隆一份:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Override public Father clone() { try { Object object = super .clone(); Father father = (Father) object; //同时克隆Child对象 Child child=father.getChild().clone(); father.setChild(child); return father; } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null ; } |
再次运行Run类,结果如下:
![attcontent/c168ff90-6755-4f70-92a7-123e0530ca27.png](http://112.74.183.95//images/attcontent/c168ff90-6755-4f70-92a7-123e0530ca27.png)