`

也谈JAVA值传参和引用传参

    博客分类:
  • Java
阅读更多
这是个老生常谈的问题了,经过了先辈们无数的争论,但是还是没有提出一个令所有人满意的答案。当然,我在这里只是就我自己对Java和其他语言的一些认识谈谈自己的理解和看法。
首先,我们要明确下面这两条不可争论的事实了:
【1】、对象是传引用的
【2】、基本类型是传值的

我对这个问题是这样理解的:
有过C++背景的朋友可能都会有一个认识,因为C/C++传参有很多种选择,直接传递对象或值,或者传递指针,传递引用等。而大家都有个共识,那就是在传递基本数据类型时,选择第一种,而传递对象的时候则选用传指针或者引用。这点可以从数据传输效率的角度上得到解释。
而Java创始人James在某些方面还是得借鉴一些C的思想的,所以上述两条就比较好理解了。
个人对传参的理解是:
函数参数相对其他字段,在编译器里肯定会有特殊处理的,而一般的编译器则是这样处理:
在运行栈中拷贝一份传递过来的参数(值参),而后面函数体的关于值参的操作实际上都是针对运行栈里保存的备份操作的。可以用这种理解验证一下C++的三种情况。例如传递过来的是引用,则编译器的处理流程是这样的:
首先拷贝传递来的值参(注意:值参也是引用,Java里new之后获得的都是对象的引用,而不是对象本身,这点和c++很不同),然后将其压入运行栈,后面的操作也就是对这份拷贝(也是对预期的对象的引用,所以可以到达对象)操作了。

我们看看以下的几个例子来增加对上述思想的理解。

Example1:
package com.shansun.ref;

public class RefInteger {

	public RefInteger() {
	}

	public void swap(Integer para1, Integer para2) {
		Integer tmp;
		tmp = para1;
		para1 = para2;
		para2 = tmp;
	}

	public void print(int int1, int int2) {
		System.out.println("Int1 is " + int1);
		System.out.println("Int2 is " + int2);
	}

	public static void main(String[] args) {
		Integer int1 = new Integer(10);
		Integer int2 = new Integer(50);

		RefInteger ref = new RefInteger();
		System.out.println("---------SWAP UNABLE----------");
		ref.swap(int1, int2);
		ref.print(int1, int2);
	}
}

运行结果:
---------SWAP UNABLE----------
Int1 is 10
Int2 is 50

我们都知道一点:void swap(int, int)是不能达到交换两个参数的值的。我们这里试试使用Integer对象呢?同样也交换失败,原因很简单:void swap(Integer para1, Integer para2)函数传递进来的是两个类型为Integer类的引用,而在函数体里实现的功能只是改变了para1和para2引用的对象,而para1和para2在内存中的值却一点变化都没有。可能这样理解更加形象一点:
例如,A省省长是小王,B省省长是小张,这样牵强的理解为小王是A省的一个引用,小张则是B省的一个引用。在执行swap(小王,小张)后,产生的效果只是:现在A省省长是小张,B省省长是小王了,即现在小张代表的不再是B省,而是A省了,而对于对象本身,如A省,其地理位置,人口数量等都没有变化。不能说在swap语句执行后,A省跑到地图上B省的位置去了吧?

Example2:
package com.shansun.ref;

public class RefObject {

	int val;

	public RefObject(int val) {
		this.val = val;
	}

	public void setVal(int val) {
		this.val = val;
	}

	public int getVal() {
		return this.val;
	}

	public void print() {
		System.out.println("Now val is " + val);
	}

	public static void swap(RefObject obj1, RefObject obj2) {
		RefObject tmp;
		tmp = obj1;
		obj1 = obj2;
		obj2 = tmp;
	}

	public static void swapx(RefObject obj1, RefObject obj2) {
		int tmp;
		tmp = obj1.getVal();
		obj1.setVal(obj2.getVal());
		obj2.setVal(tmp);
	}

	public static void main(String[] args) {
		RefObject obj1 = new RefObject(10);
		RefObject obj2 = new RefObject(50);
		obj1.print();
		obj2.print();
		swap(obj1, obj2);
		obj1.print();
		obj2.print();
		swapx(obj1, obj2);
		obj1.print();
		obj2.print();
	}
}

执行结果:
---------SWAP UNABLE----------
Int1 is 10
Int2 is 50
---------BEFORE SWAP----------
Int1 is 20
Int2 is 30
---------SWAP ENABLE----------
Int1 is 30
Int2 is 20

首先和上面例子一样,swap(obj1, obj2)也是换汤不换药的,所以交换失败。但是swapx则交换数据成功了,我们还拿上面省长的例子来解释这个现象:
国家要求小王代表的A省的C市市长和小张代表的B省的D市市长交换职位,可以吗?当然可以了,所以交换成功。

我们最后再回到Example1的问题,如果我就想交换两个int值呢?其实也不是没有办法的。
package com.shansun.ref;

public class RefInteger {

	int int1, int2;

	public RefInteger(int int1, int int2) {
		this.int1 = int1;
		this.int2 = int2;
	}

	public void swap() {
		int tmp;
		tmp = int1;
		int1 = int2;
		int2 = tmp;
	}

	public void print() {
		System.out.println("Int1 is " + int1);
		System.out.println("Int2 is " + int2);
	}

	public static void main(String[] args) {
		RefInteger ref = new RefInteger(20, 30);
		System.out.println("---------BEFORE SWAP----------");
		ref.print();
		System.out.println("---------SWAP ENABLE----------");
		ref.swap();
		ref.print();
	}
}


执行结果:
---------BEFORE SWAP----------
Int1 is 20
Int2 is 30
---------SWAP ENABLE----------
Int1 is 30
Int2 is 20

小记:
C++:使用分界符&标示引用
C#: 使用ref关键字标示引用

好了,关于传参的问题我的理解就是这样的。如有错误,敬请指出。

6
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics