Java中String顯式聲明的對象創建問題

轉載請註明出處:http://blog.csdn.net/infant09/article/details/77822913

關鍵詞:java; String; 字面量; 顯式; 聲明; 對象; 顯示; 直接聲明; 直接定義

String對象的幾種創建方式


String類是Java中比較特殊的一類。Java在設計時爲了提高String類的使用效率,使用String Pool的機制進行String管理。

String對象有以下幾種聲明方式:

  • 顯式聲明(使用字面量創建對象,String literals):

    String s = "abc";
  • new關鍵字聲明:

    //通過String創建新的String對象,此方法是redundant
    String s0 = new String("123");
    
    //通過char數組來生成String
    char c[] = {48, 49};
    String s1 = new String(c);
    
    //通過int來生成String
    int i = 123;
    String s2 = String.valueOf(i);
    
    //other function ......
  • 字符串拼接(使用字面量創建對象):

    String s = "123" + "456";

問題提出


通過new關鍵字聲明,實際上是調用了String類的構造方法。但是通過雙引號進行顯式聲明,是如何創建對象的呢?

Java的基本類型,可以直接顯式聲明,但是String不是基本類型,顯然是編譯器隱藏了一些細節。

問題解決


String源碼

通過閱讀String類的源碼,可以發現這樣一段話:

 * <blockquote><pre>
 *     String str = "abc";
 * </pre></blockquote><p>
 * is equivalent to:
 * <blockquote><pre>
 *     char data[] = {'a', 'b', 'c'};
 *     String str = new String(data);
 * </pre></blockquote><p>

因此我們可以得知,當使用String str = "abc"聲明時,等價於首先創建了char數組,然後調用new String的構造方法。

當然,這只是“等價於”(equivalent),編譯器做了一些處理。顯式調用String str = "abc"和調用char data[] = {'a', 'b', 'c'}; String str = new String(data);是不一樣的過程。

bytecode理解

  1. 顯式調用

    package Test;
    
    public class StringTest {
    
        public static void main(String[] args) {
            String s = "abc";
        }
    }

    使用$ javap -c StringTest.class查看編譯後的class文件:

    public class Test.StringTest {
    public Test.StringTest();
    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 abc
       2: astore_1
       3: return
    }
    

    根據字符串池(String Pool)的機制,String str = "abc"會先在常量池裏的字符串池查找"abc"對象,如果沒有則創建;然後在棧(stack)中創建str引用,直接指向String pool中的"abc"對象。

  2. new關鍵字聲明

    package Test;
    
    public class StringTest {
    
        public static void main(String[] args) {
            char c[] = {'a', 'b', 'c'};
            String s = new String(c);
        }
    }
    

    字節碼如下:

    public class Test.StringTest {
    public Test.StringTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    
    public static void main(java.lang.String[]);
    Code:
       0: iconst_3
       1: newarray       char
       3: dup
       4: iconst_0
       5: bipush        97
       7: castore
       8: dup
       9: iconst_1
      10: bipush        98
      12: castore
      13: dup
      14: iconst_2
      15: bipush        99
      17: castore
      18: astore_1
      19: new           #2                  // class java/lang/String
      22: dup
      23: aload_1
      24: invokespecial #3                  // Method java/lang/String."<init>":([C)V
      27: astore_2
      28: return
    }
    

    可以首先在堆(heap)中創建對象,然後調用String類的構造方法。當然,調用構造方法的過程中,會在String pool裏查找或創建"abc"對象,然後再根據這個對象去heap中創建對象。

這就是顯式聲明字符串的過程。

其他問題

  • 字符串常量池究竟在JVM的哪裏?
  • String s = new String(“abc”) + “abc”,創建了多少個對象?以及其他類似問題

參考文獻

https://tech.meituan.com/in_depth_understanding_string_intern.html

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