java克隆(clone)的两种实现⽅法
1概念
由java API⽂档可知:
clone 属于 java.long.Object的⼀个⽅法
protected Object clone() throws CloneNotSupportedException
创建并返回此对象的⼀个副本。“副本”的准确含义可能依赖于对象的类。这样做的⽬的是,对于任何对象 x,表达式:x.clone() != x为true,表达式:x.clone().getClass() == x.getClass()
也为 true,但这些并⾮必须要满⾜的要求。⼀般情况下:x.clone().equals(x)为 true,但这并⾮必须要满⾜的要求。按照惯例,返回的对象应该通过调⽤ super.clone 获得。如果⼀个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。
按照惯例,此⽅法返回的对象应该独⽴于该对象(正被复制的对象)。要获得此独⽴性,在 super.clone 返回对象之前,有必要对该对象的⼀个或多个字段进⾏修改。这通常意味着要复制包含正在被复制对象的内部“深层结构”的所有可变对象,并使⽤对副本的引⽤替换对这些对象的引⽤。如果⼀个类只包含基
本字段或对不变对象的引⽤,那么通常不需要修改 super.clone 返回的对象中的字段。Object 类的 clone ⽅法执⾏特定的复制操作。⾸先,如果此对象的类不能实现接⼝ Cloneable,则会抛出 CloneNotSupportedException。注意,所有的数组都被视为实现接⼝ Cloneable。否则,此⽅法会创建此对象的类的⼀个新实例,并像通过分配那样,严格使⽤此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被⾃我复制。所以,此⽅法执⾏的是该对象的“浅表复制”,⽽不“深层复制”操作。
Object 类本⾝不实现接⼝ Cloneable,所以在类为 Object 的对象上调⽤ clone ⽅法将会导致在运⾏时抛出异常。
2 浅克隆和深克隆
2.1 概念
如上所述,java在克隆的时候会复制并返回对象的⼀个副本(其中包含所有字段和基础数据类型字段的数值),如果对象中存在复合数据类型(⽐如数组变量、其他对象等),则只复制复合数据类型的引⽤地址,不会复制复合数据中的具体值,⽽复制对象中的基础数据类型(⽐如int,long,float,String等)则会把数值也会复制过去。因此这种克隆⽅式称为“浅克隆”。同理可得,“深克隆”就是将基础数据类型和所有复合数据类型都⼀并复制拷贝⼀份,相互之间互不影响。
clone
浅克隆是存在弊端的,假如克隆的两个对象其中⼀个更改了⾃⼰对象中的复合数据中的数据,则另⼀个对象中的数据也会同步被修改,因为他们引⽤的是同⼀个复合数据对象。
2.2 实现⽅式
实现克隆的⽅式主要有两种:
(1)、对象实现Cloneable接⼝并重写Object类中的clone()⽅法(浅克隆⽅式);
代码如下:
1(a)⽤户信息
2public class Person implements Cloneable{
3 private String name;
4 private String sex;
5 private Phone phone;
6 /**
7  * 实现Cloneable接⼝,并重写clone⽅法
8  */
9 @Override
10 protected Object clone(){
11  Person p=null;
12  try {
13/**
14    * 若要实现深克隆,此处就必须将对象中所有的复合数据类型统统单独复制拷贝⼀份,
14    * 若要实现深克隆,此处就必须将对象中所有的复合数据类型统统单独复制拷贝⼀份,
15    * 但是实际开发中,⽆法确定对象中复合数据的种类和个数,
16    * 因此⼀般不采⽤此种⽅式实现深克隆
17    */
18  p = (Person) super.clone();
19  } catch (CloneNotSupportedException e) {
20      e.printStackTrace();
21  }
22  return p;
23 }
24
25 public Person(String name, String sex, Phone phone) {
26  super();
27  this.name = name;
28  this.sex = sex;
29  this.phone = phone;
30 }
31
32 public String getName() {
33  return name;
34 }
35 public void setName(String name) {
36  this.name = name;
37 }
38 public String getSex() {
39  return sex;
40 }
41 public void setSex(String sex) {
42  this.sex = sex;
43 }
44 public Phone getPhone() {
45  return phone;
46 }
47 public void setPhone(Phone phone) {
48  this.phone = phone;
49 }
50 @Override
51 public String toString() {
52  return "[name=" + name + ", sex=" + sex + ", phone=" + phone
53    + "]";
54 }
55}
56(b)⼿机信息
57public class Phone{
58
59 private String number;
60 private String area;
61
62 public Phone(String number, String area) {
63  super();
64  this.number = number;
65  this.area = area;
66 }
67
68 public String getNumber() {
69  return number;
70 }
71 public void setNumber(String number) {
72  this.number = number;
73 }
74 public String getArea() {
75  return area;
76 }
77 public void setArea(String area) {
78  this.area = area;
79 }
79 }
80 @Override
81 public String toString() {
82  return "[number=" + number + ", area=" + area + "]";
83 }
84}
85(c)测试代码
86public static void main(String[] args) {
87  Person p=new Person("雷⼩涛", "man", new Phone("123456", "四川成都"));
88  Person p2=(Person) p.clone();
89  System.out.println("p:"+p.toString());
90  System.out.println("p2:"+p2.toString());
91  p2.setName("leixiaotao");
92  p2.getPhone().setArea("四川乐⼭");
93  System.out.println("-------其中⼀个对象修改值过后-------");
94  System.out.println("p:"+p.toString());
95  System.out.println("p2:"+p2.toString());
96 }
(d)结果:修改基础数据结构数值不会影响其他对象,修改复合数据数值,全部都会受影响
(2)、对象实现Serializable接⼝,通过对象的序列化和反序列化实现克隆(此⽅法可以实现真正的深度克隆)。代码如下:
1(a)⽤户信息类
2public class Person implements Serializable{
3
4 private static final long serialVersionUID = -3936148364278781089L;
5
6 private String name;
7 private String sex;
8 private Phone phone;
9
10 public Person(String name, String sex, Phone phone) {
11  super();
12  this.name = name;
13  this.sex = sex;
14  this.phone = phone;
15 }
16
17 public String getName() {
18  return name;
19 }
20 public void setName(String name) {
21  this.name = name;
22 }
23 public String getSex() {
24  return sex;
25 }
26 public void setSex(String sex) {
27  this.sex = sex;
28 }
29 public Phone getPhone() {
30  return phone;
31 }
32 public void setPhone(Phone phone) {
33  this.phone = phone;
34 }
34 }
35 @Override
36 public String toString() {
37  return "[name=" + name + ", sex=" + sex + ", phone=" + phone
38    + "]";
39 }
40}
41(b)⼿机信息类
42public class Phone implements Serializable{
43
44 private static final long serialVersionUID = -4482468804013490322L;
45
46 private String number;
47 private String area;
48
49 public Phone(String number, String area) {
50  super();
51  this.number = number;
52  this.area = area;
53 }
54
55 public String getNumber() {
56  return number;
57 }
58 public void setNumber(String number) {
59  this.number = number;
60 }
61 public String getArea() {
62  return area;
63 }
64 public void setArea(String area) {
65  this.area = area;
66 }
67 @Override
68 public String toString() {
69  return "[number=" + number + ", area=" + area + "]";
70 }
71}
72(c)CloneUtil⼯具类
73public class CloneUtil {
74
75 @SuppressWarnings("unchecked")
76 public static <T extends Serializable> T clone(T object) throws Exception{
77  ByteArrayOutputStream bout = new ByteArrayOutputStream();
78        ObjectOutputStream oos = new ObjectOutputStream(bout);
79        oos.writeObject(object);
80
81        ByteArrayInputStream bin = new ByteArray());
82        ObjectInputStream ois = new ObjectInputStream(bin);
83        // 此处不需要释放资源,说明:调⽤ByteArrayInputStream或ByteArrayOutputStream对象的close⽅法没有任何意义
84        // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这⼀点不同于对外部资源(如⽂件流)的释放
85        return (T) adObject();
86 }
87}
88(d)测试代码
89public static void main(String[] args) throws Exception {
90  Person p=new Person("雷⼩涛", "man", new Phone("123456", "四川成都"));
91//  Person p2=(Person) p.clone();
92  Person p2=CloneUtil.clone(p);
93  System.out.println("p:"+p.toString());
94  System.out.println("p2:"+p2.toString());
95  p2.setName("leixiaotao");
96  p2.getPhone().setArea("四川乐⼭");
97  System.out.println("-------其中⼀个对象修改值过后-------");
98  System.out.println("p:"+p.toString());
99  System.out.println("p2:"+p2.toString());
99  System.out.println("p2:"+p2.toString());
100 }
(e)结果
【四川乐⼭程序员联盟,欢迎⼤家加相互交流学习5 7 1 8 1 4 7 4 3】

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。