ก่อนที่จะอ่านบทความนี้ จากประสบการณ์และความเข้าใจของคุณเอง คุณสามารถคิดและเลือกวิธีการส่งพารามิเตอร์ของฟังก์ชัน Java ก่อน:
ก. มันผ่านค่าหรือเปล่า?
B. ผ่านการอ้างอิง?
C. บางส่วนตามมูลค่าและบางส่วนโดยการอ้างอิง?
คำตอบที่ถูกต้องจะยังไม่มีการประกาศที่นี่ เราจะให้คุณค้นหาคำตอบด้วยตัวเองผ่านตัวอย่างง่ายๆ:
1. ขั้นแรกให้กำหนดประเภทค่า
ค่าคลาสคงที่สาธารณะ { ค่าสตริงส่วนตัว = "ค่า"; สตริงสาธารณะ getValue () { ค่าส่งคืน; } setValue สาธารณะ (ค่าสตริง) { this.value = value;
2. เขียนสองฟังก์ชัน newValue และ modifiedValue: newValue จะชี้พารามิเตอร์อินพุตไปยังออบเจ็กต์ใหม่ และ modifiedValue จะเรียกใช้เมธอด setValue ของพารามิเตอร์อินพุตเพื่อแก้ไขค่าของออบเจ็กต์
โมฆะสาธารณะ newValue (มูลค่า) { value = new Value () + ", value = " + value.getValue()); } public static void modifiedValue(Value value) { value.setValue("new value"); System.out.println("ใน modifiedValue, HashCode = " + value.hashCode() + ", value = " + value.getValue()); }
3. รหัสทดสอบอย่างง่าย
โมฆะสาธารณะหลัก (สตริง [] args) { ค่า value1 = ค่าใหม่ (); System.out.println ("ก่อนที่จะแก้ไข HashCode = " + value1.hashCode () + ", value = " + value1.getValue () ); // ชี้ value1 ไปที่วัตถุ Value ใหม่ newValue(value1); System.out.println("หลังจากแก้ไข HashCode = " + value1.hashCode() + ", value = " + value1.getValue() + "/n"); ค่า value2 = ค่าใหม่ (); System.out.println("ก่อนแก้ไข HashCode = " + value2.hashCode() + ", value = " + value2.getValue()); // ใช้วิธีการ set ของ object เพื่อแก้ไขค่าภายในของ object modifiedValue(value2); System.out.println("After modified, HashCode = " + value2.hashCode() + ", value = " + value2.getValue()); }
4. บันทึกผลการดำเนินการ:
ก่อนแก้ไข HashCode = 12677476, value = value ใน newValue, HashCode = 33263331, value = ค่าใหม่ หลังจากแก้ไข HashCode = 12677476, value = value ก่อนแก้ไข HashCode = 6413875, value = value ใน modifiedValue, HashCode = 6413875, value = ค่าใหม่ หลังจากแก้ไข HashCode = 6413875 ค่า = ค่าใหม่
5. การวิเคราะห์ผลลัพธ์:
โค้ดด้านบนเป็นรูปแบบการเขียนโปรแกรมทั่วไป: กำหนด | บันทึกค่าหรืออ็อบเจ็กต์ที่อยู่รอบนอก ส่งผ่านอ็อบเจ็กต์เป็นพารามิเตอร์ไปยังเมธอด และแก้ไขคุณสมบัติและพฤติกรรมของอ็อบเจ็กต์ในเมธอด อย่างไรก็ตาม วิธีการแก้ไขของทั้งสองวิธี newValue และ modifiedValue จะแตกต่างกัน หลังจากเรียกใช้เมธอดแล้ว วัตถุจะดูแตกต่างจากภายนอกมาก! จะเข้าใจความแตกต่างนี้ได้อย่างไร? ขั้นแรก เรามาทบทวนแนวคิดเรื่องการส่งผ่านตามค่าและการส่งผ่านโดยการอ้างอิง:
* การส่งผ่านค่าหมายความว่าเมื่อคุณส่งอาร์กิวเมนต์ไปยังฟังก์ชัน ฟังก์ชันจะได้รับสำเนาของค่าดั้งเดิม ดังนั้น หากฟังก์ชันแก้ไขพารามิเตอร์ เฉพาะสำเนาเท่านั้นที่เปลี่ยนแปลง ในขณะที่ค่าดั้งเดิมยังคงไม่เปลี่ยนแปลง
* การส่งผ่านโดยการอ้างอิงหมายความว่าเมื่ออาร์กิวเมนต์ถูกส่งผ่านไปยังฟังก์ชัน ฟังก์ชันจะได้รับที่อยู่หน่วยความจำของค่าดั้งเดิม แทนที่จะเป็นสำเนาของค่า ดังนั้น หากฟังก์ชันแก้ไขพารามิเตอร์ ค่าเดิมของพารามิเตอร์ (ในโค้ดการเรียกที่อยู่นอกบล็อกฟังก์ชัน) ก็จะเปลี่ยนไปด้วย
คำตอบที่ถูกต้อง: A - ฟังก์ชัน Java ส่งผ่านพารามิเตอร์ตามค่า!
วิเคราะห์บันทึก:
* ในส่วนแรกของเอาต์พุตบันทึก พารามิเตอร์ value1 จะถูกเปลี่ยนให้ชี้ไปที่ออบเจ็กต์ใหม่ภายในเมธอด newValue และ hashCode และค่าของออบเจ็กต์ใหม่จะถูกส่งออก อย่างไรก็ตาม หลังจากกระโดดออกจากโดเมนเมธอด newValue แล้ว จะไม่เกิดการเปลี่ยนแปลง เกิดขึ้นกับ value1 ในเมธอดหลัก ซึ่งสอดคล้องกับคำจำกัดความและลักษณะของการส่งผ่านค่า หากถูกส่งผ่านโดยการอ้างอิง value1 ควรเปลี่ยนแปลงหลังจากเรียกเมธอด newValue(Value value)
* เอาต์พุตบันทึกที่สองแสดงให้เห็นว่า value2 ดำเนินการ setValue ภายในเมธอด modifiedValue ยังคงไม่เปลี่ยนแปลง แต่ค่าได้รับการแก้ไขหลังจากออกจากโดเมนเมธอด modifiedValue แล้ว value2 จะเปลี่ยนในเมธอดหลัก ผู้ที่เคยใช้ C++ สามารถเข้าใจปรากฏการณ์นี้ได้อย่างง่ายดายว่า: ส่งผ่านพารามิเตอร์ของฟังก์ชันโดยการอ้างอิง! เพราะสิ่งนี้คล้ายกับการส่งผ่านการอ้างอิงใน C ++! แต่ตรงจุดนี้เองที่มักจะตกอยู่ในความเข้าใจผิด!
หลักการที่ซ่อนอยู่เบื้องหลังปรากฏการณ์ที่แตกต่างกันของบันทึกทั้งสองคือภาษา Java ส่งผ่านพารามิเตอร์ตามค่าและอ็อบเจ็กต์โดยการอ้างอิง อ็อบเจ็กต์ที่ดำเนินการใน Java นั้นแท้จริงแล้วอ้างอิงถึงอ็อบเจ็กต์ปฏิบัติการ และอ็อบเจ็กต์เองจะถูกเก็บไว้ใน "ฮีป" และ "การอ้างอิง" ของวัตถุจะถูกจัดเก็บไว้ในรีจิสเตอร์หรือ "สแต็ก"
Pseudocode อธิบายความแตกต่างระหว่างวิธี newValue และวิธี modifiedValue:
newValue{ Value_ref2 = value_ref1; // ส่งผ่านค่าอ้างอิง value_ref1 ตามค่า และรับสำเนาของ value_ref1 value_obj2 = new Value(); // value_obj2 ถูกสร้างและเตรียมใช้งานใน "heap" value_ref2 -> value_obj2; value_obj2 value_ref2 - >value_obj2.setValue(“xxx”); // value_obj2 ค่าถูกแก้ไข printValueObj2(); // สิ่งที่พิมพ์ที่นี่คือค่าของ obj2} modifiedValue{ Value_ref2 = value_ref1; // ส่งผ่าน value_ref1 ตามค่า และรับสำเนาของ value_ref1 value_ref2 ->value_obj1.setValue("xxx "); // แก้ไขค่าของ value_obj1 printValueObj1(); // สิ่งที่พิมพ์ที่นี่คือค่าของ obj1}
ชัดเจนพอแล้ว! เมื่อ value1_ref1 ถูกส่งเข้าสู่ฟังก์ชันเป็นพารามิเตอร์ สำเนาของ value1_ref2 จะถูกคัดลอกเพื่อใช้ในโดเมนฟังก์ชันก่อน ในขณะนี้ การอ้างอิงทั้งสองชี้ไปที่โค้ด value_obj เดียวกันในฟังก์ชัน newObject [value = new Value(); ] ในความเป็นจริง value1_ref1 ชี้ไปที่อ็อบเจ็กต์ใหม่ value_obj2 การดำเนินการที่ตั้งค่าไว้หลังจากนี้เป็นการดำเนินการทั้งหมดบนอ็อบเจ็กต์ใหม่ ฟังก์ชัน modifiedValue ดำเนินการโดยตรงผ่านเมธอด set ซึ่งแตกต่างจากฟังก์ชัน newValue
ส่งผ่านพารามิเตอร์ตามค่า
เมื่อเรียกใช้เมธอด คุณต้องจัดเตรียมพารามิเตอร์ และคุณต้องระบุพารามิเตอร์ตามลำดับที่ระบุในรายการพารามิเตอร์
ตัวอย่างเช่น วิธีการต่อไปนี้จะพิมพ์ข้อความ n ครั้งติดต่อกัน:
โมฆะคงที่สาธารณะ nPrintln (ข้อความสตริง int n) { สำหรับ (int i = 0; i < n; i ++) System.out.println (ข้อความ); }
ตัวอย่าง ตัวอย่างต่อไปนี้แสดงให้เห็นถึงผลกระทบของการส่งผ่านค่า
โปรแกรมนี้สร้างเมธอดที่แลกเปลี่ยนตัวแปรสองตัว
TestPassByValue ระดับสาธารณะ { โมฆะสาธารณะหลัก (สตริง [] args) { int num1 = 1; int num2 = 2; System.out.println ("ก่อนวิธีสลับ num1 คือ " + num1 + " และ num2 คือ " + num2) ; // เรียกวิธีการสลับ swap(num1, num2); System.out.println("หลังจากวิธีสลับ num1 คือ " + num1 + " และ num2 คือ " + num2); } /** วิธีการสลับตัวแปรสองตัว*/ public static void swap(int n1, int n2) { System.out.println("/tInside the swap method"); .out.println("/t/tก่อนสลับ n1 คือ " + n1 + " n2 คือ " + n2); // สลับค่าของ n1 และ n2 int temp = n1 = n2; n2 = temp; System.out.println("/t/tหลังจากสลับ n1 คือ " + n1 + " n2 คือ " + n2 }}
ผลการรวบรวมและการทำงานของตัวอย่างข้างต้นมีดังนี้:
ก่อนสลับวิธี num1 คือ 1 และ num2 คือ 2 ภายในวิธีการสลับ ก่อนสลับ n1 คือ 1 n2 คือ 2 หลังจากสลับ n1 คือ 2 n2 คือ 1 หลังจากสลับวิธี num1 คือ 1 และ num2 คือ 2
เรียกวิธีการสลับผ่านพารามิเตอร์สองตัว ที่น่าสนใจคือค่าของพารามิเตอร์จริงจะไม่เปลี่ยนแปลงหลังจากเรียกใช้เมธอด