Java 深拷贝 浅拷贝 +代码实现

今日目标 深拷贝 浅拷贝

一、前言

任何变成语言中,其实都有浅拷贝和深拷贝的概念,Java 中也不例外。在对一个现有的对象进行拷贝操作的时候,是有浅拷贝和深拷贝之分的,他们在实际使用中,区别很大,如果对其进行混淆,可能会引发一些难以排查的问题。

本文就在 Java 中的深拷贝和浅拷贝做一个详细的解说。这也是在面试时面试官可能会让写的。
特点:Java没有引用传递,Java只有值传递。

不想了解的话最后面有一点总结:!!

浅拷贝

  • 基本数据类型直接copy值
  • 引用类型copy地址

首先implements Cloneable 这个接口
然后重写 clone() 方法

--------先写一个User实体类,实现Cloneable接口 并 并重写 clone()方法--------

public class User implements Cloneable{//Cloneable:JVM的标记接口,//并要重写克隆方法。
    private int id;
    private  String name;
    public User(){
        super();
    }
    public User(int id,String name){
        super();
        this.name=name;
        this.id=id;
    }

    public int getId() {
        return id;
    }


    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    //需要重写克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
      return super.clone();
    }
}

------写完后测试一下---------

class test{
    public static void main(String[] args) throws CloneNotSupportedException {
        User u1 = new User(1,"张三");
        System.out.println(u1);
        System.out.println("--------拷贝的u1---------");
        User u2 = (User) u1.clone();
        System.out.println(u2);
        System.out.println("-------他们是否一样呢-----------");
        System.out.println(u1==u2);
        System.out.println("-------此时我更改U1的值-----------");
        u1.setName("李四");
        System.out.println("u1:"+u1);
        System.out.println("u2:"+u2);
        System.out.println(u1==u2);
    }
}

------效果-------
可以看出此时 拷贝还没有问题 是两个对象
改变里面一个类型的值 拷贝的不会影响
因为是两个对象
user实体里面存放的都是 基本数据类型
在这里插入图片描述
—————现在我们来加一个引用类型来看看——————
Address 实体类 存放一些地址信息

class Address {
    private  String province;
    private  String city;
    private  String area;

    public Address() {
        super();
    }

    public Address(String province, String city, String area) {
        super();
        this.province = province;
        this.city = city;
        this.area = area;
    }


    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(province, address.province) &&
                Objects.equals(city, address.city) &&
                Objects.equals(area, address.area);
    }

    @Override
    public int hashCode() {
        return Objects.hash(province, city, area);
    }

    @Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", area='" + area + '\'' +
                '}';
    }
}

————在User对象里加入这个引用类型————

在这里插入图片描述
User 引入后

public class User implements Cloneable{//Cloneable:JVM的标记接口,//并要重写克隆方法。
    private int id;
    private  String name;
    private Address address;
    public User(){
        super();
    }
    public User(int id,String name){
        super();
        this.name=name;
        this.id=id;
    }

    public int getId() {
        return id;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }

    //需要重写克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

}

————测试一下————

class test2{
    public static void main(String[] args) throws CloneNotSupportedException {
        User u1 = new User(1,"张三");
        u1.setAddress( new Address ("河北省","保定市","莲池区"));
        System.out.println(u1);
        System.out.println("--------拷贝的u1---------");
        User u2 = (User) u1.clone();
        u2.getAddress().setArea("王者大陆");
        System.out.println(u2);
        System.out.println(u1);
        //此时更改后的值(更改的是引用的值Address)  父类也发生了改变
        //所以这是浅拷贝 只拷贝一层;
        //浅拷贝  拷贝的是引用值过去  具体还是操作的是一个
    }
}

------来看看效果——
在这里插入图片描述
我们发现,修改Address里面的值后 克隆对象和父类对象都变了
这说明 克隆对象和父类对象修改的 Address 都是指向的一个 印证了上面说的
只拷贝一层 而且引用类型只是copy了地址
看下图 就知道了
在这里插入图片描述
这就是所谓的浅拷贝。

接下来看看深拷贝怎么写

深拷贝

深拷贝则需要 我们把Address也要拷贝

首先Address实现Cloneable接口 并 并重写 clone()方法

class Address implements  Cloneable{
    private  String province;
    private  String city;
    private  String area;

    public Address() {
        super();
    }

    public Address(String province, String city, String area) {
        super();
        this.province = province;
        this.city = city;
        this.area = area;
    }

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

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(province, address.province) &&
                Objects.equals(city, address.city) &&
                Objects.equals(area, address.area);
    }

    @Override
    public int hashCode() {
        return Objects.hash(province, city, area);
    }

    @Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", area='" + area + '\'' +
                '}';
    }
}

然后User实体类 的clone()方法要改写
也就是在User的克隆方法处要对 Address克隆 并封装进克隆对象里返回给调用方法
在这里插入图片描述

public class User implements Cloneable{//Cloneable:JVM的标记接口,//并要重写克隆方法。
    private int id;
    private  String name;
    private Address address;
    public User(){
        super();
    }
    public User(int id,String name){
        super();
        this.name=name;
        this.id=id;
    }

    public int getId() {
        return id;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }

    //需要重写克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //深拷贝 克隆User对象
        User user=(User) super.clone();
        Address address=(Address) user.address.clone();
        user.setAddress(address);
        return user;
    }

}

-——————测试——————

class test2{
    public static void main(String[] args) throws CloneNotSupportedException {
        User u1 = new User(1,"张三");
        u1.setAddress( new Address ("河北省","保定市","莲池区"));
        System.out.println(u1);
        System.out.println("--------拷贝的u1---------");
        User u2 = (User) u1.clone();
        u2.getAddress().setArea("王者大陆");
        System.out.println(u2);
        System.out.println(u1);
        //此时更改后的值(更改的是引用的值Address)  父类也发生了改变
        //所以这是浅拷贝 只拷贝一层;
        //浅拷贝  拷贝的是引用值过去  具体还是操作的额一个
    }
}

————效果————

在这里插入图片描述
——————看见了没 我们的深拷贝 的两个对象是完全不同的两个对象————
也就是类似下图
在这里插入图片描述

总结

浅拷贝

  • 基本数据类型直接copy值
  • 引用类型copy地址

特点:

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。
(2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。

深拷贝

  • 将引用类型也拷贝 并和父类封装返回
  • 得到两个完全独立的对象

特点

(1)
对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2)
对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。 (4)
深拷贝相比于浅拷贝速度较慢并且花销较大。

举例子的话就是:
浅拷贝:考试场景
小花是个没有感情的机器,啥也不会,考试的时候
小花抄了小明的卷子,但是小明把题解告诉了小花,小花有了自己的思维
##可能小花觉得小明做的不对,因为小花有自己的思维了。
所以小明 小花在更改上述传递的答案的值时,互相都不干扰

但是有一个题 小明超了小黑的 这个题小黑没给小明讲解,所以小明没就没给小花讲解 所以小黑的值改了 他们两个都跟着改。
深拷贝:此时场景
小黑当时给小明抄的时候把题解告诉了小明 , 小明因为暗恋小花多年也直接把题解告诉了小花,这样小花也有了小黑的思想,自己随便更改值了。
那问题来了 小花最后是选择了谁作男朋友呢??
哈哈 !!
其实这个例子不是很好,就大致那么个意思,我就这点水平哈哈。


告辞了 您嘞!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值