我具有Windows 7 Professional 64位操作系统,并且正在使用JDK7。
我有一个Employee类,它支持通过构造函数进行复制:
l2 | dog | fish
l1 | a b c | d e f
--------------------------------
0 | 1 1 1 | 1 1 1
1 | 1 1 1 | 1 1 1
2 | 1 1 1 | 1 1 1
首先要测试,我创建一个employee1对象:
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
}
然后将其克隆到employee2对象;
Employee employee1 = new Employee();
employee1.setId(1);
employee1.setName("John");
employee1.setHireDate(new GregorianCalendar(2018, Calendar.DECEMBER, 19).getTime());
现在,我被告知只有原语和不可变元素不需要深度复制,并且由于 Employee employee2 = new Employee(employee1);
既不是原语也不是不可变的,所以我一直认为它将用作参考复制,因此如果我更改它在一个对象中也会在第二个对象中自动更改。
所以我在第二个对象中更改它:
java.util.Date
但是当我将它们全部打印出来时:
employee2.setHireDate(new GregorianCalendar(2017, Calendar.FEBRUARY, 5).getTime())
我看到2个不同的日期。我的理解不正确?
答案 0 :(得分:1)
尝试了解内部原理。
Employee employee1 = new Employee();
employee1.setId(1);
employee1.setName("John");
employee1.setHireDate(new GregorianCalendar(2018, Calendar.DECEMBER, 19).getTime());
执行此操作时,您有一个雇员对象employee1
,其引用为hireDate
,该引用指向包含日期(19/12/2018)的Date
对象。 / p>
现在,当您这样做时:
Employee employee2 = new Employee(employee1);
两个对象employee1
和employee2
都具有引用hireDate
,它们指向包含日期(2018年12月19日)的Date
对象。
现在,了解这一点:
employee2.setHireDate(new GregorianCalendar(2017, Calendar.FEBRUARY, 5).getTime());
执行此操作时,Date
将创建一个新的new GregorianCalendar(2017, Calendar.FEBRUARY, 5).getTime()
对象,现在,对象hireDate
的{{1}}引用指向此employee2
对象。不是上一个。
这意味着Date
对象hireDate
的引用指向包含日期(19/12/2018)的employee1
对象,而Date
对象hireDate
的引用指向到包含日期(5/2/2017)的employee2
对象。
这就是为什么您得到不同的日期。 希望这会有所帮助。
答案 1 :(得分:1)
您要将每个员工的雇用日期更改为新日期对象,因此这两个值是独立的是很有意义的。
问题在于,调用复制构造函数后,它们都共享相同的Date实例,该实例是可变的(通过其setTime method)。如果通过调用其setTime方法更改了Date对象本身(而不是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类可以完全控制自己的数据。