You are on page 1of 6

Khái niệm biến trong Java (Phần 2): Truyền giá trị vào biến

Bài này là phần tiếp theo bài Khái niệm biến trong Java (Phần 1). Nếu bạn đã tự tin là
đã nắm vững khái niệm về biến thì bạn có thể bước vào đọc ngay bài này.

Như vậy, trong bài học trước, bằng hình ảnh về cái cốc, chúng ta đã có hình ảnh về các
biến trong Java.

Bạn đã hiểu biến chẳng qua chỉ là nơi chứa giá trị. Giá trị có thể được đưa vào chứa trong
đó rồi sau này ta có thể đem một giá trị khác thay thế giá trị đó.

Nhưng là một người học thì chúng ta cũng có thể thắc mắc: Biến chứa giá trị cũng
giống như một lọ dầu chứa dầu và khi cần ta đổ dầu đó vào cỗ máy để vận hành. Cơ cấu
cần đến dầu của chúng ta chính là các phương thức - method. Vậy thì việc truyền một
biến vào một phương thức cần hiểu như thế nào? Và liệu có phải điều đó diễn ra trên các
kiểu nguyên thủy và tham chiếu đều giống như nhau không?

Đây là nhưng câu hỏi hay và để làm rõ điều đó, chúng ta cùng xem một phép gán đơn
giản sau.

1) int x = 3;

2) int y = x;

Các dòng trên nghĩa là gì vậy?

Trong dòng 1, một cái cốc có tên là x, có kích thước là int, được tạo ra và được nhét vào
trong đó giá trị 3.

Trong dòng 2, một cái cốc tên là y, có kích thước là int, được tạo ra và được nhét vào
trong đó giá trị... 3.

Biến x không hề bị ảnh hưởng gì!

Java SAO CHÉP giá trị của x (ở đây là 3) và đặt bản sao chép đó vào y.

Người ta gọi thế này là PASS-BY-VALUE tức Truyền/gán theo giá trị. Bạn cũng có
thể gọi nó là PASS-BY-COPY. Giá trị được sao chép, và chính giá trị sao chép đó được
đưa vào trong cốc mới. Bạn không hề nhét cái cái cốc này vào cái cốc kia.
Nói int y = x không hề có nghĩa là "đưa cốc x vào trong cốc y". Nó nghĩa là "sao chép giá
trị nằm bên trong x và đưa giá trị sao chép đó vào trong cốc y".

Nếu sau đó tôi thay đổi y:

y = 34;

Liệu x có bị ảnh hưởng gì không? Tất nhiên là không. Cái cốc x vẫn còn nằm ở đó,
nguyên vẹn như chưa có gì xảy ra.

Nếu sau đó tôi thay đổi x:

x = 90;

Liệu y có bị thay đổi gì không? Không hề. Các giá trị được truyền vào y hoàn toàn tách
biệt với những thay đổi của x sau khi việc sao chép và phép gán được thực hiện. Bạn hãy
thử hình dung: chúng ta có hai ngăn kéo x và y. Ngăn kéo x có 1 tờ giấy có độc nhất chữ
Java. Việc gán y =x cũng giống như chúng ta đem photocopy tờ giấy trong ngăn x và đặt
tờ photo đó vào ngăn y. Tờ gốc vẫn để ở ngăn x. Bây giờ chúng ta thay tờ gốc trong ngăn
x bằng một trang khác có dòng: Java run anywhere thì theo bạn trang trong ngăn y có
thay đổi không? Bạn đã biết cách trả lời.

Vậy ... với các biến tham chiếu (các điều khiển từ xa) thì tình hình thế nào? Phương
pháp trên làm việc ra sao?

Toàn diện mà nói thì quy tắc cũng không có gì thay đổi.

Các biến tham chiếu cũng không có gì khác cả. Bạn sẽ nhận được một bản sao của
tham chiếu.

Do vậy, nếu tôi nói:

Cat A = new Cat();

Cat B = A;
Điều khiển từ xa nằm trong A được sao chép, chứ không phải là đối tượng mà nó
dẫn chiếu tới.
Bạn vẫn chỉ có một đối tượng Cat mà thôi.
Nhưng bây giờ bạn có tới 2 tham chiếu khác nhau (hai điều khiển từ xa) để kiểm soát
cùng một đối tượng Cat.

Bây giờ chúng ta hãy xem xét việc truyền giá trị vào phương thức

Nguyên tắc: Java is pass-by-value - Java truyền/gán theo giá trị

Luôn là như vậy.

Điều đó có nghĩa là "sao chép giá trị, và truyền bản sao chép đó."

Đối với các kiểu nguyên thủy, điều này rất dễ nắm bắt:

int x = 5;
doStuff(x); // truyền/gán một bản SAO CHÉP của x (có giá trị là 5) vào phương
thức doStuff

Phương thức doStuff trông như sau:

void doStuff(int y) {

// sử dụng y theo một số cách thức nào đó y in some way


}

Một bản sao chép của giá trị nằm trong x, ở đây là 5, được truyền/gán vào phương thức doStuff().

Phương thức doStuff() có cốc mới của riêng nó với tên là y đang chờ sẵn.
Cốc y là một cốc khác, hoàn toàn mới. Khi bạn gán/truyền giá trị của x vào phương thức doStuff(), bạn sẽ
tuân thủ quy tắc: sao chép giá trị x, đưa bản sao giá trị đó vào cốc y nằm chờ sẵn trong phương thức
doStuff(). Kể từ thời điểm phép gán này được thực hiện xong thì y và x không còn tác động gì lên nhau
nữa. Nếu bạn thay đổi y, bạn sẽ chẳng sờ động gì đến x.

void doStuff(int y) {

y = 27; // cái này chẳng tác động gì đến x


}

Và ngược lại cũng vậy. Nếu bạn thay đổi x, bạn cũng sẽ chẳng gây ra bất cứ một sự thay đổi nào ttrên y.

Vậy pass-by-value làm việc với các tham chiếu như thế nào?

Rât nhiều người nói rằng "Java thực hiện gán cho các kiểu nguyên thủy theo giá trị và thực hiện gán cho
các đối tượng theo tham chiếu'. Tiếc là điều này không hoàn toàn đúng. Java gán/truyền mọi thứ theo giá
trị. Với các kiểu nguyên thủy, bạn nhận được một bản sao chép về nội dung. Với các tham chiếu, bạn cũng
nhận được một bản sao chép về nội dung.

Nhưng nội dung của một tham chiếu là cái gì?

Là điều khiển từ xa. Cái này nghĩa là điều khiển truy cập/kiểm soát đối tượng.

Khi bạn gán/truyền một tham chiếu đối tượng vào một phương thức, là bạn bạn đang gán/truyền một bản
sao của tham chiếu đó. Một nhân bản của điều khiển từ xa. Đối tượng thfi đâu vẫn nằm đó, nó chỉ chờ có ai
đó dùng điều khiển từ xa để gọi nó. Đối tượng không quan tâm có bao nhiêu điều khiển từ xa được lập trình
để kiểm soát nó. Chỉ có bạn-lập trình viên và trình dọn rác tài nguyên là quan tâm đến điều đó.

Do vậy, khi bạn nói:

Cat A = new Cat();


doStuff(A);

void doStuff(Cat B) {

// dùng B theo các cách nào đó


}
thì vẫn chỉ có một đối tượng Cat. Nhưng ở đây chúng ta có tới 2 điều khiển từ xa (tham chiếu) có thể truy
cập vào cùng một đối tượng Cat.

Do vậy lúc này thì bất cứ cái gì mà B thực hiện đối với Cat, cũng sẽ tác động lên đối tượng Cat mà A
dẫn chiếu tới, nhưng sẽ không gây tác động gì lên cốc A!

Bạn có thể thay đổi Cat, bằng cách sử dụng tham chiếu B mới của bạn (được sao chép trực tiếp từ A),
nhưng bạn không thể thay đổi A.

Thế nghĩa là thế nào?

Bạn có thể thay đổi đối tượng mà A dẫn chiếu tới, nhưng bạn không thể nắm bắt được biến tham chiếu A và
thực hiện điều gì trên nó - như là chuyển hướng nó để nó tham chiếu đến một đối tượng khác hoặc không
cho nó tham chiếu đến một đối tượng nào cả (giá trị null).

Do vậy nếu bạn thay đổi tham chiếu B (không phải là đối tượng Cat mà Ba dẫn chiếu đến mà chính là bản
thân tham chiếu B) thì bạn cũng không hề làm cho A thay đổi. Điều ngược lại cũng đúng.

Do vậy...

Cat A = new Cat();


doStuff(A);

void doStuff(Cat B) {

B = new Cat(); //không hề tác động lên tham chiếu A


}

Việc làm này đơn thuần là chỉ B đến kiểm soát một đối tượng khác. A thì vẫn còn nguyên, không bị tác
động gì.

Chúng ta cùng nhắc lại nào:

Java is pass-by-value.

Đối với các kiểu nguyên thủy, bạn gán/truyền một bản sao của giá trị thực sự.

Đối với các tham chiếu đối tượng, bạn gán/truyền bản sao của tham chiếu (điều khiển từ xa).
Bạn không bao giờ truyền/gán bản thân đối tượng. Hãy luôn nhớ rằng tất cả các đối tượng đều được lưu trữ
trong vùng nhớ tạm trữ (heap).

pcdinh (Theo:JavaBranch)

You might also like