JLS
JLS 7 3.10.5กำหนดและให้ตัวอย่างเชิงปฏิบัติ:
นอกจากนี้สตริงตัวอักษรมักจะหมายถึงอินสแตนซ์เดียวกันของคลาสสตริง นี่เป็นเพราะตัวอักษรสตริง - หรือโดยทั่วไปแล้วสตริงที่เป็นค่าของการแสดงออกอย่างต่อเนื่อง (§15.28) - เป็น "ฝึกงาน" เพื่อแบ่งปันกรณีที่ไม่ซ้ำกันโดยใช้วิธีการ String.intern
ตัวอย่าง 3.10.5-1 สตริงตัวอักษร
โปรแกรมประกอบด้วยชุดรวบรวม (§7.3):
package testPackage;
class Test {
public static void main(String[] args) {
String hello = "Hello", lo = "lo";
System.out.print((hello == "Hello") + " ");
System.out.print((Other.hello == hello) + " ");
System.out.print((other.Other.hello == hello) + " ");
System.out.print((hello == ("Hel"+"lo")) + " ");
System.out.print((hello == ("Hel"+lo)) + " ");
System.out.println(hello == ("Hel"+lo).intern());
}
}
class Other { static String hello = "Hello"; }
และหน่วยรวบรวม:
package other;
public class Other { public static String hello = "Hello"; }
ผลิตผลลัพธ์:
true true true true false true
JVMs
JVMS 7 5.1 กล่าวว่าการฝึกงานจะดำเนินการอย่างน่าอัศจรรย์และมีประสิทธิภาพด้วยโครงสร้างเฉพาะCONSTANT_String_info
(แตกต่างจากวัตถุอื่น ๆ ส่วนใหญ่ที่มีการแสดงออกทั่วไปมากขึ้น):
สตริงตัวอักษรเป็นการอ้างอิงถึงอินสแตนซ์ของคลาสสตริงและได้มาจากโครงสร้าง CONSTANT_String_info (§4.4.3) ในการเป็นตัวแทนไบนารีของคลาสหรืออินเตอร์เฟซ โครงสร้าง CONSTANT_String_info ให้ลำดับของจุดโค้ด Unicode ที่ประกอบเป็นตัวอักษรสตริง
ภาษาการเขียนโปรแกรม Java ต้องการตัวอักษรสตริงที่เหมือนกัน (นั่นคือตัวอักษรที่มีลำดับจุดรหัสเดียวกัน) ต้องอ้างถึงอินสแตนซ์เดียวกันของคลาส String (JLS §3.10.5) นอกจากนี้ถ้าเมธอด String.intern ถูกเรียกบนสตริงใด ๆ ผลลัพธ์จะเป็นการอ้างอิงไปยังอินสแตนซ์ของคลาสเดียวกันที่จะถูกส่งคืนหากสตริงนั้นปรากฏเป็นตัวอักษร ดังนั้นการแสดงออกต่อไปนี้จะต้องมีค่าจริง:
("a" + "b" + "c").intern() == "abc"
เพื่อรับสตริงตัวอักษร Java Virtual Machine จะตรวจสอบลำดับของจุดรหัสที่กำหนดโดยโครงสร้าง CONSTANT_String_info
หากก่อนหน้านี้มีการเรียกใช้เมธอด String.intern ในอินสแตนซ์ของคลาส String ที่มีลำดับของจุดโค้ด Unicode เหมือนกับที่กำหนดโดยโครงสร้าง
มิฉะนั้นอินสแตนซ์ใหม่ของคลาสสตริงจะถูกสร้างขึ้นซึ่งมีลำดับของจุดโค้ด Unicode ที่กำหนดโดยโครงสร้าง CONSTANT_String_info การอ้างอิงถึงอินสแตนซ์ของคลาสนั้นเป็นผลลัพธ์ของการสืบทอดสตริงตามตัวอักษร ในที่สุดวิธีการฝึกงานของอินสแตนซ์สตริงใหม่จะถูกเรียกใช้
bytecode
ลองแยกรหัส OpenJDK 7 บางส่วนเพื่อดูการทำงานจริง
ถ้าเราแปลความหมาย:
public class StringPool {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a);
System.out.println(b);
System.out.println(a == c);
}
}
เรามีสระว่ายน้ำคงที่:
#2 = String #32 // abc
[...]
#32 = Utf8 abc
และmain
:
0: ldc #2 // String abc
2: astore_1
3: ldc #2 // String abc
5: astore_2
6: new #3 // class java/lang/String
9: dup
10: ldc #2 // String abc
12: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne 42
38: iconst_1
39: goto 43
42: iconst_0
43: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
สังเกตว่า:
0
และ3
: ldc #2
โหลดค่าคงที่เดียวกัน(ตัวอักษร)
12
: อินสแตนซ์ของสตริงใหม่ถูกสร้างขึ้น (โดยมี#2
อาร์กิวเมนต์เป็น)
35
: a
และc
ถูกเปรียบเทียบเป็นวัตถุปกติด้วยif_acmpne
การเป็นตัวแทนของสตริงคงที่นั้นค่อนข้างน่าอัศจรรย์ใน bytecode:
และการอ้างอิง JVMS ข้างต้นดูเหมือนจะบอกว่าเมื่อใดก็ตามที่ Utf8 ชี้ไปที่เหมือนกันจากนั้นอินสแตนซ์ที่เหมือนกันจะถูกโหลดโดย ldc
ชี้ไปที่จะเหมือนกันกรณีแล้วเหมือนจะถูกโหลดโดย
ฉันได้ทำการทดสอบที่คล้ายกันสำหรับสาขาและ:
static final String s = "abc"
ชี้ไปที่ตารางค่าคงที่ผ่านแอตทริบิวต์ค่าคงที่
- เขตข้อมูลที่ไม่ใช่ครั้งสุดท้ายไม่มีแอตทริบิวต์นั้น แต่ยังสามารถเริ่มต้นได้ด้วย
ldc
สรุป : มีการสนับสนุน bytecode โดยตรงสำหรับสตริงพูลและการแสดงหน่วยความจำนั้นมีประสิทธิภาพ
โบนัส: เปรียบเทียบกับพูลจำนวนเต็มซึ่งไม่มีการสนับสนุนไบต์โดยตรง (เช่นไม่มีCONSTANT_String_info
อะนาล็อก)