Java 中的一切都是对象。基础数据类型,诸如int这些基本类型,操作时一般也是采取的值传递方式,所以有时候也称它为值类型;像数组、类Class、枚举Enum、Integer包装类等等,就是典型的引用类型;
定义两个类:Student和 Major:

赋值 vs 浅拷贝 vs 深拷贝
对象赋值
Student codeSheep = new Student();Student codePig = codeSheep;
严格来说,这种不能算是对象拷贝,因为拷贝的仅仅只是引用关系,并没有生成新的实际对象:
浅拷贝
浅拷贝属于对象克隆方式的一种:
值类型的字段会复制一份,而引用类型的字段拷贝的仅仅是引用地址,而该引用地址指向的实际对象空间其实只有一份。
实现:**
public class Test {public static void main(String[] args) throws CloneNotSupportedException {Major m = new Major("计算机科学与技术",666666);Student student1 = new Student( "CodeSheep", 18, m );// 由 student1 拷贝得到 student2Student student2 = (Student) student1.clone();System.out.println( student1 == student2 );System.out.println( student1 );System.out.println( student2 );System.out.println( "\n" );// 修改student1的值类型字段student1.setAge( 35 );// 修改student1的引用类型字段m.setMajorName( "电子信息工程" );m.setMajorId( 888888 );System.out.println( student1 );System.out.println( student2 );}}

student1==student2打印false,说明clone()方法的确克隆出了一个新对象;- 修改值类型字段并不影响克隆出来的新对象,符合预期;
- 而修改了
student1内部的引用对象,克隆对象student2也受到了波及,说明内部还是关联在一起的
深拷贝
深拷贝相较于上面所示的浅拷贝,除了值类型字段会复制一份,引用类型字段所指向的对象,会在内存中也创建一个副本,就像这个样子:
实现:
需要对更深一层次的引用类Major做改造,让其也实现Cloneable接口并重写clone()方法:
public class Major implements Cloneable {@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}// ... 其他省略 ...}public class Student implements Cloneable {@Overridepublic Object clone() throws CloneNotSupportedException {Student student = (Student) super.clone();student.major = (Major) major.clone(); // 重要!!!return student;}// ... 其他省略 ...}
利用反序列化实现深拷贝
public class Student implements Serializable {private String name; // 姓名private int age; // 年龄private Major major; // 所学专业public Student clone() {try {// 将对象本身序列化到字节流ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream =new ObjectOutputStream( byteArrayOutputStream );objectOutputStream.writeObject( this );// 再将字节流通过反序列化方式得到对象副本ObjectInputStream objectInputStream =new ObjectInputStream( new ByteArrayInputStream( byteArrayOutputStream.toByteArray() ) );return (Student) objectInputStream.readObject();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return null;}// ... 其他省略 ...}
当然这种情况下要求被引用的子类(比如这里的Major类)也必须是可以序列化的,即实现了Serializable接口:
