Java truyền bằng giá trị hay tham chiếu? Nhiều lập trình viên Java thắc mắc liệu ngôn ngữ này thuộc loại pass by value (truyền bằng giá trị) hay pass by reference (truyền bằng tham chiếu). Bài viết này sẽ giải thích tại sao Java luôn là pass by value.
Đầu tiên, bạn nên hiểu pass by value và pass by reference là gì:
- Pass by value: Các giá trị của tham số phương thức được sao chép sang một biến khác. Sau đó bản sao của chúng được truyền vào phương thức. Phương thức sẽ làm việc với bản sao này.
- Pass by reference: Một alias (bí danh) hoặc tham chiếu trực tiếp đến tham số gốc được truyền vào phương thức. Phương thức sẽ truy cập trực tiếp và có thể thay đổi tham số gốc.
Thường thì, sự nhầm lẫn xoay quanh các thuật ngữ này xuất phát từ khái niệm object reference (tham chiếu đối tượng) trong Java. Về mặt kỹ thuật, Java luôn là pass by value. Lý do là ngay cả khi một biến chứa tham chiếu đến một đối tượng khác, thì chính tham chiếu đối tượng đó cũng chính là một giá trị (địa chỉ bộ nhớ của đối tượng). Do đó, các tham chiếu đối tượng được truyền đi bằng cách sao chép giá trị của tham chiếu đó.
Cả kiểu dữ liệu tham chiếu (reference) lẫn kiểu dữ liệu nguyên thủy (primitive) đều được truyền bằng giá trị.
Ngoài việc hiểu về các kiểu dữ liệu, việc nắm rõ cách cấp phát bộ nhớ trong Java cũng rất quan trọng bởi vì kiểu dữ liệu tham chiếu và kiểu dữ liệu nguyên thủy được lưu trữ theo những cách khác nhau.
Minh họa cơ chế pass by value
Ví dụ sau đây minh họa cách các giá trị được truyền trong Java bằng một chương trình sử dụng class sau:
public class Balloon {
private String color;
public Balloon() {}
public Balloon(String c) {
this.color = c;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
Chương trình sau đây sử dụng một phương thức generic nhằm mục đích hoán đổi hai biến. Một phương thức khác là changeValue()
sẽ cố gắng thay đổi giá trị của các biến.
public class Test {
public static void main(String[] args) {
Balloon red = new Balloon("Red"); // memory reference = 50
Balloon blue = new Balloon("Blue"); // memory reference = 100
swap(red, blue);
System.out.println("After the swap method executes:");
System.out.println("`red` color value = " + red.getColor());
System.out.println("`blue` color value = " + blue.getColor());
changeValue(blue);
System.out.println("After the changeValue method executes:");
System.out.println("`blue` color value = " + blue.getColor());
}
// Generic swap method
public static void swap(Object o1, Object o2){
Object temp = o1;
o1 = o2;
o2 = temp;
}
private static void changeValue(Balloon balloon) { // balloon = 100
balloon.setColor("Red"); // balloon = 100
balloon = new Balloon("Green"); // balloon = 200
balloon.setColor("Blue"); // balloon = 200
}
}
Khi ta thực thi chương trình ví dụ, ta nhận được output sau:
After the swap method executes:
'red' color value = Red
'blue' color value = Blue
After the changeValue method executes:
'blue' color value = Red
Output cho thấy phương thức swap()
đã không thực sự hoán đổi các đối tượng gốc. Việc này thể hiện qua việc giá trị màu của chúng không đổi. Điều này giúp chứng minh rằng Java là pass by value vì phương thức này chỉ hoạt động trên các bản sao của giá trị tham chiếu đối tượng ban đầu.
Cách kiểm tra này có thể được áp dụng với bất kỳ ngôn ngữ lập trình nào để xác định xem ngôn ngữ đó là pass by value hay pass by reference.
Giải thích phương thức hoán đổi swap()
Khi ta sử dụng toán tử new
để tạo một instance của một class, đối tượng đó sẽ được tạo ra và hai biến sau sẽ chứa địa chỉ vị trí trong bộ nhớ nơi đối tượng được lưu trữ.
Balloon red = new Balloon("Red");
Balloon blue = new Balloon("Blue");
Dưới đây là giải thích cụ thể về những gì xảy ra khi phương thức hoán đổi swap()
được gọi:
- Giả sử biến red đang trỏ đến đối tượng
Balloon
tại vị trí bộ nhớ 50 và biến blue đang trỏ đến đối tượngBalloon
khác tại vị trí bộ nhớ 100. - Khi class gọi phương thức
swap()
với các biếnred
vàblue
làm đối số, hai biến tham số mớio1
vào2
được tạo ra. Chúng trỏ đến vị trí bộ nhớ 50 và 100. - Đoạn code sau gải thích những gì xảy ra trong
swap()
:
public static void swap(Object o1, Object o2) { // o1 = 50, o2 = 100
Object temp = o1; // assign the object reference value of o1 to temp: temp = 50, o1 = 50, o2 = 100
o1 = o2; // assign the object reference value of o2 to o1: temp = 50, o1 = 100, o2 = 100
o2 = temp; // assign the object reference value of temp to o2: temp = 50, o1 = 100, o2 = 50
} // method terminated
- Các giá trị trong
o1
vào2
được hoán đổi. Tuy nhiên,o1
vào2
chứa các giá trị là địa chỉ trong bộ nhớ củared
vàblue
. Do đó, giá trị của các biếnred
vàblue
không bị ảnh hưởng gì.
Vì các biến chứa tham chiếu đến các đối tượng, nhiều người thường lầm tưởng rằng ta đang truyền tham chiếu và Java là pass by reference. Tuy nhiên, thực tế là ta đang truyền một giá trị. Giá trị này là một bản sao của tham chiếu, và do đó Java là pass by value.
Giải thích phương thức changeValue()
Phương thức tiếp theo trong chương trình changeValue()
thay đổi thuộc tính color
của các đối tượng được tham chiếu bởi biến blue
:
private static void changeValue(Balloon balloon) { // balloon = 100
balloon.setColor("Red"); // balloon = 100
balloon = new Balloon("Green"); // balloon = 200
balloon.setColor("Blue"); // balloon = 200
}
Đây là giải thích từng bước về những gì xảy ra bên trong phương thức changeValue()
:
- Phương thức
changeValue()
được gọi trên biếnblue
, vốn đang tham chiếu đến vùng nhớ 100. Dòng đầu tiên tạo ra một tham chiếu cũng trỏ đến vùng nhớ 100. Thuộc tínhcolor
của đối tượng tại vùng nhớ 100 được đổi thành “Red”. - Dòng thứ hai tạo một đối tượng mới (với thuộc tính color là “Green”) nằm ở vùng nhớ 200. Mọi phương thức tiếp theo được thực thi trên biến
balloon
sẽ tác động lên đối tượng tại vùng nhớ 200 và không ảnh hưởng đến đối tượng tại vùng nhớ 100. Biếnballoon
mới này ghi đè lên tham chiếu được tạo ở dòng 1, và tham chiếuballoon
từ dòng 1 sẽ không còn có thể truy cập được bên trong phương thức này nữa. - Dòng thứ ba thay đổi thuộc tính color của đối tượng
Balloon
mới tại vùng nhớ 200 thành “Blue”, nhưng không ảnh hưởng đến đối tượng ban đầu đượcblue
tham chiếu tại vùng nhớ 100. Điều này giải thích tại sao dòng cuối cùng trong kết quả đầu ra của chương trình ví dụ lại in rablue color value = Red
(phản ánh sự thay đổi từ dòng 1).
Kết luận
Trong bài viết này, chúng ta đã trả lời câu hỏi Java truyền bằng giá trị hay tham chiếu, tìm hiểu lý do tại sao Java là pass by value. Hãy tham khảo những bài viết khác của chúng tôi để tìm hiểu thêm về những khía cạnh khác của ngôn ngữ này.