《Java核心技术 卷I》对象克隆
对象克隆
讨论Cloneable接口,指示一个类提供了一个安全的clone方法。
对象引用建立副本,任何一个变量改变都会影响另一个变量。
如果希望copy一个新对象,初始状态相同,但之后它们各自会有自己不同的状态,这种情况可以使用clone方法。
如果数据域都是数值或其他基本类型,拷贝这些域没有任何问题,但是包含子对象的引用,拷贝域就会得到相同对象的另一个引用,这样一来,原对象和克隆对象仍然会共享一些信息。
以下这种克隆就是浅拷贝,并没有克隆出生日期对象。如果原对象与浅拷贝对象共享子对象不可变,那共享就是安全的。
不过,通常子对象都是可变的,必须重新定义clone方法来建立一个深拷贝,同时克隆出所有子对象。
package corejava;import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;public class EmployeeTest {public static void main(String[] args) throws CloneNotSupportedException {Employee e = new Employee("孙悟空", 75000,1987,12,15);Employee e1 = (Employee) e.clone();System.out.println(e==e1);System.out.println(e.equals(e1));System.out.println(e.hashCode()==e1.hashCode());System.out.println(e.getHireDay()==e1.getHireDay());//结果false,true,false,true,总结来说就是浅拷贝,出生日期不该是同一对象。 }}
class Employee implements Comparable<Employee>,Cloneable {//实例字段private String name;private double salary;private Date hireDay;int percent = 10;//构造器public Employee(String n,double s,int year,int month, int day) {name = n;salary = s;GregorianCalendar calendar = new GregorianCalendar(year,month-1,day);hireDay = calendar.getTime();}public Employee() {}//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 void raiseSalary(Employee e) {e.percent = 15;double raise = salary * e.percent /100;salary += raise;}@Overridepublic boolean equals(Object otherObject) {super.equals(otherObject);
// if(this==otherObject) return true;if(otherObject==null) return false;if(getClass()!=otherObject.getClass()) return false;Employee other = (Employee) otherObject;return this.name.equals(other.name)&&this.salary==other.salary&&Objects.equals(this.hireDay, other.hireDay);}@Overridepublic int compareTo(Employee o) {return Double.compare(getSalary(), o.getSalary());}@Overrideprotected Object clone() throws CloneNotSupportedException {// TODO Auto-generated method stubreturn super.clone();}
}
对于每一个类,需要确定:
- 默认的clone方法是否满足要求。
- 是否可以在可变的子对象上调用clone来修补默认的clone方法。
- 是否不该使用clone。
实际上第3个选项是默认选项,如果选择第1项或第2项,类必须:
- 实现Cloneable接口。
- 重新定义clone方法,并指定public访问修饰符。
不实现接口,无法克隆,会报错,可以把克隆接口看成一个标识,当前类所实现的接口数组中包括克隆接口,那么虚拟机执行克隆动作。
注释:Cloneable接口是Java提供的一组标记接口(tagging interface)之一,也称为记号接口(marker interface)。它不包含任何方法,唯一作用就是允许类型查询中使用instanceof,建议你自己的程序中不要使用标记接口。
上面代码改为深拷贝的案例:
//深拷贝public Employee clone() throws CloneNotSupportedException{Employee cloned = (Employee) super.clone();cloned.hireDay = (Date) hireDay.clone();return cloned;}
所有数组都有clone方法用来建立一个新数组。