Java 对象拷贝是为对象赋值的一种方式,简单来说就是创建一个和原对象相同的对象,新创建的对象是原对象的一个副本,面试官贼拉喜欢在面试的时候问一问你浅拷贝和深拷贝的原理。因为它涉及到对象的引用关系,涉及到 Java 是传值还是传递引用关系,这通常是面试的重点。所以在聊深拷贝和浅拷贝之前,我们先来聊一聊引用关系。
关于引用在 Java 中,除了基本数据类型(四类八种数据类型)之外,还存在引用数据类型。一般使用 = 号做赋值操作的时候,对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,也就是将原对象的引用传递过去,但是他们实际上还是指向的同一个对象。如下代码所示
public class Food{ String name; int num; String taste; constructor() get and set() toString() }测试类:
public static void main(String[] args) { int i1 = 10; int i2 = i1; // 基本数据类型的拷贝,拷贝值 System.out.println("i2 = " + i2); Food milk = new Food("milk",1,"fragrance"); Food food = milk; System.out.printf("food = " + food); System.out.println("milk = " + milk); // milk 和 food 都指向同一个堆内存对象 }如果用图表示的话,应该是下面这样的:
不用纠结 Java 中到底是值传递还是引用传递这种无意义的争论中,你只要记得对于基本数据类型,传递的是数据类型的值,而对于引用类型来说,传递的是对象的引用,也就是对象的地址就可以了。
关于浅拷贝和深拷贝浅拷贝和深拷贝其实就是在引用的这个基础上来做区分的,如果在拷贝的时候,只对基本数据类型进行拷贝,对引用数据类型只是进行了引用的传递,没有真正的创建一个新的对象,这种拷贝方式就认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,这种拷贝方式就被认为是深拷贝。
浅拷贝那么如何实现浅拷贝(Shallow copy)呢?很简单,就是在需要拷贝的类上实现 Cloneable 接口并重写其 clone() 方法就可以了。
下面我们对 Food 类进行修改,我们让他实现 Cloneable 接口,并重写 clone() 方法。
public class Food implements Cloneable{ ... @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } ... }然后在测试类中的代码如下
Food milk = new Food("milk",1,"fragrance"); Food food = (Food)milk.clone(); System.out.println("milk = " + milk); System.out.println("food = " + food);可以看到,现在的 food 对象是由 milk 对象拷贝出来的,那么此时的 food 对象和 milk 对象是同一个对象吗?我们通过打印,可以看到这两个对象的原生 hashcode。
milk = com.cxuan.objectclone.Food@3cd1a2f1 food = com.cxuan.objectclone.Food@4d7e1886可以发现,food 和 milk 并不是同一个对象,那 milk 中还有三个属性值,这三个属性值在 food 中是不是也一样呢?为了验证这个猜想,我们重写了 toString 方法。
@Override public String toString() { return "Food{" + "name='" + name + '\'' + ", num=" + num + ", taste='" + taste + '\'' + '}'; }然后再次打印 food 和 milk ,可以观察到如下结果
milk = Food{name='milk', num=1, taste='fragrance'} food = Food{name='milk', num=1, taste='fragrance'}嗯哼,虽然看起来"cxuan 哥"和"cuan 哥"是两种完全不同的称呼!但是他们却有一种共同的能力:写作!
我们还是通过图示来说明一下:
这幅图看出门道了么?在堆区分别出现了两个 Food 对象,这同时表明 clone 方法会重新创建一个对象并为其分配一块内存区域;虽然出现了两个对象,但是两个对象中的属性值是一样的,这也是换汤不换药,虽然汤和药是不同的东西(对象),但是他们都溶于水(属性值)。
深拷贝虽然浅拷贝是一种换汤不换药的说法,但是在 Java 世界中还是有一种说法是。。。。。。是啥来着?
词穷了。。。。。。
哦对,还有一种改头换面的形式,它就是我们所熟悉的深拷贝(Deep copy),先来抛出一下深拷贝的定义:在进行对象拷贝的基础上,对对象的成员变量也依次拷贝的方式被称为深拷贝。
哈哈哈哈,这故作高深的深拷贝原来就是在浅拷贝的基础上再复制一下它的属性值啊,我还以为是啥高深的东西呢!上代码!