字符串常量池,intern,以及收集面試題


參考文章
美團技術團隊:深入解析String#intern

https://www.zhihu.com/question/29884421

https://javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html

字符串常量池

字符串常量池是什麼?

在HotSpot VM裏實現的string pool功能的是一個StringTable類,它是一個HashTable,默認值大小長度是1009;字符串常量由一個一個字符組成,放在了StringTable上。

  • 在JDK6.0,StringTable長度是固定了1009,如果放入String Pool中的String非常多,就會造成hash衝突,導致鏈表過長,當調String#intern()時會需要到鏈表上一個一個找,從而導致性能大幅度下降
  • JDK7.0,StringTable的長度可以通過參數指定:
-XX:StringTableSize=66666

字符串常量池存儲位置

  • JDK6:常量池存儲在永久代中
  • JDK7:常量池存儲在堆中。

字符串常量池存儲着什麼

個人認同:

字符串常量池:存放的是字符串的引用地址。

駐留intern 方法

當對象s調用intern(),首先查找字符串常量池中引用的字符串的值,看有沒有和s的相等;如果有相等,則返回與s值相等的字符串的引用;如果找不到,則這把s的引用存入字符串常量池中,並返回。

String重載“+”,“+=”

Java對String的+操作符進行了重載。
例子:

public class Demo {
    public static void main(String[] args) {
        String a = "1";
        String b = a + "2";
    }
}
  • 編譯:javac Demo.java
  • 反編譯:javap -c Demo
public class basis.wei.Demo {
  public basis.wei.Demo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String 1
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: aload_1
      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String 2
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_2
      23: return
}

編譯器自動創建StringBuilder對象, 並調用append()方法進行拼接,最後toString()生成結果。

String a = “1”+“2” 創建了幾個對象?

答案: 如果常量池沒"12",則創建1個;如果常量池有"12",則創建0個,返回該對象引用

原因: 編譯時,會自動優化,進行拼接成“12”;
證據:

public class Demo {
    public static void main(String[] args) {
        String s = "1"+"2";
    }
}
  1. javac Demo.java #編譯
  2. javap -verbose Demo #查看 常量池
public class basis.wei.Demo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#13         // java/lang/Object."<init>":()V
   #2 = String             #14            // 12
   #3 = Class              #15            // basis/wei/Demo
   #4 = Class              #16            // java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Utf8               LineNumberTable
   #9 = Utf8               main
  #10 = Utf8               ([Ljava/lang/String;)V
  #11 = Utf8               SourceFile
  #12 = Utf8               Demo.java
  #13 = NameAndType        #5:#6          // "<init>":()V
  #14 = Utf8               12
  #15 = Utf8               basis/wei/Demo
  #16 = Utf8               java/lang/Object
{
  public basis.wei.Demo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #2                  // String 12
         2: astore_1
         3: return
      LineNumberTable:
        line 10: 0
        line 11: 3
}

編譯後只有一個“12”。

String a = new String (“1”);創建了幾個對象?

參考文章:請別再拿“String s = new String(“xyz”);創建了多少個String實例”來面試了吧

答案: 兩個,一個是字符串字面量"1"所對應的、駐留(intern)在一個全局共享的字符串常量池中的實例,另一個是通過new String(String)創建並初始化的、內容與"xyz"相同的實例

在這裏插入圖片描述

String s = new String(“1”);String s2 = “1”;System.out.println(s== s2);System.out.println(s.intern()== s);System.out.println(s.intern()== s2);

本題參考String#intern

 	    public static void main(String[] args) {
        String s = new String("1");
        String s2 = "1";
        System.out.println(s== s2);
        System.out.println(s.intern()== s);
        System.out.println(s.intern()== s2);
    }

主要考查intern的使用,以及字符串常量池的概念;
JDK8環境,答案

false
false
true

圖解在這裏插入圖片描述
s== s2 爲false;可以很好理解,s1和s2的引用地址不同。

s.intern()== s爲false,s.intern()在String Pool中找到有“1”與s的值相等,所以返回找到的值相等的引用值,但與s的引用並不相等;所以false

第三個要先了解以下內容

在創建字符串對象時,會先到字符串常量池查到字符串引用的值,看有沒有值相等,如果有值相等,則將查到字符串的引用賦值給變量;如果查不到則在堆上創建實例,並將實例的引用存儲在String Table,並將引用賦值給變量。

String s2 = “1”; “1” 可以在String pool找到引用對象的值相等;所以直接將引用賦值給s2

所以上圖表示的s1的引用和常量池中的"1"的引用指向同一地址。so,s.intern()返回的是常量池中“1”的引用 與s1的引用相等。

如果以上理解了,在看看這題

  public static void main(String[] args) {
        String s = new String("1")+"1";
        System.out.println(s.intern()== s);
        String s2 = "11";
        System.out.println(s.intern()== s2);
    }

jdk 8答案

true
true

在這裏插入圖片描述
第一次s.intern() 發現String Pool中的引用的值沒有和11相等的,就將“11”的引用駐留在了String Pool;並將引用返回。s.intern()== s ;等於true。

第二個true;直接將“11”的引用返回給s2,如圖。

String,StringBuilder,StringBuffer區別

String StringBuilder StringBuffer
儲存結構 private final char value[]; char[] value; private transient char[] toStringCache;
線程是安全性 安全 不安全 安全,synchronized修飾方法
運算 1. 編譯器優化 2. “+”,“+=” 重載 效率高 效率低
使用場景 操作少量數據 單線程,操作大量數據 多線程,操作大量數據

寫一個方法來判斷一個String是否是迴文(順讀和倒讀都一樣的詞)

  /**
     * 判斷是否是迴文
     * @param s
     * @return
     */
    public static boolean isPalindromeString(String s){
        StringBuilder stringBuilder = new StringBuilder(s);
        return stringBuilder.reverse().toString().equals(s);
    }

爲什麼我們在使用HashMap的時候總是用String做key

我們知道HashMap的key只能是引用類型,而在進行路由尋址時,是根據key的hashcode來計算的。
而String有以下特點。

  • String 有個hash屬性,用來儲存hashcode,可避免重複計算hashCode。
  • String的值是根據值來計算的,所以String 作爲key,如果他的值相等,那麼hashcode也一定相等;這樣的能減少很多實際工作中可能會出現的麻煩。

參考文章:爲什麼 HashMap 常用 String 對象作 key

//緩存hashcode
/** Cache the hash code for the string */
    private int hash; // Default to 0

	// 根據值計算hashcode
	public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

沒事我來瞅瞅

在這裏插入圖片描述
如果有什麼地方不對,麻煩請指出!謝謝!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章