天天看点

关于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的接口克隆的对象是不对的了。。。哈哈哈