目录
1. clone() 方法说明
2. JVM 内存模型
3. 浅拷贝(浅克隆)与深拷贝(深克隆)的区别
4. 使用 clone() 克隆方法需要注意的事项
5. Object 类中的 clone() 方法为浅克隆
1. clone() 方法说明
clone() 方法是 Java.lang.Object 类中已经定义好的一个方法,该方法的主要作用是可以克隆一个对象。
2. JVM 内存模型
再往下继续了解之前,我们要先简单了解JVM的内存模型。在 Java 中,数据主要分为基本数据类型和引用数据类型,它们在内存中的分布也是不同的。
JVM 我们主要可以划分 JVM栈,堆,方法区(元空间),本地方法栈,程序计数器为五个部分,这里我们重点关注 JVM栈,堆,方法区。
JVM栈:运行我们所编写的方法,存储临时变量;
堆:存储引用数据类型的,例如对象;
方法区:存储 static 静态方法和类加载器;
3. 浅拷贝(浅克隆)与深拷贝(深克隆)的区别
Java中的数据类型主要分为基本数据类型和引用数据类型。基本数据类型是在各自的JVM栈中存储,而引用数据类型则是在堆中存储,JVM栈中的对象保存的不过是堆中对象的内存地址。
clone() 克隆方法又可以分为浅拷贝与深拷贝,浅拷贝与深拷贝最本质的区别就在于它们对于不同的数据类型拷贝时做的行为是不一样的。
浅拷贝:基本数据类型会复制一份完整的值,而引用数据类型则是复制一份内存地址,也就是说复制出来的对象和原来的对象引用同一个对象。
深拷贝:不仅基本数据类类型会复制一份完整的值,而且引用数据类型也会复制一份完整的值,字符串会复用字符串常量池中的值。也就是说原来的对象也复制了一份,两者操作的是不同的对象。
4. 使用 clone() 克隆方法需要注意的事项
(1)如果想要克隆一个对象,那么被克隆对象的类必须实现 Cloneable 接口,如下图所示,该接口也定义在 java.lang 包下,接口中什么都没有定义,像这种接口被称为标记型接口,没有实际意义,只是做一个标记,表示该类的对象可以被克隆,不标记则不可以;这一点与 Serializable 序列化接口极其相似。
(2)克隆对象调用 clone() 方法时,必须重写父类 Object 中的 clone() 方法,如下图所示,clone() 方法的修饰符为 protected。
被 protected 修饰的方法只能在本包中访问,或者在该类的子类中访问。
所以就有一个问题,我们的代码肯定不能在 java.lang 包下编写,而我们的业务类通常为了能够调用 clone() 去继承实体类,因为可能需要继承其他类,两种办法都行不通,所以我们不许重写父类 Object 中的 clone() 方法,扩大访问权限,才能够调用。
5. Object 类中的 clone() 方法为浅克隆
上面说到了调用 clone() 方法需要重写父类 Object 类中的 clone() 方法,如下部分代码
public class Users implements Serializable,Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
默认重写方法实际上还是调用的父类 Object 类中的方法,只是通过重写的方式让访问范围变大了,我们也可以将 protected 改为 public 。
而且默认重写方法采用的是浅拷贝,如果被克隆对象中含有引用数据类型,只会复制其对象的内存地址,和被克隆的对象操作同一引用数据类型。
如果想使用深拷贝,就只能在重写父类的 clone() 方法中自行添加其他逻辑处理变为深拷贝。