Tham trị (pass-by-value) và tham chiếu (pass-by-reference) trong Java

Khi thao tác với các hàm trong Java, với những người mới bắt đầu sẽ thường lúng túng với việc truyền tham số vào cho các hàm. Tại sao một số trường hợp khi truyền tham số vào cho hàm xong khi thoát ra khỏi hàm giá trị của tham số lúc thì bị thay đổi theo logic của hàm, lúc thì không thay đổi gì? Vấn đề ở đây chính là sự khác nhau của các loại tham số được truyền vào hàm mà đôi lúc người lập trình không để ý đến. Các loại tham số này được chia làm 2 kiểu là truyền giá trị và truyền tham chiếu.
  • Truyền tham trị là gì? Truyền tham chiếu là gì?
Mình tạm áp dụng một cách nhớ có vẻ hơi thô thiển của mình để tránh nhầm lẫn trong trường hợp này: tham trị là cùng giá trị, tham chiếu là cùng chiếu đến một vùng nhớ.
  1. Truyền tham trị là truyền / copy giá trị của một biến (A) vào một biến khác (B) để sử dụng trong phạm vi của một hàm. Ở đây mình nói đến từ copy dùng để nhấn mạnh giá trị được truyền vào là một bản sao và lúc này (A) và (B) hoàn toàn độc lập nhau, việc dùng (B) để xử lý trong hàm hoàn toàn không ảnh hưởng gì đến biến ban đầu (A).
  2. Truyền tham chiếu là làm biến (B) thành một alias (bí danh) của (A), lúc này (A) gần như là (B) và (B) gần như là (A) vì cả 2 đều tham chiếu đến cùng một đối tượng, mọi thay đổi trên (B) thực tế cũng là thay đổi trên (A)  (việc tại sao lại nói (A) va (B) lúc này gần như nhau sẽ được giải thích rõ hơn ở phần dưới). Từ đó, sau khi thoát ra khỏi hàm, dữ liệu trên vùng nhớ được (A) tham chiếu tới sẽ bị thay đổi theo các thao tác mà ta đã dùng để thay đổi dữ liệu trên vùng nhớ được (B) tham chiếu tới.
  • Làm sao xác định đâu là tham chiếu, đâu là tham trị trong Java?
Sau một hồi giảng giải dài dòng ở trên, có thể bạn sẽ nói mình khùng khi mình phát biểu tiếp câu này:
Java chỉ truyền tham trị, không truyền tham chiếu.
Thêm một câu khẳng định lại:
Phát biểu trên luôn đúng và không có ngoại lệ!
Mình không khùng đâu, cái này từng là lầm tưởng của mình trong một thời gian dài và lầm tưởng này nó phổ biến đến mức có thể xếp vào hàng những lầm tưởng phổ biến nhất đối với các lập trình viên Java. Lầm tưởng đó thường được phát biểu như sau:
Kiểu dữ liệu cơ sở được truyền theo tham trị, kiểu object được truyền theo tham chiếu.
Giải thích tiếp Java chỉ truyền tham trị, vậy nó truyền tham trị như thế nào trong trường hợp với kiểu dữ liệu cơ sở và kiểu object.
  1. Kiểu dữ liệu cơ sở: đối với kiểu dữ liệu cơ sở, Java đơn giản là copy giá trị của biến để gán vào tham số. Và như đã nói ở trên, lúc này biến và tham số sử dụng trong hàm là riêng biệt, khi thoát ra khỏi hàm biến không bị thay đổi giá trị.
  2. Kiểu object: khi truyền một biến có kiểu object vào một hàm thì lúc đó có nghĩa ta đã truyền giá trị của biến đó để sử dụng trong hàm, chứ không phải truyền đối tượng được biến đó tham chiếu tới. Vậy giá trị của một biến kiểu object là gì??? Nói có vẻ hơi lạ nhưng có thể nói giá trị của biến kiểu object là địa chỉ của object mà biến đó tham chiếu đến đến. Cho nên lúc này việc thay đổi giá trị của biến kiểu object này bằng cách gán cho một biến kiểu object khác thì object được tham chiếu đến lúc đầu không bị ảnh hưởng, chỉ khi sử dụng các method của chính các object được tham chiếu này thì dữ liệu của object mới được thay đổi sau khi ra khỏi hàm. 
  3. Tuy Java chỉ cho phép truyền tham trị, nhưng đặc biệt, tham trị được truyền của kiểu dữ liệu object là địa chỉ mà object đó tham chiếu đến. Như vậy, ta có thể thay đổi giá trị của một biến thông thường bằng cách đặt nó trong kiểu dữ liệu object như sau:  
Class Obj {
    int a;
    public void setA(int a) {
        this.a = a;
    }

    public int getA() {

        return a;
    }
}

public static void main(String[] args) {
    Obj o = new Obj();
    o.setA(5);
    System.out.println(“Before = ” + a.getA()); // 5
    Change(o);
    System.out.println(“After = ” + a.getA()); // 0
}

static void Change(Obj o) {
    o.setA(0);
}

Comments