梳理一下深拷贝和浅拷贝。
引子
先定义两个环境类,这里就以 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
类也要实现序列化。