Java中常见的创建对象方式有5种。其中包括了克隆和通过对象序列化(Serialization)两种方式。
这两者的差别就有一个,克隆是浅拷贝,对象序列化是深拷贝。
那么深拷贝和浅拷贝到底是什么呢:
浅拷贝(Shallow Copy):浅拷贝会创建一个新对象,该对象的字段将是原始对象字段的副本。如果字段本身是引用类型,则引用字段仍然会共享原始对象和副本对象之间的相同引用。浅拷贝只是复制对象的引用,而不是创建对象的副本。?
深拷贝(Deep Copy):深拷贝将创建一个新对象,并且复制对象的所有字段,包括引用字段。新对象和原始对象之间的引用是完全独立的。深拷贝会递归复制对象的所有引用类型字段及其引用的对象。
浅拷贝时,对于原始对象中的引用类型字段,克隆对象会共享相同的引用。也就是说,如果你修改了user1
中引用类型字段的值,这个修改也会反映在user2
中。这可能会导致一些数据不一致的问题。
class User implements Cloneable {
? ?private String username;
?
? ?public User(String username) {
? ? ? ?this.username = username;
? }
?
//get、set
?
? ?@Override
? ?protected Object clone() throws CloneNotSupportedException {
? ? ? ?return super.clone();
? }
}
?
public class Main {
? ?public static void main(String[] args) throws CloneNotSupportedException {
? ? ? ?User user1 = new User("Alice");
?
? ? ? ?// 使用浅拷贝克隆对象
? ? ? ?User user2 = (User) user1.clone();
?
? ? ? ?System.out.println(user1.getUsername()); ?// 输出: Alice
? ? ? ?System.out.println(user2.getUsername()); ?// 输出: Alice
?
? ? ? ?user1.setUsername("Bob");
?
? ? ? ?System.out.println(user1.getUsername()); ?// 输出: Bob
? ? ? ?System.out.println(user2.getUsername()); ?// 输出: Bob (引用字段被共享)
? }
}
在上面的代码中我们可以看到,当user1
的username
属性值被改了之后,user2
的username
属性值也发生了改变,而这是因为user1
和user2
共享了相同的引用。
另外对于基本数据类型(如int
、boolean
、double
等),在浅拷贝中是不存在共享引用的问题的。因为基本数据类型直接存储的是值,而不是引用。
当使用clone()
进行浅拷贝时,基本数据类型的值会被复制到新的对象中,而新对象和原始对象之间是相互独立的。因此,如果你修改原始对象中的基本数据类型的值,不会影响到克隆对象。
还是来段代码示例来说明这个问题:
class Data implements Cloneable {
? ?private int value;
?
? ?public Data(int value) {
? ? ? ?this.value = value;
? }
?
? ?// get、set
?
? ?@Override
? ?protected Object clone() throws CloneNotSupportedException {
? ? ? ?return super.clone();
? }
}
?
public class Main {
? ?public static void main(String[] args) throws CloneNotSupportedException {
? ? ? ?Data data1 = new Data(10);
?
? ? ? ?// 使用浅拷贝克隆对象
? ? ? ?Data data2 = (Data) data1.clone();
?
? ? ? ?System.out.println(data1.getValue()); ?// 输出: 10
? ? ? ?System.out.println(data2.getValue()); ?// 输出: 10
?
? ? ? ?// 修改原始对象的值
? ? ? ?data1.setValue(20);
?
? ? ? ?System.out.println(data1.getValue()); ?// 输出: 20
? ? ? ?System.out.println(data2.getValue()); ?// 输出: 10 (没有共享引用)
? }
}