Hãy luôn nhớ rằng, Java chỉ sử dụng truyền tham trị. Khi bạn truyền tham số vào hàm trong Java, tùy thuộc vào kiểu dữ liệu của tham số (kiểu nguyên thủy hay kiểu không nguyên thủy), việc tương tác với vùng nhớ stack và heap sẽ khác nhau.
Xét đoạn mã sau đây:
public class Main {
public static void setX(int y){
y = 2;
}
public static void main(String args[]){
int x = 1;
System.out.println(x); // -> 1
setX(x);
System.out.println(x); // -> 1
}
}
Trong ví dụ này:
- Biến
x
được khai báo và khởi tạo giá trị là1
trong phương thứcmain()
. x
là biến địa phương của phương thứcmain()
được lưu trữ trên vùng nhớ stack của phương thứcmain()
.- Khi gọi phương thức
setX(x)
, giá trị củax
được sao chép vào một biến mới tên lày
của phương thứcsetX()
. - Trong phương thức
setX()
, biếny
được thay đổi giá trị thành2
. Tuy nhiên, sự thay đổi này chỉ ảnh hưởng đếny
, biến địa phương trongsetX()
, và không ảnh hưởng gì đếnx
ở trongmain()
vìy
là một bản sao riêng biệt củax
. - Do đó, khi in ra giá trị của
x
sau khi gọisetX()
, kết quả vẫn là1
như giá trị ban đầu.
Xét tiếp đoạn mã sau:
public class Main {
public static void setX(int x) {
x = 2;
}
public static void main(String args[]) {
int x = 1;
System.out.println(x); // -> 1
setX(x);
System.out.println(x); // -> 1
}
}
Mặc dù phương thức setX()
sử dụng tham số tên là x
(giống như tên biến x
trong phương thức main()
) nhưng nó vẫn là biến địa phương của phương thức setX()
. Việc đặt tên tham số trong phương thức setX()
là x
hoặc y
(hoặc bất kỳ tên nào khác) không ảnh hưởng đến cơ chế truyền tham trị của Java, nhưng nó có thể ảnh hưởng đến tính rõ ràng và dễ đọc của mã. Việc chọn tên tham số có thể làm tăng hoặc giảm sự rõ ràng của mã, đặc biệt là trong việc hiểu rằng các biến trong các phương thức khác nhau là độc lập với nhau.
-
Khi dùng cùng tên
x
: Trong ví dụ gần nhất, cả biến địa phương trongmain()
và tham số củasetX()
đều được đặt tên làx
. Điều này có thể gây ra nhầm lẫn khi đọc và hiểu mã, vì có vẻ như cùng một biếnx
đang được sử dụng trong cả hai phương thức, mặc dù thực tế chúng là hai biến hoàn toàn độc lập. -
Khi dùng tên khác nhau (
y
): Trong ví dụ trước đó, biến trongmain()
làx
và tham số trongsetX()
lày
. Sự khác biệt trong đặt tên này giúp làm rõ rằng biếny
trongsetX()
chỉ là một bản sao của giá trị củax
và là một biến hoàn toàn độc lập. Điều này làm giảm khả năng gây nhầm lẫn về việc liệu thay đổiy
có ảnh hưởng đếnx
hay không.
Xét đoạn mã sau đây:
public class Main {
int x;
Main(int y) {
x = y;
}
static void changeX(int x) {
x = 10;
}
static void changeX(Main object) {
object.x = 10;
}
void changeX() {
this.x = 20;
}
public static void main(String[] args) {
Main obj = new Main(5);
changeX(obj.x);
System.out.println(obj.x); // -> 5
changeX(obj);
System.out.println(obj.x); // -> 10, thực sự bị thay đổi
obj.changeX();
System.out.println(obj.x); // -> 20, thực sự bị thay đổi
}
}
-
Phương thức
changeX(int x)
:- Gọi phương thức:
changeX(obj.x)
: Trong phương thức này,x
là một tham số kiểu nguyên thủy. Khi phương thức này được gọi, giá trị củaobj.x
(là5
) được truyền vào phương thức. Trong stack của thread, một khung mới (stack frame) được tạo cho lời gọichangeX(int)
, và giá trị5
được sao chép vào biến địa phươngx
của khung này. - Thực thi: Giá trị của biến địa phương
x
trong phương thức được thay đổi thành10
, nhưng điều này không ảnh hưởng đến biếnx
của đối tượngobj
trongmain
, vì chỉ bản sao của giá trị được thay đổi. - Kết quả:
System.out.println(obj.x); // -> 5
- Giá trị củax
trong đối tượngobj
không thay đổi vì chỉ giá trị sao chép trong stack của phương thứcchangeX(int)
được thay đổi.
- Gọi phương thức:
-
Phương thức
changeX(Main obj)
:- Gọi phương thức:
changeX(object)
: Ở đây, tham chiếu đến đối tượngMain
được truyền vào. Trong stack, một khung mới được tạo chochangeX(Main)
, và tham chiếu tớiobj
được sao chép vào biến địa phương. - Thực thi: Tham chiếu này trỏ đến cùng một đối tượng trên heap như tham chiếu ban đầu trong
main
. Do đó, khi thuộc tínhx
của đối tượng được thay đổi thành10
trong phương thức, nó thực sự thay đổi đối tượng trên heap. - Kết quả:
System.out.println(obj.x); // -> 10
- Giá trị củax
trong đối tượngobj
được cập nhật thành10
do sự thay đổi trực tiếp trên đối tượng trên heap.
- Gọi phương thức:
Chi tiết như sau:
- Phương thức
changeX()
của lớpMain
:- Gọi phương thức:
obj.changeX()
: Phương thức này là một phương thức phi tĩnh của lớpMain
, do đó nó cần một thực thể của lớp để gọi. - Thực thi: Trong phương thức này,
this.x = 20
; đề cập đến biếnx
của đối tượngobj
thông qua từ khóathis
, chỉ thị rằng thay đổi nên áp dụng cho đối tượng mà phương thức được gọi từ. - Kết quả:
System.out.println(obj.x); // -> 20
- Giá trị củax
được thay đổi thành20
trên cùng một đối tượngobj
trên heap.
- Gọi phương thức: