讓我們假設你有一個程序是這樣的:
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 42l
和ldc2_w #2
,你可以通過修改這個例子來測試它,然後編譯和反編譯它)。
更多鏈接:
寫'字符串s =新的字符串(){! 「字符串」}' 你以相同的方式初始化類。 – 2015-03-08 19:52:05
@MasoudHosseini你的短代碼示例中有很多錯誤... – Tom 2015-03-08 20:31:30
@Tom是的,對,我與c#混淆# 先生大衛Becher你應該從這個鏈接使用。 http://www.tutorialspoint.com/java/java_strings.htm – 2015-03-09 05:49:33