克隆的正常使用
.clone() 方法是 Object 类的一个 protected 方法
protected native Object clone() throws CloneNotSupportedException;
也就是说子类无法直接继承 Object 的 clone() 方法,只能在子类中通过 super 关键字来调用
而且 clone() 返回的数据类型是 Object ,但我们通常想直接克隆出一个同类型的新对象.
因此,我们需要自己在子类中自己声明一个 clone() 方法,在方法中用 super 关键字调用Object.clone()
然后在强转成想用类型的对象返回,完成一次克隆
// Person 类
static class Person {
private Pocket pocket; // 包含一个引用对象
/** 声明一个克隆方法 */
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
// Pocket 类
static class Pocket {
private int mony;
}
尝试克隆
public static void main(String[] args) {
Person person1 = new Person();
Pocket pocket = new Pocket();
pocket.mony = 100;
person1.pocket = pocket;
Person person2 = null;
try {
person2 = person1.clone(); // 克隆
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
执行结果:
原因是少了一个步骤,要使用 super.clone() ,必须要实现一个标记接口 Cloneable,表示这个类是可以克隆的,所以我们修改一下
// Person 类
static class Person implements Cloneable {
private Pocket pocket; // 包含一个引用对象
/** 声明一个克隆方法 */
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
// Pocket 类
static class Pocket {
private int mony;
}
再次执行就没问题啦
克隆是浅拷贝
上面的案例中 Person 对象中包含一个 Pocket 类型的引用
我们试试用 person1 克隆出 person2 后修改 person1 引用对象的 mony 属性,看看 person2 的 mony 有没有变化
public static void main(String[] args) {
Person person1 = new Person();
Pocket pocket = new Pocket();
pocket.mony = 100;
person1.pocket = pocket;
Person person2 = null;
try {
person2 = person1.clone(); // 克隆
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println("修改前person1.pocket.mony = " + person1.pocket.mony);
if (!ObjectUtils.isEmpty(person2)) {
System.out.println("修改前person2.pocket.mony = " + person2.pocket.mony);
}
person1.pocket.mony = 200; // 修改 person1 的 pocket 对象的 属性值
System.out.println("修改后person1.pocket.mony = " + person1.pocket.mony);
if (!ObjectUtils.isEmpty(person2)) {
System.out.println("修改后person2.pocket.mony = " + person2.pocket.mony);
}
}
结果
就是说克隆对于引用变量是拷贝了一份引用,而没有把引用指向的对象也给克隆一个新的对象再引用它
那么实现深克隆就要手动去把 Person 类的所有引用对象给克隆一份,可以之间new对象赋值,也可以让Pocket 对象也实现 Cloneable 接口用.clone() 方法克隆
static class Person implements Cloneable {
private Pocket pocket;
public Person clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
Pocket newPocket = new Pocket();
newPocket.mony = clone.pocket.mony;
clone.pocket = newPocket; // 引用新对象
return clone;
}
}
结果
这样表示克隆出来的就是一个船新的对象啦
小疑问
如果一个对象的引用链非常长非常深,那要深拷贝一个对象不是麻烦死?
要一层一层 new 对象赋值?
看来克隆只适合引用层比较浅的对象啊
我直接用 BeanUtils.copy() 不香吗?
啥?BeanUtils.copy()也是浅拷贝?
…