1、两者区别:
首先对于一个对象中的基本数据类型,来做浅拷贝和深拷贝其实没有什么区别,都是将原始值复制一份给新的对象,但是对于对象中的引用数据类型来说,浅拷贝只是将引用数据类型的地址值复制一份给新的对象,这样就会导致新对象和旧的对象之间共用一个引用数据,但是深拷贝是直接开辟一个新的内存地址,这样深拷贝的数据和原来的数据是八竿子打不着的!
2、浅拷贝方式
在Java中实现浅拷贝通常有几种方法,其中最常见的是使用clone()
方法和拷贝构造函数。下面是使用这两种方法来实现浅拷贝的示例:
- 使用
clone()
方法:
class MyClass implements Cloneable {private int number;private String text;public MyClass(int number, String text) {this.number = number;this.text = text;}// 实现浅拷贝@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Main {public static void main(String[] args) {MyClass original = new MyClass(10, "Hello");try {MyClass copy = (MyClass) original.clone();System.out.println("Original: " + original.getNumber() + ", " + original.getText());System.out.println("Copy: " + copy.getNumber() + ", " + copy.getText());} catch (CloneNotSupportedException e) {e.printStackTrace();}}
}
- 使用拷贝构造函数:
class MyClass {private int number;private String text;public MyClass(int number, String text) {this.number = number;this.text = text;}// 拷贝构造函数public MyClass(MyClass other) {this.number = other.number;this.text = other.text;}public int getNumber() {return number;}public String getText() {return text;}
}public class Main {public static void main(String[] args) {MyClass original = new MyClass(10, "Hello");MyClass copy = new MyClass(original);System.out.println("Original: " + original.getNumber() + ", " + original.getText());System.out.println("Copy: " + copy.getNumber() + ", " + copy.getText());}
}
这两种方法都可以实现浅拷贝,但要注意的是,浅拷贝只会复制对象本身及其所有字段的值,如果对象包含引用类型的字段,则复制的是引用,而不是引用指向的对象。
3、深拷贝方式
深拷贝的实现方式有多种,具体选择哪种方式取决于对象的结构和复杂度。以下是几种常见的实现方式:
-
递归复制:
- 对于对象的每一层级,递归地复制其所有字段,包括引用类型字段指向的对象。
- 这需要确保所有被复制的对象都能被正确地复制,避免循环引用等问题。
-
序列化与反序列化:
- 将对象序列化成字节流,然后再反序列化为一个新的对象,这个过程中会创建一个新的对象图,其中的每个对象都是独立的。
- 这种方式需要确保对象及其所有引用类型字段都是可序列化的。
-
手动实现复制构造函数或复制方法:
- 在对象的类中手动实现一个复制构造函数或者复制方法,用于创建一个新的对象并复制所有字段。
- 这需要确保所有字段都能正确地复制,并且需要考虑到对象图的复杂性。
-
使用第三方库:
- 有一些第三方库提供了深拷贝的功能,例如Apache Commons Lang库中的
SerializationUtils.clone()
方法或者Google Gson库等。
- 有一些第三方库提供了深拷贝的功能,例如Apache Commons Lang库中的
这些方法各有优缺点,选择合适的实现方式取决于具体的情况和需求。例如,如果对象结构相对简单并且没有循环引用,递归复制可能是一种简单有效的方式。如果对象结构复杂或者存在循环引用,可能需要使用序列化与反序列化或者手动实现复制方法来确保深拷贝的正确性。
递归复用:
import java.util.ArrayList;
import java.util.List;class Person {private String name; // 人员姓名private int age; // 人员年龄private List<String> hobbies; // 人员爱好列表// 构造函数,初始化姓名、年龄和爱好列表public Person(String name, int age, List<String> hobbies) {this.name = name;this.age = age;this.hobbies = hobbies;}// 深拷贝的拷贝构造函数public Person(Person other) {this.name = other.name;this.age = other.age;// 创建一个新的ArrayList并复制所有元素this.hobbies = new ArrayList<>(other.hobbies);}// Getter和Setter方法...
}public class DeepCopyExample {public static void main(String[] args) {List<String> hobbies = new ArrayList<>(); // 创建一个爱好列表hobbies.add("阅读"); // 添加爱好hobbies.add("园艺");Person originalPerson = new Person("Alice", 30, hobbies); // 创建原始的Person对象// 使用拷贝构造函数创建深拷贝Person copiedPerson = new Person(originalPerson);// 修改拷贝的Person对象中的爱好列表copiedPerson.getHobbies().add("烹饪");System.out.println("原始Person对象: " + originalPerson);System.out.println("拷贝的Person对象: " + copiedPerson);}
}
序列化和反序列化:
import java.io.*;// 定义一个可序列化的类
class Person implements Serializable {private String name; // 姓名private int age; // 年龄private List<String> hobbies; // 爱好列表// 构造函数,初始化姓名、年龄和爱好列表public Person(String name, int age, List<String> hobbies) {this.name = name;this.age = age;this.hobbies = hobbies;}// 序列化方法public byte[] serialize() throws IOException {ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream out = new ObjectOutputStream(bos);out.writeObject(this);out.close();return bos.toByteArray();}// 反序列化方法public static Person deserialize(byte[] data) throws IOException, ClassNotFoundException {ByteArrayInputStream bis = new ByteArrayInputStream(data);ObjectInputStream in = new ObjectInputStream(bis);Person person = (Person) in.readObject();in.close();return person;}// Getter和Setter方法...// toString方法...
}public class DeepCopySerializationExample {public static void main(String[] args) throws IOException, ClassNotFoundException {List<String> hobbies = new ArrayList<>(); // 创建一个爱好列表hobbies.add("阅读"); // 添加爱好hobbies.add("园艺");Person originalPerson = new Person("Alice", 30, hobbies); // 创建原始的Person对象// 使用序列化和反序列化进行深拷贝byte[] serializedData = originalPerson.serialize();Person copiedPerson = Person.deserialize(serializedData);// 修改拷贝的Person对象中的爱好列表copiedPerson.getHobbies().add("烹饪");// 打印原始Person对象和拷贝的Person对象System.out.println("原始Person对象: " + originalPerson);System.out.println("拷贝的Person对象: " + copiedPerson);}
}