ความแตกต่างระหว่างตัวแปรสมาชิกแบบคงที่และตัวแปรสมาชิกที่ไม่คงที่
ยกตัวอย่างต่อไปนี้เป็นตัวอย่าง
แพ็คเกจ cn.galc.test; คลาสสาธารณะ Cat { /** * ตัวแปรสมาชิกคงที่ * / ส่วนตัว int sid = 0; ชื่อสตริงส่วนตัว; Cat (ชื่อสตริง) { this.name = ชื่อ; } ข้อมูลโมฆะสาธารณะ () { System.out.println ("ชื่อของฉันคือ " + ชื่อ + ", NO" + id); } โมฆะสาธารณะคงที่ main (สตริง [] args) { Cat.sid = 100; Cat mimi = แมวใหม่ ("mimi"); Cat pipi = แมวใหม่ ("pipi");ทำความเข้าใจกระบวนการดำเนินการของโปรแกรมทั้งหมดโดยการวาดไดอะแกรมการวิเคราะห์หน่วยความจำ
เมื่อดำเนินการประโยคแรกของโปรแกรม: Cat.sid = 100; sid ที่นี่คือตัวแปรสมาชิกแบบคงที่ ตัวแปรคงที่จะถูกจัดเก็บไว้ในพื้นที่ข้อมูล (data seg) ดังนั้นก่อนอื่นให้จัดสรรพื้นที่ขนาดเล็ก sid ในพื้นที่ข้อมูล หลังจากประหารชีวิตประโยคแล้ว sid จะมีค่าเป็น 100
แผนผังเค้าโครงหน่วยความจำในขณะนี้มีดังนี้:
จากนั้นโปรแกรมจะดำเนินการ:
แมว mimi = แมวตัวใหม่ (“มีมี่”);
ที่นี่เรียกว่าวิธีการสร้าง Cat(String name) ของคลาส Cat วิธีการสร้างถูกกำหนดไว้ดังนี้:
แมว (ชื่อสตริง) { this.name = name; id=sid++; เมื่อทำการเรียก อันดับแรกให้จัดสรรหน่วยความจำชิ้นเล็กๆ mm ในหน่วยความจำสแต็ก ซึ่งมีที่อยู่ของวัตถุอินสแตนซ์ของคลาส Cat ในหน่วยความจำฮีป mm เป็นวัตถุอ้างอิงของวัตถุคลาส Cat ในหน่วยความจำฮีป ตัวสร้างนี้ประกาศตัวแปรพารามิเตอร์อย่างเป็นทางการของประเภทสตริง ดังนั้น "mimi" จึงถูกส่งผ่านไปยังตัวสร้างเป็นพารามิเตอร์จริง เนื่องจากค่าคงที่สตริงได้รับการจัดสรรและจัดเก็บไว้ในพื้นที่ข้อมูล จึงมีหน่วยความจำขนาดเล็กพิเศษในพื้นที่ข้อมูล ใช้เพื่อเก็บสตริง "mimi" การกระจายหน่วยความจำในเวลานี้แสดงในรูปด้านล่าง:
เมื่อเรียก Constructor ให้จัดสรรพื้นที่เล็กๆ ในหน่วยความจำสแต็กสำหรับชื่อพารามิเตอร์อย่างเป็นทางการ จากนั้นส่งสตริง "mimi" เป็นพารามิเตอร์จริงในการตั้งชื่อ สตริงยังเป็นประเภทการอ้างอิง ยกเว้นประเภทที่สี่และแปด ประเภทข้อมูลพื้นฐาน ส่วนอื่นๆ ทั้งหมดเป็นประเภทอ้างอิง ดังนั้นจึงถือได้ว่าสตริงก็เป็นวัตถุเช่นกัน ดังนั้น นี่จึงเทียบเท่ากับการส่งผ่านการอ้างอิงของวัตถุ "mimi" ไปยังชื่อ ดังนั้นตอนนี้ให้ชี้ไปที่ "mimi" ดังนั้นโครงร่างหน่วยความจำ ณ เวลานี้จึงเป็นดังนี้:
จากนั้นรันโค้ดในเนื้อหาคอนสตรัคเตอร์:
นี้.ชื่อ=ชื่อ;
นี่หมายถึงวัตถุปัจจุบัน ซึ่งหมายถึงแมวในหน่วยความจำฮีป ในที่นี้ ค่าที่มีอยู่ในชื่อในสแต็กจะถูกส่งผ่านไปยังแอตทริบิวต์ name ของวัตถุ cat ในหน่วยความจำฮีป ดังนั้นในเวลานี้ ค่าที่มีอยู่ในชื่อยังสามารถพบได้ในวัตถุสตริง "mimi" ที่อยู่ใน พื้นที่ข้อมูล ในเวลานี้ ชื่อนี้ยังเป็นวัตถุอ้างอิงของวัตถุสตริง "mimi" ด้วยค่าแอตทริบิวต์ของมัน วัตถุสตริง "mimi" ที่อยู่ในพื้นที่ข้อมูลสามารถพบได้ การกระจายหน่วยความจำในเวลานี้แสดงในรูปด้านล่าง:
จากนั้นรันโค้ดอีกบรรทัดในเนื้อหาของวิธีการ:
id=ซิด++;
ที่นี่ ค่าของ sid จะถูกส่งผ่านไปยัง id ดังนั้นค่าของ id คือ 100 หลังจากผ่าน sid แล้ว ให้บวก 1 ในเวลานี้ sid จะกลายเป็น 101 เค้าโครงหน่วยความจำในขณะนี้แสดงในรูปด้านล่าง
ณ จุดนี้ มีการเรียกเมธอด Constructor และพื้นที่หน่วยความจำทั้งหมดที่ถูกครอบครองโดยตัวแปรภายในเครื่องที่จัดสรรให้กับเมธอด Constructor นี้จะหายไป ดังนั้นหน่วยความจำชื่อที่อยู่ในพื้นที่สแต็กจะหายไป การอ้างอิงไปยังวัตถุสายอักขระ "mimi" ในพื้นที่ข้อมูลในหน่วยความจำสแต็กก็หายไปเช่นกัน ในเวลานี้ การอ้างอิงไปยังวัตถุสายอักขระ "mimi" ในหน่วยความจำฮีปเท่านั้นที่ยังคงอยู่ เค้าโครงหน่วยความจำในขณะนี้มีดังนี้:
ดำเนินการต่อไป:
แมว pipi = แมวใหม่ ("pipi"); นี่คือการเรียกเมธอดคอนสตรัคเตอร์ครั้งที่สอง Cat() กระบวนการโทรทั้งหมดจะเหมือนกับครั้งแรก หลังจากการโทรเสร็จสิ้น โครงร่างหน่วยความจำในเวลานี้จะเป็นดังแสดงในรูปด้านล่าง:
โค้ดสองบรรทัดสุดท้ายถูกพิมพ์โดยการเรียกเมธอด info() ผลลัพธ์การพิมพ์จะเป็นดังนี้:
ด้วยโปรแกรมนี้ เราจะเห็นบทบาทของตัวแปร sid สมาชิกแบบคงที่ซึ่งสามารถนับได้ เมื่อไหร่ก็ตามที่มีแมวตัวใหม่ออกมา ให้แจ้งหมายเลขให้มันด้วย ให้มันเพิ่ม 1 เอง.
หลังจากรันโปรแกรมแล้ว เค้าโครงทั้งหมดในหน่วยความจำจะเป็นดังแสดงในรูปด้านบน ดำเนินต่อไปจนกระทั่งช่วงเวลาก่อนที่การเรียกเมธอดหลักจะเสร็จสิ้น
ที่นี่ มีการเรียกเมธอดคอนสตรัคเตอร์ Cat(ชื่อสตริง) เพื่อสร้างแมวสองตัว ขั้นแรก ช่องว่างขนาดเล็กสองช่อง mimi และ pipi จะถูกจัดสรรในหน่วยความจำสแต็ก ซึ่งมีที่อยู่ซึ่งแมวสองตัวสามารถพบได้ ไปยังที่อยู่ในหน่วยความจำฮีป เครื่องหมายคำพูดแมวสองตัว วิธีการก่อสร้างที่นี่จะประกาศตัวแปรประเภทสตริง ค่าคงที่สตริงจะถูกจัดสรรในพื้นที่ข้อมูล ดังนั้นสตริง mimi และ pipi ที่ส่งผ่านจะถูกจัดเก็บไว้ในพื้นที่ข้อมูล ดังนั้น พื้นที่ข้อมูลจึงได้รับการจัดสรรหน่วยความจำขนาดเล็กสองบล็อกเพื่อจัดเก็บสตริง mimi และ pipi ซึ่งประกอบด้วยสตริง "mimi" และ "pipi" สตริงยังเป็นประเภทอ้างอิงอีกด้วย นอกเหนือจากประเภทข้อมูลพื้นฐานสี่และแปดประเภทแล้ว ประเภทข้อมูลทั้งหมดเป็นประเภทอ้างอิง ดังนั้นคุณจึงสามารถมองสตริงเป็นวัตถุได้
นี่คือแมวใหม่สองตัว แมวทั้งสองมีแอตทริบิวต์ id และชื่อของตัวเอง ดังนั้น id และชื่อที่นี่จึงเป็นตัวแปรสมาชิกที่ไม่คงที่ กล่าวคือ ไม่มีการแก้ไขแบบคงที่ ดังนั้นทุกครั้งที่สร้าง cat ใหม่ cat ใหม่นี้จะมี id และชื่อของตัวเอง นั่นคือ id และชื่อตัวแปรสมาชิกที่ไม่คงที่จะมีสำเนาแยกกันสำหรับแต่ละอ็อบเจ็กต์ แต่สำหรับตัวแปรสมาชิกแบบคงที่ จะมีเพียงสำเนาเดียว ไม่ว่าจะมีอ็อบเจ็กต์ใหม่จำนวนเท่าใด แม้ว่าจะไม่มีอ็อบเจ็กต์ใหม่ก็ตาม ตัวแปรสมาชิกแบบคงที่จะเก็บหนึ่งสำเนาไว้ในพื้นที่ข้อมูล เช่นเดียวกับ sid ที่นี่ sid จะถูกจัดเก็บไว้ในพื้นที่ข้อมูล ไม่ว่าจะมี cat ใหม่กี่ตัวในหน่วยความจำฮีปก็ตาม sid จะมีเพียงสำเนาเดียวเท่านั้น และมีเพียงสำเนาเดียวเท่านั้นที่ถูกเก็บไว้ในพื้นที่ข้อมูล
ตัวแปรสมาชิกแบบคงที่เป็นของทั้งคลาส แต่ไม่ได้อยู่ในวัตถุเฉพาะ แล้วจะเข้าถึงค่าของตัวแปรสมาชิกแบบคงที่ได้อย่างไร? ประการแรก วัตถุใดๆ สามารถเข้าถึงค่าคงที่นี้ได้ และเมื่อเข้าถึง วัตถุจะเข้าถึงหน่วยความจำเดียวกัน ประเด็นที่สองคือคุณสามารถเข้าถึงค่าคงที่นี้ได้แม้ว่าจะไม่มีวัตถุก็ตาม คุณสามารถเข้าถึงค่าคงที่นี้ผ่านทาง "ชื่อตัวแปรคลาส name.static สมาชิก" ดังนั้นในอนาคตคุณจะเห็นชื่อคลาสที่แน่นอนบวก "" ตามด้วย หากมีสิ่งใดสิ่งหนึ่งสิ่งต่อไปนี้จะต้องคงที่ เช่น "System.out" ในที่นี้จะเข้าถึงได้ผ่านชื่อคลาส (คลาสระบบ) บวก "." ดังนั้นเอาท์นี้จะต้องคงที่
หากสมาชิกของคลาสถูกประกาศแบบคงที่ คุณจะสามารถเข้าถึงได้ก่อนที่จะสร้างอ็อบเจ็กต์ใดๆ ของคลาสโดยไม่ต้องอ้างอิงอ็อบเจ็กต์ใดๆ ตัวอย่างที่พบบ่อยที่สุดของสมาชิกแบบคงที่คือ main() เนื่องจากจะต้องเรียก main() เมื่อโปรแกรมเริ่มดำเนินการ จึงถูกประกาศเป็นค่าคงที่
ตัวแปรที่ประกาศแบบคงที่ถือเป็นตัวแปรส่วนกลางเป็นหลัก เมื่อมีการประกาศอ็อบเจ็กต์ สำเนาของตัวแปรสแตติกจะไม่ถูกสร้างขึ้น แต่ตัวแปรอินสแตนซ์ทั้งหมดของคลาสจะใช้ตัวแปรสแตติกเดียวกัน ตัวอย่างเช่น: ประกาศตัวแปรสแตติกนับเป็นจำนวนอินสแตนซ์ของคลาสใหม่ วิธีการประกาศแบบคงที่มีข้อจำกัดดังต่อไปนี้:
(1) พวกเขาสามารถเรียกวิธีคงที่อื่น ๆ เท่านั้น
(2) พวกเขาสามารถเข้าถึงข้อมูลคงที่เท่านั้น
(3) พวกเขาไม่สามารถอ้างอิงสิ่งนี้หรือสุดยอดได้ แต่อย่างใด
หากคุณต้องการเริ่มต้นตัวแปรคงที่ผ่านการคำนวณ คุณสามารถประกาศบล็อกแบบคงที่ได้ บล็อกแบบคงที่จะถูกดำเนินการเพียงครั้งเดียวเมื่อมีการโหลดคลาส ตัวอย่างด้านล่างนี้แสดงให้เห็น
คลาสนี้มีวิธีการคงที่ ตัวแปรคงที่บางตัว และบล็อกการเริ่มต้นแบบคงที่: คลาสสาธารณะ UserStatic { static int a = 3; static int b; static meth(int x) { System.out.println("x = " + x); System.out.println("a = " + a); System.out.println("b = " + b); } คงที่ { System.out.println("บล็อกแบบคงที่ เตรียมใช้งาน"); b = a * 4; } โมฆะคงที่สาธารณะ main (String args []) { meth (42); } } เมื่อโหลดคลาส UseStatic แล้ว คำสั่งคงที่ทั้งหมดจะถูกรัน ขั้นแรก a ถูกตั้งค่าเป็น 3 จากนั้นสแตติกบล็อกจะถูกดำเนินการ (พิมพ์ข้อความ) และสุดท้าย b ถูกเตรียมใช้งานเป็น a*4 หรือ 12 จากนั้นจึงเรียก main() ส่วน main() เรียก meth() โดยส่งค่า 42 ไปยัง x คำสั่ง println () สามคำสั่งอ้างถึงตัวแปรคงที่สองตัว a และ b และตัวแปรท้องถิ่น x
หมายเหตุ: การอ้างอิงตัวแปรอินสแตนซ์ใดๆ ในรูปแบบคงที่ถือเป็นสิ่งผิดกฎหมาย
นี่คือผลลัพธ์ของโปรแกรมนี้:
เริ่มต้นบล็อกแบบคงที่ x = 42 a = 3 b = 12
วิธีการและตัวแปรแบบสแตติกสามารถนำไปใช้ได้อย่างอิสระจากอ็อบเจ็กต์ภายนอกคลาสที่ถูกกำหนดไว้ ในกรณีนี้ คุณเพียงแค่ต้องเพิ่มตัวดำเนินการจุด (.) หลังชื่อคลาสเท่านั้น ตัวอย่างเช่น หากคุณต้องการเรียกใช้เมธอดแบบสแตติกจากภายนอกคลาส คุณสามารถใช้รูปแบบทั่วไปต่อไปนี้:
ชื่อคลาส.method()
ที่นี่ classname คือชื่อของคลาสที่กำหนดวิธีการแบบคงที่ อย่างที่คุณเห็น รูปแบบนี้คล้ายกับรูปแบบของการเรียกเมธอดที่ไม่คงที่ผ่านตัวแปรการอ้างอิงอ็อบเจ็กต์ ตัวแปรคงที่สามารถเข้าถึงได้ในรูปแบบเดียวกัน - ชื่อคลาสบวกตัวดำเนินการจุด นี่คือวิธีที่ Java ใช้เวอร์ชันควบคุมของฟังก์ชันโกลบอลและตัวแปรโกลบอล
สรุป:
(1) สมาชิกแบบคงที่ไม่สามารถเข้าถึงได้โดยอินสแตนซ์ที่สร้างโดยคลาสที่พวกเขาอยู่
(2) หากสมาชิกที่ไม่มีการแก้ไขแบบคงที่เป็นสมาชิกของวัตถุ สมาชิกเหล่านั้นจะเป็นเจ้าของโดยแต่ละวัตถุ
(3) สมาชิกที่ถูกแก้ไขด้วย static คือสมาชิกของคลาส ซึ่งสามารถเรียกได้โดยตรงจากคลาส และมีอยู่ทั่วไปในอ็อบเจ็กต์ทั้งหมด
Java Static: ในฐานะตัวแก้ไข สามารถใช้เพื่อแก้ไขตัวแปร วิธีการ และบล็อคโค้ด (แต่จะต้องไม่แก้ไขคลาส)
(1) แก้ไขตัวแปร:
คุณสมบัติที่ใช้ร่วมกันโดยอ็อบเจ็กต์ทั้งหมดของคลาส หรือที่เรียกว่าตัวแปรคลาส สิ่งนี้คล้ายกับตัวแปรโกลบอลในภาษา C ตัวแปรคลาสจะเริ่มต้นได้เมื่อมีการโหลดคลาสและเตรียมใช้งานเพียงครั้งเดียวเท่านั้น เมื่ออ็อบเจ็กต์ใดๆ ในโปรแกรมแก้ไขตัวแปรคงที่ อ็อบเจ็กต์อื่นๆ จะเห็นค่าที่แก้ไข ดังนั้นตัวแปรคลาสจึงสามารถใช้เป็นตัวนับได้ นอกจากนี้ ตัวแปร Java Static ยังสามารถเข้าถึงได้โดยตรงโดยใช้ชื่อคลาสโดยไม่ต้องใช้อ็อบเจ็กต์
(2) วิธีการแก้ไข:
ฟังก์ชันที่ใช้ร่วมกับอ็อบเจ็กต์ทั้งหมดของคลาสเรียกว่าวิธีแบบคงที่ วิธีการแบบสแตติกยังสามารถเข้าถึงได้โดยตรงโดยใช้ชื่อคลาส โดยไม่ต้องใช้อ็อบเจ็กต์ ดังนั้นจึงไม่สามารถเข้าถึงตัวแปรที่ไม่คงที่และวิธีการที่ไม่คงที่ได้โดยตรงในวิธีการคงที่ และคำสำคัญเช่น this หรือ super ไม่สามารถปรากฏในวิธีการคงที่ได้
(3) แก้ไขบล็อคโค้ด Java:
ใช้สแตติกเพื่อแก้ไขบล็อกโค้ดอิสระในคลาส ซึ่งเรียกว่าบล็อกโค้ดสแตติก บล็อกโค้ดแบบคงที่จะถูกดำเนินการเมื่อมีการโหลดคลาสเป็นครั้งแรก และเพียงครั้งเดียวเท่านั้น บล็อกโค้ดแบบคงที่ไม่มีชื่อ ดังนั้นจึงไม่สามารถเรียกได้อย่างชัดเจน แต่จะถูกเรียกโดยเครื่องเสมือนเมื่อมีการโหลดคลาสเท่านั้น ส่วนใหญ่จะใช้เพื่อดำเนินการเริ่มต้นบางอย่างให้เสร็จสิ้น
(4) มาพูดถึงการโหลดคลาสกัน:
เมื่อ JVM ใช้คลาสเป็นครั้งแรก มันจะไปที่พาธที่ระบุโดย classpath เพื่อค้นหาไฟล์ bytecode ที่สอดคล้องกับคลาส และอ่านลงใน JVM และบันทึกกระบวนการนี้เรียกว่าการโหลดคลาส
จะเห็นได้ว่าไม่ว่าจะเป็นตัวแปร วิธีการ หรือบล็อกโค้ด ตราบใดที่มีการแก้ไขแบบคงที่ ก็จะ "พร้อม" เมื่อคลาสถูกโหลด กล่าวคือ สามารถใช้งานได้หรือถูกดำเนินการแล้ว ทั้งหมดสามารถดำเนินการได้โดยไม่ต้องใช้วัตถุ ในทางกลับกัน หากไม่มีไฟฟ้าสถิตย์ จะต้องเข้าถึงผ่านออบเจ็กต์