详细分析Java中的浅克隆和深克隆

发布于:2023-01-28 ⋅ 阅读:(558) ⋅ 点赞:(0)

本文对浅克隆深克隆两种方法(不引入别的开源工具)进行了简单的代码实现(没有内部类语法),对比了浅克隆和深克隆对引用类型的影响,暂不考虑不可变类,确保初学Java者能够看懂并学会,可直接复制源代码进行使用和测试


前言:为什么要克隆

        对象克隆是指以现有对象为基础,克隆出一个和它完全相同的对象

        为什么要进行克隆,直接new一个对象不好吗?

        因为克隆的速度快于new对象的速度


一、关于克隆的概述

        Java中的克隆分为深克隆和浅克隆

        浅克隆:将基本类型复制,但是对引用类型则只复制地址,浅克隆后引用类型地址相同;

        深克隆:是将对象中的基本类型复制,将引用类型所指向的对象也进行复制,深克隆后引用类型地址不同,克隆后成为真正独立的个体;

二、浅克隆的代码实现

1.创建一个Cat类

增加get和set方法,带参构造器,重写toString

public class Cat {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

2.同样,再创建一个Dog类

public class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

3.创建一个Pet类,重写clone方法

public class Pet implements Cloneable {//创建一个宠物类,实现Cloneable接口
    private int quantity;
    private Dog dog;//创建一个Dog引用类型
    private Cat cat;//创建一个Cat引用类型

    public Pet(int quantity, Dog dog, Cat cat) {
        this.quantity = quantity;
        this.dog = dog;
        this.cat = cat;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Pet{" +
                "quantity=" + quantity +
                ", dog=" + dog +
                ", cat=" + cat +
                '}';
    }

    @Override
    public Pet clone() throws CloneNotSupportedException {
        //此处改为public Pet是方便后续创建的CloneTest类中的对象好调用clone()
        Pet clonePet = (Pet) super.clone();
        return clonePet;
    }
}

4.创建一个CloneTest类来测试浅克隆效果

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Dog dog = new Dog("Spike", 10);
        Cat cat = new Cat("Tom", 12);
        Pet pet = new Pet(2, dog, cat);
        Pet pet1 = pet.clone();

        System.out.println(pet);
        System.out.println(pet1);

        System.out.println(pet.getCat() == pet1.getCat());//true
        System.out.println(pet.getDog() == pet1.getDog());//true

        dog.setName("WangCai");
        System.out.println(pet);
        System.out.println(pet1);//浅克隆时,克隆体随之而改变
    }
}

测试结果如下

 三、深克隆的代码实现

第一种方法:重写clone方法,对每一个引用类型再进行clone

1.把上述Cat和Dog类都实现Cloneable接口

public class Cat implements Cloneable {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public Cat clone() throws CloneNotSupportedException {
        Cat cloneCat = (Cat) super.clone();
        return cloneCat;
    }
}
public class Dog implements Cloneable {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public Dog clone() throws CloneNotSupportedException {
        Dog cloneDog = (Dog) super.clone();
        return cloneDog;
    }
}

2.重写Pet类里的clone方法,把Dog和Cat引用类型再分别进行clone

public class Pet implements Cloneable {//创建一个宠物类,实现Cloneable接口
    private int quantity;
    private Dog dog;//创建一个Dog引用类型
    private Cat cat;//创建一个Cat引用类型

    public Pet(int quantity, Dog dog, Cat cat) {
        this.quantity = quantity;
        this.dog = dog;
        this.cat = cat;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Pet{" +
                "quantity=" + quantity +
                ", dog=" + dog +
                ", cat=" + cat +
                '}';
    }

    @Override
    public Pet clone() throws CloneNotSupportedException {
        //此处改为public Pet是方便后续创建的CloneTest类中的对象好调用clone()
        Pet clonePet = (Pet) super.clone();
        clonePet.dog = clonePet.dog.clone();
        clonePet.cat = clonePet.cat.clone();
        return clonePet;
    }
}

3.测试深克隆效果

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Dog dog = new Dog("Spike", 10);
        Cat cat = new Cat("Tom", 12);
        Pet pet = new Pet(2, dog, cat);
        Pet pet1 = pet.clone();

        System.out.println(pet);
        System.out.println(pet1);

        System.out.println(pet.getCat() == pet1.getCat());//false
        System.out.println(pet.getDog() == pet1.getDog());//false

        dog.setName("WangCai");
        System.out.println(pet);
        System.out.println(pet1);//深克隆时,克隆体不会随之而改变
    }
}

测试结果如下


第二种方法:序列化和反序列化

1.准备好Cat、Dog、Pet三个类,三个类都实现Serializable接口

import java.io.Serializable;

public class Cat implements Serializable {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
import java.io.Serializable;

public class Dog implements Serializable {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
import java.io.Serializable;

public class Pet implements Serializable {
    private int quantity;
    private Dog dog;//创建一个Dog引用类型
    private Cat cat;//创建一个Cat引用类型

    public Pet(int quantity, Dog dog, Cat cat) {
        this.quantity = quantity;
        this.dog = dog;
        this.cat = cat;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Pet{" +
                "quantity=" + quantity +
                ", dog=" + dog +
                ", cat=" + cat +
                '}';
    }
}

2.创建一个SerializableCloneTest的类,进行序列化和反序列并且测试深克隆效果

import java.io.*;

public class SerializableCloneTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Dog dog = new Dog("Spike", 10);
        Cat cat = new Cat("Tom", 12);
        Pet pet = new Pet(2, dog, cat);

        //创建一个字节数组输出流和一个二进制带类型的对象输出流,两个流搭配进行使用
        ByteArrayOutputStream bAOut = new ByteArrayOutputStream();
        ObjectOutputStream oOut = new ObjectOutputStream(bAOut);

        oOut.writeObject(pet);//序列化要深克隆的对象

        //创建一个字节数组输入流和一个二进制带类型的对象输入流,字节数组输入流读取bAOut
        ByteArrayInputStream bAIn = new ByteArrayInputStream(bAOut.toByteArray());
        ObjectInputStream oIn = new ObjectInputStream(bAIn);

        //反序列化,把Object对象强转为Pet类型的对象,完成对Pet类型对象的深克隆
        Pet clonePet = (Pet) oIn.readObject();

        System.out.println(pet);
        System.out.println(clonePet);

        System.out.println(pet.getCat() == clonePet.getCat());//false
        System.out.println(pet.getDog() == clonePet.getDog());//false

        cat.setName("Hello Kitty");
        System.out.println(pet);
        System.out.println(clonePet);//深克隆时,克隆体不会随之而改变

        bAOut.close();//使用完后关闭流
        oOut.close();
        bAIn.close();
        oIn.close();
    }
}

测试结果如下

 

 四、结论

        浅克隆就是简单把对象中引用类型的地址进行复制,被克隆对象的引用类型改变,会引发克隆对象也会发生改变(不包括不可变类)        

        想要克隆完整的独立的个体,只能采用深克隆,被克隆对象的改变不会引起克隆对象的改变,被克隆对象的引用类型过多的话,推荐使用序列化和反序列化进行深克隆,比较方便。

本文含有隐藏内容,请 开通VIP 后查看