2015-03-08 97 views
0

最近我查看了java.lang.String類的源代碼(http://www.docjar.com/html/api/java/lang/String.java.html),並且我注意到使用的構造函數(或至少一個特定的構造函數)依賴於參數String類型。這怎麼可能?假設下面的代碼包含完全相同類的參數的類構造函數

String s = new String("String!");

編譯器如何做到這一點?在括號內部傳遞一個字符串對象,對吧?但是這需要以某種方式實例化(我想呢,編譯器)?或者這是如何工作的?

我有意使用String對象的實例化而不是字符串文字,因爲字符串文字在內存池而不是字符串對象(堆)上的分配方式不同。

所有最優秀的,大衛

+0

寫'字符串s =新的字符串(){! 「字符串」}' 你以相同的方式初始化類。 – 2015-03-08 19:52:05

+0

@MasoudHosseini你的短代碼示例中有很多錯誤... – Tom 2015-03-08 20:31:30

+0

@Tom是的,對,我與c#混淆# 先生大衛Becher你應該從這個鏈接使用。 http://www.tutorialspoint.com/java/java_strings.htm – 2015-03-09 05:49:33

回答

1

String類有多個構造函數。

String string = new String(); 
new String(string); 

你並不需要了解字符串文字如何創建/重用對象,以瞭解String可以構造String(String)

0

Java編譯器:在傳遞中的實例可以使用不同的構造函數來創建會編譯很多,然後做一些檢查。這是可能的,因爲類類似於.obj或.o,其後麪包含名稱「鏈接」。

在上述情況中,實際上對編譯或運行時使用沒有任何挑戰。也適用於非標準課程:您可以一直使用通行證null

還有待觀察更醜陋的東西:

public interface IFC { 
    public static final IFC SOME_CONSTANT = new IMPL(); 
} 

public class IMPL implements IFC { 
} 

關於那個例子。構造函數String(String)源於Java的開始。在C++中,可以將其稱爲拷貝構造函數。由於字符串是不可變的,它服務器沒有目的:s.equals(new String(s))所以它真的是壞風格。 (也許它被添加用於禁用==。)

0

編譯器如何做到這一點?在括號內部傳遞一個字符串對象,對吧?

不是真的。如果您查看String構造函數的API,它實際上會收到參數char array

String str = "abc"; 等同於:

char data[] = {'a', 'b', 'c'};

String str = new String(data);

0

這是引擎蓋下的編譯器做什麼。編譯器可以例如將字符串文字(​​)轉換爲字符數組並使用接受char[]的ctor。

0

讓我們假設你有一個程序是這樣的:

public class Test { 
    public static void main(String[] args) { 
     String s = new String("String!"); 
     System.out.println(s); 
    } 
} 

在編譯時,你將獲得一個包含該程序的字節碼.class文件。您可以查看通過調用javap -c -v -l -s Test.class字節碼(javap是JDK的一部分),它會打印出這樣的事:

Classfile /C:/Test.class 
    Last modified 08.03.2015; size 454 bytes 
    MD5 checksum 0649bf262c557480c276e268762a1dd5 
    Compiled from "Test.java" 
public class Test 
    minor version: 0 
    major version: 52 
    flags: ACC_PUBLIC, ACC_SUPER 
Constant pool: 
    #1 = Methodref   #8.#17   // java/lang/Object."<init>":()V 
    #2 = Class    #18   // java/lang/String 
    #3 = String    #19   // String! 
    #4 = Methodref   #2.#20   // java/lang/String."<init>":(Ljava/lang/String;)V 
    #5 = Fieldref   #21.#22  // java/lang/System.out:Ljava/io/PrintStream; 
    #6 = Methodref   #23.#24  // java/io/PrintStream.println:(Ljava/lang/String;)V 
    #7 = Class    #25   // Test 
    #8 = Class    #26   // java/lang/Object 
    #9 = Utf8    <init> 
    #10 = Utf8    ()V 
    #11 = Utf8    Code 
    #12 = Utf8    LineNumberTable 
    #13 = Utf8    main 
    #14 = Utf8    ([Ljava/lang/String;)V 
    #15 = Utf8    SourceFile 
    #16 = Utf8    Test.java 
    #17 = NameAndType  #9:#10   // "<init>":()V 
    #18 = Utf8    java/lang/String 
    #19 = Utf8    String! 
    #20 = NameAndType  #9:#27   // "<init>":(Ljava/lang/String;)V 
    #21 = Class    #28   // java/lang/System 
    #22 = NameAndType  #29:#30  // out:Ljava/io/PrintStream; 
    #23 = Class    #31   // java/io/PrintStream 
    #24 = NameAndType  #32:#27  // println:(Ljava/lang/String;)V 
    #25 = Utf8    Test 
    #26 = Utf8    java/lang/Object 
    #27 = Utf8    (Ljava/lang/String;)V 
    #28 = Utf8    java/lang/System 
    #29 = Utf8    out 
    #30 = Utf8    Ljava/io/PrintStream; 
    #31 = Utf8    java/io/PrintStream 
    #32 = Utf8    println 
{ 
    public Test(); 
    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 1: 0 

    public static void main(java.lang.String[]); 
    descriptor: ([Ljava/lang/String;)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=3, locals=2, args_size=1 
     0: new   #2     // class java/lang/String 
     3: dup 
     4: ldc   #3     // String String! 
     6: invokespecial #4     // Method java/lang/String."<init>":(Ljava/lang/String;)V 
     9: astore_1 
     10: getstatic  #5     // Field java/lang/System.out:Ljava/io/PrintStream; 
     13: aload_1 
     14: invokevirtual #6     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     17: return 
     LineNumberTable: 
     line 3: 0 
     line 4: 10 
     line 5: 17 
} 
SourceFile: "Test.java" 

雖然這看起來有些令人困惑(和我不是很深入字節碼) 我會盡量減少到這一點

常量池是類文件包含「固定」的信息,如方法簽名或文本(是:字符串文本)的一部分。代碼部分包含應該執行的實際代碼,並且它使用對常量池的引用。

那麼,文本"String!"實際上是如何加載的?在ldC#3上的代碼塊中,索引爲#3的值被加載並放入堆棧。 #3是對#19的參考,這是實際的文字String!。然後

下一字節代碼指令執行一次invokespecial使用索引#4這是String構造與另一String作爲參數 - 在其他的答案(<init>指構造,而另一部分是參數描述的「複製構造」類型和返回類型)。

雖然這可能總結一下會發生什麼,至少部分,一個細節仍不清楚莫名其妙:如何運行時轉換從#19的文字文本String!String對象

有一些類似的代碼和他們的字節碼錶示玩弄後,我只能做一個猜測:運行時已經硬編碼支持將字符串文字轉換爲String對象(例如以類似的方式代碼long c = 42L;被分裂成#2 = Long 42lldc2_w #2 ,你可以通過修改這個例子來測試它,然後編譯和反編譯它)。

更多鏈接: