Java的对象克隆

克隆

直接使用new关键字创建的对象,是一个新的对象,没有任何数据(初始化的默认值)
使用克隆创建的对象,可以复制对象的数据
Java中数据类型有值类型(八大基本数据类型)和引用类型(类,数组,接口)
基本类型复制值,引用类型复制引用地址而不是对象本身
浅克隆、深克隆区别在于是否支持引用类型的成员变量的复制

1.1浅克隆 ShallowClone

  • 如果对象的成员变量是基本类型,克隆对象会拿到成员的值
  • 如果对象的成员变量是引用类型,克隆对象会拿到成员的引用地址

在浅克隆中:源对象和克隆对象的成员变量指向相同的内存地址

浅克隆实现:

1. 重写Object类的clone()方法

示例1

在这里插入图片描述
直接重写Object的clone(),会抛出异常

在这里插入图片描述
正确用法:1.先实现实现Cloneable接口,再重写clone()方法

在这里插入图片描述
测试
在这里插入图片描述

示例2

作为属性的类(没有重写clone方法)

public class Address{

    private String  address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

被克隆的类(重写了clone方法)

public class Person implements Cloneable {

    private String name;
    private Address address;

    public Person() {}

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

测试
在这里插入图片描述
两个对象关联的同一个Address对象,Address修改了,克隆对象的信息也发生了变化

2. 在spring中提供了BeanUtils.copyProperties(source,target);

1.2深克隆 DeepClone

在深克隆中,无论源对象的成员是基本类型/引用类型,都会复制给克隆对象
也就是在深克隆中,对象和所有成员都会被克隆
而在浅克隆中,只会克隆对象和基本类型成员的信息,和引用类型的地址

实现深克隆的方式

1、实现Cloneable接口,重写Object的clone()方法

  1. 原对象重写clone方法,在clone中,复制源对象和源对象的属性
  2. 原对象关联的对象重写clone方法

当做属性的类(重写clone方法)

public class Address implements Cloneable {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }

    @Override
    protected Address clone() throws CloneNotSupportedException {
        return (Address) super.clone();
    }
}

被克隆的类(重写clone方法)

package com.example.java_hign.clone.demo2;

public class Person implements Cloneable {

    private String name;
    private Address address;

    public Person() {}

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        //深度克隆  连同person中关联的对象也一同克隆.
        person.address = (Address) address.clone();   
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

测试

public static void main(String[] args) throws CloneNotSupportedException {

        Address address = new Address();
                address.setAddress("汉中");

        Person p1 = new Person("刘备",address);

        Person p2 =p1.clone();
               p2.setName("李世明");
               address.setAddress("西安");
		
        System.out.println(p1);// Person{name='刘备', address=Address{address='西安'}}
        //深度克隆:复制了源对象和源对象的关联对象,属性不再指向同一对象
        System.out.println(p2);//Person{name='李世明', address=Address{address='汉中'}}
    }

2、通过序列化实现(Serialization),不用在关联的对象中也重写Clone()

被关联的类Address(实现Serializable 序列化接口)

public class Address  implements Serializable {

    private String  address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

被克隆的源对象(实现Serializable 序列化接口)

public class Person implements Serializable {

    private String name;
    private Address address;

    public Person() {}

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    /**
     * 自定义克隆方法
     */
    public Person myclone() {
        Person person = null;
        try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            // 将流序列化成对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            person = (Person) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

测试
在这里插入图片描述

总结:

  • 浅克隆中,克隆对象的引用对象和原对象使用的同一个对象
  • 深克隆中,克隆对象的引用对象都是重新创建的对象

版权声明:本文为lanleihhh原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。