标签:
通过前一篇文章的介绍,我们从整体上明白了,Java类中变量的差异性、不同变量在内存中的存储位置,以及变量的生命周期等。今天,我们来看一下Java中参数传递的机制。
形参:方法声明时包含的参数声明
实参:调用方法时,实际传给形参的参数值
Java方法的参数传递机制:
Java方法的参数传递只有一种:值传递。所谓值传递,就是将实际参数值的副本,传入方法内,而参数本身不会收到任何影响。
PS:传入方法的时实际参数值的复制品,不管方法中对这个复制品如何操作,实际参数本身不会受到任何影响。
首先,来看一个demo,
public class TestPrimitiveTransfer { public static void swap(int a,int b){ int temp =a ; a = b; b = temp; System.out.println("swap方法内,a="+a+";b="+b); } public static void main(String[] args) { int a = 6; int b =9; TestPrimitiveTransfer.swap(a, b); System.out.println("main函数里面,a="+a+";b="+b); } }
执行结果是:
swap方法内,a=9;b=6
main函数里面,a=6;b=9
当程序执行swap方法时,系统进入swap方法,病将main方法中的a、b变量作为参数值传入swap方法,传入swap方法的只是a、b的副本,而不是a、b本身,进入swap方法后,系统中产生了4个变量,这四个变量再内存中的存储示意图如下:
在main方法中调用swap方法时,main方法还没有结束。
因此系统分别为main方法和swap方法分类两块栈区,分别用于保存main方法和swap方法的局部变量。
main方法中的a、b变量作为参数值传入swap方法,实际上再swap方法栈区中重新产生了两个变量a、b,并将main方法栈区中的a、b变量的值,分别赋给swap方法栈区的a、b参数(就是swap方法的a、b形参进行了初始化)。
此时,系统存在两个a变量、两个b变量,只是存在于不同的方法栈区中而已。
值传递的实质:
上面的交换程序,main方法栈区中a、b的值并没有任何改变,程序改变的只是swap方法栈中的a、b。
当系统开始执行方法时,系统为形参执行初始化,就是把实参变量的值赋给方法的形参变量,方法里操作的并不是实际的实参变量。
Java对于引用类型的参数传递,一样采用的是值传递方式。但是使用对象引用时,容易发生误解,因为它能够交换成功。
看如下例子:
public class DataSwap { public int a; public int b; }
测试:
public class TestReferenceTransfer { public static void swap(DataSwap ds){ int temp = ds.a; ds.a=ds.b; ds.b=temp; System.out.println("swap方法内,a="+ds.a+";b="+ds.b); // ds = null ;// 把ds直接赋为null,让它不在指向任何有效地址。 } public static void main(String[] args) { DataSwap ds = new DataSwap(); ds.a=6; ds.b=9; swap(ds); System.out.println("main方法内,a="+ds.a+";b="+ds.b); } }
运行结果:
swap方法内,a=9;b=6
main方法内,a=9;b=6
上面运行结果看:再swap方法里,a、b两个属性值被交换成功。不仅如此,main方法里的swap方法直接结束后,a、b两个属性的值也被交换了。这很容易造成一种错觉:调用swap方法时,传入swap方法的,就是dw对象本身,而不是它的复制品。但这种结论时错误的。
程序从main方法开始执行,main方法创建了一个DataSwap对象,并定义了一个dw引用变量来指向DataSwap对象。
创建一个对象时,系统内存中有两个实体:堆内存中保存了对象本身,栈内存中保存了该对象的引用。
接着,程序通过引用操作DataSwap对象,把该对象的a、b属性分别赋值。如下图:
接下来,main方法里开始调用swap方法,main方法并未结束,系统会分别开辟出main和swap两个栈区,分别用于存放main和swap方法的局部变量。
调用swap方法时,ds变量作为实参,传入swap方法,同样采用值传递方式:
把main方法里ds变量的值,赋给swap方法里的dw形参,从而完成了swap方法的ds形参的初始化。
值得指出的是:main方法里的ds是一个引用,它保存的DataSwap对象的地址值,档把dw的值赋给swap方法的dw形参后,即让swap方法的dw形参也保存这个地址值,即也引用到堆内存的DataSwap对象。
如下图,显示了ds传入swap方法后的存储示意图:
如上图,这种参数传递方式,是不折不扣的值传递方式,系统一样复制了ds的副本传入swap方法,但关键在于ds只是一个引用变量,所以系统复制了ds变量,但并未赋值DataSwap对象。
真相揭秘:
当程序再swap方法中操作ds形参时,由于ds只是一个引用变量,故实际操作的还是堆内存中的DataSwap对象。此时,不管是操作main方法里的dw变量,还是操作swap方法里的dw参数,其实都是操作的它所引用的DataSwap对象,它们操作的是同一个对象。因此,当swap方法中交换dw参数所引用DataSwap对象的a、b属性值后,它们看到main方法中的dw变量所引用DataSwap对象的a、b属性值也被交换了。
为了更好地证明main方法中的dw变量和swap方法中的dw是两个变量,我们再swap方法的最后一行增加如下代码:
dw = null ;//把dw直接赋为null,让它不在指向任何有效地址。
public class TestReferenceTransfer { public static void swap(DataSwap ds){ int temp = ds.a; ds.a=ds.b; ds.b=temp; System.out.println("swap方法内,a="+ds.a+";b="+ds.b); ds = null ;// 把ds直接赋为null,让它不在指向任何有效地址。 } public static void main(String[] args) { DataSwap ds = new DataSwap(); ds.a=6; ds.b=9; swap(ds); System.out.println("main方法内,a="+ds.a+";b="+ds.b); } }
swap方法内,a=9;b=6
main方法内,a=9;b=6
示意图:
解析:
swap里的ds变量不在指向任何有效内存地址,程序其他地方不做任何修改。main方法调用了swap方法后,再次访问ds变量的a、b属性,依然可以输出结果。可见,main方法里的ds变量没有收到任何影响。
形参长度可变长的方法
从JDK1.5之后,Java允许定义形参长度可变的参数,从而允许为方法指定数量不确定的形参。
如果在定义方法时,再最后一个形参的类型后增加三点(...),则辨明该形参可接受多个参数值,多个参数值被当作数组传入。
限制:该中方式的参数,必须放在形参的末尾
总结一下:
Java中的参数传递,无论是基本类型,还是引用类型,他们传递的都是一份副本。基本类型传递的是值本身的副本,引用类型传递的是地址的副本。
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/liu765023051/article/details/47420971