副本中java.util.Date的行为

时间:2018-12-20 01:09:34

标签: java deep-copy shallow-copy java.util.date

我具有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个不同的日期。我的理解不正确?

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);

两个对象employee1employee2都具有引用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类可以完全控制自己的数据。