关于java语言clone函数的几个基本概念

关于java语言clone函数的几个基本概念

  1. 定义

clone函数是Object的方法

public class Object {
  ...
  protected native Object clone() throws CloneNotSupportedException;
  ...
}

接口Cloneable是一个空接口,没有任何函数的声明。

public interface Cloneable {
}

有很多讨论关于为什么clone()定义在Object,而不是在Cloneable中做声明,请自行搜索。

  1. clone()函数的定义

Object.clone()函数功能:为对象做内存块拷贝,可以这么理解,分配一块新内存,内存大小和原对象大小一样,然后使用内存拷贝操作,把原对象的内存内容,拷贝的新对象内存,使得前后两个对象的内存一模一样。内存拷贝操作只关乎内存内容,不涉及对象的逻辑结构。

两个修饰限定词:

  • native:这是一个本地方法,因为涉及内存操作。
  • protected:clone方法的原意是不允许外部对象拷贝,只允许继承对象内部拷贝,因为通常一个对象的clone函数都会调用super.clone()函数。当然这个protected可以被继承类修改为public。

注意:
如果一个对象没有声明Cloneable接口,那么调用其super.clone()函数时,会返回CloneNotSupportedException异常,因为super.clone()函数最终会依次调用到Object.clone()函数,而在Object.clone()函数里面会做判断是否声明了Cloneable接口,否则就抛出上述异常。

  1. clone的深拷贝(deep copy)和浅拷贝(shallow copy)

Java缺省是浅拷贝,因为Object.clone()函数拷贝的是对象内存,而不涉及对象的逻辑结构。而对于对象的引用成员而言,对象内只存储引用对象的地址,而不会存储引用对象的对象内存,所以在Object.clone()做内存拷贝的时候,只能拷贝引用成员的地址,使得前后两个对象引用到同一个对象。

举例:

public class Student implements Cloneable {
    int id;
    Course course;
    
    public Student(int id, Course course) {
        this.id = id;
        this.course = course;
    }
    
    public Student clone() throws CloneNotSupportedException {
        return (Student) super.clone();
    }
}
public class Course implements Cloneable {
    String name;
    int score;
    
    public Course(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public Course clone() throws CloneNotSupportedException {
        return (Course) super.clone();
    }
}

定义main函数:

    public static void main(String[] args) throws CloneNotSupportedException {
        Student stu1 = new Student(1, new Course("math", 80));
        Student stu2 = stu1.clone();
        
        System.out.printf("stu1 == stu2: %b\n", stu1 == stu2);
        System.out.printf("stu1.id == stu2.id: %b\n", stu1.id == stu2.id);
        System.out.printf("stu1.course == stu2.course: %b\n", stu1.course == stu2.course);  }

运行结果:

stu1 == stu2: false
stu1.id == stu2.id: true
stu1.course == stu2.course: true

我们看到stu1和stu2是两个不同的对象,但是stu1.course和stu2.course是相同的对象,这就是说缺省的clone()是一个浅拷贝,只拷贝了stu1和stu2的对象内存,并没有拷贝引用对象本身。

关于java语言clone函数的几个基本概念_第1张图片
image.png

下面我们把它修改成深拷贝。
修改Student的clone函数为如下:

public class Student implements Cloneable {
    
    public Student clone() throws CloneNotSupportedException {
        Student cloneStudent = (Student) super.clone();
        cloneStudent.course = cloneStudent.course.clone();  // we must re-define Course.clone() as public
        return cloneStudent;
    }
}

在运行:

stu1 == stu2: false
stu1.id == stu2.id: true
stu1.course == stu2.course: false

此时我们看到:stu1.source和stu2.course是两个不同对象了。


关于java语言clone函数的几个基本概念_第2张图片
image.png

号外,

  1. 关于stu1.id == stu2.id,因为他是一个基本数据类型,内存地址肯定是不一样的,但是值是一样的。
  2. 如果我们再比较stu1.course.name和stu2.course.name,他们是一样的,因为他们都指向字符串的常量池。
  3. String是不能clone的,因为String没有实现Cloneable接口,所以不能调用String.clone()函数,编译器报错。

你可能感兴趣的:(关于java语言clone函数的几个基本概念)