结合JVM解读浅拷贝和深拷贝

3 篇文章 0 订阅

java.lang.Cloneablejava.io.Serializable一样属于标记型接口,没有定义任何方法和属性。
在这里插入图片描述

一个类想要使用克隆方法
  • 重写clone()方法,因为Object的clone()的修饰符是protected;
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
  • 实现Cloneable接口,否则会抛出CloneNotSupportedException异常。
    在这里插入图片描述
克隆类型
  • 浅拷贝,拷贝对象时仅拷贝对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象
  • 深拷贝,不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象
举个栗子

这里使用User、Phone举例。
测试浅拷贝、深拷贝所用的Main方法如下:

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 预置数据
        Phone phone1 = new Phone();
        String number = "11111111111";
        phone1.setPhoneNum(number);
        String name = new String("dkangel");
        int age = 24;

        User user1 = new User(name, age, phone1);
        User user2 = (User) user1.clone();
        System.out.println("对象是否相等:" + (user1 == user2));
        System.out.println("类的类型是否相等:" + (user1.getClass() == user2.getClass()));
        System.out.println("age属性是否相等:" + (user1.getAge() == user2.getAge()));
        System.out.println("name属性是否相等:" + (user1.getName() == user2.getName()));
        System.out.println("对象的对象属性Phone是否相等:" + (user1.getPhone() == user2.getPhone()));
    }
}
浅拷贝

只拷贝对象本身,Phone不需要实现Cloneable接口和重写clone()方法。

public class Phone {
    private String phoneNum;

    public String getPhoneNum() {
        return phoneNum;
    }

    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }
}
public class User implements Cloneable {
    private String name;
    private int age;
    private Phone phone;

    public User(String name, int age, Phone phone) {
        this.name = name;
        this.age = age;
        this.phone = phone;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Phone getPhone() {
        return phone;
    }

    public void setPhone(Phone phone) {
        this.phone = phone;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

user2是user1的浅拷贝,只拷贝user1对象本身,未拷贝成员变量phone,所以两者phone对象相等
在这里插入图片描述

深拷贝

不仅拷贝对象本身,而且拷贝对象所指向的所有对象,所以Phone类也要支持clone。

public class Phone implements Cloneable{
    private String phoneNum;

    public String getPhoneNum() {
        return phoneNum;
    }

    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }

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

User.java 这里只粘贴clone()方法,其他和浅克隆保持一致

public class User implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        User user = (User) super.clone();
        user.phone = (Phone) user.getPhone().clone();
        return user;
    }
}

user2是user1的深拷贝,拷贝user1本身的同时也拷贝一份phone1对象,两者phone对象不再相等
在这里插入图片描述

结合JVM内存分区理解下浅拷贝和深拷贝

在这里插入图片描述
看上图可知,JVM内存分为五个部分堆、方法区、程序计数器、虚拟机栈、本地方法栈,在这里我们只关注

  • 存放对象的
  • 存放基本数据类型和对象引用的虚拟机栈
  • 存放类基本信息的方法区
浅拷贝解读

模式下从上到下解读CloneTest的main方法,得出如下对象在JVM中的分布图。
这里采用句柄模式访问对象(还有直接地址模式),简单解读下

new出来的对象phone1存入堆的实例池里;字符串number由于使用引号创建,所以存到堆中的字符串常量池;字符串name使用new创建,所以存入String实例池;age属于基本数据类型所以存入虚拟机栈;user1在堆中分配内存,通过clone对象user1本身的数据创建对象user2,user2的属性name/age/phone都与user1共用。

在这里插入图片描述

深拷贝解读

通过深拷贝创建的user2,不再与user1对象共用phone属性。深拷贝不仅拷贝了user1本身,也拷贝了user1的phone1对象。

这里只粘贴了与浅拷贝不同的地方,user2的phone属性由指向phone1改为指向phone2
在这里插入图片描述

如若有错误还望不吝指出,peace

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值