คำหลักในที่สุดใน Java มักจะใช้กับลองบล็อกลอง ใช้ในการดำเนินการปล่อยทรัพยากรบางอย่างก่อนสิ้นสุดวิธีการหรือเมื่อมีข้อยกเว้นเกิดขึ้น เมื่อเร็ว ๆ นี้ฉันยังได้เห็นบทความบางอย่างบนอินเทอร์เน็ตเพื่อพูดคุยเกี่ยวกับลำดับของการดำเนินการของการลองจับในที่สุดคำหลักและจะได้รับการดำเนินการในที่สุดบล็อกจะถูกดำเนินการในตอนท้ายของวิธีการ
มุมมองเหล่านี้โดยทั่วไปเชื่อว่า:
1) คำหลักในที่สุดจะถูกดำเนินการก่อนที่จะกลับไปที่วิธีก่อนหน้าหลังจากคำสั่งส่งคืนโปรแกรม ค่าผลตอบแทนจะถูกบันทึกไว้ในพื้นที่ชั่วคราว หลังจากดำเนินการบล็อกสุดท้ายค่าของพื้นที่ชั่วคราวจะถูกส่งคืน
2) หากมีค่าส่งคืนในบล็อกสุดท้ายมันจะแทนที่ค่าที่เก็บไว้ในพื้นที่ชั่วคราวของการลองหรือบล็อกก่อนหน้านี้ในโปรแกรม
แต่ปัญหาเป็นแบบนี้จริงหรือ? ลองคิดดูสิ JVM อธิบายและดำเนินการตามคำสั่ง bytecode ที่รันไทม์ เมื่อมันดำเนินการคำสั่ง Return เขาไม่ทราบว่ามีบล็อกในที่สุดหรือไม่? เกิดอะไรขึ้นถ้าไม่มีบล็อกในที่สุด? ไม่ว่าจะเป็นคำสั่งไบต์หรือคำสั่งคอมพิวเตอร์ควรชัดเจน JVM ไม่ฉลาด คำสั่งเดียวกันจะต้องชัดเจนและจะไม่มีความหมายสองประการ ดังนั้นไม่ว่าคำสั่งคืนจะเป็นอย่างไรเมื่อเรียกใช้เนื้อหาของสแต็กจะปรากฏขึ้นและกลับไปที่วิธีการโทร
ในเวลาเดียวกันเราจะเห็นได้ว่าหนังสือ "Deep Into Java Virtual Machine" ให้คำอธิบายอื่น เมื่อคอมไพเลอร์ Java รวบรวมประโยคในที่สุดคำสั่ง JSR จะถูกสร้างขึ้น มันทำให้ JVM ถูกเรียกไปยังรูทีนย่อยขนาดเล็กสำหรับการดำเนินการนั่นคือที่บล็อกในที่สุด ในเวลาเดียวกันคำสั่ง Return 0 ในโปรแกรมจะถูกรวบรวมลงในตัวแปร Return ในสแต็กไปยังตัวแปรโลคัลก่อนที่จะเรียกคำสั่ง JSR และคำสั่ง JSR เรียกว่าบล็อกในที่สุดจะถูกดำเนินการและการส่งคืนบล็อกในที่สุด เมื่อค่าส่งคืนในตัวแปรโลคัลถูกผลักเข้าไปในสแต็กคำสั่ง ireturn จะถูกดำเนินการค่าส่งคืนจะปรากฏขึ้นจากสแต็กและกลับไปยังวิธีการโทร ที่นี่ค่าส่งคืนจะถูกบันทึกไว้ในตัวแปรโลคัลก่อนที่จะดำเนินการคำสั่ง JSR เนื่องจากข้อยกเว้นอาจเกิดขึ้นระหว่างการดำเนินการของบล็อกในที่สุดหรือมีค่าส่งคืน ด้วยวิธีนี้ความสอดคล้องของการดำเนินการโปรแกรมขั้นสุดท้ายจะมั่นใจได้ เนื่องจาก "Deepening Java Virtual Machine" ถูกเขียนขึ้นมาระยะหนึ่งการใช้งานและเวอร์ชันของคอมไพเลอร์ JVM ที่ใช้โดยผู้เขียนก็แตกต่างจากที่กล่าวถึงในบทความนี้ ดังนั้นหลังจากการทดสอบมีความแตกต่างเล็กน้อยในการสร้างไบต์สำหรับการใช้งานคอมไพเลอร์ที่แตกต่างกันหรือเวอร์ชันของคอมไพเลอร์ที่แตกต่างกันสำหรับโปรแกรมเดียวกัน หากคุณสนใจคุณสามารถดู bytecode ที่สร้างขึ้นโดยประโยคในที่สุดในหนังสือเล่มนี้
การสร้างแบบไบต์ในบทความนี้รวบรวมและสร้างขึ้นโดยคอมไพเลอร์เวอร์ชัน JDK8U-25 ของ Oracle
ลองดูตัวอย่างด้านล่าง
1. ลองจับตัวอย่างในที่สุด:
คลาสสาธารณะในที่สุด {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {int r = test (); System.out.println (r); } การทดสอบ int แบบคงที่สาธารณะ () {ลอง {system.out.println ("ลอง"); // return 1/0; กลับ 0; } catch (exception e) {system.out.println ("ข้อยกเว้น"); กลับ 100; } ในที่สุด {system.out.println ("สุดท้าย"); -ใช้คำสั่ง return 0 ในบล็อกลองและผลการทำงานของโปรแกรมคือ:
พยายาม
ในที่สุด
0
ใช้คำสั่ง return 1/0 ในบล็อกลองและผลลัพธ์ของการทำงานของโปรแกรมคือ:
ข้อยกเว้น
ในที่สุด
100
ในความเป็นจริงผ่านผลการทำงานเราจะเห็นว่าบล็อกในที่สุดจะถูกดำเนินการหลังจากคำสั่งอื่น ๆ ก่อนที่คำสั่ง Return ในการลองหรือ catch block กล่าวอีกนัยหนึ่งลำดับการเขียนของโปรแกรมไม่ตรงกับลำดับการดำเนินการของเราเนื่องจาก JVM ตีความและดำเนินการ bytecode ดังนั้นเราจึงจำเป็นต้องดูว่า Java Compiler รวบรวมรหัสนี้อย่างไรและดูว่า Bytecode สร้างขึ้นอย่างไร
2. ส่วนหนึ่งของ bytecode ที่สร้างโดยโปรแกรม: (โปรดดูคำสั่ง Java bytecode)
การทดสอบ int แบบคงที่สาธารณะ (); descriptor: () ฉันตั้งค่าสถานะ: acc_public, acc_static รหัส: stack = 2, locals = 2, args_size = 0 0: getStatic #20 // ฟิลด์ java/lang/system.out: ljava/io/printstream; 3: ldc #36 // สตริงลอง 5: invokevirtual #38 // วิธี java/io/printstream.println: (ljava/lang/string;) v 8: getstatic #20 // ฟิลด์ Java/lang/system.out: ljava/io/printstream; 11: ldc #41 // สตริงในที่สุด 13: invokevirtual #38 // วิธี java/io/printstream.println: (ljava/lang/string;) v 16: iconst_0 17: ireturn 18: store_0 19: getstatic #20 // field java/lang/lang/lang 22: LDC #43 // String Exception 24: Invokevirtual #38 // วิธี Java/io/printstream.println: (ljava/lang/string;) v 27: getstatic #20 // ฟิลด์ Java/lang/system.out: ljava/io/printstream; 30: ldc #41 // สตริงในที่สุด 32: invokevirtual #38 // วิธี java/io/printstream.println: (ljava/lang/string;) v 35: bipush 100 37: ireturn 38: store_1 39: getstatic #20 // java/lang/lang/lang 42: ldc #41 // สตริงในที่สุด 44: invokevirtual #38 // เมธอด java/io/printstream.println: (ljava/lang/string;) v 47: aload_1 48: Athrow Exception Table: จากเป้าหมายประเภท 0 8 18
จากส่วนสีแดงเราจะเห็นว่าบรรทัดที่ 10 และ 11 สอดคล้องกับคำสั่งคำสั่งบล็อกในที่สุด 16 และ 17 สอดคล้องกับคำสั่งส่งคืน 0 หลังจากคำสั่งอื่น ๆ ในบล็อกลองก่อนกลับ 19 และ 20 สอดคล้องกับคำสั่งบล็อกในที่สุด 21 และ 22 สอดคล้องกับคำแนะนำของคำสั่งส่งคืน 100 คำสั่ง หลังจากจับข้อความอื่น ๆ และก่อนที่จะกลับมาเราจะเห็นว่าทุกอย่างเกิดขึ้นเบื้องหลังสิ่งเหล่านี้คือคอมไพเลอร์ Java ได้ทำสิ่งนี้ทั้งหมดให้เรา สำหรับข้อยกเว้นที่เกิดขึ้นในโปรแกรม JVM จะพบตำแหน่งที่อยู่ที่สอดคล้องกันสำหรับการจัดการข้อยกเว้นจากตารางข้อยกเว้นเพื่อดำเนินการ
ดังนั้นเราจึงสามารถสรุปได้ว่าคำสั่งในบล็อกในที่สุดจะถูกแทรกโดยคอมไพเลอร์ Java ก่อนที่คำสั่งลองบล็อกและบล็อกการคืนกลับและหลังจากข้อความอื่น ๆ ไม่มีรูทีนย่อยเพื่อสร้างการโทร JSR ที่นี่ นั่นเป็นเหตุผลว่าทำไมไม่ว่าจะเป็นการดำเนินการบล็อกลองหรือเรียกใช้งานบล็อกการจับบล็อกในที่สุดจะถูกดำเนินการก่อนที่วิธีการจะกลับมา
การวิเคราะห์ที่ครอบคลุมข้างต้นเกี่ยวกับเวลาดำเนินการของ Java ในที่สุดบล็อกคือเนื้อหาทั้งหมดที่ฉันแบ่งปันกับคุณ ฉันหวังว่าคุณจะให้ข้อมูลอ้างอิงและฉันหวังว่าคุณจะสนับสนุน wulin.com มากขึ้น