首页 > Java开发 > 关于Java String, StringBuilder, StringBuffer, Hashtable, HashMap的面试题

关于Java String, StringBuilder, StringBuffer, Hashtable, HashMap的面试题

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

 

[java][/java] view plaincopy

  1. public class Test1 {
  2.  public static void main(String args[]) {
  3.   String s = new String("Hello");
  4.   System.out.println(s);
  5.   foo(s);
  6.   System.out.println(s);
  7.  }
  8.  public static void foo(String s) {
  9.   s = new String("World");
  10.  }
  11. }

 

 

问程序先后两次分别会输出什么。
第一个肯定输出“Hello”。关键是第二个,个别基础不牢的朋友可能被考倒,但基础稍微扎实一点的就不会。第二个输出的也是“Hello”。
到这里,万事大吉。面试官两眼放出喜悦的光芒,赞叹道:“嗯,不错不错。不会变的,对吧。因为String是immutable类型,immutable类型改不了的。”他说的“immutable”是指String类没有任何一个方法会改变对象的状态。

 

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

 

 

[java][/java] view plaincopy

  1. public class Test2 {
  2.  public static void main(String args[]) {
  3.   StringBuilder s = new StringBuilder("Hello");
  4.   System.out.println(s);
  5.   foo(s);
  6.   System.out.println(s);
  7.  }
  8.  public static void foo(StringBuilder s) {
  9.   s = new StringBuilder("World");
  10.  }
  11. }

 

 

这次呢?会先输出“Hello”,然后输出“World”吗?不会,仍然是两次“Hello”。
足以说明这个问题跟String的immutable特性没有一毛钱的关系。不管是immutable类型,还是一般的类型,用这种方式写出来的程序main方法中前后两个对象肯定是一样的。

 

真正的原因是:Java语言的参数传递机制是“按值传递”(pass by value)。虽然Java中除基本数值类型外,其它变量都是引用,但那是另一回事。变量的语义(“引用”还是“值”)跟函数传参的机制是两个正交的概念。
各种编程语言中,最常见的参数传递方式不外乎按值传递和按引用传递(pass by reference)两种。比如,C和Java中函数参数都是按值传递的,C++和C#则同时支持按值传递和按引用传递。C和Java的不同在于,C是值类型的按值传递,而Java是引用类型的按值传递(基本的数值类型除外)。
按值传参最大的特点就是函数内部对“形参变量”本身的所做的修改外面的“实参变量”感知不到。

 

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

 

 

[java][/java] view plaincopy

  1. public class Test3 {
  2.  public static void main(String args[]) {
  3.   StringBuilder s = new StringBuilder("Hello");
  4.   System.out.println(s);
  5.   foo(s);
  6.   System.out.println(s);
  7.  }
  8.  public static void foo(StringBuilder s) {
  9.   s.replace(0, s.length(), "World");
  10.  }
  11. }

 

 

体会到不同了吗?虽然参数变量本身是按值传递的,但这次我们对变量本身不感兴趣,我们不改变变量本身,而是通过它直接修改它所引用的那个对象。这一次,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.lang.StringBuilder和java.lang.StringBuffer的区别:

StringBuffer is synchronized, StringBuilder is not.

StringBuilder is faster than StringBuffer because it's not synchronized.

 

看来以前喜欢用StringBuffer不是个好习惯,以后要多用StringBuilder !!!

 

 

 

这也让我想到了另一个经典的问题:Hashtable和HashMap的区别是什么?

 

  1. Hashtable is synchronized, whereas HashMap is not. This makes HashMap better for non-threaded applications, as unsynchronized Objects typically perform better than synchronized ones.
  2. Hashtable does not allow null keys or values. HashMap allows one null key and any number ofnull values.
  3. One of HashMap's subclasses is LinkedHashMap, so in the event that you'd want predictable iteration order (which is insertion order by default), you could easily swap out the HashMap for aLinkedHashMap. This wouldn't be as easy if you were using Hashtable.

 

Since synchronization is not an issue for you, I'd recommend HashMap. If synchronization becomes an issue, you may also look at ConcurrentHashMap.

 

总之都是unsynchronized要快一些,但鉴于Hashtable这个概念在面试中比HashMap来的多得多,所以在做面试题时还是用Hashtable吧,在项目开发中我从来都是用HashMap.


本文固定链接: http://www.devba.com/index.php/archives/3455.html | 开发吧

报歉!评论已关闭.