作者:困了电视剧
专栏:《JavaSE语法与底层详解》
文章分布:这是一篇关于接口的文章,在本篇文章中我会分享Cloneable接口的用法和机制,同时从底层分析深拷贝和浅拷贝的区别。
Cloneable接口
Cloneable这类接口可以说是最能体现接口思想的一类接口了,cloneable是“可克隆的”意思,在源码中我们可以清晰地看到这个接口本身是没有任何内容的,在这里他是一个空接口,或者又被叫做标记接口,当我们想要使用clone()方法时,如果实例化该对象的类没有实现这个接口,那将无法使用clone()方法。
这个接口就像一个属性,在未实现它之前,这个类是不可克隆的,但当实现了这个接口后,它将转变为一个可以克隆的类。
clone方法的使用
clone()方法是Object类中的一个方法。
由源码中的写法我们可以看到,clone()方法的返回值是一个Object类,clone()方法被native修饰,说明它是由c/c++实现的方法,由protected可知,在不同的包中,他只能在子类中被使用。此时我想调用这个方法应该怎么做?
本图中我的Student类实现了Cloneable接口,但是当我在main方法中使用clone()方法时,却报错了,这是为什么?
这个原因很简单,因为Object是包含在Java.lang包中的,我们在我们自己定义的包中进行使用时,就属于不同包的使用,受于protected限制符,我们无法在其他类中进行使用,此时我们可以进行重写,将该方法移到当前包中就可以进行使用了,代码如下:
public class Javabit_Code {
//这里需要声明一个编译型异常,否则会报错,我会在后面的文章中说明这个
public static void main(String[] args) throws CloneNotSupportedException{
Student student1=new Student();
Student student2=(Student) student1.clone();
}
}
class Student implements Cloneable{
String name;
int age;
double soccer;
public Student(String name, int age, double soccer) {
this.name = name;
this.age = age;
this.soccer = soccer;
}
public Student(){}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
由于该方法返回值是Object类型的,所以在接收的时候需要进行向下转型,否则会报错。
浅拷贝
在完成上述拷贝的准备工作后,我们现在进行拷贝相关的操作,下段代码中我重写了toString方法,为了是我的代码的变换更直观和方便。
public class Javabit_Code {
//这里需要声明一个编译型异常,否则会报错,我会在后面的文章中说明这个
public static void main(String[] args) throws CloneNotSupportedException{
Student student1=new Student("11",11,98);
Student student2=(Student) student1.clone();
System.out.println(student1);
System.out.println(student2);
}
}
class Student implements Cloneable{
String name;
int age;
double soccer;
public Student(String name, int age, double soccer) {
this.name = name;
this.age = age;
this.soccer = soccer;
}
public Student(){}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", soccer=" + soccer +
'}';
}
}
执行结果为:
由此可以看出我们完成了拷贝,student1和student2都是一样的内容,但此时突然有一个新的变化,我现在有一个新的Money类,每个学生类中都有一个Money类实例化的对象,然后每一个学生的money值都是固定的30,现在我将student1的nianl改为22,money值改为40,看看运行后会发生什么?
public class Javabit_Code {
//这里需要声明一个编译型异常,否则会报错,我会在后面的文章中说明这个
public static void main(String[] args) throws CloneNotSupportedException{
Student student1=new Student("11",11,98);
Student student2=(Student) student1.clone();
System.out.println(student1);
System.out.println(student2);
System.out.println("=======================");
student1.stuMoney.money=40;
student1.age=22;
System.out.println(student1);
System.out.println(student2);
}
}
class Student implements Cloneable{
String name;
int age;
double soccer;
Money stuMoney=new Money();
public Student(String name, int age, double soccer) {
this.name = name;
this.age = age;
this.soccer = soccer;
}
public Student(){}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", soccer=" + soccer +
", money=" + stuMoney +
'}';
}
}
class Money{
double money=30;
@Override
public String toString() {
return "Money{" +
"money=" + money +
'}';
}
}
这是代码,其运行结果如图:
我们可以看到一个神奇的现象,我改了student1的年龄,student2的年龄并不会改变,但当我改了student1的money值时,student2 的也相应的发生了改变,这是什么原因?
我们从代码运行的底层分析一下:
clone()方法在运作时就是在堆区创建一个和被克隆的对象一模一样的对象,这两个对象由于是两个不同的对象,所以他们的地址并不相同,但其中的所有内容均一样,包括引用类型。
这时候问题就来了,既然我引用类型也一样,那我引用类型指向的不就是同一个地址吗?对,没错,这就是浅拷贝,即他虽然将一个对象的内容信息拷贝到另一个新的对象中了,但是对象当中的引用类型所指向的地址也原封不动的进行了拷贝,这就导致当我修改其中一个对象中引用类型变量的值时,用这种方法进行拷贝的所有对象的相应的值都会发生改变,这并不是我们一直想要的,那要如何进行改变呢?
深拷贝
进行改变,即将浅拷贝转换成深拷贝,即当我进行拷贝的时候,我其中的引用类型也要指向一个全新的对象,那我不妨将clone()的方法再走一次,即我让本例中的Money类也实现Cloneable接口,然后在进行拷贝的时候同时拷贝一下money。
public class Javabit_Code {
//这里需要声明一个编译型异常,否则会报错,我会在后面的文章中说明这个
public static void main(String[] args) throws CloneNotSupportedException{
Student student1=new Student("11",11,98);
Student student2=(Student) student1.clone();
System.out.println(student1);
System.out.println(student2);
System.out.println("=======================");
student1.stuMoney.money=40;
student1.age=22;
System.out.println(student1);
System.out.println(student2);
}
}
class Student implements Cloneable{
String name;
int age;
double soccer;
Money stuMoney=new Money();
public Student(String name, int age, double soccer) {
this.name = name;
this.age = age;
this.soccer = soccer;
}
public Student(){}
@Override
protected Object clone() throws CloneNotSupportedException {
Student student=(Student) super.clone();
student.stuMoney=(Money) this.stuMoney.clone();
return student;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", soccer=" + soccer +
", money=" + stuMoney +
'}';
}
}
class Money implements Cloneable{
double money=30;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Money{" +
"money=" + money +
'}';
}
}
此时我们在进行一次运行:
student1的money发生改变,student2的money不会受到影响,此时就完成了深拷贝。
以上就是本篇博客的全部内容,如有疏漏欢迎指正!