1. Cloneable
java.lang.Cloneable: 克隆接口
程序中的克隆: 赋值一个新的对象,新的对象的属性值是从就对象中拷贝过来的
public interface Cloneable {
}
空接口:标记当前类可以被克隆,也称为 标记接口
克隆标记本身没有任何抽象方法,
当一个类实现了Cloneable接口,就表示该类具备了克隆的能力(JVM)
在堆上开辟空间和对象创建都是有JVM操作的,
JVM会识别所有克隆标识的类, 赋予克隆的能力
1.1 对象的“拷贝” ,
需要先 实现 Cloneable 接口, 否则会抛出异常 CloceNotSupportedException
然后需要重写 Object 类中的 Clone 方法, 调用这个方法可创建一个对象 “拷贝”
1.2 覆写Object 类提供的clone方法
native: 本地方法,由C++实现(JVM由C++实现) ,
此处只是方法申明, 也没有方法体 ,并不是抽象方法。
// 1、需要实现 Cloneable
public class CloneableTest implements Cloneable {
public String name;
@Override
// 2、clone 方法需要 抛出异常 CloneNotSupportedException
protected CloneableTest clone() throws CloneNotSupportedException {
return (CloneableTest) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException{
CloneableTest c = new CloneableTest();
c.name = "I am test";
// c1 通过引用 (c 引用对象的拷贝) 新拷贝对象
// 创建一个新的引用对象,将属性值进行拷贝
CloneableTest c1 = c.clone();
System.out.println(c == c1);
System.out.println(c1.name);
System.out.println(c);
System.out.println(c1);
}
}
#console
false
I am test
CloneableTest@4554617c
CloneableTest@74a14482
此时我们会发现
首先会对 c 所引用的对象(CloneableTest@4554617c) 进行拷贝,
将 拷贝对象 (CloneableTest@74a14482)赋值给新的引用 c1
对象通过clone方法我们在堆上克隆了一个副本,并将这个副本对象赋给了一个新的引用,
并且当我们修改c1这个引用所指向对象中所包含的简单类型name的值时,c2这个引用所指向对象中所包
含的简单类型String的值并没有发生改变,那么这种情况我们便称之为**深拷贝**
深拷贝: 拷贝源对象 与 拷贝对象 完全脱离关系,互不干扰
即当拷贝结束后,通过一个新的引用修改所拷贝的新的对象的其中的某个类型的值时,
并不影响原来引用所对应的相同对象中的相同类型的值,那么此时便为深拷贝
1.3 Clonable接口结合浅拷贝
public class A {
int num;
}
class B implements Cloneable{
public A a = new A();
protected B clone() throws CloneNotSupportedException {
return (B) super.clone();
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException{
B b1 = new B();
b1.a.num = 10;
B b2 = b1.clone();
System.out.println("b1.a.num = "+ b1.a.num + ", b2.a.num = "+ b2.a.num );
System.out.println(b1 == b2);
System.out.println(b1);
System.out.println(b2);
b1.a.num = 20; // 修改 num值,克隆对象 属性a.num也随之改变
System.out.println("b1.a.num = "+ b1.a.num + ", b2.a.num = "+ b2.a.num );
}
}
#console
b1.a.num = 10, b2.a.num = 10
false
B@1540e19d
B@677327b6
b1.a.num = 20, b2.a.num = 20
C2 克隆对象,只是对 C1 克隆源对象做了简单的浅拷贝, 并未对 C1成员对象进行拷贝,
C1.a 与 C2.a 引用了相同的对象,当其中一个成员修改后,另一个成员也随之修改,
即 也应该 将 C1.a 所引用的对象实例也需要进行拷贝。这种现象,称之为浅拷贝。
浅拷贝: 拷贝源对象 与 拷贝对象 没有脱离关系,互相干扰
即其当一个新的引用去修改其克隆过来的对象中的某个类型(此段代码为引用类型)的值后,
原引用调用这个类型时便会发现这个值为新修改后的值了,并不是原来的值。
如何实现深拷贝
(1) 递归实现Cloneable , 重写 clone
(2) 通过序列化实现拷贝
public class A implements Cloneable{
int num;
protected A clone() throws CloneNotSupportedException {
return (A) super.clone();
}
}
class B implements Cloneable{
public A a = new A();
protected B clone() throws CloneNotSupportedException {
//克隆b所指向的对象
B b = (B) super.clone();
//克隆B 类中的另一个引用类型m所指向的对象
// this.a.clone() = A
b.a = this.a.clone();
return b;
}
}