Cloneable接口与深度克隆、浅度克隆

Cloneable接口

我们知道。想要实现对象克隆,重写并调用Object的clone方法就可以实现对象克隆,但是我们发现,如果单单只重写clone方法并调用,会抛出CloneNotSupportedException(克隆不被支持)的异常。Cloneable接口可以看做是一个标记,所以我们必须在实现Cloneable接口的基础上,重写并调用Object的clone方法才能准确实现对象克隆。
在这里插入图片描述
Object的clone方法:
在这里插入图片描述
我们可以看到:
1、clone方法是protected修饰的,因此无法直接调用clone方法,子类只能调用受保护的clone方法克隆自己,为此,必须重新定义clone方法,并将它声明为public。
2、同时这个方法也是被native修饰,native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中,通过JNI接口调用其他语言来实现对底层的访问。
(JNI(Java Native Interface)这是一个本机编程的接口,它也是java jdk(开发工具包)的一部分,JNI可以支持java中使用其他语言,java要调用其他语言的接口,需要经过他处理。java所谓的跨平台,在一定程度上放弃了底层操作,因为不同的硬件或者操作系统底层的操作都是不一样的。)
JNI可以理解为是java访问其他编码语言的接口
在这里插入图片描述

深度克隆与浅度克隆

对象的克隆:
* 对象的克隆是指创建了一个新对象,且新对象的状态与原始对象的状态相同。
* 当对克隆的新对象进行修改时,不会影响原始对象的状态

1、拷贝对象
在这里插入图片描述
示例代码:

定义Employee类

package com.java01.day04;

/**
 * @description:
 * @author: ju
 * @date: 2020-05-07 10:53
 */
public class Employee {
    /**
     * 编号
     */
    private int number;
    /**
     * 年龄
     */
    private int age;
    /**
     * 用户
     */
    private User user;

    public Employee(int number, int age, User user) {
        this.number = number;
        this.age = age;
        this.user = user;
    }

    public Employee() {
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    
    @Override
    public String toString() {
        return "Employee{" +
                "number=" + number +
                ", age=" + age +
                ", user=" + user +
                '}';
    }
}

测试 :

		User user = new User("copyUser", new StringBuffer("女"));
        Employee employee1 = new Employee(1, 12, user);
        //拷贝一个变量时,原始变量与拷贝变量引用的是同一个变量
        Employee copy = employee1;
        copy.setAge(30);
        //改变一个变量所引用的对象将会对另一个变量产生影响
        System.out.println(employee1.getAge()); //30
        System.out.println("employee1.hashCode: " + employee1.hashCode() + " ---- copy.hashCode: " + copy.hashCode());

输出结果:
在这里插入图片描述
我们可以看到,拷贝一个变量时,原始变量与拷贝变量引用的是同一个变量;改变一个变量所引用的对象将会对另一个变量产生影响,对此,我们引出了克隆的概念。

2、克隆对象
使用clone方法,先分配内存,这里分配的内存与调用clone方法对象的内存相同,然后将源对象中各个变量的值,填充到新的对象中,填充完成后,clone方法返回一个新的地址,这个新地址的对象与源对象相同,只是地址不同。

2.1 浅度克隆
在这里插入图片描述

示例代码:

定义Employee类实现Cloneable接口,重写clone方法

package com.java01.day04;

/**
 * @description:
 * @author: ju
 * @date: 2020-05-07 10:53
 */
public class Employee implements Cloneable{
    /**
     * 编号
     */
    private int number;
    /**
     * 年龄
     */
    private int age;
    /**
     * 用户
     */
    private User user;

    public Employee(int number, int age, User user) {
        this.number = number;
        this.age = age;
        this.user = user;
    }

    public Employee() {
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public Employee clone() throws CloneNotSupportedException {
        return (Employee) super.clone();
    }

    @Override
    public String toString() {
        return "Employee{" +
                "number=" + number +
                ", age=" + age +
                ", user=" + user +
                '}';
    }
}

测试:

		User user = new User("copyUser", new StringBuffer("女"));
        Employee employee1 = new Employee(1, 30, user);
        //使用对象克隆
        Employee clone = employee1.clone();
        clone.setNumber(2);
        System.out.println(employee1.getNumber());
       
        System.out.println("employee1.hashCode: " + employee1.hashCode() + " ---- clone.hashCode: " + clone.hashCode());

        User user1 = clone.getUser();
        System.out.println("employee1.user.hashCode: " + user.hashCode() + " ---- clone.user.hashCode: " + user1.hashCode());

        user1.setName("张三");
        System.out.println(user.getName());

输出结果:
在这里插入图片描述
我们可以看到clone方法并不是把employee1对象的引用赋予clone对象,而是在堆中重新开辟了一块空间,将employee1复制过去,将新的地址返回给clone。这种情况就实现了浅度克隆。

但是我们看到user的hashcode并没有变化,User对象依旧指向的是同一对象,所以改变该对象的变量后,对另一个变量也会产生影响。对于这种情况我们引入了深度克隆来解决。

2.2 深度克隆

定义User类,让User也实现Cloneable接口,重写clone方法

package com.java01.day04;

/**
 * @description:
 * @author: ju
 * @date: 2020-05-07 13:13
 */
public class User implements Cloneable{
    private String name;

    private StringBuffer sex;

    public User(String name, StringBuffer sex) {
        this.name = name;
        this.sex = sex;
    }

    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public StringBuffer getSex() {
        return sex;
    }

    public void setSex(StringBuffer sex) {
        this.sex = sex;
    }

    @Override
    public User clone() throws CloneNotSupportedException {
        return (User) super.clone();
    }
}

重写Employee类的clone方法

	@Override
    public Employee clone() throws CloneNotSupportedException {
        Employee employee = (Employee) super.clone();
        employee.user = user.clone();
        return employee;
    }

测试:

		User user = new User("copyUser", new StringBuffer("女"));
        Employee employee1 = new Employee(1, 30, user);
        //使用对象克隆
        Employee clone = employee1.clone();
        clone.setNumber(2);
        System.out.println(employee1.getNumber());
        
        System.out.println("employee1.hashCode: " + employee1.hashCode() + " ---- clone.hashCode: " + clone.hashCode());

        User user1 = clone.getUser();
        System.out.println("employee1.user.hashCode: " + user.hashCode() + " ---- clone.user.hashCode: " + user1.hashCode());

        user1.setName("张三");
        System.out.println(user.getName());

        System.out.println("employee1.user.stringBuffer.hashCode: " + user1.getSex().hashCode() + " ---- clone.user.stringBuffer.hashCode: " + user.getSex().hashCode());

        user1.setSex(new StringBuffer("中性人"));
        System.out.println(employee1.getUser().getSex());

输出结果:
在这里插入图片描述
在这里插入图片描述

但是,我们又发现,StringBuffer对象在clone的时候将地址复制过去了,并没有创建新的对象地址,所以我们如果修改sex的值,依旧会影响之前的变量值。
而对于StringBuffer类型,我们不能去实现Cloneable接口,也不能重写clone方法,这种情况下,我们可以选择:
1、只实现浅度克隆
2、user1.setSex(new StringBuffer(“中性人”));在设置clone的时候,传一个新的new StringBuffer()

例如:

        System.out.println("employee1.user.stringBuffer.hashCode: " + user1.getSex().hashCode() + " ---- clone.user.stringBuffer.hashCode: " + user.getSex().hashCode());

        user1.setSex(new StringBuffer("中性人"));
        System.out.println(user.getSex());
        System.out.println("employee1.user.stringBuffer.hashCode: " + user1.getSex().hashCode() + " ---- clone.user.stringBuffer.hashCode: " + user.getSex().hashCode());

输出结果:发现改变user1的sex并不会影响到user的sex值,且两个user的hashcode也不一样了
在这里插入图片描述

参考文章:java native关键字干什么用的?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值