Java中实现深拷贝和浅拷贝的方法主要有使用clone()方法、通过构造函数拷贝、使用序列化、使用第三方库等。其中,使用clone()方法是一种简单且常见的方式。利用Java中的Cloneable
接口和clone()
方法,我们可以实现对象的浅拷贝。浅拷贝只复制对象的基本数据类型字段,而不复制引用对象,这意味着浅拷贝后的对象与原对象共享同一个引用对象。对于深拷贝,我们可以通过手动复制对象的所有字段,包括引用对象,或者使用序列化机制来实现。序列化机制将对象转化为字节流,再从字节流中恢复对象,从而实现深拷贝。接下来,我们将详细探讨这些方法。
一、使用clone()方法
1.1、浅拷贝
浅拷贝是指对象的字段是逐字段复制的,对于字段中引用的对象,只复制引用,不复制引用对象本身。实现浅拷贝的步骤如下:
class Person implements Cloneable {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class Main {
public static void main(String[] args) {
try {
Person person1 = new Person("John", 25);
Person person2 = (Person) person1.clone();
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
person2.name = "Doe";
person2.age = 30;
System.out.println("After modifying person2:");
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
在这个例子中,修改person2
的字段不会影响person1
,因为它们的字段是独立的,但如果对象中包含引用类型,则引用类型的字段将共享。
1.2、深拷贝
深拷贝是指不仅复制对象的基本数据类型字段,还要递归复制对象中所有引用的对象,以确保拷贝的对象与原对象完全独立。实现深拷贝的方法如下:
class Address implements Cloneable {
String city;
public Address(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Address{city='" + city + "'}";
}
}
class Person implements Cloneable {
String name;
int age;
Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) address.clone();
return cloned;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}
public class Main {
public static void main(String[] args) {
try {
Address address = new Address("New York");
Person person1 = new Person("John", 25, address);
Person person2 = (Person) person1.clone();
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
person2.name = "Doe";
person2.age = 30;
person2.address.city = "Los Angeles";
System.out.println("After modifying person2:");
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
在这个例子中,修改person2
的address
字段不会影响person1
,因为address
对象也被单独克隆了。
二、通过构造函数拷贝
2.1、浅拷贝
通过构造函数拷贝是一种手动实现浅拷贝的方法。它通过在新对象的构造函数中逐字段复制旧对象的字段来实现。
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(Person other) {
this.name = other.name;
this.age = other.age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("John", 25);
Person person2 = new Person(person1);
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
person2.name = "Doe";
person2.age = 30;
System.out.println("After modifying person2:");
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
}
}
2.2、深拷贝
通过构造函数拷贝实现深拷贝,需要确保所有引用类型的字段也被单独复制。
class Address {
String city;
public Address(String city) {
this.city = city;
}
public Address(Address other) {
this.city = other.city;
}
@Override
public String toString() {
return "Address{city='" + city + "'}";
}
}
class Person {
String name;
int age;
Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
public Person(Person other) {
this.name = other.name;
this.age = other.age;
this.address = new Address(other.address);
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}
public class Main {
public static void main(String[] args) {
Address address = new Address("New York");
Person person1 = new Person("John", 25, address);
Person person2 = new Person(person1);
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
person2.name = "Doe";
person2.age = 30;
person2.address.city = "Los Angeles";
System.out.println("After modifying person2:");
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
}
}
三、使用序列化
3.1、深拷贝
使用序列化来实现深拷贝是非常可靠的一种方法,因为它会将对象的整个状态进行复制,包括所有引用的对象。
import java.io.*;
class Address implements Serializable {
String city;
public Address(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{city='" + city + "'}";
}
}
class Person implements Serializable {
String name;
int age;
Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
public Person deepClone() {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (Person) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}
public class Main {
public static void main(String[] args) {
Address address = new Address("New York");
Person person1 = new Person("John", 25, address);
Person person2 = person1.deepClone();
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
person2.name = "Doe";
person2.age = 30;
person2.address.city = "Los Angeles";
System.out.println("After modifying person2:");
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
}
}
四、使用第三方库
4.1、Apache Commons Lang
Apache Commons Lang提供了一个SerializationUtils
类,可以方便地实现对象的深拷贝。
import org.apache.commons.lang3.SerializationUtils;
class Address implements Serializable {
String city;
public Address(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{city='" + city + "'}";
}
}
class Person implements Serializable {
String name;
int age;
Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}
public class Main {
public static void main(String[] args) {
Address address = new Address("New York");
Person person1 = new Person("John", 25, address);
Person person2 = SerializationUtils.clone(person1);
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
person2.name = "Doe";
person2.age = 30;
person2.address.city = "Los Angeles";
System.out.println("After modifying person2:");
System.out.println("Person 1: " + person1);
System.out.println("Person 2: " + person2);
}
}
通过这种方式,您不需要手动编写序列化和反序列化的代码,只需调用SerializationUtils.clone()
方法即可实现对象的深拷贝。
总结
深拷贝和浅拷贝在Java中有多种实现方式,选择哪种方式取决于具体的需求和对象的复杂性。使用clone()方法适用于简单对象的浅拷贝和深拷贝,通过构造函数拷贝也能实现浅拷贝和深拷贝,但需要手动编写代码来复制对象的每个字段。使用序列化和第三方库如Apache Commons Lang提供了更简洁和通用的深拷贝方法,适用于需要复制复杂对象的场景。无论使用哪种方法,都需要仔细考虑对象的结构和拷贝的深度,以确保拷贝后的对象能够正确地独立于原对象。
相关问答FAQs:
1. 什么是深拷贝和浅拷贝?
深拷贝和浅拷贝是在Java中用于复制对象的两种不同方式。浅拷贝仅仅复制对象的引用,而深拷贝会创建一个全新的对象,同时复制对象的所有属性。
2. 如何实现浅拷贝?
要实现浅拷贝,可以使用Java中的clone()方法。该方法会创建一个新的对象,并将原始对象的属性值复制到新对象中。但是需要注意的是,clone()方法只是复制了对象的引用,而不是对象本身。
3. 如何实现深拷贝?
要实现深拷贝,可以使用以下几种方式:
- 使用序列化和反序列化:将对象序列化为字节流,然后再反序列化为一个新的对象。这种方法会创建一个全新的对象,并复制对象的所有属性。
- 递归复制对象:遍历对象的每一个属性,如果属性是引用类型,则递归复制该属性。这样可以确保所有属性都是全新的对象。
无论是浅拷贝还是深拷贝,都要根据实际需求来选择适合的方法。如果只需要复制对象的引用,浅拷贝就可以满足需求;如果需要复制对象的所有属性,并确保每个属性都是全新的对象,就需要使用深拷贝。
原创文章,作者:Edit1,如若转载,请注明出处:https://docs.pingcode.com/baike/223052