clone方法是Object类的一个protected方法,自己编写的类中无法直接调用object中的clone方法;
如果对象中所有数据域都是基本类型(8种)或者不可以变对象(String),直接克隆域没有任何问题。但是,如果对象中包含了子对象的引用,拷贝的结构会使得新拷贝的对象与原对象引用同一个域(例如下面的orginal和copy引用同一个Date),因此原始对象与克隆对象共享这部分信息,当克隆的对象改变域值时,原始对象的域值也会跟着改变。对于浅克隆来说,刚进行克隆的时候,对与String,确实引用的是同一个对象,但由于String是不可变对象,当值发生改变后,浅克隆将不在引用同一个对象。
如下图:original 和 copy 里面的Date 和String 浅克隆后会引用同一个对象,但是由于String是不可变对象,所以当两个对象其中一个的name属性改变后将不在指向同一个对象String;由于浅克隆只会克隆外层对象Employee,不会对 对象中的可变子对象(Date)进行克隆。所以浅克隆后,Date是指向同一个域;
所以说使用克隆时,一定要注意,被克隆的对象是否有可变子对象,如果有,一定要确认可变子对象是否支持clone也就是说是否实现了Cloneable 这个接口,如果不支持克隆,这个克隆操作将带来问题。
深克隆就是将可变子对象也进行clone
默认的克隆方式是浅克隆,它并没有克隆包含在对象中的内部对象,如果使用浅克隆你会发现克隆后的对象与原对象会引用同一个子对象(域)。
对于克隆来说,更多面对的是子对象是可变的,而不是String这种;
所以对于每一个类都要做出以下判断:
- 默认的clone方法是否满足要求;
- 默认的clone方法是否可以通过调用可变的子对象的clone方法得到修补;
- 是否不应该使用clone;
实际上,选项3是默认的,如果选择1或者2,类必须:
1、实现Cloneable接口,此接口只是个标记接口,里面没有任何方法,意在告诉类设计者要进行clone处理,使用它的唯一目的是可以用instanceof进行类型检查;
2、使用public访问修饰符重新定义clone方法;
注意:
必须谨慎的在父类中设置clone方法,因为一旦父类中有了clone的方法(一定是public修饰,因为object中是protected修饰),所有子类将会继承父类中的方法,这就导致所有子类具有克隆功能,如果子类域(属性)只有基本类型或者不可变对象类型,这将不会有什么问题,不过如果子类中含有可变对象,比如说Date,那子类必须重写父类clone方法,使父子不引用同一个对象,这也就是自己写的类不能直接调用object中clone方法的原因,因为,自己的定义的类极有可能包含可变域对象。
如果子类的可变域,是继承过来的,只要父类对这个域进行clone,子类将不会出现问题;
package extendDemo;
import java.util.Date;
public class ManagerTest {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date(2018, 9, 10);
Employee employee = new Employee("xiaoo", 19.0, date);
Employee clone = (Employee) employee.clone();
clone.getHireDay().setDate(2019);
System.out.println(employee.getName() == clone.getName());
System.out.println(employee.getHireDay() == clone.getHireDay());
clone.setName("qqq");
System.out.println(employee.getName() == clone.getName());
Manager manager = new Manager("xiaoxiao", 12.0, new Date());
Manager cloneM = (Manager) manager.clone();
System.out.println(cloneM.getHireDay() == manager.getHireDay());
}
}
class Employee implements Cloneable {
private String name;
private double salary;
private Date hireDay;
public Employee(String name, double salary, Date hireDay) {
this.name = name;
this.salary = salary;
this.hireDay = hireDay;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Date getHireDay() {
return hireDay;
}
public void setHireDay(Date hireDay) {
this.hireDay = hireDay;
}
public Object clone() throws CloneNotSupportedException {
// 深克隆,就是克隆所有可变域
Employee clone = (Employee) super.clone();
clone.hireDay = (Date) hireDay.clone();
return clone;
// 浅克隆
// return (Employee)super.clone();
}
@Override
public String toString() {
return "Employee [name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]";
}
}
class Manager extends Employee {
public Manager(String name, double salary, Date hireDay) {
super(name, salary, hireDay);
}
private double bonus;
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
}
result
==================
true
false
false
false
==================