เมื่อคุณประกาศตัวแปรString
(ซึ่งไม่เปลี่ยนรูป ) เป็นfinal
และกำหนดค่าเริ่มต้นด้วยนิพจน์ค่าคงที่แบบคอมไพล์เวลามันจะกลายเป็นนิพจน์ค่าคงที่แบบคอมไพล์เวลาและค่าของมันจะถูกคอมไพล์โดยที่มันถูกใช้ ดังนั้นในตัวอย่างโค้ดที่สองของคุณหลังจากการฝังค่าการต่อสตริงจะถูกแปลโดยคอมไพเลอร์เป็น:
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
ซึ่งเมื่อเทียบกับการ"string"
ที่จะทำให้คุณtrue
เพราะตัวอักษรของสตริงจะฝึกงาน
จากJLS §4.12.4 - final
ตัวแปร :
ตัวแปรชนิดดั้งเดิมหรือประเภทString
ที่มีfinal
และเริ่มต้นได้ด้วยการแสดงออกที่รวบรวมเวลาคงที่ (§15.28) เรียกว่าตัวแปรคงที่
นอกจากนี้จากJLS §15.28 - การแสดงออกอย่างต่อเนื่อง:
เวลารวบรวมการแสดงออกอย่างต่อเนื่องของชนิดString
มักจะ"ฝึกงาน"String#intern()
เพื่อร่วมกันกรณีที่ไม่ซ้ำกันโดยใช้วิธีการ
กรณีนี้ไม่ได้ในตัวอย่างรหัสแรกของคุณที่ตัวแปรไม่ได้String
final
ดังนั้นจึงไม่ใช่นิพจน์คงที่เวลาคอมไพล์ การดำเนินการเรียงต่อกันจะมีความล่าช้าจนถึงรันไทม์จึงนำไปสู่การสร้างString
วัตถุใหม่ คุณสามารถตรวจสอบได้โดยการเปรียบเทียบรหัสไบต์ของรหัสทั้งสอง
ตัวอย่างรหัสแรก(ไม่ใช่final
รุ่น)จะรวบรวมเป็นรหัสไบต์ต่อไปนี้:
Code:
0: ldc #2; //String str
2: astore_1
3: ldc #3; //String ing
5: astore_2
6: new #4; //class java/lang/StringBuilder
9: dup
10: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: getstatic #8; //Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_3
29: ldc #9; //String string
31: if_acmpne 38
34: iconst_1
35: goto 39
38: iconst_0
39: invokevirtual #10; //Method java/io/PrintStream.println:(Z)V
42: return
เห็นได้ชัดว่ามันมีการจัดเก็บstr
และing
ในสองตัวแปรที่แยกจากกันและใช้StringBuilder
ในการดำเนินการเชื่อมต่อ
ในขณะที่ตัวอย่างโค้ดที่สองของคุณ( final
เวอร์ชัน)มีลักษณะดังนี้:
Code:
0: ldc #2; //String string
2: astore_3
3: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_3
7: ldc #2; //String string
9: if_acmpne 16
12: iconst_1
13: goto 17
16: iconst_0
17: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V
20: return
ดังนั้นจึงโดยตรง inlines ตัวแปรสุดท้ายที่จะสร้าง String string
ที่รวบรวมเวลาซึ่งเป็นที่โหลดโดยการดำเนินการในขั้นตอนที่ldc
0
แล้วตัวอักษรสตริงที่สองคือการโหลดโดยการดำเนินการในขั้นตอนที่ldc
7
ไม่เกี่ยวข้องกับการสร้างString
วัตถุใหม่ที่รันไทม์ String รู้จักกันแล้วในเวลารวบรวมและพวกเขาจะฝึกงาน