เราเคยใช้แนวคิดเรื่อง "วัตถุ" มาก่อน แต่เรายังไม่ได้กล่าวถึงวิธีเฉพาะเจาะจงในการจัดเก็บวัตถุในหน่วยความจำ การอภิปรายนี้จะนำไปสู่แนวคิดที่สำคัญของ "การอ้างอิงวัตถุ"
การอ้างอิงวัตถุ
เรายังคงใช้คลาส Human ที่กำหนดไว้ก่อนหน้านี้และมีคลาส Test:
คัดลอกรหัสรหัสดังต่อไปนี้:
การทดสอบในชั้นเรียนสาธารณะ
-
โมฆะสาธารณะคง main (String [] args)
-
Human aPerson = มนุษย์ใหม่ (160);
-
-
คลาสมนุษย์
-
-
* ตัวสร้าง
-
มนุษย์สาธารณะ(int h)
-
นี่.ความสูง = h;
-
-
*อุปกรณ์เสริม
-
สาธารณะ int getHeight()
-
กลับ this.height;
-
-
* มิวเทเตอร์
-
โมฆะสาธารณะ growHeight (int h)
-
this.height = this.height + h;
-
ความสูง int ส่วนตัว
-
คลาสสามารถถูกเรียกจากภายนอกเพื่อสร้างออบเจ็กต์ ดังตัวอย่างข้างต้นในคลาส Test:
คัดลอกรหัสรหัสดังต่อไปนี้:
Human aPerson = มนุษย์ใหม่ (160);
ออบเจ็กต์ aPerson of Human คลาสถูกสร้างขึ้น
ข้อความข้างต้นเป็นข้อความง่ายๆ แต่เรามีรายละเอียดมากมายที่ต้องพิจารณา:
1. ขั้นแรกให้ดูที่ด้านขวาของเครื่องหมายเท่ากับ ใหม่ เปิดพื้นที่สำหรับวัตถุในหน่วยความจำ โดยเฉพาะ new จะเปิดพื้นที่สำหรับอ็อบเจ็กต์บนฮีปหน่วยความจำ ในพื้นที่นี้ ข้อมูลและวิธีการของออบเจ็กต์จะถูกเก็บไว้
2. ดูที่ด้านซ้ายของเครื่องหมายเท่ากับ aPerson อ้างถึงวัตถุมนุษย์ ซึ่งเรียกว่าการอ้างอิงวัตถุ ในความเป็นจริง aPerson ไม่ใช่วัตถุ แต่คล้ายกับตัวชี้ไปยังวัตถุ aPerson มีอยู่ในสแต็กในหน่วยความจำ
3. เมื่อเราใช้เครื่องหมายเท่ากับในการกำหนดค่า ที่อยู่ของออบเจ็กต์ที่สร้างในฮีปโดย new ทางด้านขวาจะถูกกำหนดให้กับการอ้างอิงออบเจ็กต์
หน่วยความจำที่นี่หมายถึงพื้นที่หน่วยความจำกระบวนการ Java ที่สร้างเสมือนโดย JVM (Java Virtual Machine) สำหรับแนวคิดเกี่ยวกับฮีปและสแต็กในหน่วยความจำ โปรดดูที่ Linux From Program to Process
สแต็กสามารถอ่านได้เร็วกว่าฮีป แต่ข้อมูลที่จัดเก็บไว้ในสแต็กจะถูกจำกัดด้วยช่วงที่ถูกต้อง ในภาษา C เมื่อการเรียกใช้ฟังก์ชันสิ้นสุดลง เฟรมสแต็กที่เกี่ยวข้องจะถูกลบ และพารามิเตอร์และตัวแปรอัตโนมัติที่จัดเก็บไว้ในเฟรมสแต็กจะหายไป สแต็กของ Java ยังอยู่ภายใต้ข้อจำกัดเดียวกัน เมื่อการเรียกเมธอดสิ้นสุดลง ข้อมูลที่เก็บไว้ในสแต็กตามเมธอดจะถูกล้าง ใน Java วัตถุทั้งหมด (ธรรมดา) จะถูกเก็บไว้ในฮีป ดังนั้นความหมายเต็มของคีย์เวิร์ดใหม่คือการสร้างออบเจ็กต์บนฮีป
ออบเจ็กต์ประเภทดั้งเดิม เช่น int และ double จะถูกจัดเก็บไว้ในสแต็ก เมื่อเราประกาศประเภทพื้นฐาน ก็ไม่จำเป็นต้องสร้างใหม่ เมื่อประกาศแล้ว Java จะจัดเก็บข้อมูลประเภทดั้งเดิมไว้บนสแต็กโดยตรง ดังนั้น ชื่อตัวแปรของประเภทพื้นฐานจึงแสดงถึงข้อมูล ไม่ใช่การอ้างอิง
ความสัมพันธ์ระหว่างการอ้างอิงและวัตถุก็เหมือนกับว่าวกับบุคคล เมื่อเรามองดูท้องฟ้า (เขียนไว้ในโปรแกรม) สิ่งที่เราเห็นคือว่าว (อ้างอิง) แต่สิ่งที่สอดคล้องกับว่าวคือบุคคล (วัตถุ)
การแยกการอ้างอิงและวัตถุ การอ้างอิงชี้ไปที่วัตถุ
แม้ว่าการอ้างอิงและวัตถุจะถูกแยกออกจากกัน การเข้าถึงวัตถุทั้งหมดของเราต้องผ่าน "ประตู" ของการอ้างอิง เช่น การเข้าถึงวิธีวัตถุผ่าน Reference.method() ใน Java เราไม่สามารถข้ามการอ้างอิงและสัมผัสวัตถุได้โดยตรง อีกตัวอย่างหนึ่ง หากสมาชิกข้อมูลของออบเจ็กต์ a เป็นออบเจ็กต์ธรรมดา b สมาชิกข้อมูลของ a จะบันทึกการอ้างอิงไปยังออบเจ็กต์ b (หากเป็นตัวแปรประเภทพื้นฐาน สมาชิกข้อมูลของ a จะบันทึกตัวแปรประเภทพื้นฐานด้วยตัวมันเอง) .
ใน Java การอ้างอิงมีบทบาทเป็นพอยน์เตอร์ แต่เราไม่สามารถแก้ไขค่าของพอยน์เตอร์ได้โดยตรง เช่น เพิ่ม 1 ให้กับค่าของพอยน์เตอร์เหมือนในภาษา C เราสามารถดำเนินการกับวัตถุผ่านการอ้างอิงเท่านั้น การออกแบบนี้หลีกเลี่ยงข้อผิดพลาดมากมายที่อาจทำให้เกิดพอยน์เตอร์ได้
งานอ้างอิง
เมื่อเรากำหนดการอ้างอิงให้กับการอ้างอิงอื่น เรากำลังคัดลอกที่อยู่ของวัตถุจริงๆ การอ้างอิงทั้งสองจะชี้ไปที่วัตถุเดียวกัน ตัวอย่างเช่น dummyPerson=aPerson;
วัตถุสามารถมีการอ้างอิงได้หลายรายการ (บุคคลหนึ่งสามารถบินว่าวได้หลายตัว) เมื่อโปรแกรมแก้ไขออบเจ็กต์ผ่านการอ้างอิงเดียว การแก้ไขจะมองเห็นได้ผ่านการอ้างอิงอื่น ๆ เราสามารถใช้คลาส Test ต่อไปนี้เพื่อทดสอบเอฟเฟกต์จริง:
คัดลอกรหัสรหัสดังต่อไปนี้:
การทดสอบในชั้นเรียนสาธารณะ
-
โมฆะสาธารณะคง main (String [] args)
-
Human aPerson = มนุษย์ใหม่ (160);
มนุษย์จำลองบุคคล = aPerson;
System.out.println(dummyPerson.getHeight());
aPerson.growHeight(20);
System.out.println(dummyPerson.getHeight());
-
-
การแก้ไข aPerson ของเราจะส่งผลต่อ dummyPerson การอ้างอิงทั้งสองนี้ชี้ไปที่วัตถุเดียวกันจริงๆ
ดังนั้น การกำหนดการอ้างอิงให้กับการอ้างอิงอื่นจะไม่เป็นการคัดลอกออบเจ็กต์นั้นเอง เราต้องหากลไกอื่นในการคัดลอกวัตถุ
เก็บขยะ
เมื่อการเรียกเมธอดสิ้นสุดลง ตัวแปรอ้างอิงและตัวแปรประเภทดั้งเดิมจะถูกล้าง เนื่องจากวัตถุอาศัยอยู่บนฮีป หน่วยความจำที่ครอบครองโดยวัตถุจะไม่ถูกล้างเมื่อสิ้นสุดการเรียกเมธอด พื้นที่กระบวนการอาจเต็มไปด้วยวัตถุที่ถูกสร้างขึ้นอย่างรวดเร็ว Java มีกลไกการรวบรวมขยะในตัวเพื่อล้างวัตถุที่ไม่ได้ใช้เพื่อเรียกคืนพื้นที่หน่วยความจำอีกต่อไป
หลักการพื้นฐานของการรวบรวมขยะคือ เมื่อมีการอ้างอิงที่ชี้ไปที่วัตถุ วัตถุนั้นจะไม่ถูกนำกลับมาใช้ใหม่ เมื่อไม่มีการอ้างอิงที่ชี้ไปที่วัตถุ วัตถุนั้นจะถูกล้าง พื้นที่ที่มันครอบครองถูกเรียกคืน
รูปด้านบนถือว่าสถานะหน่วยความจำใน JVM ในช่วงเวลาหนึ่ง Human Object มีการอ้างอิงสามแบบ: aPerson และ dummyPerson จากสแต็ก และ President ซึ่งเป็นสมาชิกข้อมูลของออบเจ็กต์อื่น Club Object ไม่มีการอ้างอิง หากเริ่มการรวบรวมขยะในเวลานี้ Club Object จะถูกทำให้ว่างเปล่า และข้อมูลอ้างอิงของ Human Object (ประธาน) จาก Club Object จะถูกลบด้วย
การรวบรวมขยะเป็นกลไกสำคัญใน Java ซึ่งส่งผลโดยตรงต่อประสิทธิภาพการทำงานของ Java ฉันจะเข้าไปดูรายละเอียดในภายหลัง
การส่งผ่านพารามิเตอร์
เมื่อเราแยกแนวคิดของการอ้างอิงและอ็อบเจ็กต์ กลไกการส่งผ่านพารามิเตอร์ของวิธี Java นั้นชัดเจนมาก: การส่งผ่านพารามิเตอร์ Java นั้นเป็นตามค่า นั่นคือเมื่อเราส่งผ่านพารามิเตอร์ เมธอดจะได้รับสำเนาของพารามิเตอร์
ที่จริงแล้ว พารามิเตอร์ตัวหนึ่งที่เราส่งผ่านคือตัวแปรประเภทพื้นฐาน และอีกตัวคือการอ้างอิงถึงออบเจ็กต์
Pass-by-value สำหรับตัวแปรประเภทดั้งเดิมหมายความว่าตัวแปรนั้นถูกคัดลอกและส่งผ่านไปยังวิธีการ Java การแก้ไขตัวแปรด้วยวิธี Java จะไม่ส่งผลกระทบต่อตัวแปรดั้งเดิม
การส่งผ่านค่าหมายความว่าที่อยู่ของวัตถุถูกคัดลอกและส่งผ่านไปยังวิธีการ Java การเข้าถึงเมธอด Java ตามการอ้างอิงนี้จะส่งผลต่อออบเจ็กต์
มีอีกสถานการณ์หนึ่งที่ควรกล่าวถึงที่นี่: เราใช้ new ภายในเมธอดเพื่อสร้างอ็อบเจ็กต์และส่งคืนการอ้างอิงไปยังอ็อบเจ็กต์ หากได้รับการส่งคืนโดยการอ้างอิง เนื่องจากการอ้างอิงของออบเจ็กต์ไม่ใช่ 0 ออบเจ็กต์จะยังคงอยู่และจะไม่ถูกรวบรวมแบบขยะ
สรุป
ใหม่
การอ้างอิงวัตถุ
เงื่อนไขในการเก็บขยะ
พารามิเตอร์: ส่งผ่านโดยค่า