java中的浅复制和深复制

编程的艺术 ----Ctrl + C , Ctrl + V 在代码中的体现

前言 : 你是否在写代码的时候遇见过一个对象要new多个一样的或者改动不大的 复制粘贴使得代码很难看不雅观而身有体会呢?
那么这篇文章绝对适合你 它还存在一个设计模式–原型模式


浅复制

先介绍一下浅复制

浅复制是指当对象的字段值被复制时,字段引用的对象不会被复制 只会得到其引用 例如,如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅复制,那么两个对象将引用同一个字符串·

那么浅复制怎么实现呢?

其实在Object类中有一个方法 Object clone()
这个方法就可以完成浅复制 只需要子类重写这个方法 并且实现了Cloneable接口即可
例如一个Teacher类 和 Student类

public class Teacher {
    private String name;

    public Teacher(String name) {
        this.name = name;
    }

    public Teacher() {
    }

    public String getName() {
        return name;
    }

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

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

public class Student implements Cloneable {
    private int age;
    private Teacher teacher;

    public Student(int age, Teacher teacher) {
        this.age = age;
        this.teacher = teacher;
    }

    public Student() {
    }

    public int getAge() {
        return age;
    }

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

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

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

可能有人就想问了 clone方法既然是Object 的 那么 你实现了Cloneable接口难道不用重写其方法
那么让我们看一下Cloneable的源码


public interface Cloneable {
}

对的 这个接口的源码就是这个样子 其实他只是一个标识符
你实现了这个接口表示你支持复制 不实现代表不复制

接下来看一下测试类

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
       Teacher teacher = new Teacher("刘亦菲");
        Student stu1 = new Student(18,teacher);
        Student stu2 = (Student) stu1.clone();

        System.out.println(stu1);
        System.out.println(stu2);

    }
}

在这里插入图片描述
这样做的好处是什么呢?
比如一个类 有很多成员变量 你再给他set值的时候会set很多行
然后你又需要很多个一样的对象 这个时候你是不是就想到了复制粘贴
导致 代码几百行 而且都是重复的 很难看
用这种方法是不是就不用那么麻烦了
还有一个好处就是不用去初始化 万一你的构造器很复杂 很消耗时间
如果用我们的方法就

但是! 这里却有个缺陷 还记得我们之前说的如果对象类型只会得到其引用
接下来让我们看看这个事情 修改其测试类代码

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("刘亦菲");
        Student stu1 = new Student(18,teacher);
        Student stu2 = (Student) stu1.clone();

        System.out.println(stu1);
        System.out.println(stu2);

        stu2.getTeacher().setName("唐嫣");

        System.out.println(stu1);
        System.out.println(stu2);
    }
}

然后看一下输出结果

在这里插入图片描述

我们只是修改了stu2的老师的名字 stu1的也跟着变了
所以说浅复制只是获得了其引用可能有人还是不相信 看一下这个测试代码

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("刘亦菲");
        Student stu1 = new Student(18,teacher);
        Student stu2 = (Student) stu1.clone();
        System.out.println(stu1.getTeacher().equals(stu2.getTeacher()));
    }
}

输出结果
在这里插入图片描述
两个对象是同一个 这就造成了很多复制上的弊端
让我们来看一下深复制


深复制

深复制会赋值所有的属性,并复制属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深赋值贝。深复制相比于浅复制速度较慢并且花销较大。

我这里讲几种深复制的方法

1.构造深复制法

@Override
    protected Object clone() throws CloneNotSupportedException {
        Teacher teacher = new Teacher(this.teacher.getName());
        Student student = new Student(this.age, teacher);
        return student;
    }

修改复制的方法可能比较笨
然后再看测试类

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher("刘亦菲");
        Student stu1 = new Student(18,teacher);
        Student stu2 = (Student) stu1.clone();

        System.out.println(stu1);
        System.out.println(stu2);

        stu2.getTeacher().setName("唐嫣");

        System.out.println(stu1);
        System.out.println(stu2);

        System.out.println(stu1.getTeacher().equals(stu2.getTeacher()));
    }
}

在这里插入图片描述

这次就不是同一个对象了.还有一种方法就是实现序列化接口

序列化接口: Serializable接口
序列化: 就是把一个对象的状态全部用字节码保存起来 存到硬盘 数据库等等…
反序列化:就是序列化的逆过程 把你存起来的字节码恢复成对象 其底层依赖反射

在Teacher 类 和Student类中实现Serializable接口
并尝试着把Student对象写到文件里
在Student里实现一个方法

public void write() throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://12345.txt"));
        objectOutputStream.writeObject(this);

    }

当然文件路径你可以自定义 也可以不写到文件里 随便你

然后测试类

import java.io.IOException;

public class Demo2 {
    public static void main(String[] args) throws IOException {
        Student student = new Student(18, new Teacher("刘亦菲"));
        student.write();
    }
}

然后再用同样的方法读出来

public static Object read() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://12345.txt"));
        return objectInputStream.readObject();
    }

修改测试类

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Demo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student student = new Student(18,new Teacher("刘亦菲"));
        System.out.println(student);
        student.write();
        Student student1 = read();
        System.out.println(student1);
    }
    public static Student read() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://12345.txt"));
        return (Student) objectInputStream.readObject();
    }
}

在这里插入图片描述
输出结果 让我们看一下两个对象是不是同一个

修改测试类代码

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Demo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student student = new Student(18,new Teacher("刘亦菲"));
        System.out.println(student);
        student.write();
        Student student1 = read();
        System.out.println(student1);
        System.out.println(student.equals(student1));
        System.out.println(student1.getTeacher().equals(student.getTeacher()));

    }
    public static Student read() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://12345.txt"));
        return (Student) objectInputStream.readObject();
    }
}

在这里插入图片描述
这里其实就是两个不同的对象了

这里还需要强调一个字段
serialVersionUID

serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。为了提高哦啊serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。显式地定义serialVersionUID有两种用途:

如果你需要在两个不同的进程一个写 一个读的话就需要这个东西的显示定义 也就是你自己加一个字段
于是修改了Student类的定义

import java.io.*;

public class Student implements Cloneable,Serializable {
    private int age;
    private Teacher teacher;
    private static final long serialVersionUid = 1L;

    public Student(int age, Teacher teacher) {
        this.age = age;
        this.teacher = teacher;
    }

    public Student() {
    }

    public int getAge() {
        return age;
    }

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

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

    //浅复制
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void write() throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://12345.txt"));
        objectOutputStream.writeObject(this);

    }



}

代码的第六行 定义了 private static final long serialVersionUid = 1L;

好了 然后怎么进行深复制你们应该明白了 其实最好的方法不要写到硬盘中 而是写到流中
比如

  //深复制
    public Object 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 objectInputStream.readObject();

    }

学生类代码

import java.io.*;

public class Student implements Cloneable,Serializable {
    private int age;
    private Teacher teacher;
    private static final long serialVersionUid = 1L;

    public Student(int age, Teacher teacher) {
        this.age = age;
        this.teacher = teacher;
    }

    public Student() {
    }

    public int getAge() {
        return age;
    }

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

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

    //浅复制
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }



    //深复制
    public Object 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 objectInputStream.readObject();
    }
}

我们修改测试类

import java.io.IOException;

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Teacher teacher = new Teacher("刘亦菲");
        Student stu1 = new Student(18,teacher);
        Student stu2 = (Student) stu1.deepClone();

        System.out.println(stu1);
        System.out.println(stu2);

        stu2.getTeacher().setName("唐嫣");

        System.out.println(stu1);
        System.out.println(stu2);

        System.out.println(stu1.getTeacher().equals(stu2.getTeacher()));
    }
}

在这里插入图片描述
输出结果

好了 贴一下详细的代码 首先 Teacher类

import java.io.Serializable;

public class Teacher implements Serializable {
    private String name;

    public Teacher(String name) {
        this.name = name;
    }

    public Teacher() {
    }

    public String getName() {
        return name;
    }

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

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

然后Student类

import java.io.*;

public class Student implements Cloneable,Serializable {
    private int age;
    private Teacher teacher;
    private static final long serialVersionUid = 1L;

    public Student(int age, Teacher teacher) {
        this.age = age;
        this.teacher = teacher;
    }

    public Student() {
    }

    public int getAge() {
        return age;
    }

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

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

    //浅复制
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void write() throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://12345.txt"));
        objectOutputStream.writeObject(this);

    }

    public Object read() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://12345.txt"));
        return objectInputStream.readObject();
    }


    //深复制
    public Object 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 objectInputStream.readObject();

    }

}

测试类

import java.io.IOException;

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Teacher teacher = new Teacher("刘亦菲");
        Student stu1 = new Student(18,teacher);
        Student stu2 = (Student) stu1.deepClone();

        System.out.println(stu1);
        System.out.println(stu2);

        stu2.getTeacher().setName("唐嫣");

        System.out.println(stu1);
        System.out.println(stu2);

        System.out.println(stu1.getTeacher().equals(stu2.getTeacher()));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42011541/article/details/83108188