深度复制中java.util.Date的行为

深度复制中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

我有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.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
是不可变的。