Mục lục bài họcĐang ở d03-b1
← AP Computer Science A
0/16 bài đã học xong
Chương 3 · Unit 3 — Class Creation · Bài 1/4 của chương · bài 9/16 của AP Computer Science A

Anatomy of a Class: Fields, Encapsulation, Constructors

Giải phẫu một lớp: thuộc tính, đóng gói, hàm khởi tạo
← Mục lục bài học
Lý thuyết · English

A class is a blueprint; an object is one house built from it

A class definition has three kinds of members: instance variables (the state each object carries), constructors (code that runs once, when the object is created), and methods (the behaviour). Instance variables are declared private; constructors and the methods that outside code needs are declared public. That combination is encapsulation: the object owns its data and no other class can reach in and corrupt it.

A constructor has the same name as the class and no return type at all — not even void. Writing public void Book(String t) does not make a constructor; it silently makes an ordinary method named Book, and the compiler then supplies a no-argument default constructor instead.

Overloading means two constructors (or two methods) share a name but differ in their parameter lists. Java picks the version whose parameter types match the call. One constructor may delegate to another with this(...), which must be the very first statement in the body.

The keyword this is a reference to the object currently executing the method. Its everyday job is to un-shadow: when a parameter has the same name as an instance variable, the parameter wins inside the body, so this.title means the field and plain title means the parameter.

If you write no constructor, Java supplies a no-argument default that leaves every field at its zero value (0, 0.0, false, null). If you write any constructor, that free default disappears.

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

Lớp là bản vẽ, đối tượng là một căn nhà dựng từ bản vẽ đó

Một lớp gồm ba loại thành phần: biến thực thể (trạng thái mà mỗi đối tượng mang theo), hàm khởi tạo (chạy đúng một lần, lúc đối tượng ra đời) và phương thức (hành vi). Biến thực thể khai báo private; hàm khởi tạo và những phương thức mà bên ngoài cần gọi thì khai public. Ghép hai điều đó lại chính là đóng gói: đối tượng tự giữ dữ liệu của nó, không lớp nào thò tay vào sửa thẳng được.

Hàm khởi tạo trùng tên lớpkhông có kiểu trả về, kể cả void. Viết public void Book(String t) thì đó không phải hàm khởi tạo mà là một phương thức thường tên là Book — code vẫn biên dịch, vẫn chạy, chỉ là mọi đối tượng tạo ra đều rỗng. Đây là lỗi im lặng nguy hiểm nhất của chương.

Nạp chồng là hai hàm khởi tạo (hoặc hai phương thức) cùng tên nhưng khác danh sách tham số. Java chọn bản khớp kiểu tham số của lời gọi. Một hàm khởi tạo có thể gọi hàm khởi tạo khác bằng this(...), và câu lệnh đó bắt buộc phải là dòng đầu tiên trong thân hàm.

Từ khoá this là tham chiếu tới chính đối tượng đang chạy phương thức. Việc thường ngày của nó là gỡ che khuất: khi tham số trùng tên biến thực thể, trong thân hàm tham số thắng, nên this.title là biến thực thể còn title trơn là tham số.

Nếu không viết hàm khởi tạo nào, Java cấp cho một hàm khởi tạo mặc định không tham số, để mọi trường ở giá trị không (0, 0.0, false, null). Nhưng chỉ cần viết một hàm khởi tạo bất kỳ là bản mặc định miễn phí đó biến mất.

object: Book b1 private — vùng cấm title = "Dune" copies = 3 public getTitle() public borrow(int n) b1.copies = -9; lỗi biên dịch b1.borrow(1); được, có kiểm tra
Vì sao phải đóng gói. Nếu copiespublic thì bất cứ dòng nào trong bất cứ lớp nào cũng gán được b1.copies = -9, và khi số sách âm gây lỗi thì bạn phải đi tìm thủ phạm trong toàn bộ chương trình. Để private thì chỉ có đúng những phương thức trong lớp Book mới ghi được vào nó — phạm vi truy vết thu từ cả chương trình xuống một file. Đó là lý do thật, chứ không phải quy ước cho đẹp.
Ví dụ — hai hàm khởi tạo, một cái gọi cái kia

Viết lớp Book có hai biến thực thể riêng tư title (String) và copies (int). Cần hai hàm khởi tạo: một nhận cả tên sách lẫn số bản, một chỉ nhận tên sách và mặc định số bản là 1. Không được lặp lại phần gán ở hai chỗ.

Giải.

Viết hàm khởi tạo đầy đủ trước, rồi cho hàm khởi tạo ngắn gọi nó bằng this(...):

public class Book {
    private String title;
    private int copies;

    public Book(String title, int copies) {
        this.title = title;
        this.copies = copies;
    }

    public Book(String title) {
        this(title, 1);
    }
}

Ba điểm phải nhìn ra:

  • Hai hàm khởi tạo trùng tên nhưng khác danh sách tham số — đó là nạp chồng hợp lệ.
  • Trong hàm đầy đủ, this.title là biến thực thể còn title là tham số. Bỏ this. đi thì dòng title = title; chỉ gán tham số cho chính nó, biến thực thể vẫn là null.
  • this(title, 1); phải nằm ở dòng đầu tiên. Đặt nó sau một câu lệnh khác là lỗi biên dịch.
Bẫy hay mất điểm — Lỗi đắt nhất của bài này là gán che khuất: viết title = title; trong hàm khởi tạo có tham số trùng tên. Code vẫn biên dịch, vẫn chạy, không có cảnh báo nào, chỉ là mọi đối tượng đều mang null hoặc 0. Cách chặn: quy tắc cứng — trong thân hàm khởi tạo, vế trái của mọi phép gán vào biến thực thể luôn bắt đầu bằng this.. Lỗi anh em của nó là public void Book(...) — có void nên đó là phương thức thường, không phải hàm khởi tạo.
Phải nhớ — Biến thực thể để private, hàm khởi tạo trùng tên lớp và không kiểu trả về, nạp chồng phân biệt bằng danh sách tham số, this. đứng trước mọi biến thực thể bị tham số che khuất, và this(...) nếu dùng thì phải là dòng đầu tiên.

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

Bài tập của chương Unit 3 — Class Creation 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 →