由面试题引发对java字符串思考【转】
2011-10-23 15:10
435 查看
身边有些做Java开发的朋友,找工作时常常被考到一道关于字符串的题目。题目倒是很基础,然而根据朋友们事后的描述,有理由认为有的面试官自己都没有完全搞清楚这个问题。此外,在CSDN论坛中我也多次看到一些朋友在这个问题上的迷惑。索性把自己的理解写下来吧。
题目是一道简单的小程序,像下面这样:
view plaincopy
to clipboardprint?
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”,然后输出“World”吗?不会,仍然是两次“Hello”。
足以说明这个问题跟String的immutable特性没有一毛钱的关系。不管是immutable类型,还是一般的类型,用这种方式写出来的程序main方法中前后两个对象肯定是一样的。
真正的原因是:Java语言的参数传递机制是“按值传递”(pass by value)。虽然Java中除基本数值类型外,其它变量都是引用,但那是另一回事。变量的语义(“引用”还是“值”)跟函数传参的机制是两个正交的概念。
各种编程语言中,最常见的参数传递方式不外乎按值传递和按引用传递(pass by reference)两种。比如,C和Java中函数参数都是按值传递的,C++和C#则同时支持按值传递和按引用传递。C和Java的不同在于,C是值类型的按值传递,而Java是引用类型的按值传递(基本的数值类型除外)。
按值传参最大的特点就是函数内部对“形参变量”本身的所做的修改外面的“实参变量”感知不到。
不过,对于StringBuilder来说我们至少有办法让它改变,比如像下面这样:
view plaincopy
to clipboardprint?
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");
}
}
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,导致我们无法针对String写出一个像Test3那样的“可变”程序,如此说来,“二分钱的关系”应该是有的。
“pass reference by value”就像C++ 11中的“An lvalue with rvalue reference type”一样,乍一看挺绕的,但只要仔细想清楚,让正交的东西“尘归尘 土归土”,就可以加深对语言的理解。
顺便问一句,您知道java.lang.StringBuilder和java.lang.StringBuffer的区别吗?好多面试官都喜欢“顺便”问问这个,而且答对了不会加分,答不上来却会扣分,至少扣印象分。:(
题目是一道简单的小程序,像下面这样:
view plaincopy
to clipboardprint?
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");
}
}
view plaincopy to clipboardprint? 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"); } } 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 plaincopy
to clipboardprint?
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");
}
}
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,导致我们无法针对String写出一个像Test3那样的“可变”程序,如此说来,“二分钱的关系”应该是有的。
“pass reference by value”就像C++ 11中的“An lvalue with rvalue reference type”一样,乍一看挺绕的,但只要仔细想清楚,让正交的东西“尘归尘 土归土”,就可以加深对语言的理解。
顺便问一句,您知道java.lang.StringBuilder和java.lang.StringBuffer的区别吗?好多面试官都喜欢“顺便”问问这个,而且答对了不会加分,答不上来却会扣分,至少扣印象分。:(
相关文章推荐
- 一个java面试题引发的思考
- 一个面试题引发的思考(Java的值传递)
- JAVA学习提高之---- 一道面试题引发的思考之:类的初始化
- [置顶] 【面试题】java装箱拆箱引发的思考
- 一道面试题引发的思考
- java面试题训练0807_字符串逆转及回文
- 2015百度一道面试题引发的思考(shell脚本和网络)
- java引发的思考----自己是面向对象的程序设计员吗?
- 一道有关Java null笔试题引发的思考
- JAVA-OPTS引发的思考
- Java int基本类型和Integer之间引发的问题思考
- 由一道面试题引发的思考
- JAVA面试题解惑系列(六)——字符串(String)杂谈
- Java包命名规则引发的思考
- 一道习题引发的思考——Java字符运算
- Java 单例以及单例所引发的思考
- 一道面试题引发的有关随机数的思考(2)
- String方法调用面试题引发的思考
- 一道js面试题引发的思考
- 科大讯飞面试题 将整型12345转为54321(不能转为字符串)JAVA版