天天看點

關于Cloneable 的淺複制

JAVA 中在對對象克隆的時候,可以在實體類中實作Cloneable 的接口來實作複制的功能,在實體類中重寫clone()的方法,但是這樣會出現一些問題,對于clone()的方法,基本資料類型是可以成功的copy的,但是對于引用資料類型,在copy的時候就會出現錯誤,我們用一個demo來看下到底是出現了什麼樣子的問題:

在這裡我們直接定義了兩個實體,針對批量發送郵件的時候,郵件内容可能是一樣,但是針對的客戶個人資訊是不一樣的,是以 我們進行了封裝

Email實體:

//實作Cloneable 的接口
public class Email implements Cloneable, Serializable{

    private  String conten;//郵件内容
    private  String title;//郵件主題
    private  Customer customer;//客戶實體資訊
    public String getConten() {
        return conten;
    }

    public void setConten(String conten) {
        this.conten = conten;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
    @Override
    public String toString() {
        return "Email{" +
                "conten='" + conten + '\'' +
                ", title='" + title + '\'' +
                ", customer=" + customer +
                '}';
    }
    //重寫clone()的方法
    @Override
    protected Email clone() throws CloneNotSupportedException {
        return (Email) super.clone();
    }
           

Customer實體:

public class Customer , Serializable{
    private  String name;//客戶姓名
    private  String address;//郵件位址
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
           

測試類:

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Email  email =new Email();
        email.setConten("客戶您好,我店在于7月27日有優惠活動。");
        email.setTitle("優惠活動");
        Customer customer=new Customer();
        customer.setAddress("[email protected]");
        customer.setName("zhangsan");
        email.setCustomer(customer);
        Email  email1=email.clone();
        email.getCustomer().setName("lisi");
        email.getCustomer().setAddress("[email protected]");
        System.out.println(email.getCustomer()==email1.getCustomer());
        System.out.println(email);
        System.out.println(email1);

    }
}

           

這裡我們實作的效果應該是郵件的内容是一樣的,但是客戶的資訊是不一樣的,但是我們運作的時候發現以下的結果

true
Email{conten='客戶您好,我店在于7月27日有優惠活動。', title='優惠活動', customer=Customer{name='lisi', address='[email protected]'}}
Email{conten='客戶您好,我店在于7月27日有優惠活動。', title='優惠活動', customer=Customer{name='lisi', address='[email protected]'}}
           

我們可以看到 雖然對第二個Email的Customer進行了複制,但是發現第一個Email的資訊也發生了變化,并且,我們列印兩者的Customer的對象 發現了 為 true,這說明了,這個對象指向的位址 是用一個位址,是以列印的資訊也是一樣的。

是以在Clone接口針對引用類型的屬性,會指向用一個引用的位址,導緻資料的錯誤,是以我們針對這個現象,我們在實體中自己寫一個深複制的方法進行複制。

在Email的實體類中加入了一下的深克隆的方法

public  Email   deepClone() throws IOException, ClassNotFoundException {
        //先寫入
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);
        //在讀取
        ByteArrayInputStream byteArrayInputStream =new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream  objectInputStream=new ObjectInputStream(byteArrayInputStream);
        return  (Email) objectInputStream.readObject();
    }
           

在測試類中我們就變成了以下的,換了一下克隆的方法

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Email  email =new Email();
        email.setConten("客戶您好,我店在于7月27日有優惠活動。");
        email.setTitle("優惠活動");

        Customer customer=new Customer();
        customer.setAddress("[email protected]");
        customer.setName("zhangsan");
        email.setCustomer(customer);

        Email  email1=email.deepClone();
        email.getCustomer().setName("lisi");
        email.getCustomer().setAddress("[email protected]");
        System.out.println(email.getCustomer()==email1.getCustomer());
        System.out.println(email);
        System.out.println(email1);

    }
}
           

測試列印結果:

false
Email{conten='客戶您好,我店在于7月27日有優惠活動。', title='優惠活動', customer=Customer{name='lisi', address='[email protected]'}}
Email{conten='客戶您好,我店在于7月27日有優惠活動。', title='優惠活動', customer=Customer{name='zhangsan', address='[email protected]'}}
           

這樣就得到了我們所預期的樣子了,

不過要注意的是,在這樣進行克隆的時候,實體類一定要實作 Serializable 的接口

不僅 Email要實作Serializable的接口,Customer 也需要實作Serializable的接口。

當遇到類似的場景,就不要納悶Cloneable的接口克隆的對象是不對的了。。。哈哈哈