1. ตัวแปรอ้างอิง

ในภาษา Java มีตัวแปรอยู่ 2 ชนิด ได้แก่ ตัวแปรดั้งเดิมและอื่นๆ เมื่อมันเกิดขึ้น เราจะพูดถึง "อย่างอื่นทั้งหมด" ในตอนนี้

ในความเป็นจริง มันจะถูกต้องกว่าหากกล่าวว่ามีตัวแปรดั้งเดิมและตัวแปรอ้างอิง แล้วตัวแปรอ้างอิงเหล่านี้คืออะไร?

ซึ่งแตกต่างจากประเภทดั้งเดิมซึ่งตัวแปรเก็บค่าโดยตรง ตัวแปรอ้างอิงจะจัดเก็บการอ้างอิงไปยังวัตถุ นั่นคือมีวัตถุอยู่ในหน่วยความจำ และตัวแปรอ้างอิงก็เก็บที่อยู่ของวัตถุนี้ไว้ในหน่วยความจำ (การอ้างอิงถึงวัตถุ)

เฉพาะประเภทดั้งเดิมเท่านั้นที่เก็บค่าภายในตัวแปรโดยตรง ประเภท อื่นๆ ทั้งหมดจัดเก็บเฉพาะการอ้างอิงวัตถุ อย่างไรก็ตาม คุณได้พบตัวแปรสองประเภทดังกล่าวแล้ว — Stringตัวแปรและตัวแปรอาร์เรย์

ทั้งอาร์เรย์และสตริงเป็นวัตถุที่เก็บอยู่ในหน่วยความจำ Stringตัวแปรและตัวแปรอาร์เรย์เก็บเฉพาะการอ้างอิงถึงวัตถุ

ตัวแปรอ้างอิงในภาษาจาวา

int a, int b and double dเป็นตัวแปรดั้งเดิมที่เก็บค่าของมันไว้ในตัวมันเอง

ตัวแปรString strคือการอ้างอิงและจัดเก็บที่อยู่ (อ้างอิง) ไปยังStringวัตถุในหน่วยความจำ

เมื่อกำหนดค่าดั้งเดิมให้กับตัวแปรประเภทดั้งเดิม ค่านั้นจะถูกคัดลอก (ทำซ้ำ) เมื่อกำหนดตัวแปรอ้างอิงเฉพาะที่อยู่ของวัตถุเท่านั้นที่จะถูกคัดลอก — ตัววัตถุเอง จะไม่ถูกคัดลอก


2. การอ้างอิงทั้งหมดเกี่ยวกับอะไร

อะไรคือความแตกต่างพื้นฐานระหว่างตัวแปรอ้างอิงและตัวแปรดั้งเดิม

ตัวแปรดั้งเดิมเป็นเหมือนกล่อง: คุณสามารถเก็บค่าบางอย่างไว้ในนั้นได้ ตัวแปรอ้างอิงเป็นเหมือนกระดาษที่มีหมายเลขโทรศัพท์อยู่

รถ vs กุญแจรถ

จินตนาการว่าคุณตัดสินใจมอบรถให้เพื่อนของคุณในวันเกิดของเขา คุณจะไม่ห่อมันไว้ในกล่องแล้วพกติดตัวไปด้วย: รถมันใหญ่เกินไปสำหรับสิ่งนั้น

สะดวกกว่ามากที่จะนำเสนอกุญแจรถในกล่องที่ใหญ่พอที่จะบรรจุได้ เพื่อนของคุณจะเข้าใจทุกอย่างเมื่อเขาได้กุญแจออกมาจากกล่อง คุณไม่จำเป็นต้องพกรถทั้งคันไปกับคุณ เพียงแค่คุณมอบกุญแจเท่านั้น

บุคคล vs หมายเลขโทรศัพท์ของเธอ

หรือนี่คือการเปรียบเทียบอื่น: บุคคลและหมายเลขโทรศัพท์ของเธอ หมายเลขโทรศัพท์ไม่ใช่บุคคล แต่สามารถใช้หมายเลขโทรศัพท์เพื่อโทรหาเธอ ขอข้อมูลบางอย่างจากเธอ หรือให้คำแนะนำได้

ในทำนองเดียวกัน การอ้างอิงจะใช้เพื่อโต้ตอบกับวัตถุ วัตถุทั้งหมดโต้ตอบกันโดยใช้การอ้างอิง แทนที่จะ "แลกคน" เราก็แค่แลกเบอร์โทรกัน

เมื่อกำหนดค่าให้กับตัวแปรดั้งเดิม ค่าจะถูกคัดลอก (ทำซ้ำ) เมื่อกำหนดค่าให้กับตัวแปรอ้างอิง เฉพาะที่อยู่ (หมายเลขโทรศัพท์) ของวัตถุเท่านั้นที่จะถูกคัดลอก — ตัววัตถุเองจะไม่ถูกคัดลอก

การอ้างอิงมีข้อดีอีกประการหนึ่ง: คุณสามารถส่งการอ้างอิงวัตถุไปยังเมธอดบางเมธอดได้ และเมธอดจะสามารถแก้ไข (เปลี่ยนแปลง) อ็อบเจกต์ได้โดยใช้การอ้างอิงถึงเมธอด เรียกใช้เมธอดและเข้าถึงข้อมูลภายในออบเจกต์


3. การกำหนดการอ้างอิง

เมื่อกำหนดตัวแปรอ้างอิง จะมีการกำหนดเฉพาะที่อยู่ของวัตถุในหน่วยความจำเท่านั้น วัตถุจะไม่ปรากฏหรือหายไป

วิธีการนี้หลีกเลี่ยงการคัดลอกหน่วยความจำจำนวนมาก หากคุณต้องการส่งวัตถุขนาดใหญ่มากไปยังเมธอด เราก็แค่ส่งการอ้างอิงวัตถุ แค่นั้น การอ้างอิงใช้พื้นที่น้อยกว่ามาก

การกำหนดการอ้างอิง

ขนาดของตัวแปรอ้างอิงทั้งหมด (โดยไม่คำนึงถึงประเภท) จะเท่ากัน — 4 ไบต์ (เช่น int) แต่! หากแอปพลิเคชันของคุณทำงานบนเครื่อง Java 64 บิต การอ้างอิงทั้งหมดจะมีขนาด 8 ไบต์ (64 บิต)

ยิ่งไปกว่านั้น สามารถกำหนดการอ้างอิงให้กันและกันเท่านั้น คุณไม่สามารถเปลี่ยนการอ้างอิงหรือกำหนดค่าตามอำเภอใจให้กับตัวแปรอ้างอิง:

รหัส คำอธิบาย
String hello = "Hello";
String s = hello;
สิ่งนี้ได้รับอนุญาต
String hello = "Hello";
hello++;
แต่สิ่งนี้ไม่ได้รับอนุญาต
String hello = 0x1234;
และสิ่งนี้ไม่ได้รับอนุญาต

4. nullการอ้างอิง

และตัวแปรอ้างอิงจะเก็บอะไรถ้ายังไม่ได้กำหนดอะไรเลย?

มันเก็บข้อมูลอ้างอิงที่เป็นโมฆะ nullเป็นคีย์เวิร์ดพิเศษของ Java ซึ่งหมายถึงการไม่มีการอ้างอิง (การอ้างอิงที่ว่างเปล่า) nullสามารถกำหนดค่าให้กับตัวแปรอ้างอิงใดๆ

ตัวแปรอ้างอิงทั้งหมดnullเว้นแต่จะมีการอ้างอิงบางชนิดกำหนดไว้

ตัวอย่าง:

รหัส คำอธิบาย
class Person
{
   public static String name;
   public static int age;
}


ตัวแปรString nameมีค่าเริ่มต้น: null.
ตัวแปรint ageมีค่าเริ่มต้น: 0.

ตัวแปรเฉพาะที่ไม่ได้รับการกำหนดค่าจะถือว่าไม่ได้กำหนดค่าเริ่มต้นสำหรับทั้งประเภทดั้งเดิมและประเภทอ้างอิง

หากตัวแปรเก็บการอ้างอิงถึงวัตถุบางอย่าง และคุณต้องการล้างค่าของตัวแปร ให้กำหนดค่าการอ้างอิงเป็นค่าว่าง

รหัส คำอธิบาย
String s = null;
s = "Hello";
s = null;
sร้านnullค้า
sเก็บการอ้างอิงไปยัง
sที่เก็บnullวัตถุ สตริง

5. ผ่านการอ้างอิงถึงวิธีการ

หากเมธอดมีพารามิเตอร์ที่เป็นประเภทการอ้างอิงค่าจะถูกส่งผ่านไปยังเมธอดในลักษณะเดียวกับเมื่อทำงานกับตัวแปรที่ไม่ใช่การอ้างอิง พารามิเตอร์เป็นเพียงการกำหนดค่าของตัวแปรอื่น

ตัวอย่าง:

รหัส คำอธิบาย
class Solution
{
   public static void fill(String[] array, String value)
   {
      for (int i = 0; i < array.length; i++)
        array[i] = value;
   }

   public static void main(String[] args)
   {
     String[] data = new String[10];
     fill(data, "Hello");
   }
}


เติมfillอาร์เรย์ที่ผ่าน ( array) ด้วยค่าที่ผ่าน ( value)

เมื่อมีfillการเรียกใช้เมธอดarrayพารามิเตอร์จะถูกกำหนดการอ้างอิงไปยังdataอาร์เรย์ ตัวแปรvalueถูกกำหนดให้อ้างอิงถึงวัตถุสตริง ("สวัสดี")

นี่คือลักษณะของหน่วยความจำก่อนที่จะเรียกใช้ fillเมธอด:

ผ่านการอ้างถึงเมธอด

นี่คือลักษณะของหน่วยความจำเมื่อ fill เมธอดทำงาน :

ส่งผ่านการอ้างอิงถึงเมธอด 2

ตัวแปรdataและarrayอ้างถึง (จัดเก็บการอ้างอิงถึง) คอนเทนเนอร์เดียวกันในหน่วยความจำ

ตัวแปรvalueเก็บการอ้างอิงถึงวัตถุสตริง ( "Hello")

เซลล์ของอาร์เรย์ยังเก็บการอ้างอิงไปยัง"Hello"วัตถุ เท่านั้น

อันที่จริงแล้ว ไม่มีออบเจ็กต์ใดซ้ำกัน — เฉพาะการอ้างอิงเท่านั้นที่จะถูกคัดลอก



6. การเปรียบเทียบกับภาษา C/C++

ในการสัมภาษณ์ บางครั้งโปรแกรมเมอร์ Java ถูกถามว่าข้อมูลถูกส่งไปยังเมธอดใน Java อย่างไร และบางครั้งคำถามคือว่าข้อมูลถูกส่งโดยการอ้างอิงหรือตามค่า?

คำถามนี้มาจาก C++ แต่ไม่มีความหมายมากนักใน Java ใน Java พารามิเตอร์จะได้รับการกำหนดค่าของอาร์กิวเมนต์เสมอ ดังนั้นคำตอบที่ถูกต้องคือ " ตามมูลค่า "

แต่จงเตรียมพร้อมที่จะอธิบายตำแหน่งของคุณเนื่องจากคุณอาจได้ยินคำโต้กลับในทันทีว่า "ประเภทดั้งเดิมจะถูกส่งผ่านตามค่า และประเภทการอ้างอิงจะถูกส่งผ่านโดยการอ้างอิง"

ที่มาของปัญหานี้เกิดจากการที่โปรแกรมเมอร์ Java จำนวนมากเคยเป็นโปรแกรมเมอร์ C++ มาก่อน ในภาษาโปรแกรมนั้น คำถามเกี่ยวกับวิธีส่งผ่านพารามิเตอร์ไปยังเมธอดมีความสำคัญมาก

ใน Java ทุกอย่างไม่คลุมเครือ: ประเภทดั้งเดิมจะเก็บค่าและประเภทการอ้างอิงก็เก็บค่าเช่นกัน — การอ้างอิง เป็นคำถามว่าตัวแปรถือเป็นค่าหรือไม่

ใน C++ ตัวแปรสามารถเก็บทั้งการอ้างอิงถึงวัตถุและตัววัตถุเอง เช่นเดียวกับประเภทดั้งเดิม: ตัวแปรดั้งเดิมสามารถเก็บค่าหรือประกาศตัวแปรเป็นการอ้างอิงไปยังไฟล์int. ดังนั้น เพื่อหลีกเลี่ยงความสับสน โปรแกรมเมอร์ C++ มักจะอ้างถึงออบเจกต์ไปยังข้อมูลอ้างอิงเป็นข้อมูลอ้างอิงและตัววัตถุเองเป็นค่า

ใน C++ คุณสามารถมีสถานการณ์ที่ตัวแปรหนึ่งมีวัตถุได้อย่างง่ายดาย แต่อีกตัวแปรหนึ่งมีการอ้างอิงถึงวัตถุนั้น ดังนั้น คำถามเกี่ยวกับสิ่งที่ตัวแปรจัดเก็บ — ตัววัตถุเองหรือเพียงแค่การอ้างอิงถึงตัวแปร — จึงมีความสำคัญมาก เมื่อวัตถุถูกส่งไปยังเมธอด มันจะถูกคัดลอก (หากส่งผ่านค่า) และไม่ถูกคัดลอก (หากส่งผ่านโดยการอ้างอิง)

ใน Java ไม่มีความเป็นคู่นี้ ดังนั้นคำตอบที่ถูกต้องคือ: ค่าจะถูกส่งผ่านอาร์กิวเมนต์ไปยังเมธอด Java ตามค่า เมื่อเราพูดถึงตัวแปรอ้างอิง ค่านี้เป็นค่าอ้างอิง