Java中的拷贝
在Java中,深拷贝和浅拷贝主要用于对象的拷贝,即将一个对象复制给另一个对象,而引用拷贝一般用于对象的赋值和参数传递。
三者的对比图
浅拷贝
浅拷贝指只拷贝对象的引用,不拷贝对象本身。也就是说,如果对象中有引用类型的成员变量,那么在拷贝之后,引用类型的成员变量指向的对象仍然是原来的对象,如果修改拷贝对象中引用类型成员变量的值的时候,原对象中引用类型变量的值也会改变。
package com.thc.copy;
/**
* @author jacky_Tang
* @version 1.0
*/
public class ShallowCopy {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("Beijing", "Haidian");
Person person1 = new Person("Alice", 20, address);
// 浅拷贝两种实现方式
// 1.通过类的构造器
//Person person2 = new Person(person1);
// 2.通过实现Cloneable接口,重写clone方法
Person person2 = (Person) person1.clone();
System.out.println(person1.getAddress() == person2.getAddress()); // true
System.out.println("改变拷贝对象前person1:address:" + person1.getAddress());
Address address2 = person2.getAddress();
address2.setCity("Chongqing");
address2.setStreet("Yubei");
System.out.println("改变拷贝对象后person1:address:" + person1.getAddress());
}
}
class Person implements Cloneable{
private String name;
private int age;
private 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 = other.address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
class Address {
private String city;
private String street;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public Address(String city, String street) {
this.city = city;
this.street = street;
}
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
", street='" + street + '\'' +
'}';
}
}
打印结果:
浅拷贝的实现方式:
- 将要拷贝对象的类实现Cloneable接口,并且重写clone方法
- 通过类的构造器进行拷贝
深拷贝
深拷贝则是指在拷贝对象时,不仅复制对象的值,还要复制对象引用的对象,即复制对象的所有内容。这样,在拷贝得到的对象中修改引用类型成员变量的值时,原对象中的相应成员变量不会被修改。
package com.thc.copy;
import java.io.*;
/**
* @author jacky_Tang
* @version 1.0
*/
public class DeepCopy {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Hometown hometown = new Hometown("Beijing", "Haidian");
Student student1 = new Student("Alice", 20, hometown);
Student student2 = student1.deepCopy();
System.out.println(student1.getHometown() == student2.getHometown()); // false
System.out.println("改变拷贝对象前student1:hometown:" + student1.getHometown());
Hometown hometown1 = student2.getHometown();
hometown1.setCity("Chongqing");
hometown1.setStreet("Yubei");
System.out.println("改变拷贝对象后student1:hometown:" + student1.getHometown());
System.out.println("改变拷贝对象后student2:hometown:" + student2.getHometown());
}
}
class Student implements Serializable {
private String name;
private int age;
private Hometown hometown;
public Student(String name, int age, Hometown hometown) {
this.name = name;
this.age = age;
this.hometown = hometown;
}
public Student deepCopy() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Student) ois.readObject();
}
public Hometown getHometown() {
return hometown;
}
public void setHometown(Hometown hometown) {
this.hometown = hometown;
}
}
class Hometown implements Serializable {
private String city;
private String street;
public Hometown(String city, String street) {
this.city = city;
this.street = street;
}
@Override
public String toString() {
return "Hometown{" +
"city='" + city + '\'' +
", street='" + street + '\'' +
'}';
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
打印结果:
深拷贝的实现方式:
- 需要拷贝对象的类实现Serializable接口,使用序列化通过字节流的方式进行拷贝
- 递归拷贝,手动的进行拷贝
手动进行拷贝例子:
public 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 deepCopy() {
List<String> hobbiesCopy = new ArrayList<>(this.hobbies);
return new Person(this.name, this.age, hobbiesCopy);
}
// 省略getter和setter方法
}
引用拷贝
引用拷贝是指在拷贝对象时,只是复制对象的引用,而不是对象本身。这和浅拷贝是相同的,都是复制对象的引用,但是不同的是,引用拷贝通常只针对引用类型进行拷贝。
在Java中,对象的赋值和传参都是引用拷贝。也就是说,如果把一个对象赋值给另一个对象或者将一个对象作为参数传递给方法,实际上传递的是对象的引用,而不是对象本身。
package com.thc.copy;
/**
* @author jacky_Tang
* @version 1.0
*/
public class ReferenceCopy {
public static void main(String[] args) {
Animal animal1 = new Animal("panda", 2);
Animal animal2 = animal1;
animal2.setAge(3);
System.out.println(animal1.getAge());// 输出3
}
}
class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
引用拷贝的实现方式:
- 通过赋值
- 参数传递一般是用引用传递实现
引用拷贝和浅拷贝的区别
引用拷贝和浅拷贝都是让新对象与原对象共享一份数据,但它们的区别在于共享的程度不同。
引用拷贝是将新对象指向原对象所在的内存地址,这两个对象实际上是同一个对象,任何对于其中一个对象的修改都会影响到另一个对象。引用拷贝仅仅是在栈空间上复制了一个指针,不会在堆空间上创建新的对象。
而浅拷贝是将原对象的所有成员变量复制到新对象中,包括基本类型和引用类型,但是对于引用类型的成员变量,只是复制了引用,新对象和原对象还是共享同一个引用所指向的对象。所以如果浅拷贝对象中的引用类型成员变量发生了改变,那么原对象中对应的引用类型成员变量也会受到影响。