Java String面試題

sunjiaminaini發表於2017-08-11

試題一、String s = new String(“abc”)建立了幾個物件?
String s = new String(“abc”)實際上是”abc”本身就是字串池中的一個物件,在執行 new String()時,把字串池的字串”abc”複製到堆中,並把這個物件的應用交給s,所以建立了兩個String物件,一個在字串池中,一個在堆中。(注:我們假設的是字串池中預設是沒有abc字串的,如果之前已存在的話,則該題的答案就是一個物件了)
下面看這個程式,建立了幾個物件。



public class stringPool {  

     public static void main(String[] args) {  
         String s1 = new String("abc");  
         String s2 = new String("abc");  
         if (s1 == s2) {  
             System.out.println("在堆中只建立了一個物件");  
       } else {  
               System.out.println("在堆中建立了二個物件");  
           }  
       }  
   }  

輸出:

在堆中建立了二個物件

可知在堆中建立了兩個物件,但是在字串池中還有一個物件,所以共建立了三個物件。

試題二、String name=new String(“Java”+”hello”);建立了幾個物件?
對於該題常見的解釋有如下兩種,
其一:
1.”java”建立了一個物件,存於String常量池
2.”hello”建立了一個物件,存於String常量池
3.”java”+”hello”,建立了一個物件,存於常量池(基於字串的+操作,如帶有引用的,將在堆中建立物件,否則值會存於字元常量池)
4.new將會建立一個物件,將字元常量池中的”javahello”複製到堆中
此例將建立4個物件
其二:
像”java”+”hello”,java在編譯期間會自己先優化的,會合併成一個物件”javahello”的,然後在字串池中保留,然後new的時候再在堆中建立新的物件。因此共建立了兩個物件。

 [root@hina stringtest]# more TestString.java   
public class TestString{  
public static void main(String[] args) {  
String name = new String("java" + "hello");  
}  
}  
 [root@hina stringtest]# javac TestString.java  
[root@hina stringtest]# javap -c TestString  

Compiled from "TestString.java"  
public class TestString extends java.lang.Object{  
public TestString();  
 Code:  
    0:   aload_0  
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V  
  4:   return  

public static void main(java.lang.String[]);  
Code:  
  0:   new #2; //class java/lang/String  
   3:   dup  
   4:   ldc #3; //String javahello  
   6:   invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V  
   9:   astore_1  
   10:  return  
  }  

看清楚了,編譯的位元組碼裡沒有java也沒有hello,而是javahello。這個javahello不是在執行的時候被建立,而是jvm啟動的時候初始化好的。
實際上只有一個new,也就是說只建立了一個物件:
0 new 建立string物件
3 dup 複製頂棧內容,這是所有new物件都會做的事情,因為jvm要做
4 ldc 把常量池中javahello的引用壓入棧中
6 invokespecial 物件初始化
9 astore_1 把棧中的引用賦給第一個變數
所以,上面的第二種解釋是正確的,即:建立了兩個物件,一個javahello的物件在字串池中,一個new出的物件在堆上。

試題三,見下面的程式碼

String str1="abc";
String str2="def";
String str3=str1+str2;
System.out.println(str3=="abcdef");

//結果是false
因為str3指向堆中的”abcdef”物件並未是字串池中的物件,而”abcdef”是字串池中的物件,所以結果為false。JVM對String str=”abc”物件放在常量池中是在編譯時做的,而String str3=str1+str2是在執行時刻才能知道的。new物件也是在執行時才做的。而這段程式碼總共建立了5個物件,字串池中兩個、堆中三個。+運算子會在堆中建立來兩個String物件,也就是說從字串池中複製這兩個值,然後在堆中建立兩個物件,然後再建立物件str3,然後將”abcdef”的堆地址賦給str3。
而如果,如下的程式碼:

 String str="abc"+"def";   //直接將"abcdef"放入字串池中  
 System.out.println(str=="abcdef");  
//結果為true  

String str1="abc";       
   String str2=str1+"def";    //str1是在堆中建立的物件  
  System.out.println(str2=="abcdef");  
//結果為false  

本例子說明,str2也並未放到字串池中。從上面的結果中我們不難看出,只有使用引號包含文字的方式建立的String物件之間使用“+”連線產生的新物件才會被加入字串池中。對於所有包含new方式新建物件(包括null)的“+”連線表示式,它所產生的新物件都不會被加入字串池中。
由於字串物件的大量使用(它是一個物件,一般而言物件總是在堆中分配記憶體),Java中為了節省記憶體空間和執行時間(如比較字串時,==比equals()快),在編譯階段就把所有的字串文字放到一個字串池中,而執行時字串池成為常量池的一部分。字串池的好處,就是該池中所有相同的字串常量被合併,只佔用一個空間。
我們知道,對兩個引用變數,使用==判斷它們的值(引用)是否相等,即指向同一個物件:

 String s1 = "abc" ;  
  String s2 = "abc" ;  
if( s1 == s2 )   
 System.out.println("s1,s2 refer to the same object");  
else   
System.out.println("trouble");  

這裡的輸出顯示,兩個字串文字儲存為一個物件。就是說,上面的程式碼只在字串中建立了一個String物件。

現在看String s = new String(“abc”);語句,這裡”abc”本身就是pool中的一個物件,而在執行時執行new String()時,將字串池中的物件複製一份放到堆中,並且把堆中的這個物件的引用交給s持有。ok,這條語句就建立了2個String物件。

String s1 = new String("abc") ;
String s2 = new String("abc") ;
if( s1 == s2 ){ //不會執行的語句}

這時用==判斷就可知,雖然兩個物件的”內容”相同(equals()判斷),但兩個引用變數所持有的引用不同,上面的程式碼建立了幾個String 物件? (3個,字串池中1個,堆中2個。)

建立字串有兩種方式:兩種記憶體區域(字串池,堆)
1.” ” 引號建立的字串在字串池中
2.new,new建立字串時首先檢視池中是否有相同值的字串,如果有,則拷貝一份到堆中,然後返回堆中的地址;如果池中沒有,則在堆中建立一份,然後返回堆中的地址(注意,此時不需要從堆中複製到池中,否則導致浪費池的空間)
3.另外,對字串進行賦值時,如果右運算元含有一個或一個以上的字串引用時,則在堆中再建立一個字串物件,返回引用;如String str2=str1+ “abc”;
比較兩個已經存在於字串池中字串物件可以用”==”進行,擁有比equals操作符更快的速度。

相關文章