Java中關於String問題的總結

Java面試中總喜歡出幾個關於String對象的題,好比:html

1.String s = new String("abc");建立了幾個String對象。java

2.String s1 = "abc";
   String s2 = "a";
   String s3 = s2 + "bc";
   String s4 = "a" + "bc";
   String s5 = s3.intern();
請問s1==s3是true仍是false,s1==s4是false仍是true。s1==s5呢?面試

這樣的題碰到不少了,今天來全面的總結一下,之因此敢說全面,是由於我參考的是最權威的資料……Java語言規範和Java API。express

API中說明:api

字符串是常量;它們的值在建立以後不能更改。由於 String 對象是不可變的,因此能夠共享。(http://gceclub.sun.com.cn/Java_Docs/jdk6/html/zh_CN/api/java/lang/String.html)ide

那麼,是如何共享的勒,String類的intern方法有以下說明:spa

字符串池,初始爲空,它由類 String 私有地維護。(http://gceclub.sun.com.cn/Java_Docs/jdk6/html/zh_CN/api/java/lang/String.html#intern())code

也就是說,String類本身維護了一個池,把程序中的String對象放到那個池裏面,因而咱們代碼中只要用到值相同的String,就是同一個String對象,節省了空間。orm

可是,並非任什麼時候候任何String對象都在這個字符串池中,否則也就不會有那些面試題了。那麼,到底哪些String對象在池中,哪些在堆中?請看Java語言規範。htm

Java語言規範第三版,第3.10.5小節,String Literals的最後一段以下:

 

Literal strings within the same class (§8) in the same package (§7) represent references to the same String object (§4.3.1).

Literal strings within different classes in the same package represent references to the same String object.

Literal strings within different classes in different packages likewise represent references to the same String object.

Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals.

Strings computed by concatenation at run time are newly created and therefore distinct.

The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents.

以上6句話就是權威且全面的解釋了。我依次解釋下。

首先解釋下什麼是字符串字面常數(String Literals),字面常數(Literals)就是你寫在源代碼裏面的值,好比說int i = 6; 6就是一個整數形字面常數。String s = "abc"; 「abc」就是一個字符串字面常數。Java中,全部的字符串字面常數都放在上文提到的字符串池裏面,是能夠共享的,就是說,String s1 = "abc"; String s2 = "abc"; s1,s2都引用的同一個字符串對象,並且這個對象在字符串池裏面,所以s1==s2。另外,字符串字面常數是何時實例化並放到字符串池裏面去的呢?答案是Load Class的時候(Java Spec 12.5)。

下面看那6句話。

前面三句基本廢話,想繞口令同樣,意思就是說任何類任何包,值相同的字符串字面常數(String Literals)都引用同一個對象。

第四句是說,經過常量表達式(constant expressions)計算出來的字符串,也算字符串字面常數,就是說他們也在字符串池中。什麼是常量表達式(constant expressions)待會說,這個很重要。

注意第五句話,在程序運行時經過鏈接(+)計算出來的字符串對象,是新建立的,他們不是字面常數,就算他們值相同,他們也不在字符串池裏面,他們在堆內存空間裏,所以引用的對象各不相同。

最後一句話也很重要,String類的intern方法,返回一個值相同的String對象,可是這個對象就像一個字符串字面常數同樣,意思就是,他也到字符串池裏面去了。

如今咱們來看開頭的兩個題目。

 

1.String s = new String("abc");建立了幾個String對象。

答案是2個,一個是字符串字面常數,在字符串池中。一個是new出來的字符串對象,在堆中。

2.String s1 = "abc";
   String s2 = "a";
   String s3 = s2 + "bc";
   String s4 = "a" + "bc";
   String s5 = s3.intern();
請問s1==s3是true仍是false,s1==s4是false仍是true。s1==s5呢?

此題注意兩點,由於s2是一個變量,因此s3是運行時才能計算出來的字符串,是new的,在堆中不在字符串池中。s4是經過常量表達式計算出來的,他等同於字符串字面常數,在字符串池中。因此,s1!=s3,s1==s4。再看s5,s5是s3放到字符串池裏面返回的對像,因此s1==s5。這裏新手要注意的是,s3.intern()方法,是返回字符串在池中的引用,並不會改變s3這個變量的引用,就是s3仍是指向堆中的那個"abc",並無因調用了intern()方法而改變,實際上也不可能改變。

好,如今咱們回到前文沒有說清楚的一個問題,到底什麼算常量表達式(constant expressions)。上面提到了兩種很是量表達式,new 和變量相加。至於什麼算常量表達式(constant expressions),請看Java語言規範。

 

15.28 Constant Expression

 ConstantExpression: Expression

A compile-time constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following:

  • Literals of primitive type and literals of type String 
  • Casts to primitive types and casts to type String
  • The unary operators +-~, and (but not ++ or --)
  • The multiplicative operators */, and %
  • The additive operators + and -
  • The shift operators <<>>, and >>>
  • The relational operators <<=>, and >= (but not instanceof)
  • The equality operators == and !=
  • The bitwise and logical operators &^, and |
  • The conditional-and operator && and the conditional-or operator ||
  • The ternary conditional operator ? :
  • Parenthesized expressions whose contained expression is a constant expression.
  • Simple names that refer to constant variables
  • Qualified names of the form TypeName . Identifier that refer to constant variables

Compile-time constant expressions are used in case labels in switch statements and have a special significance for assignment conversion. Compile-time constants of type String are always "interned" so as to share unique instances, using the method String.intern.

A compile-time constant expression is always treated as FP-strict, even if it occurs in a context where a non-constant expression would not be considered to be FP-strict.

看着是否是有點暈,這狀況也太多了,面試應該不會那麼變態的。其實看着複雜,記住一個原則就行了,那就是編譯時能肯定的,就算,運行時才能肯定的,就不算。如下爲例子

String s = 1 + "23";//算,符合第二條Casts to primitive types and casts to type String
String s = (2 > 1) + "" ;//算,意味着s=="true",且這個「true」已經放到字符串池裏面去了。
String s = (o instanceof Object) + "";//不算,instanceof這個操做符決定了不算。s=="true",但這個"true"對象在堆中。

留意下面的狀況。

   final String s2 = "a";
   String s3 = s2 + "bc";//算

注意如今的s2+"bc"也算一個常量表達式,理由是那個列表裏面的最後兩條,s2是一個常量變量(constant variables),問題又來了,什麼是常量變量?規範裏也說了,被final修飾,而且經過常量表達式初始化的變量,就是常量變量。變量s2被final修飾,他經過常量表達式"a"初始化,因此s2是一個常量變量,因此s3引用的"abc",也在字符串池裏面咯,明白了吧。

再舉個反例:

final String s2 = getA();//s2不是常量變量,可是s2引用的"a"其實仍是在常量池中,這兩點不矛盾
public String getA(){return "a"}

String s3 = s2 + "bc";//此時s3不算常量表達式,由於s2不是常量變量

這是時候的s2,就不是常量變量了哦,由於getA()不是一個常量表達式

 轉自:http://blog.51cto.com/cymoft/473220

相關文章
相關標籤/搜索