Java中的参数传递到底是值传递还是引用传递,深层探究!!!

Java中的参数传递到底是值传递还是引用传递,深层探究!!!

看本文前需要明确两个重要的知识点!
1.什么是形参,什么是实参?

形参
定义:全称"形式参数",用于定义方法的时候使用的参数,目的用来接收调用该方法时传递的参数。
说明:只有在被调用时才会分配内存单元,在调用结束,即刻释放所分配的内存单元。因此,只在方法内才有效。

实参
定义:全称"实际参数",用于调用时传递给方法的参数,即传递给被调用方法的值。
说明:预先创建并赋予确定值。

2.什么是栈?什么是堆?

什么是栈内存?
栈内存是Java的另一种内存,主要是用来执行程序用的,比如:基本类型的变量和对象的引用变量。
栈内存的特点?
第一点:栈内存就好像向桶里装大饼,往里面放入大饼,那么先放入的就在桶的最底层,那么你要取出大饼的时候,得从上往下逐步取出,所以它的特点是:先进后出,后进先出。
第二点:存取速度比堆要快,仅次于寄存器,栈数据可以共享,但缺点是,存在栈中的数据大小与生存必须是确定的,缺乏灵活性。

什么是堆内存?
堆内存是java内存中的一种,它的作用是用于存储java中的对象和数组,当我们new一个对象或者创建一个数组的时候,就会在堆内存中开辟一段空间给它,用于存放。
堆内存的特点是什么?
第一点:堆其实可以看作是平时去排队买票,你得先排队,你先排先买票,后排后买票。所以堆内存的特点就是:先进先出,后进后出,
第二点:堆可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,但缺点是,由于要在运行时动态分配内存,存取速度较慢。

概念理解清楚了,就直接上代码!!!
首先上基本类型参数传递案例!!!

//基本类型参数传递案例
package exercise;
public class exercise56 {
	public static void swap(int value1, int value2) {
		int temp=value1;
		value1=value2;
		value2=temp;
		System.out.println("在swap方法中进行值交换后的value1:"+value1+" 进行值交换后的value2:"+value2);
	}
	public static void main(String[] args) {
		int value1=10;
		int value2=20;
		System.out.println("进行值交换前的value1:"+value1+" 进行值交换前的value2:"+value2);
		swap(value1,value2);   
		System.out.println("执行swap方法过后的value1:"+value1+" 进行值交换后的value2:"+value2);
	}
}

//运行结果
//进行值交换前的value1:10 进行值交换前的value2:20
//在swap方法中进行值交换后的value1:20 进行值交换后的value2:10
//执行swap方法过后的value1:10 进行值交换后的value2:20

分析:我们希望在调用了swap()方法之后交换value1与value2的值,但是事实却没有,这是怎么回事呢?在main栈区的swap栈区的value1跟value2是互不干扰的,swap栈区的value1跟value2只是main的副本而已,也就是说这里有五个变量!分别是swap()栈区中的temp,value1,value2与main栈区中的value1,value2.如果你还是理解不了的话,可以这样理解,大家都听说过平行宇宙,每一个平行宇宙都有这两个人(value1跟value2),但是他们的命运却是截然不同,也就是平行宇宙中宇宙与宇宙之间互不干扰!所以这也为什么没有值没有交换成功的原因!

初窥内存图
在这里插入图片描述
jvm到底做了什么呢?

第一步:jvm创建main栈区,在栈区先定义value1并初始化,再定义value2并初始化,将这两个实参传入到swap()方法中。
第二步:jvm创建swap栈区,在栈区定义两个形参,value1和value2,来接收传入的两个值,接收成功,形参变为实参。再定义temp变量,将value1的值赋值给temp,再将value2的值赋值给value1,最后将temp的值赋值给value2,在swap栈区完成交换操作。将swap栈区的所有东西,从上至下的进行弹栈操作,swap栈区消失。
第三步:main方法执行完毕,将main栈区的所有东西,从上至下的进行弹栈操作,main栈区消失。

结论:java里基本类型的参数传递是值传递,值传递实际上就是将实际参数的的副本传入方法内,而参数本身不会受到任何影响。

话多但还是希望大家能够深层理解值传递,再上代码!!!
再上引用类型传参案例!!

//引用类型参数传递
package exercise;
public class exercise59 {
	int value1;
	int value2;
}

package exercise;
public class exercis58 {
	public static void  swap(exercise59 e){
		int temp=e.value1;
		e.value1=e.value2;
		e.value2=temp;
		System.out.println("在swap方法中进行值交换后的value1:"+e.value1+" 进行值交换后的values2:"+e.value2);
	}
	public static void main(String[] args) {
		exercise59 e = new exercise59();
		e.value1=10;
		e.value2=20;
		System.out.println("进行值交换前的value1:"+e.value1+" 进行值交换后的value2:"+e.value2);
		swap(e);
		System.out.println("执行swap方法过后的value1:"+e.value1+" 进行值交换后的value2:"+e.value2);
	}
}
//运行结果
//进行值交换前的value1:10 进行值交换后的value2:20
//在swap方法中进行值交换后的value1:20 进行值交换后的values2:10
//执行swap方法过后的value1:20 进行值交换后的value2:10

分析:为什么这一次又成功的改变了值呢?

main栈区跟swap栈区都引用了exercise59这个类,只是main方法先对exercise59这个类中的value1跟value2进行变量的初始化,然后swap栈区再对exercise59这个类中的value1跟value2进行值交换。

初窥内存图
在这里插入图片描述
jvm到底做了什么呢?

第一步:jvm创建main栈区,通过new exercise59() ,将exercise59这个类加载到堆内存中去,并分配内存地址,把这个16进制的内存地址,记录在了main栈区里,传给了e这个变量,并且将exercise59这个类中的value1和value2进行了形参定义。随后对value1和value2进行了赋值操作,将形参变为实参。最后将对象e的地址传参给swap()方法。
第二步:jvm创建swap栈区,在栈区定义一个形参exercise59 e,来接收传入的exercise59的对象。接收成功,形参变为实参。再初始化变量temp,将exercise59这个类中的value1的值赋值给temp,再将exercise59这个类中的value2的值赋值给exercise59这个类中的value1,最后将temp的值赋值给exercise59这个类中的value2,在swap栈区完成交换操作。将swap栈区的所有东西,从上至下的进行弹栈操作,swap栈区消失。
第三步:main方法执行完毕,将main栈区的所有东西,从上至下的进行弹栈操作,main栈区消失。

分析:在swap()方法里,value1和value2这两个值被交换成功。不仅如此,swap()方法执行完毕,main()方法里的value1跟value2也被交换了,这很容易造成一种错觉,什么样的错觉呢?传入swap()方法的是一个对象,而不是他的复制品。但事实上呢,举个例子来说吧,假如我有一台电脑,我可以有多种关机的方法,我可以在电脑面前关直接电源关机,但是我偏不,我用远程的ssh连接这台电脑的ip地址,我用命令给他关机了,ip地址就等于这个案例中的e对象的引用地址,我在当前电脑操作跟我在远程操作是一样的,因为引用对象是一样的。所以说这还是不折不扣的值传递方式,e只是一个引用变量,系统复制了e变量,但是他并没有复制exercise59这个类。

我们可以证明一下swap()方法中的e跟main()方法中的e到底是不是两个变量!
我们可以在swap()方法中的最后一行加入:

//将null赋值给e,让这个对象不再指向任何有效地址
e=null;
//运行结果:
//进行值交换前的value1:10 进行值交换后的value2:20
//在swap方法中进行值交换后的value1:20 进行值交换后的values2:10
//执行swap方法过后的value1:20 进行值交换后的value2:10

分析:即使将swap()方法中的e对象指向null,也能交换值。这是由于e虽然是两个变量但是他们指向了同一块内存地址!,即操作的也是同一块内存地址!,操作的过程就是进行了值转递!

再窥内存图:

在这里插入图片描述
结论:java中的引用类型的参数传递也为值传递。

总结论:java中的参数传递只有一种,即值传递。

来一波,推送吧!
群号:781121386
群名:人生苦短,我学编程
欢迎大家加入我们,一起交流技术!!!

发布了38 篇原创文章 · 获赞 128 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/lujiangyang123/article/details/103294590