课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > 【昆明Java培训机构】避免对象的浅拷贝
  • 【昆明Java培训机构】避免对象的浅拷贝

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

  • 昆明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提供的一种简单拷贝机制,不便于直接使用。

    推荐文章

上一篇:让工具类不可实例化

下一篇:推荐使用序列化对象的拷贝

最新开班日期  |  更多

Java--零基础全日制班

Java--零基础全日制班

开班日期:11/30

Java--零基础业余班

Java--零基础业余班

开班日期:11/30

Java--周末提升班

Java--周末提升班

开班日期:11/30

Java--零基础周末班

Java--零基础周末班

开班日期:11/30

  • 网址:http://km .java.tedu.cn      地址:昆明市官渡区春城路62号证券大厦附楼6楼
  • 课程培训电话:186 8716 1620      qq:2066486918    全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56