对象的克隆

什么是对象的克隆

 一种比较官方的解释:需要修改一个对象,同时不想改变调用者的对象,就要制作该对象的一个本地副本。
用我们自己的话来说:把你U盘里面的数据全给我拷贝一份,这样我修改我的的U盘里面的数据,你的U盘里面的数据不会改变(这样是不是变得更容易理解)

在Java中如何实现对象的克隆

 其实我们可以思考一个问题,在Java中数据类型分为引用数据类型和基本数据类型。毫无疑问,在对一个对象进行拷贝时,基本数据类型是可以完美复制的(复制的是值),但对一个引用数据类型的属性,我们还能完整复制吗?或者说,这个克隆的新对象包含的引用是否还指向原来的对象?答案当然是肯定的,要不然还有什么可讲的。

 由于上述原因,在Java中克隆分为两种方式,一种是浅克隆,一种是深克隆。

浅克隆

 如若一个类中只含有基本数据类型,使用Object类中的clone()方法进行克隆就是浅克隆。

 看例子:有两个类 Customer 和 Address,其中 ID ,age是基本数据类型,address是引用数据类型

    class Customer implements Cloneable{  
            public int ID;  
            public int age;  
            public Address address;  
            public int getID() {  
                return ID;  
            }  
        public void setID(int iD) {  
                    ID = iD;  
            }  
        public int getAge() {  
                return age;  
        }  
        public void setAge(int age) {  
                this.age = age;  
        }  
        public Address getAddress() { 
                return address;  
        }  
        public void setAddress(Address address) {  
                this.address = address;  
        }  
        public Customer(int iD, int age, Address address) {  
                super();  
                ID = iD;  
                this.age = age;  
                this.address = address;  
        }  
        @Override  
        public String toString() {  
       return "Customer [ID=" + ID + ", age=" + age + ", address=" + address 
                    + "]";  
        }  
        @Override  
        public Customer clone() throws CloneNotSupportedException {  
                return (Customer) super.clone();  
        }  
    }  

    class Address{  
            private String country;  
            private String province;  
            private String city;  

        public String getCountry() {  
                return country;  
        }  
        public void setCountry(String country) {  
                this.country = country;  
        }  
        public String getProvince() {  
                return province;  
        }  
        public void setProvince(String province) {  
                this.province = province;  
        }  
        public String getCity() {  
                return city;  
        }  
        public void setCity(String city) {  
                this.city = city;  
        }  
        @Override  
        public String toString() {  
                return "Address [country=" + country + ", province=" + province  
                        + ", city=" + city + "]";  
        }  
        public Address(String country, String province, String city) {  
                super();  
                this.country = country;  
                this.province = province;  
                this.city = city;  
        }  
    }  

    public static void main(String[] args) throws CloneNotSupportedException {  
        Address address = new Address("CH" , "SD" , "QD");  
        Customer customer1 = new Customer(1 , 23 , address);  
        Customer customer2 = customer1.clone();  
        customer2.getAddress().setCity("JN");  
        customer2.setID(2);  
        System.out.println("customer1:"+customer1.toString());  
        System.out.println("customer2:"+customer2.toString());  
    }  
    }  

    输出结果为
    customer1:Customer [ID=1, age=23, address=Address [country=CH, province=SD, city=JN]]  
    customer2:Customer [ID=2, age=23, address=Address [country=CH, province=SD, city=JN]] 

    结果分析:
    1. 通过分析两个类可知,实现克隆分为两步:先实现 Cloneable 接口,其次是重写 Object中的 clone() 方法,并将clone() 方法的权限修改为                         public(Object 中的 clone() 方法时 Protected修饰的,所以不可以直接使用对象调用)
    2. 通过结果可知,经过对象的克隆,基本数据类型的数据在修改后不会对原来的对象产生影响,但是引用数据类型数据在修改后原对象的数据也发生改变,想一想这是为什么呢?
        
    解释:在这个例子中,我们只是对Customer这个类使用了clone() 方法(可以将其理解为对象的增强,对谁使用就对谁有效),那么自然克隆的只会是                      Customer 这个类的对象,对于 Customer类 中的 Address类型 由于没有经过 clone()方法的克隆,自然没有对其进行完全的复制,只是如同基                    本数据类型那样保存了一个引用罢了,在对空间不会有什么改变。
深克隆

 既然出现了上面的问题——引用数据类型无法通过浅克隆实现克隆,那么我们有什么解决办法呢,答案就是深克隆(浅的不行就深的,简单粗暴的逻辑)

直接看例子

    //1. 在Customer类中重写的clone()方法里对Address进行克隆(进行增强)
    @Override  
    public Customer clone() throws CloneNotSupportedException {  
    Customer customer = (Customer) super.clone(); 
             customer.address = address.clone();  
             return customer;  
    }  

    //2. 让Address类实现 Cloneable(),并在该类中重写Object的clone() 方法
    @Override  
    public Address clone() throws CloneNotSupportedException {  
           return (Address) super.clone();  
    }  
    
    输出结果为
    customer1:Customer [ID=1, age=23, address=Address [country=CH, province=SD, city=QD]]  
    customer2:Customer [ID=2, age=23, address=Address [country=CH, province=SD, city=JN]] 
总结
  • 深克隆与浅克隆的区别:浅克隆不会克隆原对象中的引用类型,仅仅拷贝了引用类型的指向。深克隆是照单全收,所有都进行了拷贝。

  • 使用序列化的方式也可以进行深克隆,但其效率远低于上面所说的方法,但存在必有其意义,如果大家感兴趣可以自己去摸索(提示:上面我讲的是关于一般类类型的克隆,那么对一些特殊的比如List等对象的克隆或许就不能使用这个方法)。

  • 要使用clone方法,类的成员变量上不要加final关键字

学到这,对于浅克隆和深克隆我们都有了基本的认识,那么你会不会有什么疑问呢?

请问:我们知道,String,Integer等也是引用数据类型,那么我们需不需要对其进行深克隆操作吗?
 这就考验基础了,String,Integer等是线程安全的不可变类(类是用final修饰的,不可变),所以克隆一次,虽说也只是克隆了一个引用,但堆内存会为这个引用开辟一个新的空间来保存新的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值