• Java深拷贝浅拷贝


        首先,Java中常用的拷贝操作有三个,operator = 、拷贝构造函数 和 clone()方法。由于Java不支持运算符重载,我们无法在自己的自定义类型中定义operator=。拷贝构造函数大家应该很熟悉,现在看一下如何支持clone方法:

     

    实现 Cloneable接口,因为 Object clone方法将检查类是否实现了 Cloneable接口,如果没有将抛出异常 CloneNotSupportedException对象。 Cloneable接口没有任何方法,只是个标志,所以只需要简单的写上 implements Cloneable即可。

    改写从 Object继承而来的 clone方法,使它的访问权限为 public,因为为了防止意外的支持 clone操作, Object clone方法是 protected权限。


        通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现Cloneable接口。
        现在,来看一下不同的类型在拷贝过程中的表现:

     

     

    Operator =

    拷贝构造函数

    clone方法

     

     

     

     

    预定义非集合类型

    深拷贝

    如果支持拷贝构造函数的类型,则是深拷贝

    不支持

    自定义类型

    浅拷贝

    取决于实现

    取决于实现

    预定义集合类型

    浅拷贝

    会逐个调用每个元素的operator=方法

    会逐个调用每个元素的operator=方法

     

     


        

     

     

     

     

     

     

     

     

    下面是测试代码,首先测试的是预定义非集合类型的operator =操作:
            int x=1;
            int y=x;
            y=2;
            if(x!=y){
                System.out.println("deep copy");
            }


            Integer a=1;
            Integer b=a;
            b=2;
            if(!a.equals(b)){
                System.out.println("deep copy");
            }


            String m="ok";
            String n=m;
            n="no";
            if(!m.equals(n)){
                System.out.println("deep copy");
            }

            程序运行后,输出三行deep copy,测试结果表明,这三种类型的operator =操作都是深拷贝。由于我没有测试完所有的预定义非集合类型,我这里推测它们的operator =都是深拷贝。

        下面测试预定义非集合类型的拷贝构造函数:
            Integer a=1;
            Integer b=new Integer(a);
            b=2;
            if(!a.equals(b)){
                System.out.println("deep copy");
            }


            String m="ok";
            String n=new String(m);
            n="no";
            if(!m.equals(n)){
                System.out.println("deep copy");
            }

            程序运行后,输出两行deep copy,测试结果表明,这两种类型的拷贝构造函数都是深拷贝。int没有拷贝构造函数。
        
            现在我们来测试自定义类型的operator=操作,假设我有一个类Person,代码如下:
    public class Person implements Cloneable{
        private int age;
        private String name;
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getName() {
            return name;
        }
        void setName(String name) {
            this.name = name;
        }
    }
        测试代码:
            Person p=new Person();
            p.setAge(32);
            p.setName("陈抒");

            Person p2=p;
            p.setAge(33);
            p.setName("老陈");

            if( (p.getAge()!=p2.getAge())&&(!p.getName().equals(p2.getName())) ){
                System.out.println("deep copy");
            }

            运行后,没有输出deep copy,说明这是浅拷贝。这里就是我们常说的两个引用之间的赋值,仅仅是让两个引用指向同一个对象。

        现在,我们来测试预定义集合类型的operator=操作:
            ArrayList list1=new ArrayList();
            list1.add("yangzhou");
            ArrayList list2=list1;
            list1.clear();

            if(list2.isEmpty()){
                System.out.println("shallow copy");
            }

        结果输出为shallow copy

        现在我来测试拷贝构造函数:
            ArrayList list1=new ArrayList();
            list1.add("yangzhou");
            ArrayList list2=new ArrayList(list1);
            list1.clear();

            if(list2.isEmpty()){
                System.out.println("shallow copy");
            }else{
                System.out.println("deep copy");
            }

            输出结果是deep copy;
        clone方法的测试代码只是将第三行换成list1.clone(),加上类型转换,这里不再贴代码了。结果也证明是深拷贝 。
        预定义集合类的深拷贝 实际上就是调用每个元素的operator =。如果元素都是自定义类型的化,实际上还是浅拷贝。现在来看测试代码:
        ArrayList list1=new ArrayList();
            Person p1=new Person();
            p1.setAge(32);
            p1.setName("陈抒");
            list1.add(p1);

            ArrayList list2=(ArrayList) list1.clone();
            list2.get(0).setName("chenshu");

            if(list2.get(0).getName().equals(list1.get(0).getName())){
                System.out.println("shallow copy");
            }else{
                System.out.println("deep copy");

            }
        
        输出为shallow copyPerson是自定义类型,它的operator =运算符只是引用之间赋值,是浅拷贝。因此当修改了list2的第一个元素指向的Person对象的name属性,也就是修改了list1第一个元素所指向的Person对象的name属性。对于这种拷贝,我自己起了一个名字,叫做第一层深拷贝。
        
        现在我们有了表格中的结论,自己实现拷贝构造函数或者clone方法的时候就心里有数多了。
        假如我的自定义类型内部成员变量都是预定义非集合类型,那么在clone方法中只需要调用Object.clone即可完成深拷贝操作。在拷贝构造函数中需要使用operator=来一个个的深拷贝;
        假如我们的自定义类型内部成员变量有一些预定义类型,另一些是自定义类型,如果要深拷贝的话,最好调用自定义类型成员变量的拷贝构造函数或者clone方法。下面是例子代码:
    public class Company {
        public Company(){

        }

        public Company(Company c){
            name=c.name;
            person=new Person(c.person);
        }
        private String name;
        private Person person;

        public Person getPerson() {
            return person;
        }

        public void setPerson(Person person) {
            this.person = person;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public Object clone() throws CloneNotSupportedException{
            Company c=new Company();
            c.setName(name);
            c.setPerson((Person) person.clone());
            return c;
        }
    }

    public class Person implements Cloneable{
        public Person(){

        }

        public Person(Person p){
            age=p.age;
            name=p.name;
        }
        private int age;
        private String name;
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        
        @Override
        public Object clone() throws CloneNotSupportedException{
            return super.clone();
        }
    }
        Person类的两个成员变量都是预定义非集合类型,所以只需要在clone方法中简单的调用super.clone()即可实现深拷贝。Company类有一个Person成员变量,因此要调用Personclone方法


  • 相关阅读:
    ガリレオの苦悩 操縦る 3
    ガリレオの苦悩 操縦る 2
    ガリレオの苦悩 操縦る 1
    ガリレオの苦悩 落下る 2
    ガリレオの苦労 落下る 1
    magento搬家步骤和可能遇到的问题
    Magento 自定义URL 地址重写 分类分级显示
    234的笔记
    Magento架构师的笔记-----Magento显示当前目录的父分类和子分类的分类名
    怎么用jquery判断浏览器类型和版本号?
  • 原文地址:https://www.cnblogs.com/cl1024cl/p/6205304.html
Copyright © 2020-2023  润新知