ได้รับการคุ้มครอง
มาพูดคุยเกี่ยวกับปัญหาสิทธิการเข้าถึงที่ได้รับการป้องกัน ดูตัวอย่างที่ 1 ด้านล่าง:
test.java
คลาส myobject {} การทดสอบคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {myobject obj = new myobject (); obj.clone (); // รวบรวมข้อผิดพลาด - วิธีการโคลนจากวัตถุประเภทไม่สามารถมองเห็นได้
เราได้เข้าใจแล้วว่า Object.clone () เป็นวิธีการที่ได้รับการป้องกัน สิ่งนี้แสดงให้เห็นว่าวิธีการสามารถเข้าถึงได้โดยคลาสย่อยของแพ็คเกจเดียวกัน (java.lang) และมัน (java.lang.Object) นี่คือคลาส MyObject (การสืบทอดเริ่มต้นของ java.lang.Object)
ในทำนองเดียวกันการทดสอบยังเป็นคลาสย่อยของ java.lang.Object อย่างไรก็ตามวิธีการที่ได้รับการป้องกันของคลาสย่อยอื่นไม่สามารถเข้าถึงได้ในคลาสย่อยหนึ่งคลาสแม้ว่าทั้งสองคลาสย่อยที่สืบทอดมาจากคลาสพาเรนต์เดียวกัน
มาดูตัวอย่าง 2 อีกครั้ง:
test2.java
คลาส myobject2 {object clone ที่ได้รับการป้องกัน () พ่น clonenotsupportedexception {return super.clone (); }} คลาสสาธารณะ test2 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่น clonenotsupportedException {myObject2 obj = new myObject2 (); obj.clone (); // รวบรวมตกลง - ที่นี่เราแทนที่เมธอด clone () ของคลาสแม่ในคลาส MyObject2, เรียกวิธีการโคลน () ในคลาสอื่น test2 และรวบรวมและผ่าน
เหตุผลในการรวบรวมนั้นชัดเจน เมื่อคุณแทนที่เมธอด clone () ในคลาส MyObject2 คลาส MyObject2 และคลาส test2 อยู่ภายใต้แพ็คเกจเดียวกันดังนั้นวิธีการป้องกันนี้จะมองเห็นได้ในคลาส test2
ณ จุดนี้เราจำคำแถลงในบทที่ 2.2 ในบทความคัดลอกตื้นและลึกใน Java, ②เขียนทับวิธีการโคลน () ของชั้นเรียนฐานในชั้นเรียนที่ได้รับและประกาศว่าเป็นสาธารณะ ตอนนี้ฉันเข้าใจเหตุผลของประโยคนี้ (เพื่อให้คลาสอื่นสามารถเรียกวิธีการโคลน () ของคลาสนี้หลังจากการโอเวอร์โหลดคุณลักษณะของวิธีการโคลน () จะต้องตั้งค่าเป็นสาธารณะ)
มาดูตัวอย่างที่ 3:
test3.java
แพ็คเกจ 1 Class MyObject3 {object object clone () พ่น clonenotsupportedException {return super.clone (); }} แพ็คเกจ 2Public คลาส test3 ขยาย myObject3 {โมฆะคงที่สาธารณะหลัก (สตริง args []) {myObject3 obj = ใหม่ myObject3 (); obj.clone (); // รวบรวมข้อผิดพลาด test3 TOBJ = ใหม่ test3 (); tobj.clone (); // สอดคล้องกับตกลง - ที่นี่ฉันใช้คลาส test3 เพื่อสืบทอด myobject3 โปรดทราบว่าคลาสทั้งสองนี้มีแพ็คเกจที่แตกต่างกันมิฉะนั้นเป็นกรณีของตัวอย่าง 2 ในคลาส Test3 ให้เรียกวิธีการโคลน () ของอินสแตนซ์ TOBJ ของคลาส test3 และรวบรวมและผ่าน และยังเรียกวิธีการโคลน () ของอินสแตนซ์ OBJ ของคลาส MyObject3 และเรียกว่าข้อผิดพลาดในการรวบรวม!
ผลลัพธ์ที่ไม่คาดคิดวิธีการป้องกันไม่สามารถเข้าถึงได้โดยคลาสที่สืบทอดมาหรือไม่?
จะต้องชัดเจนว่าคลาส test3 จะสืบทอดคลาส myobject3 (รวมถึงวิธีการโคลน) ดังนั้นคุณสามารถเรียกวิธีการโคลนของคุณเองในคลาส test3 อย่างไรก็ตามวิธีการที่ได้รับการป้องกันของคลาส MyObject3 นั้นมองไม่เห็นกับ Subclass ของ BUN Subclass ที่แตกต่างกัน
นี่คืออีกข้อความหนึ่งจาก "Java in a Nutshell":
การเข้าถึงที่ได้รับการป้องกันต้องใช้แรงงานเพิ่มขึ้นเล็กน้อย สมมติว่า Class A ประกาศฟิลด์ที่ได้รับการป้องกัน X และขยายโดยคลาส B ซึ่งกำหนดไว้ในแพ็คเกจที่แตกต่างกัน (จุดสุดท้ายนี้มีความสำคัญ) คลาส B สืบทอดฟิลด์ที่ได้รับการป้องกันและรหัสสามารถเข้าถึงฟิลด์นั้นในอินสแตนซ์ปัจจุบันของ B หรือในกรณีอื่น ๆ ของ B ที่รหัสสามารถอ้างถึงได้ อย่างไรก็ตามนี่ไม่ได้หมายความว่ารหัสของคลาส B สามารถเริ่มอ่านฟิลด์ที่ได้รับการป้องกันของอินสแตนซ์โดยพลการของ A! หากวัตถุเป็นอินสแตนซ์ของ A แต่ไม่ใช่อินสแตนซ์ของ B ฟิลด์ของมันจะไม่ได้รับการสืบทอดโดย B และรหัสของคลาส B ไม่สามารถอ่านได้
โดยวิธีการที่หนังสือ Java จำนวนมากในประเทศจีนมีการอธิบายโดยทั่วไปในลักษณะนี้เมื่อแนะนำการอนุญาตการเข้าถึง (รูปแบบต่าง ๆ และเนื้อหาที่สอดคล้องกัน):
การควบคุมการเข้าถึงวิธีการ:
คงที่
1. คำหลักคงที่ (จำสิ่งเหล่านี้ก่อนจากนั้นอ่านลง)
1) วิธีการคงที่และตัวแปรคงที่เป็นวัตถุที่เป็นของคลาสที่แน่นอน แต่ไม่ใช่คลาส
2) การอ้างอิงถึงวิธีการคงที่และตัวแปรคงที่จะถูกอ้างอิงโดยตรงผ่านชื่อคลาส
3) วิธีที่ไม่คงที่และตัวแปรสมาชิกที่ไม่คงที่ไม่สามารถเรียกใช้ในวิธีการคงที่ มิฉะนั้นก็โอเค
4) ตัวแปรคงที่มีความคล้ายคลึงกับตัวแปรทั่วโลกในภาษาอื่น ๆ ในบางโปรแกรมและสามารถเข้าถึงได้นอกชั้นเรียนหากไม่ใช่ส่วนตัว
2. เมื่อใดควรใช้แบบคงที่
เมื่อเราสร้างอินสแตนซ์ของคลาส (วัตถุ) เรามักจะใช้วิธีการใหม่เพื่อให้พื้นที่ข้อมูลของคลาสนี้ถูกสร้างขึ้นและวิธีการของมันสามารถเรียกได้
อย่างไรก็ตามบางครั้งเราหวังว่าถึงแม้ว่าคลาสสามารถสร้างขึ้นด้วยวัตถุ N (เห็นได้ชัดว่าพื้นที่ข้อมูลของวัตถุ N เหล่านี้แตกต่างกัน) ข้อมูลบางอย่างของวัตถุ N เหล่านี้เหมือนกันนั่นคือโดยไม่คำนึงถึงจำนวนอินสแตนซ์ที่คลาสมีข้อมูลมีสำเนาหน่วยความจำของอินสแตนซ์เหล่านี้ (ดูตัวอย่าง 1) นี่เป็นกรณีที่มีตัวแปรคงที่
อีกสถานการณ์หนึ่งคือคุณต้องการให้วิธีการที่จะไม่เชื่อมโยงกับวัตถุใด ๆ ของคลาสที่มี กล่าวคือวิธีนี้สามารถเรียกได้แม้ว่าวัตถุจะไม่ถูกสร้างขึ้น การใช้วิธีที่สำคัญของวิธีการคงที่คือการเรียกมันโดยไม่ต้องสร้างวัตถุใด ๆ (ดูตัวอย่างที่ 2) นี่เป็นกรณีที่มีวิธีการคงที่
นอกจากนี้ยังมีการใช้งานพิเศษในชั้นเรียนภายใน โดยปกติแล้วคลาสปกติจะไม่ได้รับอนุญาตให้ประกาศแบบคงที่มีเพียงชั้นในเท่านั้นที่สามารถทำได้ ในเวลานี้คลาสภายในนี้ประกาศว่าสามารถใช้งานได้โดยตรงเป็นคลาสปกติโดยไม่จำเป็นต้องอินสแตนซ์ชั้นนอก (ดูตัวอย่างที่ 3) นี่เป็นกรณีที่มีคลาสคงที่
ตัวอย่างที่ 1
คลาสสาธารณะ tstatic {int Static int i; tstatic สาธารณะ () {i = 4; } สาธารณะ tstatic (int j) {i = j; } โมฆะคงที่สาธารณะหลัก (สตริง args []) {system.out.println (tstatic.i); tstatic t = tstatic ใหม่ (5); // ประกาศการอ้างอิงวัตถุและสร้างอินสแตนซ์ ในเวลานี้ i = 5 system.out.println (ti); tstatic tt = new tstatic (); // ประกาศการอ้างอิงวัตถุและสร้างอินสแตนซ์ ในเวลานี้ i = 4 system.out.println (ti); System.out.println (tt.i); System.out.println (ti); -
ผลลัพธ์:
05444
ตัวแปรคงที่ถูกสร้างขึ้นเมื่อโหลดคลาส ตราบใดที่ชั้นเรียนยังมีอยู่ตัวแปรคงที่มีอยู่ พวกเขาจะต้องเริ่มต้นเมื่อกำหนด ในตัวอย่างข้างต้นฉันไม่ได้เริ่มต้นดังนั้นค่าเริ่มต้นเริ่มต้น 0 จะได้รับ 0 ตัวแปรสแตติกสามารถเริ่มต้นได้เพียงครั้งเดียวและตัวแปรสแตติกยอมรับการเริ่มต้นล่าสุดเท่านั้น
ในความเป็นจริงนี่ยังคงเป็นปัญหาของหลาย ๆ กรณีที่แบ่งปันตัวแปรคงที่
ตัวอย่างที่ 2
ไม่ได้ประกาศว่าคงที่
คลาส Classa {int b; โมฆะสาธารณะ ex1 () {} คลาส classb {void ex2 () {int i; classa a = new classa (); i = ab; // ที่นี่การเข้าถึงตัวแปรสมาชิก B A.EX1 () ผ่านการอ้างอิงวัตถุ; // ที่นี่การเข้าถึงฟังก์ชั่นสมาชิก ex1 ผ่านการอ้างอิงวัตถุ}}}
ประกาศว่าคงที่
คลาส Classa {int Static Int B; โมฆะคงที่ ex1 () {}} คลาส classb {void ex2 () {int i; i = classa.b; // ที่นี่เข้าถึงตัวแปรสมาชิก B classa.ex1 () ผ่านชื่อคลาส; // ที่นี่การเข้าถึงฟังก์ชั่นสมาชิก ex1 ผ่านชื่อคลาส}} เมื่อใช้วิธีการคงที่ให้ระวังว่าวิธีการที่ไม่คงที่ไม่สามารถเรียกใช้ในวิธีการคงที่และอ้างอิงตัวแปรสมาชิกที่ไม่คงที่ (สิ่งนี้หรือซูเปอร์ไม่สามารถอ้างอิงได้ในวิธีใดในวิธีการคงที่) เหตุผลนั้นง่ายมาก สำหรับสิ่งที่คงที่เมื่อ JVM โหลดคลาสมันจะเปิดช่องว่างคงที่เหล่านี้ในหน่วยความจำ (ดังนั้นจึงสามารถอ้างอิงโดยตรงผ่านชื่อคลาส) และในเวลานี้คลาสที่วิธีการที่ไม่คงที่และตัวแปรสมาชิกจะไม่ได้รับการยกตัวอย่าง
ดังนั้นหากคุณต้องการใช้วิธีการที่ไม่คงที่และตัวแปรสมาชิกคุณสามารถสร้างอินสแตนซ์คลาสที่เมธอดหรือตัวแปรสมาชิกอยู่ในวิธีการคงที่โดยตรง นั่นเป็นวิธีที่โมฆะคงที่สาธารณะทำ
ตัวอย่างที่ 3
คลาสสาธารณะ StaticCls {โมฆะคงที่สาธารณะหลัก (String [] args) {outercls.innercls oi = new outercls.innercls (); // ไม่จำเป็นต้องใหม่ outercls ก่อนหน้านี้}} ชั้น Outercls -
ผลลัพธ์:
innercls
3. การเริ่มต้นแบบคงที่
ตัวแปรที่กำหนดโดยสแตติกจะมีความสำคัญกว่าตัวแปรที่ไม่คงที่อื่น ๆ โดยไม่คำนึงถึงลำดับที่ปรากฏ บล็อกรหัสคงที่ (ตามด้วยชิ้นส่วนของรหัส) ใช้เพื่อดำเนินการเริ่มต้นตัวแปรคงที่อย่างชัดเจน รหัสนี้จะเริ่มต้นเพียงครั้งเดียวและเมื่อคลาสถูกโหลดเป็นครั้งแรก ดูตัวอย่างด้านล่าง
ค่าคลาส {คงที่ int c = 0; ค่า () {c = 15; } ค่า (int i) {c = i; } static void inc () {c ++; }} การนับคลาส {โมฆะสาธารณะคงที่ PRT (String S) {System.out.println (S); } value v = ค่าใหม่ (10); ค่าคงที่ v1, v2; คงที่ {prt ("ในบล็อกคงที่ของ cals count v1.c =" + v1.c + "v2.c =" + v2.c); v1 = ค่าใหม่ (27); PRT ("ในบล็อกคงที่ของ Cals Count v1.c =" + v1.c + "v2.c =" + v2.c); v2 = ค่าใหม่ (); PRT ("ในบล็อกคงที่ของ Cals Count v1.c =" + v1.c + "v2.c =" + v2.c); }} คลาสสาธารณะ tStaticBlock {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {นับ ct = count ใหม่ (); count.prt ("ในหลัก:"); count.prt ("ct.c =" + ct.vc); count.prt ("v1.c =" + count.v1.c + "v2.c =" + count.v2.c); count.v1.inc (); count.prt ("v1.c =" + count.v1.c + "v2.c =" + count.v2.c); count.prt ("ct.c =" + ct.vc); -
ผลลัพธ์:
ในบล็อกคงที่ของ calss นับ v1.c = 0 v2.c = 0in บล็อกคงที่ของ calss นับ v1.c = 27 v2.c = 27 ในบล็อกคงที่ของ calss นับ v1.c = 15 v2.c = 15 ในหลัก: ct.c = 10v1.c = 10 v2.c = 10v1.c = 11 v2.c = 11 v2.c =
ไม่ว่าจะเป็น V, V1 หรือ V2 ตัวแปรสมาชิกที่ใช้งานอยู่นั้นเป็นตัวแปรคงที่เดียวกัน c
ในการนับคลาส V1 และ V2 (ค่าคงที่ v1, v2;) จากนั้นเริ่มต้นบล็อกรหัสคงที่ (คงที่ {}) และในที่สุดก็เริ่มต้น v