1.首先,你要知道怎么实现克隆:实现cloneable接口,在bean里面重写clone()方法,权限为public。
2.其次,你要大概知道什么是地址传递,什么是值传递。
3.最后,你要知道你为什么使用这个clone方法。
先看第一条,简单的克隆代码的实现。这个也就是我们在没了解清楚这个java的clone的时候,会出现的问题。
看完代码,我再说明这个时候的问题。
先看我要克隆的学生bean的代码:
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
38
39
|
package com.lxk.model; /** * 学生类:有2个属性:1,基本属性-string-name;2,引用类型-car-car。 * <p> * created by lxk on 2017/3/23 */ public class student implements cloneable { private string name; private car car; public string getname() { return name; } public void setname(string name) { this .name = name; } public car getcar() { return car; } public void setcar(car car) { this .car = car; } @override public string tostring() { return "student{" + "name='" + name + '\ '' + ", car=" + car + '}' ; } @override public student clone() { student student = null ; try { student = (student) super .clone(); } catch (clonenotsupportedexception ignored) { system.out.println(ignored.getmessage()); } return student; } } |
学生内部引用了car这个bean
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
package com.lxk.model; import java.util.list; public class car implements comparable<car> { private string sign; private int price; private list<dog> mydog; private list<string> boys; public car() { } public car(string sign, int price) { this .sign = sign; this .price = price; } public car(string sign, int price, list<dog> mydog) { this .sign = sign; this .price = price; this .mydog = mydog; } public car(string sign, int price, list<dog> mydog, list<string> boys) { this .sign = sign; this .price = price; this .mydog = mydog; this .boys = boys; } public string getsign() { return sign; } public void setsign(string sign) { this .sign = sign; } public int getprice() { return price; } public void setprice( int price) { this .price = price; } public list<dog> getmydog() { return mydog; } public void setmydog(list<dog> mydog) { this .mydog = mydog; } public list<string> getboys() { return boys; } public void setboys(list<string> boys) { this .boys = boys; } @override public int compareto(car o) { //同理也可以根据sign属性排序,就不举例啦。 return this .getprice() - o.getprice(); } @override public string tostring() { return "car{" + "sign='" + sign + '\ '' + ", price=" + price + ", mydog=" + mydog + ", boys=" + boys + '}' ; } } |
最后就是main测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.lxk.findbugs; import com.lxk.model.car; import com.lxk.model.student; /** * 引用传递也就是地址传递需要注意的地方,引起的bug * <p> * created by lxk on 2017/3/23 */ public class bug2 { public static void main(string[] args) { student student1 = new student(); car car = new car( "oooo" , 100 ); student1.setcar(car); student1.setname( "lxk" ); //克隆完之后,student1和student2应该没关系的,修改student1不影响student2的值,但是完之后发现,你修改car的值,student2也受影响啦。 student student2 = student1.clone(); system.out.println( "学生2:" + student2); //先输出student2刚刚克隆完之后的值,然后在修改student1的相关引用类型的属性值(car)和基本属性值(name) car.setsign( "x5" ); student1.setname( "xxx" ); system.out.println( "学生2:" + student2); //再次输出看修改的结果 } } |
之后就该是执行的结果图了:
对上面执行结果的疑惑,以及解释说明:
我们可能觉得自己在bean里面实现clone接口,重写了这个clone方法,那么学生2是经由学生1clone,复制出来的,
那么学生1和学生2,应该是毫不相干的,各自是各自,然后,在修改学生1的时候,学生2是不会受影响的。
但是结果,不尽人意。从上图执行结果可以看出来,除了名字,这个属性是没有被学生1影响,关于car的sign属性已经因为学生1的变化而变化,这不是我希望的结果。
可见,这个简单的克隆实现也仅仅是个“浅克隆”,也就是基本类型数据,他是会给你重新复制一份新的,但是引用类型的,他就不会重新复制份新的。引用类型包括,上面的其他bean的引用,list集合,等一些引用类型。
那么怎么实现深克隆呢?
对上述代码稍作修改,如下:
学生bean的clone重写方法如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@override public student clone() { student student = null ; try { student = (student) super .clone(); if (car != null ) { student.setcar(car.clone()); } } catch (clonenotsupportedexception ignored) { system.out.println(ignored.getmessage()); } return student; } |
然后还要car类实现cloneable接口,复写clone方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@override public car clone() { car car = null ; try { car = (car) super .clone(); if (mydog != null ) { car.setmydog(lists.newarraylist(mydog)); } if (boys != null ) { car.setboys(lists.newarraylist(boys)); } } catch (clonenotsupportedexception ignored) { system.out.println(ignored.getmessage()); } return car; } |
主测试代码不动,这个时候的执行结果如下:
可以看到,这个时候,你再修改学生1的值,就不会影响到学生2的值,这才是真正的克隆,也就是所谓的深克隆。
怎么举一反三?
可以看到,这个例子里面的引用类型就一个car类型的属性,但是实际开发中,除了这个引用其他bean类型的属性外,可能还要list类型的属性值用的最多。
那么要怎么深克隆呢,就像我在car bean类里面做的那样,把所有的引用类型的属性,都在clone一遍。那么你在最上层调用这个clone方法的时候,他就是真的深克隆啦。
我代码里面那么判断是为了避免空指针异常。当然,这个你也得注意咯。
注意 重写clone方法的时候,里面各个属性的null的判断哦。
上面的是override clone()
方法来实现深克隆的。如果你这个要克隆的对象很复杂的话,你就不得不去每个引用到的对象去复写这个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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
/** * 对象的深度克隆,此处的对象涉及collection接口和map接口下对象的深度克隆 * 利用序列化和反序列化的方式进行深度克隆对象 * * @param object 待克隆的对象 * @param <t> 待克隆对象的数据类型 * @return 已经深度克隆过的对象 */ public static <t extends serializable> t deepcloneobject(t object) { t deepclone = null ; bytearrayoutputstream baos = null ; objectoutputstream oos = null ; bytearrayinputstream bais = null ; objectinputstream ois = null ; try { baos = new bytearrayoutputstream(); oos = new objectoutputstream(baos); oos.writeobject(object); bais = new bytearrayinputstream(baos .tobytearray()); ois = new objectinputstream(bais); deepclone = (t)ois.readobject(); } catch (ioexception | classnotfoundexception e) { e.printstacktrace(); } finally { try { if (baos != null ) { baos.close(); } } catch (ioexception e) { e.printstacktrace(); } try { if (oos != null ) { oos.close(); } } catch (ioexception e) { e.printstacktrace(); } try { if (bais != null ) { bais.close(); } } catch (ioexception e) { e.printstacktrace(); } try { if (ois != null ) { ois.close(); } } catch (ioexception e) { e.printstacktrace(); } } return deepclone; } |
具体的使用如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/** * 使用序列化来实现深拷贝简单。但是,所涉及到的所有对象都的实现序列化接口。 */ private static void clonebyserializable() { student student1 = new student(); car car = new car( "oooo" , 100 , lists.newarraylist( new dog( "aaa" , true , true ))); student1.setcar(car); student1.setname( "lxk" ); student student2 = deepcloneobject(student1); system.out.println( "学生2:" + student2); car.setsign( "x5" ); car.setmydog( null ); student1.setname( "xxx" ); system.out.println( "学生2:" + student2); } |
实现的效果,还是和上面的一样的,但是这个就简单多来,只需要给涉及到的每个引用类型,都去实现序列化接口就好啦。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对服务器之家的支持。如果你想了解更多相关内容请查看下面相关链接
原文链接:https://blog.csdn.net/qq_27093465/article/details/65443355