昆明java培训
达内昆明广州春城路

18487146383

热门课程

【昆明Java培训机构】避免对象的浅拷贝

  • 时间:2016-09-30
  • 发布:昆明Java培训
  • 来源:达内新闻

昆明Java培训机构的老师知道一个类实现了Cloneable接口就表示它具备了被拷贝的能力。如果在覆写clone()方法就会完全具备拷贝能力。拷贝是在内存中运行的,所以在性能方面比直接通过new生成的对象要快很多,特别是在大对象的生成上,这会使得性能的提升非常显著。但是对象拷贝也有一个比较容易忽略的问题:浅拷贝(Shadow Clone,也叫作影子拷贝)存在对象属性拷贝不彻底的问题。我们来看这样一段代码:

1 public class Person implements Cloneable {

2    public static void main(String[] args) {

3        //定义父亲

4        Person f = new Person("父亲");

5        //定义大儿子

6        Person s1 = new Person("大儿子", f);

7        //小儿子的信息时通过大儿子拷贝过来的

8        Person s2 = s1.clone();

9        s2.setName("小儿子");

10        System.out.println(s1.getName() + "的父亲是" + s1.getFather().getName());

11        System.out.println(s2.getName() + "的父亲是" + s2.getFather().getName());

12    }

13    //姓名

14    private String name;

15    //父亲

16    private Person father;

17

18    public Person(String _name) {

19        name = _name;

20    }

21

22    public Person(String _name, Person _parent) {

23        name = _name;

24        father = _parent;

25    }

26

27    @Override

28    public Person clone() {

29        Person p = null;

30        try {

31            p = (Person) super.clone();

32        } catch (CloneNotSupportedException e) {

33            e.printStackTrace();

34        }

35        return p;

36

37    }

38    

39  /*setter和getter方法略*/

40 }

程序中我们描述了这样一个场景:一个父亲,有两个儿子,大小儿子同根同种,所以小儿子的对象就通过拷贝大儿子的对象来生成,运行输出结果如下:

大儿子的父亲是父亲

小儿子的父亲是父亲

这很正确,没有问题。突然有一天,父亲心血来潮想让大儿子去认个干爹,也就是大儿子的父亲名称需要重新设置一下,代码如下:

public static void main(String[] args) {

//定义父亲

Person f = new Person("父亲");

//定义大儿子

Person s1 = new Person("大儿子", f);

//小儿子的信息时通过大儿子拷贝过来的

Person s2 = s1.clone();

s2.setName("小儿子");

//认干爹

s1.getFather().setName("干爹");

System.out.println(s1.getName() + "的父亲是" + s1.getFather().getName());

System.out.println(s2.getName() + "的父亲是" + s2.getFather().getName());

}

大儿子重新设置了父亲名称,我们期望的输出结果是:将大儿子的父亲名称修改为干爹,小儿子的父亲名称保持不变。运行一下结果如下:

大儿子的父亲是干爹

小儿子的父亲是干爹

怎么回事,小儿子的父亲也成了"干爹"?两个儿子都木有了,这老子估计被要被气死了!出现这个问题的原因就在于clone方法,我们知道所有类都继承自Object,Object提供了一个对象拷贝的默认方法,即页面代码中的super.clone()方法,但是该方法是有缺陷的,他提供的是一种浅拷贝,也就是说它并不会把对象的所有属性全部拷贝一份,而是有选择的拷贝,它的拷贝规则如下:

基本类型:如果变量是基本类型,则拷贝其值。比如int、float等

对象:如果变量是一个实例对象,则拷贝其地址引用,也就是说此时拷贝出的对象与原有对象共享该实例变量,不受访问权限的控制,这在Java中是很疯狂的,因为它突破了访问权限的定义:一个private修饰的变量,竟然可以被两个不同的实例对象访问,这让java的访问权限体系情何以堪。

String字符串:这个比较特殊,拷贝的也是一个地址,是个引用,但是在修改时,它会从字符串池(String pool)中重新生成新的字符串,原有的字符串对象保持不变,在此处我们可以认为String是一个基本类型。

明白了这三个原则,上面的例子就很清晰了。小儿子的对象是通过大儿子拷贝而来的,其父亲是同一个人,也就是同一个对象,大儿子修改了父亲的名称后,小儿子也很跟着修改了——于是,父亲的两个儿子都没了。其实要更正也很简单,clone方法的代码如下:

public Person clone() {

Person p = null;

try {

p = (Person) super.clone();

p.setFather(new Person(p.getFather().getName()));

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

return p;

}

然后再运行,小儿子的父亲就不会是干爹了,如此就实现了对象的深拷贝(Deep Clone),保证拷贝出来的对象自成一体,不受"母体"影响,和new生成的对象没有什么区别。

昆明Java培训机构的老师提醒大家注意:浅拷贝只是Java提供的一种简单拷贝机制,不便于直接使用。

上一篇:让工具类不可实例化
下一篇:推荐使用序列化对象的拷贝

腾讯游戏Switch独立销量领先——昆明达内

达内java语言编程学以致用

苹果技术:A11芯片上新菜【达内培训】

达内培训之国产手机vivo领跑,小米再上榜

选择城市和中心
贵州省

广西省

海南省