Mục lục bài họcĐang ở d01-b1
← AP Computer Science A
0/16 bài đã học xong
Chương 1 · Unit 1 — Using Objects and Methods · Bài 1/4 của chương · bài 1/16 của AP Computer Science A

Objects, References, and Constructors

Đối tượng, tham chiếu và hàm dựng
← Mục lục bài học
Lý thuyết · English

A variable of a class type holds a reference, not the object

A class is a blueprint; an object is one thing built from that blueprint. The keyword new does three jobs in one step: it reserves memory for a new object, runs a constructor to initialise that object's instance variables, and returns a reference to the object.

Rectangle box = new Rectangle(3, 5);

Read that line as three separate ideas: Rectangle box declares a variable whose type is a class, new Rectangle(3, 5) builds the object, and = stores the reference in the variable. The variable does not contain the rectangle; it contains an arrow that points at the rectangle.

Three consequences appear on the exam every year. First, b = a; between two object variables copies the arrow, so both names then refer to the same object and a change made through one name is visible through the other. Second, a class-type variable that has not been given an object holds null, and calling any method on it throws a NullPointerException at run time, not at compile time. Third, a class may declare several constructors with different parameter lists; the compiler picks the one whose parameter types match the arguments you supply.

Giải thích tiếng Việt

Lớp (class) là bản thiết kế; đối tượng là một sản phẩm dựng theo bản thiết kế đó. Từ khoá new làm ba việc trong một nhịp: xin bộ nhớ cho đối tượng mới, chạy hàm dựng để đặt giá trị ban đầu cho các biến thực thể, rồi trả về một tham chiếu tới đối tượng.

Rectangle box = new Rectangle(3, 5);

Đọc dòng trên thành ba ý tách rời: Rectangle box khai báo một biến có kiểu là lớp, new Rectangle(3, 5) dựng ra đối tượng, dấu = cất tham chiếu vào biến. Biến không chứa hình chữ nhật; nó chứa một mũi tên trỏ tới hình chữ nhật.

Ba hệ quả năm nào cũng ra đề. Một, phép gán b = a; giữa hai biến đối tượng chỉ chép mũi tên, không chép đối tượng — sau đó hai tên cùng trỏ vào một đối tượng, sửa qua tên này thì nhìn qua tên kia cũng thấy. Hai, biến kiểu lớp chưa được gán đối tượng thì mang giá trị null, và gọi bất kỳ phương thức nào trên nó sẽ ném NullPointerException lúc chạy, không phải lúc biên dịch — nghĩa là chương trình vẫn dịch được, vẫn nộp được, chỉ chết giữa chừng. Ba, một lớp có thể có nhiều hàm dựng khác nhau ở danh sách tham số; trình biên dịch chọn hàm dựng có kiểu tham số khớp với đối số bạn truyền.

Ngăn biến (stack) Vùng đối tượng (heap) Counter a tham chiếu Counter b = a; chép mũi tên MỘT đối tượng Counter count = 2 a.increment(); b.increment(); → count = 2, không phải 1 mỗi bên a == b là true vì hai mũi tên chỉ cùng một chỗ
Vì sao phải vẽ mũi tên. Với kiểu nguyên thuỷ (int, double, boolean) thì ô nhớ chứa chính con số, nên b = a tạo ra hai con số độc lập. Với kiểu lớp thì ô nhớ chứa địa chỉ, nên b = a tạo ra hai mũi tên cùng chỉ một chỗ. Mọi câu hỏi khó về đối tượng của Unit 1 đều quy về việc bạn vẽ đúng hay sai bức hình này: đếm số ô chữ nhật bên phải chính là đếm số lần chữ new đã chạy.
Ví dụ — đếm chữ new trước khi đếm biến

Cho lớp Counter có hàm dựng không tham số đặt count bằng 0, phương thức increment() tăng count thêm 1 và phương thức getCount() trả về count. Đoạn mã sau in ra gì?

Counter a = new Counter();
Counter b = new Counter();
Counter c = a;
a.increment();
c.increment();
b.increment();
System.out.println(a.getCount() + " " + b.getCount() + " " + c.getCount());
Giải.

Bước 1 — đếm chữ new. Có đúng hai chữ new, nên chỉ có hai đối tượng tồn tại, dù có ba biến.

Bước 2 — nối mũi tên. a trỏ vào đối tượng thứ nhất, b trỏ vào đối tượng thứ hai, còn c = a chép mũi tên nên c trỏ vào đối tượng thứ nhất.

Bước 3 — cộng dồn theo đối tượng, không theo tên biến. Đối tượng thứ nhất nhận hai lệnh tăng (một qua a, một qua c) nên count bằng 2. Đối tượng thứ hai nhận một lệnh tăng nên count bằng 1.

Bước 4 — đọc kết quả. a.getCount()c.getCount() hỏi cùng một đối tượng nên cùng ra 2. Dòng in ra là 2 1 2.

Người trả lời 1 1 1 đã đếm theo tên biến chứ không theo đối tượng — đó chính là cái bẫy của cả chương.

Bẫy hay mất điểm — Gán c = a rồi tưởng đã có bản sao. Không có bản sao nào cả: chỉ đối tượng nào được dựng bằng new mới là đối tượng riêng. Cách chặn: mỗi lần đọc mã, gạch chân từng chữ new và vẽ đúng chừng ấy ô chữ nhật, rồi mới nối tên biến vào ô. Bẫy anh em của nó là NullPointerException: biến kiểu lớp khai báo xong mà chưa gán new thì đang giữ null, và lỗi chỉ nổ lúc chạy nên trình biên dịch không hề cảnh báo.
Phải nhớ — Số đối tượng bằng số lần chạy new, không bằng số biến. Phép gán giữa hai biến kiểu lớp chép mũi tên chứ không chép đối tượng. Biến kiểu lớp chưa gán mang null, và null.method() nổ lúc chạy.

Đọc xong rồi — làm thử ngay

Bài tập của chương Unit 1 — Using Objects and Methods gồm 14 câu trắc nghiệm và 8 đề tự luận. Đáp án hiện ngay khi chọn, miễn phí.

Làm bài tập chương →