JAVA的里面的深拷贝与浅拷贝、值传递与引用传递的详解

本文详细介绍了值传递和引用传递的概念,并通过Java代码示例展示了它们的区别。值传递是基本类型数据的拷贝,而引用传递则是对象的引用。接着,文章阐述了深拷贝和浅拷贝,指出浅拷贝仅拷贝基本数据,而深拷贝会创建新对象。通过实例分析,展示了如何在Java中实现深拷贝和浅拷贝的效果。

一、什么是值传递、什么是引用传递?

1.概念介绍

值传递:实参把值传递给形参。
引用传递:实际参数传给形参堆内存的引用地址
  
区别:所以引用传递实际上指向的是用一块区域,对形参的修改会影响到实参,而值传递的形参与实参在内存上是两个独立的变量,一方修改互不影响。

2.代码演示
1.值传递


public class Father {
    int age;
    public void setAge(int x){
        x=100;
    }


    public static void main(String[] args) {
        Father fat=new Father();
        fat.age=32;
        fat.setAge(fat.age);
        System.out.println(fat.age);

    }
}

输出结果:
32
Process finished with exit code 0

在setAge函数中,age作为实参把值(32)传给了形参x,然后改变形参x的值(x=100),可以看到并不影响实参age的值.

2.引用传递


public class Father {

    public void changeVlaue(int [] x){
        x[2]=100;
    }

    public static void main(String[] args) {
        Father fat =new Father();
        int [] array={0,1,2,3,4};
        for(int temp:array){
            System.out.print(temp+" ");
        }
        System.out.println();
    
        fat.changeVlaue(array);
        for(int temp:array){
            System.out.print(temp+" ");
        }

    }
}

运行结果:
0 1 2 3 4 
0 1 100 3 4 
Process finished with exit code 0

这个changeVlaue中我们可以看到,我们把array的首地址传给x了,所以当对数组x做改变的时候,原来的array也发生了改变.(实际上我们也经常这样做,把一个数组的首地址当作形参传入某个函数,对这个数组进行排序等操作,其实这个就是引用传递)

3.总结

   JAVA里面的数据类型,如图在这里插入图片描述

所以我们可以下结论:
形参为基本类型数据都是值传递(只是在拷贝它的值),
而形参是引用数据类型的都是引用传递(只是对对象的引用,实际上指的是同一个对象).

(当然C++里面 int & x,也可以做到引用传递,又当然String这个东西是值传递)

  

二、什么是深拷贝、什么是浅拷贝?

在我们了解值传递与引用传递的基础上,我们可以更好的了解深拷贝与浅拷贝

1.概念介绍

浅拷贝:只是对基本数据进行了拷贝,而对引用类型进行了引用传递.
深拷贝:只是对基本数据进行了拷贝,而对引用类型进行了拷贝的时候,是创建了一个新的对象并拷贝.
  
区别:很明显,两者对基本数据类型的处理都是值传递,而对引用类型数据,前者直接引用,后者是开辟新的空间,并赋值,所以我们回到深拷贝的目的,改变形参不影响实参,两者的区别根本在于创建新的空间。

2.代码介绍
(1)我们先看类中只有基本数据类型的代码

package test;

/**
 * @author gsw
 */
public class Father {
    int age;

    Father(int x) {
        this.age = x;
    }

    Father(Father temp) {
        this(temp.age);
    }


    public static void main(String[] args) {
    //fat2没有创建了新的空间,而fat3创建了新的空间
        Father fat1 = new Father(32);
        Father fat2 = fat1;
        Father fat3 = new Father(fat1);

        System.out.println(fat1+" "+fat1.age);
        System.out.println(fat2+" "+fat2.age);
        System.out.println(fat3+" "+fat3.age);

        fat1.age=300;
        System.out.println("改变值之后:");
        System.out.println(fat1+" "+fat1.age);
        System.out.println(fat2+" "+fat2.age);
        System.out.println(fat3+" "+fat3.age);



    }
}
运行结果:
test.Father@133314b 32
test.Father@133314b 32
test.Father@b1bc7ed 32
改变值之后:
test.Father@133314b 100
test.Father@133314b 100
test.Father@b1bc7ed 32

我们一步一步分析
(i)构造函数

    Father(int x) {
        this.age = x;
    }
    Father(Father temp) {
        this(temp.age);
    }

  很明显这个复制构造函数本质上是调用有形参的构造函数(值传递),而且Father这个类里面只有一个成员变量那就是int age ,一个基本数据类型.所以这样的复制也一定都是浅复制,因为类里面只涉及基本数据类型。
  
(ii)main函数代码
  fat2的地址与fat1指向的地址一样,而fat3与fat1指向的地址不一样。所以改变fat1.age的时候,fat2随之改变而fat3没有。可以看到fat2没有创建了新的空间,而fat3用new创建了新的空间,所以尽管两者调用的构造函数都是浅拷贝(值传递),但是fat3却达到了深拷贝的目的。

(2)我们再看类中有基本数据类型与引用类型的代码

package test;

/**
 * @author GT
 */
public class GrandFather {
    int head;
    GrandFather (int x){
        head=x;
    }
    GrandFather (){
        head=0;
    }
}


package test;
/**
 * @author gsw
 */
public class Father   {
    int age;
    GrandFather grandFather=new GrandFather();
    Father(int head,int x) {
        grandFather.head=head;
        this.age = x;
    }

    Father(Father temp) {
        this(temp.grandFather.head,temp.age);
    }


    public static void main(String[] args) {
        Father fat1 = new Father(1,32);
        Father fat2 = fat1;
        Father fat3 = new Father(fat1);

        System.out.println(fat1+" "+fat1.age+"      "+fat1.grandFather+"  head:"+fat1.grandFather.head);
        System.out.println(fat1+" "+fat2.age+"      "+fat2.grandFather+"  head:"+fat2.grandFather.head);
        System.out.println(fat3+" "+fat3.age+"      "+fat3.grandFather+"  head:"+fat3.grandFather.head);


        fat1.age=300;
        fat1.grandFather.head=2;
        System.out.println("改变值之后:");
        System.out.println(fat1+" "+fat1.age+"      "+fat1.grandFather+"  head:"+fat1.grandFather.head);
        System.out.println(fat1+" "+fat2.age+"      "+fat2.grandFather+"  head:"+fat2.grandFather.head);
        System.out.println(fat3+" "+fat3.age+"      "+fat3.grandFather+"  head:"+fat3.grandFather.head);
    }
}

test.Father@cc34f4d 32      test.GrandFather@17a7cec2  head:1
test.Father@cc34f4d 32      test.GrandFather@17a7cec2  head:1
test.Father@65b3120a 32      test.GrandFather@6f539caf  head:1
改变值之后:
test.Father@cc34f4d 300      test.GrandFather@17a7cec2  head:2
test.Father@cc34f4d 300      test.GrandFather@17a7cec2  head:2
test.Father@65b3120a 32      test.GrandFather@6f539caf  head:1

我们在Father类中,增加了一个GrandFather类型的成员,至此Father里面有基本数据类型成员变量与引用类型数据成员变量,同样的,fat1与fat2的中的引用数据类型的类的地址还是一样的,说明fat2还是对fa1的整个引用。

在这里插入图片描述
那fat3为什么可以实现深复制

        Father fat1 = new Father(1,32);
        Father fat2 = fat1;
        Father fat3 = new Father(fat1);

之前讲过类本来就是引用类型,这家伙你不给它开辟内存空间,复制就都是引用。所以,不管类里面套娃了多少类或者其余的引用类型,只要给每一个类开辟了空间,那就属于深复制。

到这里,差不多讲完了,但是我总感觉有地方不严谨,没有研究透彻,本文将持续更新,欢迎读者指正、交流。
此文给我很大的启示,讲的是clone方法的深、浅复制,点此进入 https://www.html.cn/qa/other/22850.html

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值