java数组深拷贝和浅拷贝_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

39package com.lxk.model;

/**

* 学生类:有2个属性:1,基本属性-String-name;2,引用类型-Car-car。

*

* Created by lxk on 2017/3/23

*/

public class Studentimplements 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

63package com.lxk.model;

import java.util.List;

public class Carimplements Comparable {

private String sign;

private int price;

private List myDog;

private List boys;

public Car() {

}

public Car(String sign,int price) {

this.sign = sign;

this.price = price;

}

public Car(String sign,int price, List myDog) {

this.sign = sign;

this.price = price;

this.myDog = myDog;

}

public Car(String sign,int price, List myDog, List 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 getMyDog() {

return myDog;

}

public void setMyDog(List myDog) {

this.myDog = myDog;

}

public List getBoys() {

return boys;

}

public void setBoys(List 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

22package com.lxk.findBugs;

import com.lxk.model.Car;

import com.lxk.model.Student;

/**

* 引用传递也就是地址传递需要注意的地方,引起的bug

*

* 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);//再次输出看修改的结果

}

}

之后就该是执行的结果图了:

155770366_1_20190306075057192

对上面执行结果的疑惑,以及解释说明:

我们可能觉得自己在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;

}

主测试代码不动,这个时候的执行结果如下:

155770366_2_20190306075057301

可以看到,这个时候,你再修改学生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 待克隆对象的数据类型

* @return 已经深度克隆过的对象

*/

public static 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);

}

实现的效果,还是和上面的一样的,但是这个就简单多来,只需要给涉及到的每个引用类型,都去实现序列化接口就好啦。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。如果你想了解更多相关内容请查看下面相关链接

155770366_3_20190306075057442

原文链接:https://blog.csdn.net/qq_27093465/article/details/65443355

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值