欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java参数传递只存在值传递

程序员文章站 2022-03-11 18:59:16
敲黑板-----Java中只存在值传递,不存在引用传递我们知道,Java中的常见现象:基本数据类型作为参数传递,方法对参数的操作会随方法结束而无法保留,所以方法无法修改参数调用的对应的变量值;引用数据类型作为参数传递,方法对参数的操作即使方法结束也会保留,所以方法可以修改参数引用的对应的变量值那么可以说基本数据类型传递是值传递,引用数据类型传递是引用传递呢?当然不可以,因为常见现象不代表全部现象,引用数据类型传递依然存在无法保留操作的情况,来来来来个栗子public static...

敲黑板-----Java中只存在值传递,不存在引用传递

我们知道,Java中的常见现象:

基本数据类型作为参数传递,方法对参数的操作会随方法结束而无法保留,所以方法无法修改参数调用的对应的变量值;

引用数据类型作为参数传递,方法对参数的操作即使方法结束也会保留,所以方法可以修改参数引用的对应的变量值

那么可以说基本数据类型传递是值传递,引用数据类型传递是引用传递呢?

当然不可以,因为常见现象不代表全部现象,引用数据类型传递依然存在无法保留操作的情况,来来来来个栗子

public static void main(String[] args){
        int[] arr1 = {1,2};
        int[] arr2 = {3,4};
        System.out.println(Arrays.toString(arr1));    //[1,2]
        System.out.println(Arrays.toString(arr2));    //[3,4]
        wrap(arr1 , arr2);
        System.out.println(Arrays.toString(arr1));    //[1,2]
        System.out.println(Arrays.toString(arr2));    //[3,4]
    }
    public static void wrap(int[] a,int[] b){
        int[] temp=a;
        a=b;
        b=temp;
        System.out.println(Arrays.toString(a));    //[3,4]
        System.out.println(Arrays.toString(b));    //[1,2]
    } 

发生了什么呢,我们的wrap方法中实现了数组的互换,但是当我们结束方法后,再次打印数组,发现数组并没有互换,所以特殊情况还是存在的(特例并不止这一种)

解释一下吧,

Java中,基本数据类型的传递

public static void main(String[] args){
        int a=0;
        a=1;
        System.out.println(a);    //    1
        plus(a);
        System.out.println(a);    //    1
    }
    public static void plus(int n){
        n++;
        System.out.println(n);    //    2
    }

吃完栗子发现,值为1的a作为基本数据类型的实参传入plus方法后,确实在方法内实现了自增,输出为2,但是一旦方法结束,输出仍为1

1.程序运行,首先调用main(),JVM立即向虚拟机栈中压入一个栈帧,栈帧中存在局部变量表,存放创建的main的局部变量a,然后在栈中查找是否存在字面量0,由于并不存在,JVM继续在栈中开辟空间存储字面量0,并使局部变量a指向它.此时完成了a的初始化

2.程序继续往下走,来到a=1;此时JVM继续查找栈中是否存在字面量1.由于并不存在,JVM在栈中继续开辟空间存储字面量1,并是局部变量a指向它的地址.此时完成了a的重新赋值

3.程序继续来到plus(a);此时调用plus()方法,此时a作为实参传入,形参n获得实参a的值(实参a的值1的副本,并非实参a的值1本体),执行plus()方法时,JVM向虚拟机栈中继续压入一个栈帧,成为当前栈帧(最顶部),形参n就存储在这个栈帧的局部变量表中

4.我们发现,调用plus()而压入的栈帧取代了调用main()的栈帧,开始作用,一旦plus()调用结束,栈帧出栈销毁,main()所在的栈帧又回到顶部成为当前栈帧开始作用.同时由于形参n实参a分别存在不同的栈帧中,并且值传递(形参的值为实参的副本),所以互不干扰,即使形参n在plus()所在的栈帧中进行再多操作,一旦栈帧出栈销毁,一切针对形参n的操作都烟消云散了

5,值传递的是真是内容的一个副本,副本与本体不存在关联,对副本的操作不影响本体,即对形参的操作不会关联到实参对应的内容

Java中,引用数据类型传递

public static void main(String[] args){
        int[] arr1 = {1,2};
        int[] arr2 = {3,4};
        System.out.println(Arrays.toString(arr1));    //[1,2]
        System.out.println(Arrays.toString(arr2));    //[3,4]
        wrap(arr1 , arr2);
        System.out.println(Arrays.toString(arr1));    //[1,2]
        System.out.println(Arrays.toString(arr2));    //[3,4]
    }
    public static void wrap(int[] a,int[] b){
        int[] temp=a;
        a=b;
        b=temp;
        System.out.println(Arrays.toString(a));    //[3,4]
        System.out.println(Arrays.toString(b));    //[1,2]
    } 

1.程序运行,首先调用main(),压入栈帧,JVM会向堆中开辟连续的空间存放数组,再返回给存放在栈之中的数组变量名arr1,arr2(在栈中存放堆中数组的第一个地址,并让变量名指向它)

2.当执行wrap()方法时,在栈中继续压入栈帧,形参获得实参的内容(数组的地址)的副本,获得的这个地址副本是指向堆中的数组本体的,所以通过操作形参(操作地址副本)来操作堆中的数组本体,即使方法结束,栈帧出栈被销毁,对数组的操作也是保留下来的.但是如果如上面的栗子,形参仅仅交换了存放在栈中地址,并没有修改堆中的数组,那么一旦栈帧被销毁,main()所在的栈帧回到栈顶作用,一切又恢复如初

3.分析一波

3.1形参获得地址副本拥有对堆中数组的访问权限(堆是公共的,不同线程都可以访问)

3.2方法调用,就会压入栈帧,方法结束,栈帧出栈销毁

3.3形参实参分别存在于不同的栈帧,是独立的,互不干扰的


总结

1.Java只有值传递(传递内容的副本)

2.形参实参分别存在于不同栈帧中

3.栈帧是独立的,堆是公共的

4.基本数据类型,栈中存储的是实际值

5.引用数据类型,栈中存储的是地址,堆中存储的是实际值

6.方法调用,就会压入栈帧,方法结束,栈帧出栈销毁

7.基本数据类型,在形参的栈帧中操作实际值的副本,随栈帧的销毁而操作失效

8.引用数据类型,在形参的栈帧中操作地址的副本,通过地址副本访问公共的堆中的数组,实现操作,栈帧销毁但数组操作保留

9.注意,如果引用数据类型的操作,没有改变堆中的原先数组,则操作不会保留(如上述例子,改变的是地址副本)

本文地址:https://blog.csdn.net/MojaveGhost1057/article/details/107865217

相关标签: Java 参数传递