Menu

Access modifier trong Java: public, private, protected

Cách bốn mức truy cập của Java (public, private, protected và package-private) kiểm soát những gì code khác được thấy và được chạm vào.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Ai được chạm vào code của bạn

Access modifier là một từ khóa (keyword) đặt trước lớp, trường, phương thức hoặc hàm khởi tạo, quyết định những ai khác được phép dùng nó. Đây là nền tảng của tính đóng gói (encapsulation): bạn giấu phần ruột phức tạp của một lớp và chỉ để lộ những phần bạn tin tưởng cho code khác gọi.

Java có bốn mức, từ mở nhất đến khóa chặt nhất: public, protected, mặc định (không có từ khóa nào) và private. Dùng đúng các mức này thì sau này bạn có thể thay đổi lớp một cách an toàn, vì không có gì bên ngoài phụ thuộc vào những chi tiết bạn chưa từng hứa giữ nguyên.

private: chỉ thấy được bên trong lớp

private là mức chặt nhất. Một thành viên private chỉ có thể được chạm vào bởi code viết bên trong cùng lớp: không phải lớp con, không phải các lớp khác trong cùng package, không ai cả. Đây là chỗ của hầu hết các trường.

balance là private, nên cách duy nhất để thay đổi nó là qua deposit, phương thức từ chối số tiền âm. Nếu balance là public, bất kỳ code nào cũng có thể viết account.balance = -9999 và bỏ qua bước kiểm tra. Chốt chặn đó chính là lý do để giấu trường.

Thử bỏ chú thích dòng account.balance = 500; trong main và bạn sẽ gặp lỗi biên dịch: balance has private access in BankAccount.

public: thấy được ở mọi nơi

public là thái cực ngược lại: bất kỳ ai, ở bất kỳ đâu, đều dùng được thành viên đó. Những phương thức mà code khác cần gọi thường là public. Bản thân lớp cũng vậy khi nó cần được truy cập từ package khác.

Mẫu thường gặp, đôi khi được gọi là public API của một lớp, là trường private, phương thức public. Các trường giữ trạng thái mà không ai khác nên đụng vào; các phương thức là những cánh cửa ra vào có kiểm soát. getBalance và deposit ở trên chính là như vậy.

Mặc định (package-private): không có từ khóa nào

Nếu bạn không viết modifier nào, bạn có mức truy cập mặc định, còn gọi là package-private. Thành viên đó thấy được với mọi lớp trong cùng package và vô hình với mọi thứ bên ngoài. Mức này không có từ khóa: chính việc không có từ khóa là mức truy cập.

class Invoice {       // no modifier -> visible only within this package
    int amount;       // no modifier -> package-private field
}

Điều này làm nhiều người mới học vấp: bỏ public không làm thứ gì đó thành private, mà làm nó thành package-private. Một trường bạn định giữ bí mật vẫn đọc và ghi được hoàn toàn bởi bất kỳ lớp nào tình cờ nằm trong cùng package. Nếu muốn nó thực sự bị ẩn, bạn phải viết private rõ ràng.

Mức truy cập mặc định thực sự hữu ích cho các lớp phụ trợ là chi tiết cài đặt của một package: bạn không công khai chúng ra ngoài, nhưng các lớp phối hợp với nhau trong package vẫn dùng chúng thoải mái.

protected: package cộng thêm lớp con

protected lỏng hơn mặc định một bậc. Một thành viên protected thấy được ở mọi nơi mà mức mặc định thấy được (cùng package), và cả các lớp con, kể cả lớp con nằm ở package khác. Nó được thiết kế cho những thứ bạn muốn chia sẻ với các lớp kế thừa lớp của bạn, nhưng không phải với cả thế giới.

Dog truy cập được name và sound() vì chúng là protected và Dog là lớp con. Code bên ngoài không phải lớp con và không nằm trong package vẫn không chạm được vào chúng. Hãy dùng protected khi bạn thiết kế một lớp để được kế thừa; còn lại thì ưu tiên private.

Bốn mức truy cập trong một bảng

Đây là bức tranh đầy đủ, từ chặt nhất đến mở nhất:

ModifierCùng lớpCùng packageLớp con (package khác)Mọi nơi
privatecókhôngkhôngkhông
mặc địnhcócókhôngkhông
protectedcócócókhông
publiccócócócó

Một quy tắc đơn giản: bắt đầu với private và chỉ nới lỏng khi có thứ thực sự cần truy cập rộng hơn. Nới rộng quyền truy cập sau này dễ hơn nhiều so với siết lại khi code khác đã bắt đầu phụ thuộc vào một thành viên.

Modifier trên chính các lớp

Access modifier cũng áp dụng cho lớp, không chỉ cho thành viên, nhưng một lớp cấp cao nhất (không lồng trong lớp khác) chỉ có thể là public hoặc mặc định. Bạn không thể viết một lớp cấp cao nhất private hay protected.

public class Order { ... }   // reachable from any package
class LineItem { ... }       // default - only this package can see it

Một lớp cấp cao nhất public phải nằm trong tệp mang tên của nó (Order.java). Ngược lại, lớp lồng nhau có thể dùng cả bốn modifier, đó là lý do các ví dụ trên đánh dấu được lớp bên trong là private hoặc mặc định. Một điểm tinh tế: phương thức public trả về một kiểu package-private thì không hữu ích mấy với code gọi từ bên ngoài, nên hãy giữ phần công khai của bạn nhất quán.

Tiếp theo: thành viên static

Đến giờ, mọi trường và phương thức ở đây đều thuộc về một instance: bạn tạo đối tượng bằng new rồi gọi phương thức trên nó. Nhưng đôi khi một giá trị hay hành vi thuộc về cả lớp, dùng chung cho mọi instance, như bộ đếm số đối tượng đã tạo. Đó là việc của từ khóa static, và nó là nội dung tiếp theo.

Câu hỏi thường gặp

Bốn access modifier trong Java là gì?

public, private, protected và mức mặc định (không có từ khóa, còn gọi là package-private). public thấy được ở mọi nơi, private chỉ trong cùng lớp, protected trong cùng package cộng thêm các lớp con, còn mặc định chỉ trong cùng package.

public và private trong Java khác nhau thế nào?

public nghĩa là mọi code ở bất kỳ đâu đều truy cập được thành viên đó. private nghĩa là chỉ code bên trong cùng lớp mới truy cập được; lớp con và các lớp khác thậm chí không thấy nó. Thông thường bạn để trường là private và cho truy cập qua các phương thức public.

Một trường trong Java không có access modifier thì nghĩa là gì?

Không có từ khóa nghĩa là mức truy cập mặc định (package-private): thành viên đó thấy được với mọi lớp trong cùng package, nhưng vô hình với bên ngoài. Nó chặt hơn public nhưng lỏng hơn private, và rất dễ vô tình bỏ quên, nên hãy dùng nó một cách có chủ đích.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu