java对象克隆详解

概述: 当我们new一个对象时,其中的属性就会被初始化, 那么想要保存刚开始初始化的值就靠clone方法来实现, 平时我们最常见的是一个对象的引用指向另一个对象,并不是创建了两个对象.

 Person p1 = new Person(100,"jim");
        Person p2 = p1;

        System.out.println(p1==p2);//true

克隆肯定是创建了两个对象

 Person p1 = new Person(100,"jim");
        Person p2 =p1.clone();//克隆的新对象

        System.out.println(p1==p2);//false

克隆分为浅克隆(ShallowClone)和深克隆(DeepClone)。

在 Java 语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括 int、double、byte、boolean、char 等简单数据类型,引用类型包括类、接口、数组等复杂类型。基本类型的值可以直接复制,引用类型只能复制引用地址。所以浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复

制.

浅克隆

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。

实现方式:

1.在 Java 语言中,通过覆盖 Object 类的 clone()方法可以实现浅克隆。

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

这里我们主要演示通过重写object中clone方法来实现

1.首先定义一个类(需要被克隆的类)

public class Person implements  Cloneable{

     int num;
     String name;
     Address address;

    public Person() {
    }

    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    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{" +
                "num=" + num +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

2.可以看到Person类关联着Address类,也写出来

public class Address  {

     String  address;

    public String getAddress() {
        return address;
    }

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

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

3.写一个Test类进行测试

public class Test {

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

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

        Person p1 = new  Person(100,"jim");
               p1.setAddress(address);

        Person p2 =p1.clone();
               p2.setName("tom");
               address.setAddress("西安");

        System.out.println(p1); // jim   西安
        System.out.println(p2);// tom    西安
    }
}

首先看name属性,p1的name为jim,克隆出另一个对象p2,将name改成了tom,因为是两个对象,所以输出的结果分别都不同.

再看关联对象, 首先将有汉中信息的address加入到了p1中,所以目前p1中的address是汉中,经过克隆出p2后,其实对于address来说只克隆了地址,所以说p1和p2指向的都是同一个address,所以都是汉中,再经过一次修改成了西安,所以都是西安.

所以说浅克隆只是克隆了引用类型变量的地址.

深克隆

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制

在 Java 语言中,如果需要实现深克隆,可以通过覆盖 Object 类的 clone()方法实现,也可以通过序列化(Serialization)等方式来实现。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable 接口,否则无法实现序列化操作

1.重写Object类中的clone方法

1.首先定义一个类(需要被克隆的类),相比于上面的浅克隆增加了一行克隆address对象的代码

public class Person implements  Cloneable{

     int num;
     String name;
     Address address;

    public Person() {
    }

    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    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.address = (Address)address.clone();   //深度复制  联同person中关联的对象也一同克隆.
        return person;
    }

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

2.Address写出来,相比于上面浅克隆多了一个重写Object类的clone方法

public class Address  {

     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();
    }
}

3.还是Test测试类

public class Test {

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

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

        Person p1 = new  Person(100,"jim");
               p1.setAddress(address);

        Person p2 =p1.clone();
               p2.setName("tom");
               address.setAddress("西安");

        System.out.println(p1); // jim   西安
        System.out.println(p2);// tom    汉中
    }
}

这次的结果会有所不同, 因为深克隆不仅克隆了自己, 还克隆了关联着的类的对象, 所以说原来的p1存储的汉中被克隆在了p2中,而最后一行更改为西安的是原来的address对象, p2中已经克隆了原来的address并且保存了下来

2.序列化(Serialization)的方式

如果需要被克隆的类中关联的其他类的对象太多, 那么继续用深克隆的话需要耗费大量的时间去一个一个克隆关联着的对象, 而序列化的方式可以将该类中所有关联的对象化成流从而高校的进行克隆.

1.还是创建一个关联着被克隆的类的对象

public class Address  implements Serializable {

     String  address;

    public String getAddress() {
        return address;
    }

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

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

}

2.Person类,里面写一个自己的序列化方式的克隆方法

public class Person implements Serializable {

     int num;
     String name;
     Address address;

    public Person() {
    }

    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    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;
    }

    /**
     * 自定义克隆方法
     * @return
     */
    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{" +
                "num=" + num +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

3.Test类测试

public class Test {

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

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

        Person p1 = new  Person(100,"jim");
        p1.setAddress(address);

        Person p2 =p1.myclone();
               p2.setName("tom");
               address.setAddress("西安");

        System.out.println(p1);jim   西安
        System.out.println(p2);tom   汉中
    }
}

所以说, 得看具体情况进行选择

猜你喜欢

转载自blog.csdn.net/weixin_71243923/article/details/128959956