梳理一下深拷贝和浅拷贝。
引子
先定义两个环境类,这里就以 Student 和 Major 为例,表示学生和学生所学专业,它们应该是包含关系。
1 2 3 4 5 6 7 8 9 class Student { public String name; public Major major; } class Major { public String major; public String majorid; }
先看一种写法:
1 2 3 4 5 Major major = new Major ("1" , "2" );Student student = new Student ("zty" , major);Student student2 = student;System.out.println(student == student2);
严格来讲,这其实并不是“拷贝”,只是给了 student2 一个引用关系,因为没有生成新的实际对象,验证:
1 2 3 student2.setName("galaxy" ); System.out.println(student.getName());
可以看到修改 student2 后 student 也被影响。
浅拷贝
浅,就是对于一个对象来说,值类型的字段会复制一份,但是引用类型的字段拷贝的仅仅是引用地址,引用的对象没有被拷贝,只有一份。
实现方式
让被复制对象的类实现 Cloneable 接口,并重写 clone() 方法。
1 2 3 4 5 6 7 8 class Student implements Cloneable { public String name; public Major major; @Override protected Object clone () throws CloneNotSupportedException { return super .clone(); } }
1 2 3 4 5 6 7 8 9 10 11 Major major = new Major ("1" , "2" );Student student = new Student ("zty" , major);Student student3 = (Student) student.clone();System.out.println(student == student3); student3.setName("galaxy" ); student3.major.setMajor("fdsfds" ); System.out.println(student.getName() + " " + student3.getName()); System.out.println(student.getMajor() == student3.getMajor());
可以看到克隆了一个新对象,且值类型不再于原对象关联,但是引用类型仍然是一个。
深拷贝
深,就是除了值类型,引用也会复制一个副本出来。clone() 默认是浅拷贝,要想实现深拷贝,就要重写,进行深度遍历复制。
实现方式
先对 Major 类进行改造,这里 Major 里面没东西了,直接浅拷贝:
1 2 3 4 5 6 7 8 class Major implements Cloneable { public String major; public String majorid; @Override protected Object clone () throws CloneNotSupportedException { return super .clone(); } }
在 Student 中重写 clone 方法,实现深拷贝:
1 2 3 4 5 6 @Override protected Object clone () throws CloneNotSupportedException { Student student = (Student) super .clone(); student.setMajor((Major) major.clone()); return student; }
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Student student3 = (Student) student.clone();System.out.println(student == student3); student3.setName("galaxy" ); student3.major.setMajor("fdsfds" ); System.out.println(student.getName() + " " + student3.getName()); System.out.println(student.getMajor() == student3.getMajor()); student3.getMajor().setMajor("xiugai" ); System.out.println(student.getMajor().getMajor() + " " + student3.getMajor().getMajor());
序列化实现深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Student implements Serializable { public String name; public Major major; @Override public Student clone () { try { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (byteArrayOutputStream); objectOutputStream.writeObject(this ); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream (byteArrayInputStream); return (Student) objectInputStream.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } return null ; } }
注意,此种方法,Major 类也要实现序列化。