`
JackyCheng2007
  • 浏览: 250003 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Java中函数参数的传递

    博客分类:
  • Java
阅读更多
一般谈到函数的参数传递的时候都会想到值传递和引用传递。看看本文的例子就会很清楚了。

public class MethodParameter {
	public static void main(String[] args) {
		String str = "Hello";
		char[] c = new char[] { 'W', 'o', 'r', 'l', 'd' };
		StringBuffer sb = new StringBuffer("Jacky");

		changeAddr(str, c, sb);
		System.out.println(str);
		System.out.println(c);
		System.out.println(sb.toString());

		changeValue(str, c, sb);
		System.out.println(str);
		System.out.println(c);
		System.out.println(sb.toString());
	}

	private static void changeAddr(String str, char[] c, StringBuffer sb) {
		str = "Hi";
		c = new char[] { 'C', 'h', 'i', 'n', 'a' };
		sb = new StringBuffer(" Cheng");
	}

	private static void changeValue(String str, char[] c, StringBuffer sb) {
		str.toUpperCase();
		c[0] = 'C';
		sb.append(" Cheng");
	}
}


你认为会得到什么结果呢?不如先别看结果看看你是不是能做对。

引用

Hello
World
Jacky
Hello
Corld
Jacky Cheng

因为值传递很简单,比如传int值,你肯定知道在函数里无论你怎么修改这个int值都不会改变它传进来之前的变量的值。所以,我的这个例子中都是引用传递。
我觉得得说明一下什么是值,什么时候引用。其实值通常是只原始数据类型比如int,char等,但不包括String哦。所谓引用就是只指向对象或者数组的地址的值。

知道了这个,就应该知道上面sb的变化原因了。至于str,你可知道String是immutable类,所以你是没有办法修改它的值的,只能改变它的引用。对于c,是一个数组,数组引用和对象引用遵循同样的规制。

另外其实数组也是一种对象,而且还是一级对象,还记的你对数组的构建也是new出来的吗?无论是原始值数组还是对象数组基本没有什么区别,只是前者存原始类型的值,后者存贮对象的引用值。数组只有一个只读成员length,它告诉你数组的长度。另外还有唯一一个访问方法就是'[]',通过这个你可以读取或者赋值某一个数组元素。如果你用过java.lang.System提供的数组copy方法:
public static void arraycopy(Object src,
                             int srcPos,
                             Object dest,
                             int destPos,
                             int length)

int[] dataFrom = new int[]{1,2,3};
int[] dataTo = new int[10];
//把dataFrom 所有元素copy到dataTo里从3开始的后面。
System.arraycopy(dataFrom , 0, dataTo, 3, 3);


你看,数组是可以作为Object传入这个方法的,利用这一点,你也可以自己写通用的数组函数了。只要把Object作为形参类型就好。

进一步的思考
可以看出Java中参数的传递是传递的值,不过这个值有两种,一种是我们看得到的值,也就是primitive类型,比如int,double,char类型的值。另一种就是我们看不到的值,也就是地址值,也可以说是引用值,就是你传递对象或数组类型的参数时传递的值。
其实我们也可以这么理解,当传参数的时候,会把参数的primitive值或者引用值copy给形式参数。因此,任你在方法里面怎么修改这些值都不会对原来的变量有影响。
但是对于引用值,如果你在方法里面利用这个引用值访问对象或者数组里面的数据或方法的时候,这和在方法外面直接做事一样的。因为他们持有的是同一个地址。

理解了这些,试想一下,如果在参数前加上final有什么好处吗?
如果是primitive的参数,有什么用?只能是说你在方法里面不能修改这个变量的值。其实你就算修改了,也只是对方法自己有影响,不会对调用者有影响。
如果是引用类型的参数呢?又有什么用?不可能说不让你修改对象的值,只能不让你修改这个形式参数变量的引用值。到底有什么好处呢?
从设计API的角度来说,如果你在primitive参数上加上final,就是告诉自己,这些值对于我的方法来说就是常量,是我需要参考的东西。他们的值在我的整个方法的所有的地方都是一样的。试想,如果不加final,有可能你在方法里面前后两次访问这个变量的值不同,因为你在这之间无意中修改了它。这样就达不到你的意图。
那么如果是引用类型的参数呢?这里面的意图,就是告诉你在方法里面访问(不论是读还是修改)的这个对象就是传进来的对象,而不是别的。同样保证你在方法里面不同地方的操作都是对这一个对象的操作。

再进一步的思考
有了前面的思考,我们就可以知道,在设计方法的时候,尽量在参数前面加上final,不管是primitive类型的还是在引用类型的前面。因为这样意图非常简单清楚,在方法里面也可以大胆的引用。回顾你需要修改它们的时候,是不是无意中把他们当成自己定义的临时变量了,这样很危险,容易出乱子。这样是为什么当你用sun的规范做check style的时候他让你改的原因。

还要再来一点思考
为什么当你调用回调函数的时候必须得加final呢?看了一篇文章说是多线程以及垃圾回收的原因迫使的,我得研究以后再来补充。


2
0
分享到:
评论
1 楼 swincle 2011-10-19  
讲的很好~

相关推荐

Global site tag (gtag.js) - Google Analytics