1. จุดความรู้เกี่ยวกับการควบคุมวัตถุและหน่วยความจำ
1. กระบวนการเริ่มต้นของตัวแปร Java รวมถึงตัวแปรท้องถิ่นตัวแปรสมาชิก (ตัวแปรอินสแตนซ์และตัวแปรคลาส)
2. ในความสัมพันธ์การสืบทอดเมื่อประเภทการรวบรวมเวลาและประเภทรันไทม์แตกต่างจากประเภทการรวบรวมเวลาของตัวแปรอ้างอิงวัตถุที่ใช้มีความแตกต่างในคุณสมบัติและวิธีการเข้าถึงวัตถุ
3. ลักษณะตัวดัดแปลงสุดท้าย
2. การแบ่งและกระบวนการเริ่มต้นของตัวแปร Java
ตัวแปรของโปรแกรม Java สามารถแบ่งออกเป็นตัวแปรสมาชิกและตัวแปรท้องถิ่นได้โดยประมาณ ตัวแปรสมาชิกสามารถแบ่งออกเป็นตัวแปรอินสแตนซ์ (ตัวแปรที่ไม่คงที่) และตัวแปรคลาส (ตัวแปรคงที่) โดยทั่วไปตัวแปรท้องถิ่นที่เราพบจะปรากฏในสถานการณ์ต่อไปนี้:
(1) พารามิเตอร์อย่างเป็นทางการ: ตัวแปรท้องถิ่นที่กำหนดไว้ในลายเซ็นวิธีการถูกกำหนดโดยผู้โทรและหายไปเมื่อวิธีการสิ้นสุด
(2) ตัวแปรท้องถิ่นภายในวิธี: ตัวแปรท้องถิ่นที่กำหนดไว้ในวิธีการจะต้องเริ่มต้น (กำหนดค่าเริ่มต้น) ในวิธีการและหายไปเมื่อการเริ่มต้นตัวแปรเริ่มต้นและสิ้นสุด
(3) ตัวแปรโลคัลในบล็อกรหัส: ตัวแปรโลคัลที่กำหนดไว้ในบล็อกรหัสจะต้องเริ่มต้น (กำหนดค่าเริ่มต้น) ที่ต้องแสดงในบล็อกรหัส พวกเขาจะมีผลเมื่อการเริ่มต้นเสร็จสิ้นและตายเมื่อบล็อกรหัสสิ้นสุดลง
แพ็คเกจ com.zlc.array; Public Class Testfield {{String B; // หากไม่ได้เริ่มต้นคอมไพเลอร์จะรายงานตัวแปรท้องถิ่น B อาจไม่ได้เริ่มต้น System.out.println (b); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {int a; // หากไม่ได้เริ่มต้นคอมไพเลอร์จะรายงานตัวแปรท้องถิ่น A อาจไม่ได้รับการเริ่มต้น System.out.println (a); - ตัวแปรสมาชิกที่แก้ไขด้วยสแตติกเป็นตัวแปรคลาสซึ่งเป็นของคลาสเอง ตัวแปรสมาชิกที่ไม่ได้แก้ไขด้วยสแตติกเป็นตัวแปรอินสแตนซ์ อินสแตนซ์ที่เป็นของคลาสนี้ใน JVM เดียวกันแต่ละคลาสสามารถสอดคล้องกับวัตถุคลาสหนึ่งเท่านั้น แต่แต่ละคลาสสามารถสร้างวัตถุ Java หลายรายการ (นั่นคือตัวแปรคลาสต้องใช้พื้นที่หน่วยความจำเพียงชิ้นเดียวและทุกครั้งที่ชั้นเรียนสร้างอินสแตนซ์มันจำเป็นต้องจัดสรรพื้นที่ของพื้นที่ให้กับตัวแปรอินสแตนซ์)
กระบวนการเริ่มต้นของตัวแปรอินสแตนซ์: จากมุมมองทางไวยากรณ์โปรแกรมสามารถดำเนินการเริ่มต้นของตัวแปรอินสแตนซ์ในสามสถานที่:
(1) ระบุค่าเริ่มต้นเมื่อกำหนดตัวแปรอินสแตนซ์
(2) ระบุค่าเริ่มต้นสำหรับตัวแปรตัวอย่างในบล็อกที่ไม่คงที่
(3) ระบุค่าเริ่มต้นสำหรับตัวแปรตัวอย่างในตัวสร้าง
ในหมู่พวกเขาเวลาเริ่มต้นของทั้งสองวิธี (1) และ (2) เร็วกว่าของ (3) ในตัวสร้างและคำสั่งเริ่มต้นทั้งสอง (1) และ (2) ถูกกำหนดในลำดับที่พวกเขาจัดเรียงในซอร์สโค้ด
แพ็คเกจ com.zlc.array; Public Class Testfield {Public TestField (อายุ int) {System.out.println ("เริ่มต้นสิ่งนี้. age ใน constructor ="+this.age); this.age = อายุ; } {system.out.println ("เริ่มต้นในบล็อกที่ไม่คงที่"); อายุ = 22; } // เริ่มต้นอายุ int = 15; โมฆะคงที่สาธารณะหลัก (สตริง [] args) {field testfield = New TestField (24); System.out.println ("อายุสุดท้าย ="+field.age); - ผลการดำเนินการคือ: เริ่มต้นสิ่งนี้ age = 15 ในตัวสร้างการเริ่มต้นในบล็อกที่ไม่คงที่
อายุสุดท้าย = 24
หากคุณรู้วิธีใช้ Javap คุณสามารถใช้ Javap -C XXXX (ไฟล์คลาส) เพื่อดูว่าคลาส Java ถูกรวบรวมอย่างไร
เมื่อกำหนดตัวแปรอินสแตนซ์ให้ระบุค่าเริ่มต้น ในบล็อกการเริ่มต้นสถานะของคำสั่งที่ระบุค่าเริ่มต้นสำหรับตัวแปรอินสแตนซ์มีค่าเท่ากัน หลังจากคอมไพเลอร์ถูกรวบรวมและประมวลผลพวกเขาทั้งหมดถูกกล่าวถึงในตัวสร้าง อายุ int ที่กล่าวถึงข้างต้น = 15 จะถูกแบ่งออกเป็นสองขั้นตอนต่อไปนี้เพื่อดำเนินการ:
1) อายุ int; เมื่อสร้างวัตถุ Java ระบบจะจัดสรรหน่วยความจำให้กับวัตถุตามคำสั่ง
2) อายุ = 15; คำสั่งนี้จะถูกสกัดลงในตัวสร้างของคลาส Java และดำเนินการ
กระบวนการเริ่มต้นของตัวแปรคลาส: จากมุมมองทางไวยากรณ์โปรแกรมสามารถเริ่มต้นและกำหนดค่าให้กับตัวแปรคลาสจากสองสถานที่
(1) ระบุค่าเริ่มต้นเมื่อกำหนดตัวแปรคลาส
(2) ระบุค่าเริ่มต้นสำหรับตัวแปรคลาสในบล็อกคงที่
คำสั่งการดำเนินการทั้งสองนั้นเหมือนกับการจัดเรียงของพวกเขาในซอร์สโค้ด ลองยกตัวอย่างที่ผิดปกติเล็กน้อย:
แพ็คเกจ com.zlc.array; คลาส testStatic {// การสาธิตสมาชิกชั้นเรียนอินสแตนซ์ทดสอบการสาธิตการทดสอบแบบคงที่ขั้นสุดท้าย = testStatic ใหม่ (15); // member คลาสคงที่ int อายุ = 20; // อินสแตนซ์ตัวแปร curage int carage; TestStatic สาธารณะ (int ปี) {// todo todo สร้างตัวสร้าง Auto -Auto -Auto -Auto -stub carage = อายุ - ปี; }} การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println (teststatic.demo.curage); testStatic staticDemo = testStatic ใหม่ (15); System.out.println (StaticDemo.Curage); - ผลลัพธ์ผลลัพธ์จะถูกพิมพ์ในสองบรรทัด หนึ่งคือการพิมพ์ตัวแปรอินสแตนซ์ของการสาธิตแอตทริบิวต์คลาสทดสอบและที่สองคือการส่งออกแอตทริบิวต์อินสแตนซ์ของ testStatic ผ่าน StaticDemo ของวัตถุ Java ตามกระบวนการเริ่มต้นของตัวแปรอินสแตนซ์และตัวแปรคลาสที่เราวิเคราะห์ข้างต้นเราสามารถอนุมานได้:
1) ในขั้นตอนแรกของการเริ่มต้นเมื่อโหลดคลาสจัดสรรพื้นที่หน่วยความจำสำหรับการสาธิตตัวแปรคลาสและอายุ ในเวลานี้ค่าเริ่มต้นของการสาธิตและอายุเป็นโมฆะและ 0 ตามลำดับ
2) ในขั้นตอนที่สองของการเริ่มต้นโปรแกรมกำหนดค่าเริ่มต้นให้กับการสาธิตและอายุตามลำดับ TestStatic (15) จำเป็นต้องเรียกตัวสร้างของ TestStatic ในเวลานี้อายุ = 0 ดังนั้นผลการพิมพ์คือ -15 เมื่อ StaticDemo เริ่มต้นอายุได้รับมอบหมายให้ 20 ดังนั้นผลลัพธ์ผลลัพธ์คือ 5
3. ความแตกต่างระหว่างตัวแปรสมาชิกที่สืบทอดและการสืบทอดวิธีการสมาชิกในความสัมพันธ์การสืบทอด
เมื่อสร้างวัตถุ Java ใด ๆ โปรแกรมจะเรียกบล็อกที่ไม่ใช่แบบคงที่และคอนสตรัคเตอร์คลาสแม่ของคลาสแม่ก่อนและในที่สุดก็เรียกบล็อกที่ไม่ใช่แบบคงที่และตัวสร้างของคลาสนี้ การเรียกตัวสร้างของคลาสแม่ผ่านตัวสร้างของคลาสย่อยโดยทั่วไปแบ่งออกเป็นสองสถานการณ์: หนึ่งคือการโทรโดยนัยและอีกอันคือจอแสดงผลสุดยอดเพื่อเรียกคอนสตรัคเตอร์ของคลาสแม่
เมธอดเด็กสามารถเรียกตัวแปรอินสแตนซ์ของคลาสแม่ นี่เป็นเพราะคลาสเด็กสืบทอดคลาสหลักและจะได้รับตัวแปรสมาชิกและวิธีการของคลาสแม่ อย่างไรก็ตามวิธีการคลาสแม่ไม่สามารถเข้าถึงตัวแปรอินสแตนซ์ของคลาสลูกได้เนื่องจากคลาสแม่ไม่ทราบว่าคลาสใดที่จะสืบทอดและตัวแปรสมาชิกประเภทใดที่จะเพิ่มคลาสย่อย แน่นอนในตัวอย่างที่รุนแรงบางชั้นผู้ปกครองยังสามารถเรียกตัวแปรคลาสเด็กได้ ตัวอย่างเช่น: คลาสเด็กเขียนเมธอดพาเรนต์คลาสใหม่และโดยทั่วไปจะพิมพ์ค่าเริ่มต้นเนื่องจากตัวแปรอินสแตนซ์ของคลาสเด็กยังไม่ได้เริ่มต้นในเวลานี้
แพ็คเกจ com.zlc.array; ชั้นเรียน {int อายุ = 50; พ่อสาธารณะ () {// todo toDo ที่สร้างขึ้นอัตโนมัติ stub Stub System.out.println (this.getClass ()); //this.sonmethod (); ไม่สามารถโทรหาข้อมูล (); } ข้อมูลโมฆะสาธารณะ () {system.out.println (อายุ); }} ลูกชายระดับสาธารณะขยายพ่อ {int อายุ = 24; ลูกชายสาธารณะ (อายุ int) {// todo toDo ที่สร้างขึ้นอัตโนมัติ stub this.age = อายุ; } @Override โมฆะสาธารณะข้อมูล () {// TODO วิธีการที่สร้างขึ้นอัตโนมัติระบบ Stub System.err.println (อายุ); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ลูกชายคนใหม่ (28); } // วิธีการเฉพาะย่อยสาธารณะโมฆะสาธารณะ sonmethod () {system.out.println ("วิธีลูกชาย"); - ตามการอนุมานปกติของเราตัวสร้างคลาสแม่จะถูกเรียกโดยปริยายผ่านคลาสย่อยและวิธีการข้อมูล () เรียกว่าในคอนสตรัคเตอร์คลาสแม่ (หมายเหตุ: ฉันไม่ได้บอกว่าคลาสแม่ถูกเรียก) ในทางทฤษฎีมันจะส่งออกตัวแปรอินสแตนซ์อายุของคลาสแม่ ผลการพิมพ์คาดว่าจะเป็น 50 แต่ผลการส่งออกจริงคือ 0. การวิเคราะห์เหตุผล:
1) การจัดสรรหน่วยความจำของวัตถุ Java ยังไม่เสร็จสมบูรณ์ในตัวสร้าง ตัวสร้างจะเสร็จสิ้นกระบวนการกำหนดค่าเริ่มต้นเท่านั้น นั่นคือก่อนที่จะเรียกตัวสร้างของคลาสหลัก JVM ได้จัดประเภทพื้นที่หน่วยความจำสำหรับวัตถุลูกชาย อวกาศนี้เก็บคุณลักษณะสองอายุหนึ่งคืออายุของคลาสย่อยและอื่น ๆ คืออายุของชั้นเรียนหลัก
2) เมื่อโทรหาลูกชายคนใหม่ (28) กระแสนี้เป็นวัตถุที่แสดงถึงวัตถุที่เป็นลูกชายย่อย เราสามารถพิมพ์ Object.getClass () และรับผลลัพธ์ของคลาส com.zlc.array.son อย่างไรก็ตามกระบวนการเริ่มต้นในปัจจุบันนั้นดำเนินการในตัวสร้างของคลาสหลักและไม่สามารถเรียกผ่าน this.sonmethod () เพราะประเภทการรวบรวมนี้เป็นพ่อ
3) เมื่อประเภทการรวบรวมเวลาของตัวแปรแตกต่างจากประเภทรันไทม์เมื่อเข้าถึงตัวแปรอินสแตนซ์ของวัตถุอ้างอิงผ่านตัวแปรค่าของตัวแปรอินสแตนซ์จะถูกกำหนดโดยประเภทของตัวแปรที่ประกาศ อย่างไรก็ตามเมื่อวิธีการอินสแตนซ์ของวัตถุมันอ้างอิงผ่านตัวแปรพฤติกรรมของวิธีการจะถูกกำหนดโดยวัตถุที่อ้างอิงจริง ดังนั้นวิธีการข้อมูลของคลาสย่อยจึงถูกเรียกที่นี่ดังนั้นอายุของคลาสย่อยจะถูกพิมพ์ เนื่องจากอายุยังไม่ได้เริ่มต้นอย่างเร่งด่วนค่าเริ่มต้นคือ 0
ในแง่ของ Layman เมื่อประเภทที่ประกาศไม่สอดคล้องกับประเภทใหม่จริงแอตทริบิวต์ที่ใช้คือคลาสหลักและวิธีการที่เรียกว่าคือคลาสเด็ก
ผ่าน Javap -C เราสามารถเข้าใจได้โดยตรงว่าทำไมจึงมีความแตกต่างอย่างมากระหว่างคุณลักษณะและวิธีการที่สืบทอดมา หากเราลบวิธีการเขียนข้อมูลของ Subclass Son ในตัวอย่างด้านบนวิธีการข้อมูลของคลาสพาเรนต์จะถูกเรียกในเวลานี้เพราะเมื่อรวบรวมวิธีการข้อมูลของคลาสพาเรนต์จะถูกถ่ายโอนไปยังคลาสย่อยและตัวแปรสมาชิกชื่อเสียงจะถูกทิ้งไว้ในคลาสพาเรนต์ ด้วยวิธีนี้คลาสย่อยและคลาสแม่มีตัวแปรอินสแตนซ์ที่มีชื่อเดียวกัน หาก subclass เขียนวิธีการของคลาสแม่ด้วยชื่อเดียวกันวิธีการย่อยจะเขียนทับวิธีการของคลาสแม่อย่างสมบูรณ์ (สำหรับสาเหตุที่ Java ได้รับการออกแบบเช่นนี้ฉันไม่ชัดเจนมาก) ตัวแปรที่มีชื่อเดียวกันสามารถมีอยู่และไม่เขียนทับในเวลาเดียวกัน คลาสย่อยของวิธีการที่มีชื่อเดียวกันจะเขียนทับวิธีชื่อเดียวกันของคลาสแม่
โดยทั่วไปสำหรับตัวแปรอ้างอิงเมื่อเข้าถึงตัวแปรอินสแตนซ์ของวัตถุที่อ้างอิงผ่านตัวแปรค่าของตัวแปรอินสแตนซ์ขึ้นอยู่กับประเภทเมื่อประกาศตัวแปรและเมื่อเข้าถึงวิธีการของวัตถุที่อ้างอิงผ่านตัวแปรพฤติกรรมวิธีการขึ้นอยู่กับประเภทของวัตถุที่อ้างอิงจริง
ในที่สุดฉันจะตรวจสอบด้วยเคสเล็ก ๆ :
แพ็คเกจ com.zlc.array; สัตว์ชั้น {อายุ int; สัตว์สาธารณะ () {} สัตว์สาธารณะ (อายุ int) {// toDo todo stub ที่สร้างขึ้นอัตโนมัติ stub this.age = อายุ; } void run () {system.out.println ("Animal Run"+อายุ); }} สุนัขชั้นเรียนขยายสัตว์ {อายุ int; ชื่อสตริง; Public Dog (อายุ int, ชื่อสตริง) {// toDo todo stuffructor stub this.age = อายุ; this.name = ชื่อ; } @Override เป็นโมฆะ Run () {System.out.println ("Dog Run"+อายุ); }} คลาสสาธารณะ testextends {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สัตว์สัตว์ = สัตว์ใหม่ (5); System.out.println (Animal.age); Animal.run (); Dog Dog = สุนัขตัวใหม่ (1, "Xiaobai"); System.out.println (dog.age); dog.run (); Animal Animal2 = สุนัขตัวใหม่ (11, "Wangcai"); System.out.println (Animal2.age); Animal2.run (); สัตว์สัตว์ 3; Animal3 = สุนัข; System.out.println (Animal3.age); Animal3.run (); - หากคุณต้องการเรียกวิธีการคลาสแม่: คุณสามารถเรียกมันผ่าน Super แต่คำหลัก Super ไม่ได้อ้างถึงวัตถุใด ๆ และไม่สามารถใช้เป็นตัวแปรอ้างอิงจริงได้ เพื่อนที่สนใจสามารถศึกษาด้วยตัวเอง
ข้างต้นเป็นตัวอย่างตัวแปรและวิธีการ ตัวแปรคลาสและวิธีการคลาสนั้นง่ายกว่ามากดังนั้นการใช้ชื่อคลาสโดยตรง วิธีการที่สะดวกกว่ามากและคุณจะไม่พบปัญหามากนัก
4. การใช้ตัวดัดแปลงสุดท้าย (โดยเฉพาะการเปลี่ยนแมโคร)
(1) Inal สามารถแก้ไขตัวแปรได้ หลังจากตัวแปรแก้ไขโดยสุดท้ายจะถูกกำหนดค่าเริ่มต้นแล้วจะไม่สามารถกำหนดได้อีกครั้ง
(2) Inal สามารถแก้ไขวิธีการและวิธีการแก้ไขขั้นสุดท้ายไม่สามารถเขียนใหม่ได้
(3) Inal สามารถปรับเปลี่ยนคลาสและคลาสที่แก้ไขโดยรอบสุดท้ายไม่สามารถรับ subclasses ได้
ค่าเริ่มต้นที่ระบุที่ตัวแปรแก้ไขโดยสุดท้ายจะต้องแสดง:
ตัวอย่างเช่นตัวแปรที่ได้รับการแก้ไขขั้นสุดท้ายค่าเริ่มต้นสามารถกำหนดได้ที่ตำแหน่งที่ระบุสามตำแหน่งต่อไปนี้เท่านั้น
(1) ระบุค่าเริ่มต้นเมื่อกำหนดตัวแปรอินสแตนซ์สุดท้าย
(2) ระบุค่าเริ่มต้นสำหรับตัวแปรอินสแตนซ์สุดท้ายในบล็อกที่ไม่คงที่
(3) ระบุค่าเริ่มต้นสำหรับตัวแปรอินสแตนซ์สุดท้ายในตัวสร้าง
ในที่สุดพวกเขาจะถูกกล่าวถึงในตัวสร้างสำหรับการเริ่มต้น
สำหรับตัวแปรคลาสที่ระบุด้วยขั้นสุดท้าย: ค่าเริ่มต้นสามารถกำหนดได้ในสองสถานที่ที่ระบุเท่านั้น
(1) ระบุค่าเริ่มต้นเมื่อกำหนดตัวแปรคลาสสุดท้าย
(2) ระบุค่าเริ่มต้นสำหรับตัวแปรคลาสสุดท้ายในบล็อกคงที่
ประมวลผลโดยคอมไพเลอร์ซึ่งแตกต่างจากตัวแปรอินสแตนซ์ตัวแปรคลาสทั้งหมดถูกกล่าวถึงเพื่อกำหนดค่าเริ่มต้นในบล็อกคงที่ในขณะที่ตัวแปรอินสแตนซ์ถูกกล่าวถึงกับตัวสร้าง
มีคุณสมบัติอื่นของตัวแปรคลาสที่แก้ไขโดยรอบชิงชนะเลิศซึ่งเป็น "การแทนที่มาโคร" เมื่อตัวแปรคลาสที่ปรับเปลี่ยนเป็นไปตามค่าเริ่มต้นเมื่อกำหนดตัวแปรค่าเริ่มต้นสามารถกำหนดได้ในระหว่างการรวบรวม (ตัวอย่างเช่น: 18, "AAAA", 16.78 และปริมาณโดยตรงอื่น ๆ ) จากนั้นตัวแปรคลาสที่แก้ไขโดยสุดท้ายไม่ใช่ตัวแปรและระบบจะถือว่าเป็น "ตัวแปรแมโคร" หากค่าเริ่มต้นสามารถกำหนดได้ในระหว่างการรวบรวมมันจะไม่ถูกกล่าวถึงในบล็อกสแตติกสำหรับการเริ่มต้นและค่าเริ่มต้นจะถูกแทนที่โดยตรงด้วยตัวแปรสุดท้ายในนิยามคลาส มายกตัวอย่างอายุลบปี:
แพ็คเกจ com.zlc.array; คลาส testStatic {// การสาธิตสมาชิกชั้นเรียนอินสแตนซ์ทดสอบการสาธิตการทดสอบแบบคงที่ขั้นสุดท้าย = testStatic ใหม่ (15); // ระดับสมาชิกระดับสุดท้ายอายุคงที่ int = 20; // อินสแตนซ์ตัวแปร curage int carage; TestStatic สาธารณะ (int ปี) {// todo todo สร้างตัวสร้าง Auto -Auto -Auto -Auto -stub carage = อายุ - ปี; }} การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println (teststatic.demo.curage); TestStatic Static1 = TestStatic ใหม่ (15); System.out.println (Static1.Curage); - ในเวลานี้อายุได้รับการแก้ไขโดยสุดท้ายดังนั้นเมื่อรวบรวมทุกวัยในคลาสหลักกลายเป็น 20 ไม่ใช่ตัวแปรเพื่อให้ผลลัพธ์ผลลัพธ์สามารถตอบสนองความคาดหวังของเรา
โดยเฉพาะอย่างยิ่งเมื่อเปรียบเทียบสตริงมันสามารถแสดงได้มากขึ้น
แพ็คเกจ com.zlc.array; Public Class TestString {สตริงคงที่ static_name1 = "java"; สตริงคงที่ static_name2 = "me"; สตริงคงที่ statci_name3 = static_name1+static_name2; สตริงคงสุดท้าย final_static_name1 = "java"; สตริงคงสุดท้าย final_static_name2 = "me"; // เพิ่มสุดท้ายหรือไม่สามารถแทนที่ด้วยแมโครที่ด้านหน้า สตริงสุดท้าย final_statci_name3 = final_static_name1+final_static_name2; โมฆะคงที่สาธารณะหลัก (สตริง [] args) {สตริงชื่อ 1 = "java"; สตริงชื่อ 2 = "ฉัน"; สตริงชื่อ 3 = name1+name2; // (1) System.out.println (name3 == "javame"); // (2) System.out.println (testString.statci_name3 == "javame"); // (3) System.out.println (teststring.final_statci_name3 == "javame"); - ไม่มีอะไรที่จะพูดเกี่ยวกับการใช้วิธีการดัดแปลงขั้นสุดท้ายและคลาสเพียงว่าหนึ่งไม่สามารถเขียนใหม่โดย subclasses (เช่นส่วนตัว) และอื่น ๆ ไม่สามารถรับ subclasses ได้
เมื่อแก้ไขตัวแปรท้องถิ่นด้วยขั้นสุดท้าย Java ต้องการให้ตัวแปรท้องถิ่นเข้าถึงโดยคลาสภายในจะถูกแก้ไขด้วยขั้นสุดท้าย มีเหตุผล สำหรับตัวแปรท้องถิ่นทั่วไปขอบเขตของพวกเขายังคงอยู่ในวิธีการ เมื่อวิธีการสิ้นสุดตัวแปรท้องถิ่นจะหายไป แต่คลาสภายในอาจสร้าง "การปิด" โดยนัยซึ่งทำให้ตัวแปรท้องถิ่นยังคงแยกออกจากวิธีการที่มันอยู่
บางครั้งเธรดจะใหม่ในวิธีการจากนั้นตัวแปรท้องถิ่นของวิธีการเรียก ในเวลานี้ตัวแปรการเปลี่ยนแปลงจะต้องประกาศว่าเป็นการแก้ไขขั้นสุดท้าย
5. วิธีการคำนวณของหน่วยความจำการครอบครองวัตถุ
ใช้วิธีการ freememory (), TotalMemory () และ MaxMemory () ในคลาส java.lang.runtime เพื่อวัดขนาดของวัตถุ Java วิธีนี้มักจะใช้เมื่อต้องมีการพิจารณาทรัพยากรจำนวนมากอย่างแม่นยำ วิธีนี้เกือบจะไร้ประโยชน์ในการใช้แคชระบบการผลิต ข้อได้เปรียบของวิธีนี้คือชนิดข้อมูลเป็นอิสระจากขนาดและระบบปฏิบัติการที่แตกต่างกันสามารถรับหน่วยความจำที่ครอบครองได้
มันใช้การสะท้อน API เพื่อสำรวจลำดับชั้นของตัวแปรสมาชิกของวัตถุและคำนวณขนาดของตัวแปรดั้งเดิมทั้งหมด วิธีการนี้ไม่จำเป็นต้องใช้ทรัพยากรจำนวนมากและสามารถใช้สำหรับการใช้งานแคช ข้อเสียคือขนาดประเภทดั้งเดิมนั้นแตกต่างกันและการใช้งาน JVM ที่แตกต่างกันมีวิธีการคำนวณที่แตกต่างกัน
หลังจาก JDK5.0 เครื่องมือวัด API ให้วิธี getObjectSize เพื่อคำนวณขนาดหน่วยความจำที่ครอบครองโดยวัตถุ
โดยค่าเริ่มต้นขนาดของวัตถุที่อ้างอิงจะไม่ถูกคำนวณ ในการคำนวณวัตถุที่อ้างอิงคุณสามารถใช้การสะท้อนเพื่อรับ วิธีการต่อไปนี้คือการใช้งานที่มีให้ในบทความข้างต้นที่คำนวณขนาดของวัตถุอ้างอิง:
คลาสสาธารณะ sizeofagent {เครื่องมือวัดแบบคงที่; / ** เริ่มต้น Agent*/ Public Static Void Premain (String AgentArgs, Instplessation Instp) {inst = instp; } /*** ส่งคืนขนาดวัตถุโดยไม่มีวัตถุย่อยสมาชิก * @param o วัตถุที่จะได้รับขนาดของ * @return วัตถุขนาด */ขนาดยาวคงที่สาธารณะ (Object O) {ถ้า (inst == null) {โยนใหม่ unlegalStateException ("ไม่สามารถเข้าถึงสภาพแวดล้อมการใช้เครื่องมือ/n" + "โปรดตรวจสอบว่าไฟล์ jar ที่มีขนาด/n" + " } return inst.getObjectSize (O); } /** * คำนวณขนาดเต็มของวัตถุที่วนซ้ำ * กราฟลำดับชั้น * @Param Object เพื่อคำนวณขนาดของ * @return วัตถุขนาด */ FullSizeof แบบคงที่แบบคงที่ (Object OBJ) {MAP <Object, Object> Visited = New IdentityHashMap <Object, Object> (); สแต็ก <Ojrop> stack = ใหม่สแต็ก <Ojrop> (); ผลลัพธ์ยาว = internalsizeof (obj, สแต็ค, เยี่ยมชม); ในขณะที่ (! stack.isempty ()) {result += internalsizeof (stack.pop (), สแต็ค, เยี่ยมชม); } เยี่ยมชม. clear (); ผลการกลับมา; } private boolean private skipobject (Object OBJ, MAP <Object, Object> เข้าเยี่ยมชม) {ถ้า (OBJ InstanceT ของสตริง) {// ข้ามสตริงฝึกงาน interned if (obj == ((String) OBJ) .intern ()) {ส่งคืนจริง; }} return (obj == null) // ข้ามวัตถุที่เข้าชม || เยี่ยมชม. containskey (obj); } InternalsizeF (Object OBJ, Stack <Object> สแต็ก, แผนที่ <วัตถุ, วัตถุ> เข้าเยี่ยมชม) {ถ้า (skipObject (obj, เยี่ยมชม)) {return 0; } Visited.put (obj, null); ผลลัพธ์ยาว = 0; // รับขนาดของวัตถุ + ตัวแปรดั้งเดิม + คะแนนสมาชิกผลลัพธ์ + = sizeofagent.sizeof (obj); // ประมวลผลองค์ประกอบอาร์เรย์ทั้งหมดคลาส clazz = obj.getClass (); if (clazz.isarray ()) {if (clazz.getName (). ความยาว ()! = 2) {// ข้ามประเภทอาร์เรย์ดั้งเดิมความยาว int array = array.getLength (OBJ); สำหรับ (int i = 0; i <length; i ++) {stack.add (array.get (obj, i)); }} ผลการส่งคืน; } // ประมวลผลฟิลด์ทั้งหมดของวัตถุในขณะที่ (clazz! = null) {ฟิลด์ [] ฟิลด์ = clazz.getDeclaredFields (); สำหรับ (int i = 0; i <fields.length; i ++) {ถ้า (! modifier.isstatic (ฟิลด์ [i] .getModifiers ())) {ถ้า (ฟิลด์ [i] .getType (). isprimitive ())) {ดำเนินการต่อ; // ข้ามเขตข้อมูลดั้งเดิม} else {ฟิลด์ [i] .setAccessible (จริง); ลอง {// วัตถุที่จะประมาณจะถูกนำไปใช้กับสแต็ก Object ObjectToadd = ฟิลด์ [i] .get (obj); if (ObjectToadd! = null) {stack.add (ObjectToadd); }} catch (unglegalAccessException ex) {ยืนยันเท็จ; }}}}} clazz = clazz.getSuperClass (); } ผลตอบแทนผลลัพธ์; -