我们提供安全,免费的手游软件下载!

安卓手机游戏下载_安卓手机软件下载_安卓手机应用免费下载-先锋下载

当前位置: 主页 > 软件教程 > 软件教程

浅拷贝和深拷贝

来源:网络 更新时间:2024-06-17 11:33:11

浅拷贝是指在拷贝对象时,如果对象中包含引用类型的字段,那么拷贝后的对象中的引用类型字段指向的是同一个对象。相比之下,深拷贝则是在拷贝对象时,会将对象中的引用类型字段也进行拷贝,使得拷贝后的对象中的引用类型字段指向的是不同的对象。

为了更好地理解浅拷贝和深拷贝,首先创建两个类,分别是Student和Bag。

@Data
class Student implements Cloneable{
    //年龄和名字是基本属性
    private int age;
    private String name;
    //书包是引用属性
    private Bag bag;

    public Student(int age, String name, Bag bag) {
        this.age = age;
        this.name = name;
        this.bag = bag;
    }

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

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

Student类中包含了一个引用类型字段Bag,而Bag类如下所示:

@Data
class Bag {
    private String color;
    private int price;

    public Bag(String color, int price) {
        this.color = color;
        this.price = price;
    }

    @Override
    public String toString() {
        return "color='" + color + ", price=" + price;
    }
}

接下来,我们来看一下Cloneable接口。Cloneable接口是一个标记接口,它并没有包含任何属性和方法。它的作用是用来表示某个功能在执行的时候是合法的。如果不实现Cloneable接口直接重写并调用clone()方法,会抛出CloneNotSupportedException异常。

下面是一个测试类TestClone,用于测试浅拷贝的效果:

class TestClone {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student(18, "张三", new Bag("红",100));
        Student student2 = (Student) student1.clone();

        System.out.println("浅拷贝后:");
        System.out.println("student1:" + student1);
        System.out.println("student2:" + student2);

        //修改非引用类型属性name
        student2.setName("李四");

        //修改引用类型属性bag
        Bag bag = student2.getBag();
        bag.setColor("蓝");
        bag.setPrice(200);

        System.out.println("修改了 student2 的 name 和 bag 后:");
        System.out.println("student1:" + student1);
        System.out.println("student2:" + student2);
    }
}

运行结果表明,浅拷贝后,修改了student2的非引用类型属性name,student1的name并不会跟着改变,但修改了student2的引用类型属性bag,student1的bag跟着改变了。这说明浅拷贝克隆的对象中,引用类型的字段指向的是同一个对象,当改变任何一个对象,另外一个对象也会随之改变。

接下来,我们来看一下深拷贝。深拷贝和浅拷贝的区别在于,深拷贝中的引用类型字段也会克隆一份,当改变任何一个对象,另外一个对象不会随之改变。下面是一个例子:

@Data
class Bag implements Cloneable {
    private String color;
    private int price;

    public Bag(String color, int price) {
        this.color = color;
        this.price = price;
    }

    @Override
    public String toString() {
        return "color='" + color + ", price=" + price;
    }

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

在Bag类中,我们重写了clone()方法,并实现了Cloneable接口,以便在深拷贝时也能够克隆该字段。接下来是Student类的修改:

@Data
class Student implements Cloneable{
    //年龄和名字是基本属性
    private int age;
    private String name;
    //书包是引用属性
    private Bag bag;

    public Student(int age, String name, Bag bag) {
        this.age = age;
        this.name = name;
        this.bag = bag;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student s = (Student) super.clone();
        s.setBag((Bag) s.getBag().clone());
        return s;
    }
}

在Student类中,clone()方法不再只调用Object的clone()方法对Student进行克隆,还对Bag也进行了克隆。接下来是一个深拷贝的测试类:

class TestClone {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student(18, "张三", new Bag("红",100));
        Student student2 = (Student) student1.clone();

        System.out.println("深拷贝后:");
        System.out.println("student1:" + student1);
        System.out.println("student2:" + student2);

        //修改非引用类型属性name
        student2.setName("李四");

        //修改引用类型属性bag
        Bag bag = student2.getBag();
        bag.setColor("蓝");
        bag.setPrice(200);

        System.out.println("修改了 student2 的 name 和 bag 后:");
        System.out.println("student1:" + student1);
        System.out.println("student2:" + student2);
    }
}

测试结果表明,通过clone()方法实现的深拷贝,student1和student2是不同的对象,它们中的bag也是不同的对象。所以,改变了student2中的bag并不会影响到student1。

然而,通过clone()方法实现的深拷贝比较笨重,因为要将所有的引用类型都重写clone()方法。更好的方法是利用序列化。

序列化

序列化是将对象写入流中,而反序列化是将对象从流中读取出来。写入流中的对象就是对原始对象的拷贝。需要注意的是,每个要序列化的类都要实现Serializable接口,该接口和Cloneable接口类似,都是标记型接口。

下面是一个例子:

@Data
class Bag implements Serializable {
    private String color;
    private int price;

    public Bag(String color, int price) {
        this.color = color;
        this.price = price;
    }

    @Override
    public String toString() {
        return "color='" + color + ", price=" + price;
    }
}

Bag类需要实现Serializable接口。接下来是Student类的修改:

@Data
class Student implements Serializable {
    //年龄和名字是基本属性
    private int age;
    private String name;
    //书包是引用属性
    private Bag bag;

    public Student(int age, String name, Bag bag) {
        this.age = age;
        this.name = name;
        this.bag = bag;
    }

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

    //使用序列化拷贝
    public Object serializeClone() throws IOException, ClassNotFoundException {
        // 序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);

        oos.writeObject(this);

        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);

        return ois.readObject();
    }
}

在Student类中,增加了一个serializeClone()的方法,利用OutputStream进行序列化,InputStream进行反序列化,从而实现深拷贝。接下来是一个序列化拷贝的测试类:

class TestClone {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Student student1 = new Student(18, "张三", new Bag("红",100));
        Student student2 = (Student) student1.serializeClone();

        System.out.println("序列化拷贝后:");
        System.out.println("student1:" + student1);
        System.out.println("student2:" + student2);

        //修改非引用类型属性name
        student2.setName("李四");

        //修改引用类型属性bag
        Bag bag = student2.getBag();
        bag.setColor("蓝");
        bag.setPrice(200);

        System.out.println("修改了 student2 的 name 和 bag 后:");
        System.out.println("student1:" + student1);
        System.out.println("student2:" + student2);
    }
}

测试结果表明,序列化拷贝的效果与通过clone()方法实现的深拷贝一样。需要注意的是,由于序列化涉及到输入流和输出流的读写,在性能上要比HotSpot虚拟机实现的clone()方法差很多。