[Java] Java Pass by Reference , Pass by Value และรู้จักกับ Literal Pool

          สำหรับเรื่องนี้ เนื่องจากมหาฯลัยที่ผมเรียนมา ค่อนข้างเน้นภาษา C, C++ เป็นหลัก ก็เลยเข้าใจเรื่องของ pass by value กับ pass by reference ในลักษณะของ C คือจะส่งต้องมี Ampersand (&) ปะหน้า เพื่อบอกว่านี่คือ reference นะ อารมณ์ประมาณนี้ และเมื่อผมได้รู้จัก Java ที่เป็นภาษาที่ไม่มี Pointer หรือ * อย่างในภาษา C (pointer กับ ampersand มักจะมาคู่กันเสมอ) ทำให้ผมไม่เข้าใจว่า ภาษา Java มันจะ Pass by Reference ได้อย่างไร ??

          และเมื่อได้มีโอกาสเขียนโปรแกรมเยอะขึ้น ทำให้ได้เห็นว่า Java เองก็มีเหมือนกัน เพียงแต่การใช้ reference มันแอบทำอยู่เบื้องหลังเท่านั้นเอง (ต้องรู้ไว้ ไม่งั้นอาจมีผลต่อโปรแกรมที่เขียนแน่ๆ)

          ขยายความหน่อยสำหรับคนที่ยังไม่รู้ว่า pass by value กับ pass by reference มันคืออะไรกัน?
Pass by value คือ การส่งค่าแบบปกติที่เราทำๆกัน ใช่่ a = 5, a = "string" เป็นต้น
Pass by reference คือ การส่งค่าโดยใช้ที่อยู่ (Address) หรือก็คือ ไม่ว่าเราจะเปลี่ยนแปลงอะไรกับตัวแปรที่มี reference เดียวกัน ก็จะทำให้ค่าเปลี่ยนแปลงตามกันไปหมด อธิบายแบบบ้านๆ ก็เหมือนเราเก็บของไว้ในบ้านหลังเดียวกันนั่นเอง

          ทีนี้ สำหรับสิ่งที่ผมเจอมา ทำให้ผมสงสัยอยู่นานเหมือนกันว่า มันเกิดขึ้นได้อย่างไร ตอนนั้นผมพยายามจะ copy ค่าใน Object ตัวนึง (สมมติว่าชื่อ objA) ไปยังอีกตัวนึง (สมมติว่าชื่อ objB) เมื่อผมใส่ค่าให้ objA เรียบร้อยแล้ว และผมจะให้ objB มันมีค่าเท่ากันกับ objA แต่จะเอาไปใช้ทำอย่างอื่นที่แตกต่างกัน โดยผมสร้าง objB จากการ new ขึ้นมาใหม่ แล้วจับเท่ากับ objA ซะ

TestClass objA = new TestClass();
objA.set...............................
objA.set...............................
objA.set...............................

TestClass objB = new TestClass();
objB = objA;

          บรรทัดสุดท้ายนี่แหละครับ คือ จุดที่ผมสังเกตุได้ว่า objB ในที่นี้ มันก็คือ objA นั่นเอง เพราะฉะนั้นไม่ว่าหลังจากนี้เราจะทำอะไรก็แล้วแต่กับ objB ที่เราคิดว่ามันคือ object ตัวใหม่ที่มีค่าเหมือน objA ทุกประการ แต่สิ่งที่ทำกับ objB นั้นจะส่งผลให้ objA เปลี่ยนแปลงไปเช่นเดียวกัน ถ้าจะพูดให้ง่ายขึ้น การทำแบบนี้ไม่มีประโยชน์ใดๆเลย ไม่ต่างอะไรจากการที่เราเอา objA ไปใช้เลย เหตุผลก็คือ ทั้ง 2 ตัวนี้ มี reference เดียวกัน


เพิ่มเติมอีกหน่อยกับสำหรับเรื่องของ Reference ครับ
          หลายคนอาจเคยได้ยินคำว่า Literal หรือ Pool หรือ String Literal Pool กันมาบ้างนะครับ
คำว่า String Literal Pool นี้ คือเป็นที่เก็บข้อมูลสำหรับ String ที่ตายตัว (Literal) เช่น "Hello", "0", "abc" เป็นต้นครับ เมื่อมีการเก็บลงสู่หน่วยความจำที่มีไว้สำหรับเก็บ Literal โดยเฉพาะ (Literal Pool)  ก็จะทำให้เมื่อเรามีการประกาศตัวแปรที่เป็น literal เมื่อไร ก็จะมีการชี้ไปยัง reference ที่อยู่ใน literal ครับ เช่น

String hello = "Hello";

          เมื่อมีการประกาศตัวแปรอื่นที่มีค่าเป็น "Hello" ก็จะหมายความว่าชี้ไปที่ reference เดียวกัน เพื่อลดการจองหน่วยความจำลง แทนที่จะเก็บค่านั้นๆเป็นของตัวเอง ก็ชี้ไปที่ๆเดียวกัน หากมีค่าเหมือนกัน

          เพราะฉะนั้นทำให้การเปรียบเทียบค่าระหว่าง String ซึ่งเป็น Object หนึ่งของ Java สามารถทำได้โดยใช้ (==) ถึงแม้ว่า ถ้าใช้กับ Object จะเป็นการเทียบ reference ก็ตาม

String literal_0 = "0";
literal_0 == "0" ? true : false; //true
          จากตัวอย่างนี้ที่ผลเป็นจริงก็เพราะว่า "0" ถูกเก็บอยู่ใน literal pool และ literal_0 ก็ถูกชี้ไปที่ literal pool ที่มีค่าเป็น "0"

          จากนั้นถ้าเราประกาศตัวแปรที่จะมีการจองหน่วยความจำเป็นของตัวเอง ก็สามารถทำได้ง่ายๆ ด้วยการ new ขึ้นมาใหม่ ไม่ว่าจะเป็น new Object ของเราเองที่มี attribute ที่มีค่าเป็น literal ก็จะมีการเก็บในเนื้อที่ของตัวเอง ไม่เกี่ยวกับ literal pool แต่อย่างใด (อาจจะเป็นเหตุผลที่ static เกิดขึ้นมาก็ได้นะ (เดา))

String literal_0 = new String("0");
literal_0 == "0" ? true : false; //false
          เพียงแค่เขียนแบบนี้ก็ให้ผลที่แตกต่างกันแล้ว เหตุผลอย่างที่ได้กล่าวไปก่อนหน้า (==) จะ check reference จากตัวอย่างนี้ก็ถือว่าคนละที่กัน ทำให้ได้ผลเป็นเท็จ เพราะฉะนั้น ถ้าเราอยากจะเปรียบเทียบค่าจริงๆ ของ String นี้ต้องใช้ .equals()

String literal_0 = "0";
literal_0.equals("0") ? true : false; //true
          ลองศึกษาดูนะครับ เรื่องของ pass by value และ pass by reference เป็นอีกเรื่องหนึ่งที่มีความสำคัญและมักพลาดกันอยู่บ่อยๆ อาจเป็นเพราะความกำกวมของ Datatype ของ Java เอง ซึ่งโอกาสหน้าจะเขียนเรื่อง primitive type และ class type ให้อ่านกันนะครับ



แหล่งข้อมูล
https://www.xyzws.com/Javafaq/what-is-string-literal-pool/3

Facebook Comment

Popular post of 7 days

[Android] เปิดเครื่องไม่ได้ โลโก้ค้าง (Boot Loop)

[Java] ความแตกต่างระหว่าง Overloading กับ Overriding

ลืมรหัสปลดล็อค Android เข้าเครื่องไม่ได้ มีทางออกครับ