对象的克隆

本文深入探讨了Java中对象克隆的概念,详细讲解了浅克隆与深克隆的区别及实现方式,通过实例演示了基本数据类型与引用数据类型在克隆过程中的行为差异。
什么是对象的克隆

 一种比较官方的解释:需要修改一个对象,同时不想改变调用者的对象,就要制作该对象的一个本地副本。
用我们自己的话来说:把你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修饰的,不可变),所以克隆一次,虽说也只是克隆了一个引用,但堆内存会为这个引用开辟一个新的空间来保存新的值。

Java提供了多种对象克隆的方法和实现方式,以下是详细介绍: ### 实现Cloneable接口并重写clone()方法 实现Cloneable接口是Java中实现对象克隆的一种常见方式。需要类实现Cloneable接口,并重写Object类中的clone()方法。示例代码如下: ```java public class User implements Cloneable{ private String username; private String password; public User(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public boolean equals(Object obj) { User user = (User) obj; if (username.equals(user.username) && password.equals(user.password)) { return true; } return false; } } ``` 在使用时,可以通过以下方式克隆对象: ```java User user1 = new User("testUser", "testPassword"); try { User user2 = (User) user1.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } ``` ### 通过静态工厂方法实现克隆逻辑 通过静态工厂方法可以集中控制克隆过程。示例代码如下: ```java class Person { private String name; private int age; private Address address; private Person() {} // 私有构造 // 工厂克隆方法 public static Person newInstance(Person other) { Person p = new Person(); p.name = other.name; p.age = other.age; p.address = Address.newInstance(other.address); // 深拷贝 return p; } static class Address { public static Address newInstance(Address other) { Address addr = new Address(); addr.city = other.city; return addr; } } } // 使用示例 Person p2 = Person.newInstance(p1); // 工厂克隆 ``` ### 使用序列化/反序列化(深克隆) 通过对象的序列化和反序列化可以实现深克隆。需要类实现Serializable接口,将对象写入流中,再从流中读取出来,得到的就是一个新的克隆对象。 ### 浅拷贝与深拷贝 浅拷贝只复制对象的基本数据类型和引用类型的引用,而不复制引用类型的对象本身;深拷贝会复制对象的所有属性,包括引用类型的对象本身。在实现深拷贝时,可以使用序列化实现深拷贝、递归克隆实现深拷贝等方法 [^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值