Java参数传递只存在值传递
敲黑板-----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
下一篇: js开发环境配置