我的Java开发学习之旅------>Java语言中方法的參数传递机制

      

实參:假设声明方法时包括来了形參声明,则调用方法时必须给这些形參指定參数值,调用方法时传给形參的參数值也被称为实參。

     Java的实參值是怎样传入方法?这是由Java方法的參数传递机制来控制的,Java里方法的參数传递方式仅仅有一种:值传递。所谓值传递,就是将实际參数的副本(复制品)传入方法内,而參数本身不会收到不论什么影响。


一、參数类型是原始类型值传递


以下通过一个程序来演练 參数类型是原始类型值传递的效果:

public class ParamTransferTest {
	
	public static void swap(int a,int b){
		//以下三行代码实现a、b变量的值交换
		//定义一个暂时变量来保存a变量的值
		int temp=a;
		//把b的值赋给a
		a=b;
		//把暂时变量temp的值赋给a
		b=temp;
		System.out.println("swap方法中。a的值是:"+a+", b的值是:"+b);
	}
	
	public static void main(String[] args) {
		int a=6;
		int b=9;
		swap(a, b);
		System.out.println("交换结束后。变量a的值是:"+a+", 变量b的值是:"+b);
	}

}

     程序结果为:

swap方法中,a的值是:9, b的值是:6
交换结束后,变量a的值是:6, 变量b的值是:9

从上面执行结果来看,swap方法中的a和b的值是9、6。交换结束后。变量a和b的值依旧是6、9。

从这个执行结果能够看出,main方法里的变量a和b。并非swap方法中的a和b。

swap方法中的a和b仅仅是main方法中的变量a和b的复制品。以下通过示意图来说明上面程序的运行过程。

Java程序总是从main方法開始运行。main方法開始定义了a、b两个局部变量,两个变量在内存中存储示意图如图1所看到的。

      当程序运行swap方法是,系统进入swap方法,并将main方法中的a、b变量作为參数传入swap方法,传入swap方法的仅仅是a、b的副本。而不是a、b本身。进入swap方法后系统产生了4个变量。这4个变量在内存中的存储示意图如图2所看到的。

     

在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变量,仅仅是存在于不同的方法栈区中而已。

 程序在swap方法中交换a、b两个变量的值,实际上是对图2中灰色覆盖区域的a、b变量进行交换,交换结束后swap方法中输出a、b变量的值,看到a的值为9,b的值为6,此时内存中的存储示意图如图3所看到的。


对照图3和图2,两个示意图中main方法栈区的a、b值并未有不论什么改变,程序改变的仅仅是swap方法栈区中的a、b值。

这就是值传递的实质:当系统開始运行方法时,系统为形參运行初始化,就是把实參变量的值赋给方法的形參变量。方法里操作并非实际的參数变量。




二、參数类型是引用类型值传递

以下通过一个程序来演练 參数类型是引用类型值传递的效果:

class DataWrap {
	public int a;
	public int b;
}

public class ReferenceTransferTest {

	public static void swap(DataWrap dw) {
		// 以下三行代码实现dw对象的a、b两个Field交换
		// 定义一个暂时变量来保存dw对象a Field的值
		int temp = dw.a;
		// 把dw对象b Field的值赋给a Field
		dw.a = dw.b;
		// 把暂时变量temp的值赋给dw对象b Field
		dw.b = temp;
		System.out.println("swap方法中,a Field的值是:" + dw.a + ", b Field的值是:"
				+ dw.b);
	}

	public static void main(String[] args) {
		DataWrap dw = new DataWrap();
		dw.a = 6;
		dw.b = 9;
		swap(dw);
		System.out.println("交换结束后,a Field的值是:" + dw.a + ", b Field的值是:" + dw.b);
	}

}

程序结果为:

swap方法中,a Field的值是:9, b Field的值是:6
交换结束后。a Field的值是:9, b Field的值是:6

从上面运行结果来看,在swap方法里,a、b两个Field值被交换成功。不仅如此,main方法里swap方法运行结束后,a、b两个Field值也被交换了。这非常easy造成一个错觉:调用swap方法时。传入swap方法的就是dw对象本身,而不是它的复制品。可是这不过一种错觉,以下还是结合示意图来说明程序的运行过程。

程序从main方法開始运行。main方法開始创建了一个DataWrap对象,并定义了一个dw引用变量来指向DataWrap对象,这是一个与基本类型不同的地方。

创建一个对象时,系统内存中有两个东西:堆内存中保存对象本身。栈内存中保存了引用该对象的引用变量。接着程序通过引用来操作DataWrap对象,把该对象的a、b两个Field分别赋值给6、9。

此时系统内存中的存储示意图如图4所看到的。




接下来。main方法中開始调用swap方法,main方法并未结束,系统会分别开辟出main和swap两个栈区,用于存放main和swap方法的局部变量。调用swap方法时,dw变量作为实參传入swap方法,相同採用值传递方式:把main方法里的dw变量的值赋给swap方法里的dw形參。从而完毕swap方法的dw形參的初始化。值得指出的是:main方法中的dw是一个引用(也就是一个指针),它保存了DataWrap对象的地址值,当把dw的值赋给swap方法的dw形參后,即让swap方法的dw形參也保存了这个地址值,即也会引用到堆内存中的DataWrap对象。图5显示了dw传入swap方法后的存储示意图。



从图5来看。这样的參数传递方式是不折不扣的值传递方式,系统一样复制了dw的副本传入swap方法。但关键在于dw仅仅是一个引用变量。所以系统复制了dw变量。但并未复制DataWrap对象 。

当程序在swap方法中操作dw形參时。因为dw仅仅是一个引用变量,故实际操作的还是堆内存中的DataWrap对象。此时,无论操作main方法里的dw变量,还是操作swap方法里的dw变量,事实上都是操作它所引用的DataWrap对象。它们操作的是同一个对象。

因此。当swap方法中交换dw參数所引用DataWrap对象的a、b两个Field值后,我们看到main方法中的dw变量所引用DataWrap对象的a、b两个Field值也被交换了。


为了更好地证明main方法中的dw和swap方法中的dw是两个变量。我们在swap方法的最好一行添加例如以下代码:dw=null;

public static void swap(DataWrap dw) {
		// 以下三行代码实现dw对象的a、b两个Field交换
		// 定义一个暂时变量来保存dw对象a Field的值
		int temp = dw.a;
		// 把dw对象b Field的值赋给a Field
		dw.a = dw.b;
		// 把暂时变量temp的值赋给dw对象b Field
		dw.b = temp;
		System.out.println("swap方法中,a Field的值是:" + dw.a + ", b Field的值是:"
				+ dw.b);
		//把dw直接赋值为null,让它不再指向不论什么有效地址
		dw=null;
	}

运行上面改动后的代码结果是swap方法中的dw变量不再指向不论什么有效内存,程序其它地方不做不论什么改动。

main方法调用swap方法后。再次訪问dw变量a、b两个Field,依旧能够输出9、6。可见,main方法中的dw变量没有收到不论什么影响。

实际上,当swap方法中添加dw=null;代码后,内存中的存储示意图如图6所看到的。

从图6来看,把swap方法中的dw赋值为null后,swap方法中失去了DataWrap对象的引用,不可再訪问堆内存中的DataWrap对象。但main方法中的dw变量不受不论什么影响,依旧引用DataWrap对象。所以依旧能够输出DataWrap对象的a、b Field值。


三、总结


      Java 编程语言仅仅有值传递參数

当一个对象实例作为一个參数被传递到方法中时,參数的值就是该对象的引用一个副本。指向同一个对象,对象的内容能够在被调用的方法中改变。但对象的引用(不是引用的副本)是永远不会改变的。

  Java參数,无论是原始类型还是引用类型。传递的都是副本(有第二种说法是传值,可是说传副本更好理解吧,传值一般是相对传址而言)。

  假设參数类型是原始类型。那么传过来的就是这个參数的一个副本,也就是这个原始參数的值,这个跟之前所谈的传值是一样的。假设在函数中改变了副本的值不会改变原始的值.
  假设參数类型是引用类型。那么传过来的就是这个引用參数的副本。这个副本存放的是參数的地址。假设在函数中没有改变这个副本的地址。而是改变了地址中的值。那么在函数内的改变会影响到传入的參数。假设在函数中改变了副本的地址,如new一个。那么副本就指向了一个新的地址。此时传入的參数还是指向原来的地址,所以不会改变參数的值。

                        

                 ========以上内容出处为李刚老师的《疯狂Java讲义》=======




==================================================================================================

  作者:欧阳鹏  欢迎转载。与人分享是进步的源泉!

  转载请保留原文地址http://blog.csdn.net/ouyang_peng

==================================================================================================



原文地址:https://www.cnblogs.com/wgwyanfs/p/6878916.html