首先我们需要明确一下,Java中的对象拷贝指的是拷贝一个对象的副本,而不是两个对象共享同一块内存地址。在Java中,我们可以使用浅拷贝和深拷贝两种方式来实现对象的拷贝。
浅拷贝
浅拷贝最简单的方式就是使用Object类的clone()方法,该方法可以复制一个Java对象。但是,它并不是完全的复制。当我们使用clone()方法来复制一个Java对象时,它会返回一个新的对象,但是该对象的非基本类型字段将会和原对象共享同一块内存地址。也就是说,如果我们修改了新对象中的某个非基本类型字段的值,原对象中的对应字段的值也会发生变化。
示例1:
public class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person("Tom", 18);
Person person2 = (Person) person1.clone();
person2.setName("Jerry");
System.out.println("person1 name: " + person1.getName()); //输出Tom
System.out.println("person2 name: " + person2.getName()); //输出Jerry
}
//省略getter和setter方法
}
在上述示例中,我们创建了一个名为Person的类,并实现了Cloneable接口,然后使用Object类的clone()方法实现了复制。我们首先创建了一个名为person1的Person对象,然后调用clone()方法复制出了一个名为person2的新对象,接着我们修改了person2对象中的name属性值,最后打印出了person1和person2对象的name属性值。从结果可以看出,person1和person2对象的name属性值不同,说明复制成功。
但是,如果我们将Person类中的name属性改为一个非基本类型的Student类,就会发现修改person2对象中的Student类的属性值时,person1中对应的属性值也会发生变化。
示例2:
public class Person implements Cloneable {
private String name;
private Student student;
public Person(String name, Student student) {
this.name = name;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student("Jack", 19);
Person person1 = new Person("Tom", student1);
Person person2 = (Person) person1.clone();
student1.setName("Jerry");
System.out.println("person1 student name: " + person1.getStudent().getName()); //输出Jerry
System.out.println("person2 student name: " + person2.getStudent().getName()); //输出Jerry
}
//省略getter和setter方法
}
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//省略getter和setter方法
}
在上述示例中,我们在Person类中新增了一个Student类型的属性,然后在main方法中创建了一个Person对象,并将一个Student对象赋值给了它的student属性。然后,我们使用clone方法将该对象复制到person2中,并修改了student1对象中的name属性值。最后,我们打印出了person1和person2中student属性的name属性值,发现它们是一样的。
深拷贝
为了避免浅拷贝带来的问题,我们可以使用深拷贝。深拷贝会复制所有的属性,包括对象的非基本类型属性。在Java中,常用的方式是使用序列化和反序列化实现深拷贝。
示例3:
public class Person implements Serializable {
private String name;
private Student student;
public Person(String name, Student student) {
this.name = name;
this.student = student;
}
public Person deepClone() 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 (Person) ois.readObject();
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student student1 = new Student("Jack", 19);
Person person1 = new Person("Tom", student1);
Person person2 = person1.deepClone();
student1.setName("Jerry");
System.out.println("person1 student name: " + person1.getStudent().getName()); //输出Jerry
System.out.println("person2 student name: " + person2.getStudent().getName()); //输出Jack
}
//省略getter和setter方法
}
class Student implements Serializable {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//省略getter和setter方法
}
在上述示例中,我们实现了Serializable接口,并定义了deepClone方法实现深拷贝。在该方法中,我们使用ByteArrayOutputStream和ObjectOutputStream将对象序列化成二进制流,然后使用ByteArrayInputStream和ObjectInputStream将二进制流反序列化成对象。最后,我们调用deepClone方法将person1对象深拷贝到person2中,并修改student1对象中的name属性值,最终发现person1和person2对象的student属性的name属性值不同。
再给出一个深拷贝的例子:
示例4:
public class Person implements Cloneable {
private String name;
private Student student;
public Person(String name, Student student) {
this.name = name;
this.student = student;
}
protected Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.student = (Student) this.student.clone();
return person;
}
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student("Jack", 19);
Person person1 = new Person("Tom", student1);
Person person2 = (Person) person1.clone();
student1.setName("Jerry");
System.out.println("person1 student name: " + person1.getStudent().getName()); //输出Jerry
System.out.println("person2 student name: " + person2.getStudent().getName()); //输出Jack
}
//省略getter和setter方法
}
class Student implements Cloneable {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
//省略getter和setter方法
}
在上述示例中,我们使用了深拷贝的第二种方式——手动进行深拷贝。在Person类中,我们在实现clone()方法时,首先调用了super.clone()方法复制出一个新的Person对象。接着,我们将原对象中的student对象再次进行clone(),然后将其设置到新的person对象中。最后,我们调用clone()方法将person1对象深拷贝到person2中,并修改student1对象中的name属性值,最终发现person1和person2对象student属性的name属性值不同。
总之,通过以上的例子,我们可以看到,Java对象的拷贝分为浅拷贝和深拷贝。使用浅拷贝可以简单地复制一个对象的基本类型成员变量,但不会复制它的非基本类型成员变量。使用深拷贝可以更完整地复制一个对象,包括它的基本类型成员变量和其非基本类型成员变量。在Java中,我们可以使用序列化和反序列化,以及手动进行clone()方法实现深拷贝。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java对象拷贝详解及实例 - Python技术站