Java深入学习26:Java深度克隆

深克隆和浅克隆区别

  浅克隆: 只copy对象引用,不copy对象本身。即对象地址不变,仍然只存在一个对象。

  深克隆: 不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。

 

深克隆的两个方案

方案1——实现Cloneable接口,重写Object类地 clone()方法

  分如下三步

  1. 对象的类实现Cloneable接口(必须,否则会抛出CloneNotSupportedException异常);

  2. 覆盖Object类的clone()方法 (覆盖clone()方法,将访问修饰符改为public,默认是protected);

  3. 在clone()方法中调用super.clone();

public class CloneTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        Student stu = new Student("A");
        Student deep = stu.clone();//深克隆
        Student shallow = stu;//浅克隆
        shallow.setName("B");
        System.out.println("original: " + stu);//Student{name='B'}
        System.out.println("deep: " + deep);//Student{name='A'}
        System.out.println("shallow: " + shallow);//Student{name='B'}

    }


}

class Student implements  Cloneable{
    private String name;

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

    public String getName() {
        return name;
    }

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

    @Override
    public Student clone() throws CloneNotSupportedException {
        return (Student) super.clone();
    }

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

 

深克隆的多级深克隆问题:

  重写clone()方法,只会深克隆类和类中所有非基本数据类型的属性对应的类。对于类中的属性对应的类,是无法深克隆的,如果深克隆类中的属性对应的类,需要额外的调用类中的属性对应的类clone方法

public class CloneTest {

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

        School sch  = new School("a",new Student("A"));
        School deepSch = sch.clone();//深克隆
        School shallowSch = sch;//浅克隆
        shallowSch.getStu().setName("B");
        System.out.println(sch);//School{name='a', stu=Student{name='B'}}
        System.out.println(deepSch);//School{name='a', stu=Student{name='A'}}
        System.out.println(shallowSch);// School{name='a', stu=Student{name='B'}}

    }

}

class School implements  Cloneable{
    private String name;
    private Student stu;


    @Override
    public School clone() throws CloneNotSupportedException {
        //return (School)super.clone();//该方法无法深克隆Sch中的Student
        School clone = (School) super.clone();
        clone.setStu(this.stu.clone());
        return clone;

    }


    public School(String name, Student stu) {
        this.name = name;
        this.stu = stu;
    }

    public String getName() {
        return name;
    }

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

    public Student getStu() {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu;
    }

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

 

方案2——序列化深克隆

什么是序列化?

  1- java序列化是指把java对象转换为字节序列的过程,而java反序列化是指把字节序列恢复为java对象的过程

  2- 序列化:对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。序列化后的字节流保存的java对象的状态以及相关的描述信息。序列化机制的核心作用就是对象状态的保存与重建。

  3- 反序列化:客户端从文件中或网络上获得序列化后的对象字节流后,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。

  4- 序列化就是把实体对象状态按照一定的格式写入到有序字节流,反序列化就是从有序字节流重建对象,恢复对象状态

 

import java.io.*;

public class SerializeDeepCloneTest {
    public static void main(String[] args) {
        SchoolSeri sch = new SchoolSeri("zjut",new StudentSeri("tyj"));
        SchoolSeri schSeri = null;
        System.out.println(sch);
        try {
            schSeri = SerializeDeepCloneUtil.deepClone(sch);
            System.out.println(sch);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(sch == schSeri);
    }
}

class SerializeDeepCloneUtil{

    //序列化深克隆
    static <T extends Serializable> T deepClone(T obj){
        if(obj ==null){
            return null;
        }

        T cloneObj = null;

        //序列化
        ByteArrayOutputStream bout = null;
        ObjectOutputStream oos = null;
        try {
            //创建字节数组输出流
            //new ByteArrayOutputStream() Creates a new byte array output stream.
            bout = new ByteArrayOutputStream();
            //创建对象输出流
            //new ObjectOutputStream(OutputStream out): Creates an ObjectOutputStream that writes to the specified OutputStream.
            oos = new ObjectOutputStream(bout);
            //向对象输出流中写数据
            //void writeObject(Object obj): Write the specified object to the ObjectOutputStream.
            oos.writeObject(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            close(oos);
            close(bout);
        }

        //反序列化
        ByteArrayInputStream bin = null;
        ObjectInputStream ois = null;

        try {
            //创建字节数组输入流
            //new ByteArrayInputStream(byte buf[]): Creates a ByteArrayInputStream so that it  uses buf as its buffer array.
            bin = new ByteArrayInputStream(bout.toByteArray());
            //创建对象输入流
            //new  ObjectInputStream(InputStream in): Creates an ObjectInputStream that reads from the specified InputStream.
            ois = new ObjectInputStream(bin);
            //从对象输入流中读取数据
            //Object readObject(): Read an object from the ObjectInputStream.
            cloneObj = (T)ois.readObject();

        } catch (IOException  | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            close(ois);
            close(bin);
        }
        return cloneObj;
    }


    //关闭流
    private static void close(Closeable closeable){
        if(closeable != null){
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 

 

public class SchoolSeri implements Serializable{
    private String name;
    private StudentSeri stu;

    public SchoolSeri(String name, StudentSeri stu) {
        this.name = name;
        this.stu = stu;
    }

    public String getName() {
        return name;
    }

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

    public StudentSeri getStu() {
        return stu;
    }

    public void setStu(StudentSeri stu) {
        this.stu = stu;
    }

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

public class StudentSeri implements Serializable{

    private String name;

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

    public String getName() {
        return name;
    }

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

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

 

END

 

posted on 2020-05-05 14:18  我不吃番茄  阅读(1682)  评论(1编辑  收藏  举报