ชั้นสุดท้าย
เมื่อคลาสถูกกำหนดให้เป็นคลาสสุดท้ายหมายความว่าชั้นเรียนไม่สามารถสืบทอดได้โดยคลาสอื่นนั่นคือมันไม่สามารถใช้หลังจากขยาย มิฉะนั้นคุณจะได้รับข้อผิดพลาดระหว่างการรวบรวม
แพ็คเกจ com.iderzheng.finalkeyword; คลาสสุดท้ายของสาธารณะ FinalClass {} // ข้อผิดพลาด: ไม่สามารถสืบทอดจาก FinalClass Packageclass ขยาย FinalClass {} Java สนับสนุนการกำหนดคลาสเป็นขั้นสุดท้ายซึ่งดูเหมือนว่าจะละเมิดหลักการพื้นฐานของการเขียนโปรแกรมเชิงวัตถุ อย่างไรก็ตามในทางกลับกันคลาสที่ปิดล้อมยังช่วยให้มั่นใจได้ว่าวิธีการทั้งหมดของคลาสได้รับการแก้ไขและจะไม่มีการแทนที่คลาสย่อยที่จะโหลดแบบไดนามิก สิ่งนี้ให้ความเป็นไปได้มากขึ้นสำหรับคอมไพเลอร์ที่จะปรับให้เหมาะสม ตัวอย่างที่ดีที่สุดคือสตริงซึ่งเป็นคลาสสุดท้าย คอมไพเลอร์ Java สามารถเปลี่ยนค่าคงที่สตริงโดยตรง (ที่มีอยู่ในคำพูดสองเท่า) เป็นวัตถุสตริงและในเวลาเดียวกันเพิ่มประสิทธิภาพการทำงานของโอเปอเรเตอร์ + เป็นค่าคงที่ใหม่โดยตรงเนื่องจากการปรับเปลี่ยนขั้นสุดท้ายทำให้มั่นใจได้ว่าไม่มี subclasses ส่งคืนค่าที่แตกต่างกันสำหรับการดำเนินการ
สำหรับคำจำกัดความของคลาสที่แตกต่างกันทั้งหมด - คลาสระดับบนสุด (Global หรือ Package มองเห็นได้) คลาสซ้อนกัน (คลาสซ้อนภายในหรือแบบคงที่) สามารถแก้ไขได้ด้วยขั้นสุดท้าย อย่างไรก็ตามโดยทั่วไปแล้วสุดท้ายส่วนใหญ่จะใช้ในการปรับเปลี่ยนคลาสที่กำหนดเป็นสาธารณะเนื่องจากสำหรับคลาสที่ไม่ใช่ Global ตัวดัดแปลงการเข้าถึงได้ จำกัด การมองเห็นของพวกเขาและมันยากที่จะสืบทอดคลาสเหล่านี้ดังนั้นจึงไม่จำเป็นต้องเพิ่มเลเยอร์ของข้อ จำกัด ขั้นสุดท้าย
มีการกล่าวถึงว่าแม้ว่าคลาสที่ไม่ระบุชื่อจะไม่สามารถสืบทอดได้ แต่ก็ไม่ได้ จำกัด อยู่ที่คอมไพเลอร์
นำเข้า java.lang.reflect.modifier; คลาสสาธารณะหลัก {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {runnable anonymous = new runnable () {@Override โมฆะสาธารณะเรียกใช้ () {}}; System.out.println (modifier.isfinal (anonymous.getClass (). getModifiers ())); -เอาท์พุท:
เท็จ
วิธีสุดท้าย
เกี่ยวข้องอย่างใกล้ชิดกับแนวคิดของการสืบทอดคือ polymorphism ซึ่งเกี่ยวข้องกับความแตกต่างระหว่างแนวคิดของการเอาชนะและการซ่อน (เพื่อความสะดวกต่อไปนี้เรียกว่า "การเขียนใหม่") อย่างไรก็ตามแตกต่างจากคำนิยามวิธีการใน C ++ ไม่ว่าจะเป็นการเพิ่มคำหลักเสมือนลงใน subclass ไม่ว่าจะเป็นวิธีการของลายเซ็นคลาสย่อยที่ถูกเขียนทับหรือซ่อนอยู่ใน Java subclasses ใช้ลายเซ็นวิธีการเดียวกันเพื่อเขียนทับคลาสพาเรนต์ซึ่งจะเป็นวิธีคลาสที่ซ่อนอยู่ เนื่องจาก Java อนุญาตให้เข้าถึงวิธีการคลาสโดยตรงผ่านวัตถุ Java จึงไม่อนุญาตให้ใช้วิธีการคลาสและวิธีการวัตถุที่มีลายเซ็นเดียวกันในคลาสเดียวกัน
ชั้นเรียนสุดท้ายกำหนดว่าทั้งชั้นเรียนไม่สามารถสืบทอดได้และยังหมายความว่าวิธีการทั้งหมดในชั้นเรียนไม่สามารถครอบคลุมและซ่อนอยู่โดยคลาสย่อย เมื่อคลาสไม่ได้รับการแก้ไขโดยรอบสุดท้ายวิธีบางวิธียังสามารถแก้ไขได้โดยใช้ขั้นสุดท้ายเพื่อป้องกันไม่ให้วิธีการเหล่านี้ถูกเขียนใหม่โดยคลาสย่อย
ในทำนองเดียวกันการออกแบบดังกล่าวจะทำลายความหลากหลายของวัตถุที่มุ่งเน้น แต่วิธีสุดท้ายสามารถมั่นใจได้ว่าการกำหนดการดำเนินการของมันจึงทำให้มั่นใจได้ถึงความเสถียรของการเรียกวิธีการ ในการออกแบบเฟรมเวิร์กบางวิธีที่นำไปใช้บางวิธีของคลาสนามธรรมมักจะถูก จำกัด ไว้ที่สุดท้ายเนื่องจากรหัสไดรเวอร์บางอย่างในกรอบจะพึ่งพาวิธีการเหล่านี้เพื่อให้บรรลุเป้าหมายที่กำหนดไว้ดังนั้นจึงไม่มีคลาสย่อยที่ครอบคลุม
ตัวอย่างต่อไปนี้แสดงบทบาทของการดัดแปลงขั้นสุดท้ายในวิธีการประเภทต่าง ๆ :
แพ็คเกจ com.iderzheng.other; ระดับสาธารณะ FinalMethods {โมฆะสาธารณะคงที่ PublicStaticMethod () {} สาธารณะสุดท้ายเป็นโมฆะสาธารณะ PublicFinalMethod () {} สาธารณะคงที่สาธารณะสุดท้ายเป็นโมฆะ PublicStaticFinalMethod () {} void protectedfinalMethod () Void StaticFinalMethod () {} ส่วนตัวคงที่ครั้งสุดท้ายเป็นโมฆะ PrivatestaticFinalMethod () {} ส่วนตัวสุดท้ายเป็นโมฆะ PrivateFinalMethod () {}} แพ็คเกจ com.iderzheng.finalkeyword; นำเข้า com.iderzheng.other.finalmethods; วิธีการระดับสาธารณะขยาย FinalMethods {โมฆะสาธารณะคงที่สาธารณะ publicStaticMethod () {} // ข้อผิดพลาด: ไม่สามารถแทนที่โมฆะสุดท้ายสาธารณะสาธารณะ publicfinalmethod () {} // ข้อผิดพลาด: ไม่สามารถแทนที่ void สุดท้าย publicfinalmethod () {} //} สาธารณะคงที่สุดท้ายเป็นโมฆะ PublicStaticFinalMethod () {} // ข้อผิดพลาด: ไม่สามารถแทนที่ void void protectedfinalmethod () {} // ข้อผิดพลาด: ไม่สามารถแทนที่โมฆะสุดท้ายที่ได้รับการป้องกันขั้นสุดท้าย PrivatestaticFinalMethod () {} เป็นโมฆะส่วนตัวสุดท้าย Void PrivateFinalMethod () {}} ก่อนอื่นโปรดทราบว่าในตัวอย่างข้างต้นวิธีสุดท้ายและวิธีการถูกกำหนดภายใต้แพ็คเกจที่แตกต่างกัน สำหรับ PublicStaticMethod ครั้งแรก subclass ประสบความสำเร็จในการเขียนวิธีการคงที่ของคลาสแม่ แต่เป็นวิธีการคงที่สิ่งที่เกิดขึ้นจริงคือ "ซ่อน" โดยเฉพาะวิธีการโทร PublicStaticMethod () จะดำเนินการการใช้งานในคลาสวิธีการ เมื่อเรียก FinalMethods.publicStaticMethod () การใช้งานจะไม่เกิดขึ้นกับการโหลด polymorphic ของคลาสย่อย แต่จะใช้การใช้งานของ FinalMethod โดยตรง ดังนั้นเมื่อใช้คลาสย่อยเพื่อเข้าถึงวิธีการมองเห็นวิธีการที่ลงนามโดยคลาสแม่จะถูกซ่อนไว้
สำหรับวิธีการทั่วโลก publicfinalmethod ตามที่อธิบายไว้ในวิธีการปรับเปลี่ยนขั้นสุดท้าย subclass ถูกห้ามไม่ให้เขียนทับมันและข้อยกเว้นจะถูกโยนลงในเวลาคอมไพล์ อย่างไรก็ตามชื่อวิธีการเหมือนกันในคลาสย่อย แต่มีพารามิเตอร์เช่น: PublicFinalMethod (String X) ก็โอเคเพราะนี่เป็นลายเซ็นวิธีการแบบซิงโครนัส
ใน Intellij, IDE แสดงคำเตือนไปยัง PublicStaticFinalMethod: วิธี 'คงที่' ประกาศ 'สุดท้าย' ดูเหมือนว่าซ้ำซ้อน แต่จากตัวอย่างมันจะเห็นได้ว่าสุดท้ายยังห้ามนิยาม subclass จากวิธีการคงที่เพื่อซ่อนมัน ในการพัฒนาจริงพฤติกรรมของการกำหนดวิธีการแบบคงที่เดียวกันของคลาสย่อยและคลาสแม่เป็นที่ต้องการอย่างยิ่งเนื่องจากวิธีการที่ซ่อนอยู่ต้องการให้นักพัฒนาให้ความสนใจกับการใช้ชื่อคลาสที่แตกต่างกันเพื่อกำหนดเอฟเฟกต์ที่แตกต่างกันซึ่งง่ายต่อการเกิดข้อผิดพลาด ยิ่งไปกว่านั้นภายในชั้นเรียนคุณสามารถเรียกวิธีการคงที่โดยตรงโดยไม่ต้องใช้ชื่อคลาส เมื่อนักพัฒนาได้รับมรดกอีกครั้งเขาอาจไม่สังเกตเห็นการดำรงอยู่ที่ซ่อนอยู่ โดยค่าเริ่มต้นเมื่อใช้วิธีการคลาสแม่เขาจะพบว่ามันไม่ใช่ผลลัพธ์ที่คาดหวัง ดังนั้นวิธีการคงที่ควรเป็นที่สิ้นสุดโดยค่าเริ่มต้นและไม่ควรซ่อนอยู่ดังนั้น IDE จึงคิดว่าเป็นการปรับเปลี่ยนที่ไม่จำเป็น
วิธีการปรับเปลี่ยนที่ได้รับการป้องกันและวิธีการปรับเปลี่ยนสาธารณะในคลาสหลักสามารถมองเห็นได้ในคลาสย่อยดังนั้นสถานการณ์ของการปรับเปลี่ยนวิธีสุดท้ายของวิธีการที่ได้รับการป้องกันจึงเหมือนกับวิธีการสาธารณะ มีการกล่าวถึงว่าในการพัฒนาที่แท้จริงวิธีการที่ได้รับการป้องกันโดยทั่วไปจะไม่ค่อยมีการกำหนดเพราะวิธีการดังกล่าวใช้งานได้จริงเกินไป
สำหรับวิธีแพคเกจคลาสแม่, คลาสย่อยภายใต้แพ็คเกจที่แตกต่างกันนั้นมองไม่เห็น วิธีการส่วนตัวได้รับการปรับแต่งและคลาสหลักเท่านั้นที่สามารถเข้าถึงได้ ดังนั้นคอมไพเลอร์อนุญาตให้คลาสย่อยสามารถกำหนดวิธีเดียวกัน แต่สิ่งนี้ไม่ได้ก่อให้เกิดการแทนที่หรือซ่อนเนื่องจากคลาสแม่ได้ซ่อนวิธีการเหล่านี้ผ่านตัวดัดแปลงไม่ได้เกิดจากการเขียนคลาสย่อยใหม่ แน่นอนถ้าคลาสย่อยและคลาสหลักอยู่ในแพ็คเกจเดียวกันสถานการณ์จะเหมือนกับสาธารณะก่อนหน้าและได้รับการปกป้อง
ทำไมวิธีสุดท้ายจึงมีประสิทธิภาพ?
วิธีสุดท้ายจะใช้กลไกการฝังเพื่อเพิ่มประสิทธิภาพอินไลน์ระหว่างการรวบรวม การเพิ่มประสิทธิภาพแบบอินไลน์หมายถึง: การเปลี่ยนรหัสฟังก์ชั่นการโทรโดยตรงระหว่างการรวบรวมแทนที่จะเรียกใช้ฟังก์ชั่นการโทรที่รันไทม์ อินไลน์จำเป็นต้องรู้ว่าฟังก์ชันใดที่จะใช้ในตอนท้ายเมื่อรวบรวม เห็นได้ชัดว่ามันเป็นไปไม่ได้ที่จะใช้โดยไม่ต้องสุดท้าย วิธีการที่ไม่ใช่รอบรองชนะเลิศอาจถูกเขียนใหม่ในคลาสย่อย เนื่องจาก polymorphism ที่เป็นไปได้คอมไพเลอร์ไม่สามารถกำหนดประเภทที่แท้จริงของวัตถุที่จะเรียกวิธีการในอนาคตในระหว่างขั้นตอนการรวบรวมและไม่สามารถกำหนดวิธีการโทร
ตัวแปรสุดท้าย
พูดง่ายๆคือตัวแปรสุดท้ายใน Java สามารถเท่านั้นและจะต้องเริ่มต้นหนึ่งครั้งและจากนั้นตัวแปรจะถูกผูกไว้กับค่า อย่างไรก็ตามการมอบหมายนี้ไม่จำเป็นต้องเริ่มต้นทันทีเมื่อมีการกำหนดตัวแปร Java ยังสนับสนุนผลลัพธ์ที่แตกต่างกันสำหรับตัวแปรสุดท้ายผ่านคำสั่งตามเงื่อนไข แต่ตัวแปรสามารถกำหนดได้เพียงครั้งเดียวในกรณีใด ๆ
อย่างไรก็ตามตัวแปรสุดท้ายของ Java ไม่ใช่ค่าคงที่สัมบูรณ์เนื่องจากตัวแปรวัตถุของ Java เป็นเพียงค่าอ้างอิงเท่านั้นดังนั้นสุดท้ายก็หมายความว่าการอ้างอิงไม่สามารถเปลี่ยนแปลงได้และเนื้อหาของวัตถุยังสามารถแก้ไขได้ เมื่อเทียบกับพอยน์เตอร์ C/C ++ มันเป็นเหมือนตัวแปรประเภท * const มากกว่าตัวแปรประเภท const *
ตัวแปร Java สามารถแบ่งออกเป็นสองประเภท: ตัวแปรท้องถิ่น (ตัวแปรท้องถิ่น) และตัวแปรสมาชิกชั้น (ฟิลด์คลาส) ต่อไปนี้เป็นรหัสเพื่อแนะนำสถานการณ์การเริ่มต้นแยกต่างหาก
ตัวแปรท้องถิ่น
ตัวแปรท้องถิ่นส่วนใหญ่อ้างถึงตัวแปรที่กำหนดไว้ในวิธีการ พวกเขาจะหายไปและไม่สามารถเข้าถึงได้หลังจากวิธีการ มีกรณีพิเศษที่สามารถแบ่งออกเป็น: พารามิเตอร์ฟังก์ชัน สำหรับกรณีนี้การเริ่มต้นของมันจะถูกผูกไว้กับพารามิเตอร์ที่ส่งผ่านเมื่อเรียกใช้ฟังก์ชัน
สำหรับตัวแปรท้องถิ่นอื่น ๆ พวกเขาจะถูกกำหนดไว้ในวิธีการและค่าของพวกเขาสามารถเริ่มต้นตามเงื่อนไข:
วิธีสตริงสาธารณะ (บูลีนสุดท้าย FinalParam) {// ข้อผิดพลาด: พารามิเตอร์สุดท้าย FinalParam อาจไม่ได้รับการกำหนด // finalparam = true; วัตถุสุดท้าย finallocal = finalparam? วัตถุใหม่ (): null; Finalvar สุดท้าย; if (finallocal! = null) {finalVar = 21; } else {finalVar = 7; } // ข้อผิดพลาด: ตัวแปร FinalVar อาจได้รับการกำหนดแล้ว // finalVar = 80; สตริงสุดท้าย Finalret; Switch (FinalVar) {กรณีที่ 21: finalret = "me"; หยุดพัก; กรณีที่ 7: finalret = "เธอ"; หยุดพัก; ค่าเริ่มต้น: finalret = null; } return finalret;} จากตัวอย่างข้างต้นจะเห็นได้ว่าพารามิเตอร์ฟังก์ชันที่แก้ไขโดยสุดท้ายไม่สามารถกำหนดค่าใหม่ได้ แต่ตัวแปรท้องถิ่นสุดท้ายอื่น ๆ สามารถกำหนดค่าในคำสั่งเงื่อนไขได้ นอกจากนี้ยังให้ความยืดหยุ่นบางอย่างสำหรับขั้นสุดท้าย
แน่นอนเงื่อนไขทั้งหมดในคำสั่งเงื่อนไขควรมีการมอบหมายให้กับตัวแปรท้องถิ่นสุดท้ายมิฉะนั้นคุณจะได้รับข้อผิดพลาดว่าตัวแปรอาจไม่ได้เริ่มต้น
วิธีสตริงสาธารณะ (วัตถุสุดท้าย FinalParam) {Final Int FinalVar; if (finalparam! = null) {finalVar = 21; } สตริงสุดท้าย Finalret; // ข้อผิดพลาด: ตัวแปร FinalVar อาจไม่ได้เริ่มต้นสวิตช์ (FinalVar) {กรณีที่ 21: finalret = "me"; หยุดพัก; กรณีที่ 7: finalret = "เธอ"; หยุดพัก; } // ข้อผิดพลาด: ตัวแปร finalret อาจไม่ได้รับการเริ่มต้น return finalret;} ในทางทฤษฎีตัวแปรท้องถิ่นไม่จำเป็นต้องกำหนดเป็นขั้นสุดท้ายและวิธีการออกแบบที่สมเหตุสมผลควรสามารถรักษาตัวแปรท้องถิ่นได้ดี เป็นเพียงว่าเมื่อใช้ฟังก์ชั่นที่ไม่ระบุชื่อเพื่อปิดในวิธี Java Java ต้องการให้ตัวแปรท้องถิ่นอ้างอิงต้องกำหนดเป็นขั้นสุดท้าย:
วิธีการเรียกใช้สาธารณะ (สตริงสตริง) {int integer = 12; ส่งคืนใหม่ runnable () {@Override โมฆะสาธารณะ run () {// ข้อผิดพลาด: จำเป็นต้องประกาศระบบสุดท้าย System.out.println (สตริง); // ข้อผิดพลาด: จำเป็นต้องมีการประกาศ System.out.out.println (จำนวนเต็ม); -เขตชั้นเรียน
ตัวแปรสมาชิกในชั้นเรียนสามารถแบ่งออกเป็นสองประเภท: คงที่และไม่คงที่ สำหรับตัวแปรสมาชิกระดับสแตติกเนื่องจากเกี่ยวข้องกับคลาสนอกเหนือจากการเริ่มต้นโดยตรง ณ เวลานิยามพวกเขายังสามารถวางไว้ในบล็อกคงที่และการใช้หลังสามารถเรียกใช้คำสั่งที่ซับซ้อนมากขึ้น:
แพ็คเกจ com.iderzheng.finalkeyword; นำเข้า java.util.hashset; นำเข้า java.util.linkedhashset; นำเข้า java.util.set; คลาสสาธารณะ StaticFinalfields {คงที่ int final int Static_final_init_inline = 7; ชุดสุดท้ายคงที่ <Integer> static_final_init_static_block; / ** บล็อกคงที่ **/ คงที่ {if (system.currentTimeMillis () % 2 == 0) {static_final_init_static_block = new hashset <> (); } else {static_final_init_static_block = ใหม่ linkedhashset <> (); } static_final_init_static_block.add (7); static_final_init_static_block.add (21); -นอกจากนี้ยังมีบล็อกที่ไม่คงที่ใน Java ที่สามารถเริ่มต้นตัวแปรสมาชิกที่ไม่คงที่ แต่สำหรับตัวแปรเหล่านี้มักจะถูกวางไว้ในตัวสร้างสำหรับการเริ่มต้น แน่นอนว่ามีความจำเป็นเพื่อให้แน่ใจว่าตัวแปรสุดท้ายแต่ละตัวจะเริ่มต้นหนึ่งครั้งในตัวสร้าง หากตัวสร้างอื่น ๆ ถูกเรียกผ่านสิ่งนี้ () ตัวแปรสุดท้ายเหล่านี้จะไม่สามารถกำหนดในตัวสร้างได้อีกต่อไป
แพ็คเกจ com.iderzheng.finalkeyword; ชั้นเรียนสาธารณะ Finalfields {สุดท้าย Long Final_init_Inline = System.currentTimeMillis (); Final Final_init_block สุดท้าย; Final Long Final_init_Constructor; / ** บล็อกเริ่มต้น **/ {final_init_block = system.nanotime (); } finalfields () {this (217); } finalfields (บูลีนบูล) {final_init_constructor = 721; } FinalFields (เริ่มต้นยาว) {final_init_constructor = init; -เมื่อสุดท้ายถูกใช้เพื่อแก้ไขคลาส (คลาส) และวิธีการ (วิธีการ) ส่วนใหญ่จะส่งผลกระทบต่อการสืบทอดเชิงวัตถุ หากไม่มีการสืบทอดจะไม่มีการพึ่งพารหัสของคลาสย่อยในคลาสหลัก ดังนั้นเมื่อแก้ไขรหัสในระหว่างการบำรุงรักษาไม่จำเป็นต้องพิจารณาว่าการใช้งาน subclass จะถูกทำลายหรือไม่ซึ่งทำให้สะดวกยิ่งขึ้น เมื่อใช้กับตัวแปร Java จะมั่นใจได้ว่าค่าตัวแปรจะไม่ถูกแก้ไข หากการออกแบบเพิ่มเติมทำให้มั่นใจได้ว่าสมาชิกของชั้นเรียนไม่สามารถแก้ไขได้ตัวแปรทั้งหมดสามารถเปลี่ยนเป็นค่าคงที่ซึ่งเป็นประโยชน์อย่างมากสำหรับการเขียนโปรแกรมแบบมัลติเธรด ดังนั้นรอบสุดท้ายมีผลดีมากในการบำรุงรักษารหัส