【剧前爆米花--爪哇岛寻宝】Cloneable接口和深浅拷贝

作者:困了电视剧

专栏:《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不会受到影响,此时就完成了深拷贝。

以上就是本篇博客的全部内容,如有疏漏欢迎指正!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值