Java中String与Date对象的深拷贝差异及Date深拷贝实现
解答你的Java对象拷贝问题
1. Java中String对象与Date对象的深拷贝差异
咱们得从不可变性和可变性这个核心点说起,这是两者拷贝逻辑差异的根源:
- String对象:String是Java的不可变类,一旦创建就没法修改内部的字符序列。不管是直接赋值
String s2 = s1还是用其他方式“拷贝”,本质都是共享同一个对象引用,但因为String不可变,你永远没法通过原引用修改对象内容——这种引用共享的表现和深拷贝几乎一样,但实际上并没有创建新的String对象(除非你主动调用new String(s1),但这完全没必要,不可变性已经保证了引用共享的安全性)。 - Date对象:Date是可变类,内部持有一个
long类型的时间戳,还提供了setTime()这类修改方法。浅拷贝(比如直接赋值Date d2 = d1)只会复制引用,修改原Date的时间戳会直接影响拷贝后的对象;而深拷贝必须创建全新的Date实例,让两个对象各自持有独立的时间戳,修改其中一个不会波及另一个。
2. 为学生类的birthDate创建深拷贝,以及与其他属性的差异
假设你的学生类结构大致是这样:
class Student { private String name; private int age; private Date birthDate; // 构造器、getter、setter省略 }
实现birthDate的深拷贝
有两种常用的可靠方式:
- 方式一:利用Date自带的
clone()方法(Date已经实现了Cloneable接口):// 在学生类的拷贝逻辑中 Date clonedBirthDate = (Date) this.birthDate.clone(); - 方式二:通过Date构造器,基于原对象的时间戳创建新实例:
Date clonedBirthDate = new Date(this.birthDate.getTime());
与name、age的拷贝差异
- age(数值类型):int是基本数据类型,拷贝时直接复制值,不存在引用共享的问题——修改原对象的age,拷贝后的对象age完全不受影响,这本身就是“深拷贝”的效果,不需要额外处理。
- name(String类型):因为String不可变,直接赋值
clonedName = this.name就足够安全。就算原对象后续重新给name赋值(比如student.setName("NewName")),拷贝对象的name依然指向原来的String实例,不会被修改——这是因为赋值只是让原对象的name引用指向了新的String,而非修改原有String的内容。 - birthDate(Date类型):Date是可变对象,必须主动创建新实例才能实现深拷贝,否则浅拷贝会导致原对象和拷贝对象共享同一个Date引用,修改其中一个的时间会直接影响另一个。
3. 你的克隆示例是否属于深拷贝?
仅从你给出的输出:
OUTPUT Original Date:Mon Jan 05 00:00:00 IST 2018 Cloned Date:Mon Jan 05 00:00:00 IST 2018 – adding additional Info...
没法直接判定是不是深拷贝,关键要看修改原Date对象后,克隆对象的时间是否会跟着变化:
- 如果修改原Date的时间(比如调用
originalDate.setTime(...)),克隆的Date依然保持Mon Jan 05的时间,那就是深拷贝; - 如果克隆的Date时间也变成了修改后的数值,那说明只是浅拷贝(只是复制了引用,两个变量指向同一个Date对象)。
举个直观的例子,如果你的克隆代码是下面这样,那就是深拷贝:
Date original = new Date(1515090600000L); // 对应Mon Jan 05 2018 Date cloned = (Date) original.clone(); original.setTime(1515177000000L); // 修改为Tue Jan 06 2018 System.out.println("Original Date:" + original); System.out.println("Cloned Date:" + cloned); // 此时克隆对象的输出还是Mon Jan 05,说明是深拷贝
如果你的代码只是Date cloned = original;,那修改原对象后,克隆对象的时间也会变成Tue Jan 06,这就是浅拷贝。
4. 将克隆代码整合进自定义类
要给自定义的Student类实现完整的深拷贝,常规做法是让类实现Cloneable标记接口,并重写clone()方法:
class Student implements Cloneable { private String name; private int age; private Date birthDate; // 构造器、getter、setter @Override protected Object clone() throws CloneNotSupportedException { // 先调用父类clone()完成基本类型和不可变类型的拷贝 Student clonedStudent = (Student) super.clone(); // 对可变的Date对象单独做深拷贝 clonedStudent.birthDate = (Date) this.birthDate.clone(); // 也可以用构造器方式:clonedStudent.birthDate = new Date(this.birthDate.getTime()); return clonedStudent; } }
使用时可以这样调用:
try { Student originalStudent = new Student("Alice", 20, new Date()); Student clonedStudent = (Student) originalStudent.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); }
注意几点:
- 必须重写
clone()方法,否则默认调用Object的clone()只会做浅拷贝; Cloneable是标记接口,没有任何方法,只是告诉JVM这个类允许被克隆;- 如果你不想让调用者处理
CloneNotSupportedException,也可以封装一个自定义的copy()方法,在内部捕获异常,让调用更友好。
内容的提问来源于stack exchange,提问作者Vinod
相关产品推荐
相关产品推荐

