好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java Clone深拷贝与浅拷贝的两种实现方法

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

查看更多关于Java Clone深拷贝与浅拷贝的两种实现方法的详细内容...

  阅读:14次