您的位置:首页 > 职场人生

Java面试题- java中函数传参传递的是引用的拷贝

2011-10-24 09:40 585 查看
身边有些做Java开发的朋友,找工作时常常被考到一道关于字符串的题目。题目倒是很基础,然而根据朋友们事后的描述,有理由认为有的面试官自己都没有完全搞清楚这个问题。此外,在CSDN论坛中我也多次看到一些朋友在这个问题上的迷惑。索性把自己的理解写下来吧。

 

题目是一道简单的小程序,像下面这样:

view plain

public class Test1 {  
 public static void main(String args[]) {  
  String s = new String("Hello");  
  System.out.println(s);  
  
  foo(s);  
  System.out.println(s);  
 }  
  
 public static void foo(String s) {  
  s = new String("World");  
 }  
}  

问程序先后两次分别会输出什么。

第一个肯定输出“Hello”。关键是第二个,个别基础不牢的朋友可能被考倒,但基础稍微扎实一点的就不会。第二个输出的也是“Hello”。

到这里,万事大吉。面试官两眼放出喜悦的光芒,赞叹道:“嗯,不错不错。不会变的,对吧。因为String是immutable类型,immutable类型改不了的。”他说的“immutable”是指String类没有任何一个方法会改变对象的状态。

 

这可就有问题了。确实程序两次输出的都是“Hello”,但原因却不是String的immutable特性。不信换一个“mutable”的试试,比如StringBuilder或StringBuffer。

view plain

public class Test2 {  
 public static void main(String args[]) {  
  StringBuilder s = new StringBuilder("Hello");  
  System.out.println(s);  
  
  foo(s);  
  System.out.println(s);  
 }  
  
 public static void foo(StringBuilder s) {  
  s = new StringBuilder("World");  
 }  
}  

这次呢?会先输出“Hello”,然后输出“World”吗?不会,仍然是两次“Hello”。

足以说明这个问题跟String的immutable特性没有一毛钱的关系。不管是immutable类型,还是一般的类型,用这种方式写出来的程序main方法中前后两个对象肯定是一样的。

 

真正的原因是:Java语言的参数传递机制是“按值传递”(pass by value)。虽然Java中除基本数值类型外,其它变量都是引用,但那是另一回事。变量的语义(“引用”还是“值”)跟函数传参的机制是两个正交的概念。

各种编程语言中,最常见的参数传递方式不外乎按值传递和按引用传递(pass by reference)两种。比如,C和Java中函数参数都是按值传递的,C++和C#则同时支持按值传递和按引用传递。C和Java的不同在于,C是值类型的按值传递,而Java是引用类型的按值传递(基本的数值类型除外)。
按值传参最大的特点就是函数内部对“形参变量”本身的所做的修改外面的“实参变量”感知不到。

 

不过,对于StringBuilder来说我们至少有办法让它改变,比如像下面这样:

view plain

public class Test3 {  
 public static void main(String args[]) {  
  StringBuilder s = new StringBuilder("Hello");  
  System.out.println(s);  
  
  foo(s);  
  System.out.println(s);  
 }  
  
 public static void foo(StringBuilder s) {  
  s.replace(0, s.length(), "World");  
 }  
}  

体会到不同了吗?虽然参数变量本身是按值传递的,但这次我们对变量本身不感兴趣,我们不改变变量本身,而是通过它直接修改它所引用的那个对象。这一次,Java语言“几乎一切皆引用”的特点起作用了,程序第一次输出“Hello”,第二次输出“World”。熟悉C的朋友可能马上联想到:这很像C语言中通过指针来修改它指向的内容。

而先前那个String版本的程序,我们无法写出一个相应的可变版本。为什么呢?……“嗯,不错不错。因为String是immutable类型……”,这次真对了。——可见先前说“没有一毛钱的关系”也不尽然。因为immutable,导
a2e5
致我们无法针对String写出一个像Test3那样的“可变”程序,如此说来,“二分钱的关系”应该是有的。

 

“pass reference by value”就像C++ 11中的“An lvalue with rvalue reference type”一样,乍一看挺绕的,但只要仔细想清楚,让正交的东西“尘归尘 土归土”,就可以加深对语言的理解。

顺便问一句,您知道java.lang.StringBuilder和java.lang.StringBuffer的区别吗?好多面试官都喜欢“顺便”问问这个,而且答对了不会加分,答不上来却会扣分,至少扣印象分。:(
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息