Summary: Java中函数参数的传递
函数调用参数传递类型(java)的用法介绍.
java方法中传值和传引用的问题是个基本问题,但是也有很多人一时弄不清。
(一)基本数据类型:传值,方法不会改变实参的值。
1 public class TestFun { 2 3 public static void testInt(int i){ 4 5 i=5; 6 7 } 8 9 10 11 public static void main(String[] args) { 12 13 int a=0 14 15 TestFun.testInt(a); 16 17 System.out.println("a="+a); 18 19 } 20 21 }
程序执行结果:a=0 。
(二)对象类型参数:传引用,方法体内改变形参引用,不会改变实参的
引用,但有可能改变实参对象的属性值。
举两个例子:
(1)方法体内改变形参引用,但不会改变实参引用,实参值不变。
1 public class TestFun2 { 2 3 public static void testStr(String str){ 4 5 str="hello";//型参指向字符串 “hello” 6 7 } 8 9 public static void main(String[] args) { 10 11 String s="1" 12 13 TestFun2.testStr(s); 14 15 System.out.println("s="+s); //实参s引用没变,值也不变 16 17 } 18 19 }
执行结果打印:s=1
(2)方法体内,通过引用改变了实际参数对象的内容,注意是“内容”,引用还是不变的。
1 import java.util.HashMap; 2 3 import java.util.Map; 4 5 public class TestFun3 { 6 7 public static void testMap(Map map){ 8 9 map.put("key2","value2");//通过引用,改变了实参的内容 10 11 } 12 13 14 public static void main(String[] args) { 15 16 Map map = new HashMap(); 17 18 map.put("key1", "value1"); 19 20 new TestFun3().testMap(map); 21 22 System.out.println("map size:"+map.size()); //map内容变化了 23 24 } 25 26 }
执行结果,打印:map size:2 。可见在方法testMap()内改变了实参的内容。
(3)第二个例子是拿map举例的,还有经常涉及的是StringBuffer :
1 public class TestFun4 { 2 3 public static void testStringBuffer(StringBuffer sb){ 4 5 sb.append("java");//改变了实参的内容 6 7 } 8 9 public static void main(String[] args) { 10 11 StringBuffer sb= new StringBuffer("my "); 12 13 new TestFun4().testStringBuffer(sb); 14 15 System.out.println("sb="+sb.toString());//内容变化了 16 17 } 18 19 }
执行结果,打印:sb=my java 。
所以比较参数是String和StringBuffer 的两个例子就会理解什么是“改变实参对象内容”了。
总结:
第一:java方法基本数据类型是传值,对象类型传引用,这是千真万确的。
第二:当参数是对象时,无论方法体内进行了何种操作,都不会改变实参 对 对象的引用。
第三:当参数是对象时,只有在方法内部改变了对象的内容时,才会改变实参对象内容。
进一步的思考
可以看出Java中参数的传递是传递的值,不过这个值有两种,一种是我们看得到的值,也就是primitive类型,比如int,double,char类型的值。另一种就是我们看不到的值,也就是地址值,也可以说是引用值,就是你传递对象或数组类型的参数时传递的值。
其实我们也可以这么理解,当传参数的时候,会把参数的primitive值或者引用值copy给形式参数。因此,任你在方法里面怎么修改这些值都不会对原来的变量有影响。
但是对于引用值,如果你在方法里面利用这个引用值访问对象或者数组里面的数据或方法的时候,这和在方法外面直接做事一样的。因为他们持有的是同一个地址。
理解了这些,试想一下,如果在参数前加上final有什么好处吗?
如果是primitive的参数,有什么用?只能是说你在方法里面不能修改这个变量的值。其实你就算修改了,也只是对方法自己有影响,不会对调用者有影响。
如果是引用类型的参数呢?又有什么用?不可能说不让你修改对象的值,只能不让你修改这个形式参数变量的引用值。到底有什么好处呢?
从设计API的角度来说,如果你在primitive参数上加上final,就是告诉自己,这些值对于我的方法来说就是常量,是我需要参考的东西。他们的值在我的整个方法的所有的地方都是一样的。试想,如果不加final,有可能你在方法里面前后两次访问这个变量的值不同,因为你在这之间无意中修改了它。这样就达不到你的意图。
那么如果是引用类型的参数呢?这里面的意图,就是告诉你在方法里面访问(不论是读还是修改)的这个对象就是传进来的对象,而不是别的。同样保证你在方法里面不同地方的操作都是对这一个对象的操作。
再进一步的思考
有了前面的思考,我们就可以知道,在设计方法的时候,尽量在参数前面加上final,不管是primitive类型的还是在引用类型的前面。因为这样意图非常简单清楚,在方法里面也可以大胆的引用。回顾你需要修改它们的时候,是不是无意中把他们当成自己定义的临时变量了,这样很危险,容易出乱子。这样是为什么当你用sun的规范做check style的时候他让你改的原因。
还要再来一点思考
为什么当你调用回调函数的时候必须得加final呢?看了一篇文章说是多线程以及垃圾回收的原因迫使的,我得研究以后再来补充。