深度复制中java.util.Date的行为
我有Windows 7 Professional 64位操作系统,我正在使用JDK 7 我有一个支持通过构造函数复制的Employee类:深度复制中java.util.Date的行为,java,deep-copy,shallow-copy,java.util.date,Java,Deep Copy,Shallow Copy,Java.util.date,我有Windows 7 Professional 64位操作系统,我正在使用JDK 7 我有一个支持通过构造函数复制的Employee类: public class Employee { private int id; private String name; private java.util.Date hireDate; public Employee() { } public Employee(Employee e) { this.i
public class Employee {
private int id;
private String name;
private java.util.Date hireDate;
public Employee() {
}
public Employee(Employee e) {
this.id = e.id;
this.name = e.name;
this.hireDate = e.hireDate;
}
// getters and setters
}
要首先进行测试,请创建employee1对象:
Employee employee1 = new Employee();
employee1.setId(1);
employee1.setName("John");
employee1.setHireDate(new GregorianCalendar(2018, Calendar.DECEMBER, 19).getTime());
然后我将其克隆到employee2对象
Employee employee2 = new Employee(employee1);
现在我被告知,只有原语和不可变项不需要深度复制,因为java.util.Date
既不是原语也不是不可变项,所以我认为它将充当引用复制,所以如果我在一个对象中更改它,它也会在第二个对象中自动更改
所以我在第二个对象中更改它:
employee2.setHireDate(new GregorianCalendar(2017, Calendar.FEBRUARY, 5).getTime())
但当我把它们都打印出来时:
System.out.println("employee1.getHireDate()=" + employee1.getHireDate());
System.out.println("employee2.getHireDate()=" + employee2.getHireDate());
我看了两次不同的约会。我理解错误的地方是什么?试着理解内部结构
Employee employee1 = new Employee();
employee1.setId(1);
employee1.setName("John");
employee1.setHireDate(new GregorianCalendar(2018, Calendar.DECEMBER, 19).getTime());
执行此操作时,您有一个employee对象,employee1
,其中有一个引用hireDate
,该引用指向包含日期的日期
对象(2018年12月19日)
现在,当你这么做的时候:
Employee employee2 = new Employee(employee1);
两个对象employee1
和employee2
都有参考hireDate
,它们指向包含日期的对象(2018年12月19日)
现在,请理解这一点:
employee2.setHireDate(new GregorianCalendar(2017, Calendar.FEBRUARY, 5).getTime());
执行此操作时,一个新的Date
对象由new gregorianalendar(2017年,日历2月5日)创建。getTime()
现在,hireDate
对象的引用employee2
指向此Date
对象。不是前一个
这意味着hireDate
对象参考employee1
指向Date
包含日期的对象(2018年12月19日),而hireDate
对象参考employee2
指向Date
包含日期的对象(2017年2月5日)
这就是为什么你会有不同的约会。
希望这能有所帮助。试着理解内部结构
Employee employee1 = new Employee();
employee1.setId(1);
employee1.setName("John");
employee1.setHireDate(new GregorianCalendar(2018, Calendar.DECEMBER, 19).getTime());
执行此操作时,您有一个employee对象,employee1
,其中有一个引用hireDate
,该引用指向包含日期的日期
对象(2018年12月19日)
现在,当你这么做的时候:
Employee employee2 = new Employee(employee1);
两个对象employee1
和employee2
都有参考hireDate
,它们指向包含日期的对象(2018年12月19日)
现在,请理解这一点:
employee2.setHireDate(new GregorianCalendar(2017, Calendar.FEBRUARY, 5).getTime());
执行此操作时,一个新的Date
对象由new gregorianalendar(2017年,日历2月5日)创建。getTime()
现在,hireDate
对象的引用employee2
指向此Date
对象。不是前一个
这意味着hireDate
对象参考employee1
指向Date
包含日期的对象(2018年12月19日),而hireDate
对象参考employee2
指向Date
包含日期的对象(2017年2月5日)
这就是为什么你会有不同的约会。
希望这能有所帮助。您正在将每个员工的雇用日期更改为一个新的日期对象,因此这两个值是独立的是有意义的
问题是,在调用复制构造函数之后,它们都共享同一个日期实例,该实例是可变的(通过its)。如果通过调用日期对象的setTime方法更改了日期对象本身(而不是Employee属性),则可以看到效果:
Employee employee1 = new Employee();
employee1.setId(1);
employee1.setName("John");
employee1.setHireDate(new GregorianCalendar(2018, Calendar.DECEMBER, 19).getTime());
Employee employee2 = new Employee(employee1);
// Change the state of the Date object shared by both instances.
employee2.getHireDate().setTime(
new GregorianCalendar(2018, Calendar.JANUARY, 19).getTimeInMillis());
System.out.println("employee1.getHireDate()=" + employee1.getHireDate());
System.out.println("employee2.getHireDate()=" + employee2.getHireDate());
解决方案是执行日期的防御性复制:
public Employee(Employee e) {
this.id = e.id;
this.name = e.name;
this.hireDate = (e.hireDate != null ? (Date) e.hireDate.clone() : null);
}
另外,getter和setter方法应该做同样的事情:
public Date getHireDate() {
return hireDate != null ? (Date) hireDate.clone() : null);
}
public void setHireDate(Date newDate) {
this.hireDate = (newDate != null ? (Date) newDate.clone() : null);
}
这样,除非调用setHireDate方法(或使用反射,但这是一个单独的问题),否则无法更改雇用日期。Employee类可以完全控制自己的数据。您正在将每个员工的雇用日期更改为新的日期对象,因此这两个值是独立的是有意义的
问题是,在调用复制构造函数之后,它们都共享同一个日期实例,该实例是可变的(通过its)。如果通过调用日期对象的setTime方法更改了日期对象本身(而不是Employee属性),则可以看到效果:
Employee employee1 = new Employee();
employee1.setId(1);
employee1.setName("John");
employee1.setHireDate(new GregorianCalendar(2018, Calendar.DECEMBER, 19).getTime());
Employee employee2 = new Employee(employee1);
// Change the state of the Date object shared by both instances.
employee2.getHireDate().setTime(
new GregorianCalendar(2018, Calendar.JANUARY, 19).getTimeInMillis());
System.out.println("employee1.getHireDate()=" + employee1.getHireDate());
System.out.println("employee2.getHireDate()=" + employee2.getHireDate());
解决方案是执行日期的防御性复制:
public Employee(Employee e) {
this.id = e.id;
this.name = e.name;
this.hireDate = (e.hireDate != null ? (Date) e.hireDate.clone() : null);
}
另外,getter和setter方法应该做同样的事情:
public Date getHireDate() {
return hireDate != null ? (Date) hireDate.clone() : null);
}
public void setHireDate(Date newDate) {
this.hireDate = (newDate != null ? (Date) newDate.clone() : null);
}
这样,除非调用setHireDate方法(或使用反射,但这是一个单独的问题),否则无法更改雇用日期。Employee类可以完全控制自己的数据。Date
是不可变的。Date
是不可变的。