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