转载:详解Java中的clone方法
虽然是转载,这里大概说一下自己的理解:
无论是深拷贝还是浅拷贝,只要是想让对象具有拷贝的功能,当前类必须实现Cloneable接口(说明该类具有clone的性质,如果不实现会抛出CloneNotSupportedException异常),并在类中覆写Object类的clone()方法(并且设置访问限定符为public
),该方法是需要在别的类中被调用的。
对于基本数据类型来说,无论哪种拷贝,都是将值进行传递,肯定会新开一块内存空间存储该值,并将该值赋给新的引用变量。而对于引用类型来讲,若只是简单的将引用赋值给新的变量(实际上拷贝前后对象的属性引用指向同一块内存空间),这样就是浅拷贝;而若新创建一个属性与原对象完全相同的对象(拷贝前后对象的属性引用指向不同的内存空间),这样就是深拷贝。
Object类中默认的clone()方法采用的是浅拷贝,若需要实现深拷贝需要在覆写的clone()方法中添加属性是引用数据类型的克隆,如果属性是引用数据类型,则要要实现深拷贝就需要实现一条"引用链"。
这里做一点补充:
深拷贝除了可以采用文中对所有"引用链"中的每一级对象都要进行显示的拷贝外,还可以采用序列化与反序列化的方式进行对象的深拷贝
。
要使用序列化的方式实现深拷贝,必须保证该类(Student)及其引用属性类(Teacher)都必须实现Serializable接口(也就是整个引用链的所有类),否则会报NotSerializableException异常。通过将当前对象序列化为字节数组存放在内存中,再将内存中的字节数组反序列化为对象(借助内存流),从而实现深拷贝。
实例:
浅拷贝:
package com.xiaoaxiao.test.clone_test;
/**
* Created by xiaoaxiao on 2019/12/17
* Description: 浅拷贝
* 直接调用当前对象的clone(),当前对象会是直接被拷贝,
* 但当前对象内部属性是引用数据类型的对象并不会被拷贝(只是引用传递)
*/
class Person implements Cloneable{
private Face face;
private int age;
public Person(Face face, int age) {
this.face = face;
this.age = age;
}
public Face getFace() {
return face;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Face{
}
public class ShallowClone {
public static void main(String[] args) throws CloneNotSupportedException {
Person person1 = new Person(new Face(),30);
Person person2 = (Person)person1.clone();
System.out.println(person1==person2);
System.out.println(person1.getFace()==person2.getFace());
}
}
// 输出
false
true(对象的属性只是引用的变化,实际上指向的是同一个地址)
深拷贝:
package com.xiaoaxiao.test.clone_test;
/**
* Created by xiaoaxiao on 2019/12/17
* Description: 深拷贝
* 要想实现深拷贝,就得在当前对象的clone()方法中将其属性对象也完全拷贝一份,
* 这就要求其属性对象必须覆写clone()
*/
class Animal implements Cloneable{
private Body body;
public Animal(Body body) {
this.body = body;
}
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Animal newAnimal = (Animal)super.clone();
// 引用数据类型的属性进行拷贝
Body newBody = (Body)body.clone();
newAnimal.setBody(newBody);
return newAnimal;
}
}
class Body implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class DeepClone {
public static void main(String[] args) throws CloneNotSupportedException {
Animal animal1 = new Animal(new Body());
Animal animal2 = (Animal)animal1.clone();
System.out.println(animal1==animal2);
System.out.println(animal1.getBody()==animal2.getBody());
}
}
// 输出
false
false
序列化与反序列化实现深拷贝:
class Student implements Serializable{
private Teacher teacher;
public Student(Teacher teacher) {
this.teacher = teacher;
}
public Teacher getTeacher() {
return teacher;
}
/**
* 为了体现封装性,将这些序列化与反序列化的操作放在该类中,该类向外提供一个方法以供深拷贝即可。
*/
public Student cloneObject() throws IOException, ClassNotFoundException {
// 通过内存流进行序列化,将对象序列化为字节数组存放在内存中
ByteOutputStream baos = new ByteOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 通过内存流进行反序列化,将内存中的字节数组反序列化为对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.getBytes());
ObjectInputStream ois = new ObjectInputStream(bais);
Student newStu = (Student)ois.readObject();
return newStu;
}
}
class Teacher implements Serializable{
}
public class DeepCloneBySerializable {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student student = new Student(new Teacher());
Student newStu = student.cloneObject();
System.out.println(student==newStu);
System.out.println(student.getTeacher()==newStu.getTeacher());
}
}
// 输出
false
false