Java ผ่านการอ้างอิงหรือส่งผ่านค่า?
การเขียนโปรแกรมภาษาจำนวนมากให้ผ่านพารามิเตอร์โดยการอ้างอิงหรือตามมูลค่า ใน Java เราสามารถส่งผ่านพารามิเตอร์ด้วยค่าเท่านั้น สิ่งนี้กำหนดข้อ จำกัด บางประการและก่อให้เกิดคำถาม ตัวอย่างเช่นหากค่าพารามิเตอร์มีการเปลี่ยนแปลงในเมธอดจะเกิดอะไรขึ้นกับค่าหลังจากการเรียกใช้เมธอด คุณอาจสงสัยว่า Java จัดการค่าอ็อบเจ็กต์ในฮีปหน่วยความจำอย่างไร นี้Java ชาเลนเจอร์จะช่วยให้คุณแก้ไขคำถามที่พบบ่อยเหล่านี้และอื่นเกี่ยวกับการอ้างอิงวัตถุในชวา
รับซอร์สโค้ด
รับรหัสสำหรับ Java Challenger นี้ คุณสามารถเรียกใช้การทดสอบของคุณเองในขณะที่ทำตามตัวอย่าง
การอ้างอิงอ็อบเจ็กต์จะถูกส่งผ่านค่า
การอ้างอิงอ็อบเจ็กต์ทั้งหมดใน Java จะถูกส่งผ่านด้วยค่า ซึ่งหมายความว่าสำเนาของค่าจะถูกส่งไปยังเมธอด แต่เคล็ดลับก็คือการส่งสำเนาค่าจะเปลี่ยนมูลค่าที่แท้จริงของวัตถุด้วย เพื่อทำความเข้าใจสาเหตุให้เริ่มต้นด้วยตัวอย่างนี้:
public class ObjectReferenceExample { public static void main(String... doYourBest) { Simpson simpson = new Simpson(); transformIntoHomer(simpson); System.out.println(simpson.name); } static void transformIntoHomer(Simpson simpson) { simpson.name = "Homer"; } } class Simpson { String name; }
คุณคิดว่าsimpson.name
จะเป็นอย่างไรหลังจากใช้transformIntoHomer
วิธีนี้
ในกรณีนี้จะเป็นโฮเมอร์! เหตุผลก็คือตัวแปรออบเจ็กต์ Java เป็นเพียงการอ้างอิงที่ชี้ไปยังวัตถุจริงในฮีปหน่วยความจำ ดังนั้นแม้ว่า Java จะส่งผ่านพารามิเตอร์ไปยังวิธีการตามค่าหากตัวแปรชี้ไปที่การอ้างอิงวัตถุวัตถุจริงก็จะเปลี่ยนไปด้วยเช่นกัน
หากคุณยังไม่ชัดเจนว่ามันทำงานอย่างไรลองดูรูปด้านล่าง

ประเภทดั้งเดิมถูกส่งผ่านโดยมูลค่าหรือไม่?
เช่นเดียวกับประเภทวัตถุประเภทดั้งเดิมจะถูกส่งผ่านด้วยค่าเช่นกัน คุณสามารถสรุปสิ่งที่จะเกิดขึ้นกับประเภทดั้งเดิมในตัวอย่างโค้ดต่อไปนี้ได้หรือไม่?
public class PrimitiveByValueExample { public static void main(String... primitiveByValue) { int homerAge = 30; changeHomerAge(homerAge); System.out.println(homerAge); } static void changeHomerAge(int homerAge) { homerAge = 35; } }
หากคุณพิจารณาแล้วว่าค่าจะเปลี่ยนเป็น 30 แสดงว่าคุณถูกต้อง มันเป็น 30 เพราะ (อีกครั้ง) Java ส่งผ่านพารามิเตอร์วัตถุตามค่า ตัวเลข 30 เป็นเพียงสำเนาของค่าไม่ใช่มูลค่าที่แท้จริง ชนิดดั้งเดิมถูกจัดสรรไว้ในหน่วยความจำสแต็กดังนั้นเฉพาะค่าโลคัลเท่านั้นที่จะถูกเปลี่ยน ในกรณีนี้ไม่มีการอ้างอิงวัตถุ
การส่งผ่านการอ้างอิงวัตถุที่ไม่เปลี่ยนรูป
จะเกิดอะไรขึ้นถ้าเราทำการทดสอบเดียวกันกับString
วัตถุที่ไม่เปลี่ยนรูป?
JDK มีคลาสที่ไม่เปลี่ยนรูปมากมาย ตัวอย่าง ได้แก่ ประเภทกระดาษห่อInteger
, Double
, Float
, Long
, Boolean
, BigDecimal
และแน่นอนที่รู้จักกันเป็นอย่างดีString
ระดับ
ในตัวอย่างถัดไปสังเกตว่าจะเกิดอะไรขึ้นเมื่อเราเปลี่ยนค่าของไฟล์String
.
public class StringValueChange { public static void main(String... doYourBest) { String name = ""; changeToHomer(name); System.out.println(name); } static void changeToHomer(String name) { name = "Homer"; } }
คุณคิดว่าผลลัพธ์จะเป็นอย่างไร? ถ้าคุณเดาได้“” ก็ขอแสดงความยินดีด้วย! สิ่งนี้เกิดขึ้นเนื่องจากString
วัตถุไม่เปลี่ยนรูปซึ่งหมายความว่าฟิลด์ภายในString
เป็นขั้นสุดท้ายและไม่สามารถเปลี่ยนแปลงได้
การทำให้String
คลาสไม่เปลี่ยนรูปทำให้เราสามารถควบคุมอ็อบเจ็กต์ที่ใช้มากที่สุดของ Java ได้ดีขึ้น หากString
สามารถเปลี่ยนแปลงค่าของ a ได้ก็จะสร้างข้อบกพร่องมากมาย โปรดทราบว่าเราไม่ได้เปลี่ยนแอตทริบิวต์ของString
คลาส แต่เราเพียงกำหนดString
ค่าใหม่ให้กับมัน ในกรณีนี้ค่า“ โฮเมอร์” จะถูกส่งผ่านไปname
ยังchangeToHomer
เมธอด String
“โฮเมอร์” จะมีสิทธิ์ที่จะเก็บขยะทันทีที่changeToHomer
วิธีการดำเนินการเสร็จสิ้น แม้ว่าวัตถุจะไม่สามารถเปลี่ยนแปลงได้ แต่ตัวแปรภายในจะเป็น
สตริงและอื่น ๆ
เรียนรู้เพิ่มเติมเกี่ยวกับString
คลาสของ Java และอื่น ๆ : ดูโพสต์ทั้งหมดของ Rafael ในซีรีส์ Java Challengers
การส่งผ่านการอ้างอิงอ็อบเจ็กต์ที่ไม่แน่นอน
แตกต่างจากString
วัตถุส่วนใหญ่ใน JDK นั้นเปลี่ยนแปลงได้เช่นเดียวกับStringBuilder
คลาส ตัวอย่างด้านล่างคล้ายกับตัวอย่างก่อนหน้า แต่มีคุณสมบัติStringBuilder
มากกว่าString
:
static class MutableObjectReference { public static void main(String... mutableObjectExample) { StringBuilder name = new StringBuilder("Homer "); addSureName(name); System.out.println(name); } static void addSureName(StringBuilder name) { name.append("Simpson"); } }
คุณสามารถอนุมานผลลัพธ์สำหรับตัวอย่างนี้ได้หรือไม่? ในกรณีนี้เนื่องจากเรากำลังทำงานกับวัตถุที่เปลี่ยนแปลงได้ผลลัพธ์จะเป็น "Homer Simpson" คุณสามารถคาดหวังพฤติกรรมเดียวกันจากอ็อบเจ็กต์ที่เปลี่ยนแปลงได้อื่น ๆ ใน Java
คุณได้เรียนรู้แล้วว่าตัวแปร Java ถูกส่งผ่านด้วยค่าซึ่งหมายความว่าจะมีการส่งผ่านสำเนาของค่า เพียงจำไว้ว่าค่าที่คัดลอกชี้ไปที่วัตถุจริงในฮีปหน่วยความจำ Java การส่งผ่านค่ายังคงเปลี่ยนค่าของวัตถุจริง
รับความท้าทายในการอ้างอิงวัตถุ!
ใน Java Challenger นี้เราจะทดสอบสิ่งที่คุณได้เรียนรู้เกี่ยวกับการอ้างอิงวัตถุ ในตัวอย่างโค้ดด้านล่างคุณจะเห็นคลาสที่ไม่เปลี่ยนรูปString
และไม่เปลี่ยนแปลง StringBuilder
แต่ละรายการจะถูกส่งผ่านเป็นพารามิเตอร์ไปยังเมธอด เมื่อรู้ว่า Java ส่งผ่านค่าเท่านั้นคุณเชื่อว่าอะไรจะเป็นผลลัพธ์เมื่อดำเนินการเมธอดหลักจากคลาสนี้
public class DragonWarriorReferenceChallenger { public static void main(String... doYourBest) { StringBuilder warriorProfession = new StringBuilder("Dragon "); String warriorWeapon = "Sword "; changeWarriorClass(warriorProfession, warriorWeapon); System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon); } static void changeWarriorClass(StringBuilder warriorProfession, String weapon) { warriorProfession.append("Knight"); weapon = "Dragon " + weapon; weapon = null; warriorProfession = null; } }
นี่คือตัวเลือกตรวจสอบส่วนท้ายของบทความนี้เพื่อดูคีย์คำตอบ
A : Warrior = null Weapon = null
B : นักรบ = อาวุธมังกร = มังกร
C : นักรบ = อาวุธอัศวินมังกร = ดาบมังกร
D : นักรบ = อาวุธอัศวินมังกร = ดาบ
เกิดอะไรขึ้น?
พารามิเตอร์แรกในตัวอย่างข้างต้นคือwarriorProfession
ตัวแปรซึ่งเป็นวัตถุที่เปลี่ยนแปลงไม่ได้ พารามิเตอร์ที่สองคืออาวุธไม่เปลี่ยนรูปString
:
static void changeWarriorClass(StringBuilder warriorProfession, String weapon) { ... }
ตอนนี้เรามาวิเคราะห์สิ่งที่เกิดขึ้นในวิธีนี้ ในบรรทัดแรกของวิธีนี้เราจะผนวกKnight
ค่าเข้ากับwarriorProfession
ตัวแปร จำไว้ว่าwarriorProfession
เป็นวัตถุที่เปลี่ยนแปลงไม่ได้ ดังนั้นวัตถุที่แท้จริงจะถูกเปลี่ยนและมูลค่าจากมันจะเป็น "อัศวินมังกร"
warriorProfession.append("Knight");
ในคำสั่งที่สองString
ตัวแปรโลคัลที่ไม่เปลี่ยนรูปจะเปลี่ยนเป็น "ดาบมังกร" อย่างไรก็ตามวัตถุจริงจะไม่ถูกเปลี่ยนแปลงเนื่องจากString
ไม่เปลี่ยนรูปและคุณสมบัติของมันถือเป็นที่สิ้นสุด:
weapon = "Dragon " + weapon;
สุดท้ายเราส่งผ่านnull
ไปยังตัวแปรที่นี่ แต่ไม่ส่งไปยังวัตถุ วัตถุจะยังคงเหมือนเดิมตราบเท่าที่ยังสามารถเข้าถึงได้จากภายนอก - ในกรณีนี้โดยใช้วิธีการหลัก และแม้ว่าตัวแปรโลคัลจะเป็นโมฆะ แต่ก็ไม่มีอะไรเกิดขึ้นกับวัตถุ:
weapon = null; warriorProfession = null;
จากทั้งหมดนี้เราสามารถสรุปได้ว่าค่าสุดท้ายจากการเปลี่ยนแปลงStringBuilder
และไม่เปลี่ยนรูปของเราString
จะเป็น:
System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon);
ค่าเดียวที่เปลี่ยนแปลงในchangeWarriorClass
เมธอดคือwarriorProfession
เนื่องจากเป็นStringBuilder
วัตถุที่ไม่แน่นอน โปรดทราบwarriorWeapon
ว่าไม่ได้เปลี่ยนแปลงเพราะเป็นString
วัตถุที่ไม่เปลี่ยนรูป
ผลลัพธ์ที่ถูกต้องจากรหัสชาเลนเจอร์ของเราคือ:
D : นักรบ = อาวุธอัศวินมังกร = ดาบ
วิดีโอท้า! การดีบักการอ้างอิงอ็อบเจ็กต์ใน Java
การดีบักเป็นวิธีที่ง่ายที่สุดวิธีหนึ่งในการดูดซับแนวคิดการเขียนโปรแกรมอย่างสมบูรณ์ในขณะที่ปรับปรุงโค้ด ในวิดีโอนี้คุณสามารถทำตามในขณะที่ฉันแก้จุดบกพร่องและอธิบายการอ้างอิงวัตถุใน Java
ข้อผิดพลาดทั่วไปเกี่ยวกับการอ้างอิงวัตถุ
- พยายามเปลี่ยนค่าที่ไม่เปลี่ยนรูปโดยการอ้างอิง
- พยายามเปลี่ยนตัวแปรดั้งเดิมโดยการอ้างอิง
- Expecting the real object won't change when you change a mutable object parameter in a method.
What to remember about object references
- Java always passes parameter variables by value.
- Object variables in Java always point to the real object in the memory heap.
- A mutable object’s value can be changed when it is passed to a method.
- An immutable object’s value cannot be changed, even if it is passed a new value.
- “Passing by value” refers to passing a copy of the value.
- “Passing by reference” refers to passing the real reference of the variable in memory.
Learn more about Java
- Get more quick code tips: Read all of Rafael's posts in the JavaWorld Java Challengers series.
- Learn more about mutable and immutable Java objects (such as
String
andStringBuffer
) and how to use them in your code. - คุณอาจแปลกใจที่ทราบว่าประเภทดั้งเดิมของ Java นั้นขัดแย้งกัน ในคุณสมบัตินี้จอห์นไอ. มัวร์ทำกรณีนี้เพื่อรักษาพวกเขาและเรียนรู้ที่จะใช้มันให้ดี
- สร้างทักษะการเขียนโปรแกรม Java ของคุณต่อไปใน Java Dev Gym
- หากคุณชอบการดีบักการสืบทอด Java โปรดดูวิดีโอเพิ่มเติมในรายการเล่นวิดีโอ Java Challenges ของ Rafael (วิดีโอในชุดนี้ไม่มีส่วนเกี่ยวข้องกับ JavaWorld)
- ต้องการทำงานในโครงการที่ปราศจากความเครียดและเขียนโค้ดที่ปราศจากข้อบกพร่องหรือไม่? ตรงไป NoBugsProject สำหรับสำเนาของไม่มี Bugs, No Stress - สร้างชีวิตเปลี่ยนซอฟแวร์โดยไม่ทำลายชีวิตของคุณ
เรื่องนี้ "Java ผ่านการอ้างอิงหรือส่งผ่านค่า" เผยแพร่ครั้งแรกโดย JavaWorld