Menu

static trong Java: thuộc tính và phương thức static

Từ khóa static làm gì trong Java, thuộc tính và phương thức static thuộc về lớp thay vì đối tượng ra sao, và khi nào nên dùng khối static và hằng số.

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.

Thuộc về lớp, không thuộc về đối tượng

Phần lớn các thuộc tính và phương thức bạn viết đến giờ đều thuộc về đối tượng: mỗi lần bạn gọi new, mỗi instance có bản sao thuộc tính riêng, và các phương thức chạy trên trạng thái của chính đối tượng đó. Từ khóa static đảo ngược điều này. Một thành viên static thuộc về chính lớp: chỉ có một bản sao, dùng chung cho mọi instance, và nó tồn tại dù bạn có tạo đối tượng nào hay không.

Chỉ một khác biệt đó giải thích được thuộc tính static, phương thức static, hằng số, và cả lý do main luôn là static.

Thuộc tính static dùng chung

Thuộc tính không static cho mỗi đối tượng một ô riêng. Thuộc tính static cho lớp một ô duy nhất mà ai cũng dùng chung. Ví dụ kinh điển là một biến đếm theo dõi số đối tượng đã được tạo:

Để ý rằng bạn đọc biến đếm là User.count, thông qua tên lớp, vì giá trị này không thuộc về một User nào cả. Ngược lại, mỗi name nằm bên trong đối tượng của riêng nó. Thay đổi count qua bất kỳ instance nào thì mọi instance đều thấy giá trị mới, vì chỉ có một bản duy nhất.

Phương thức static

Phương thức static cũng thuộc về lớp, nên bạn gọi nó trên lớp mà không cần đối tượng:

Đây chính là mẫu đằng sau thư viện chuẩn: Math.max, Integer.parseInt, Arrays.sort và List.of đều là static, những hành vi tiện ích không cần đối tượng nào để hoạt động. Hãy dùng phương thức static khi công việc chỉ phụ thuộc vào đối số, không phụ thuộc vào trạng thái riêng của đối tượng.

Bẫy lớn: static không nhìn thấy instance

Phương thức static chạy mà không có đối tượng nào, nên không có this. Điều đó có nghĩa là nó không thể truy cập trực tiếp thuộc tính instance hay gọi phương thức instance, vì không có đối tượng cụ thể nào để đọc. Đây là lỗi phổ biến nhất của người mới khi dùng static:

class Account {
    int balance = 100;          // instance field

    static int show() {
        return balance;         // COMPILE ERROR: non-static field 'balance'
    }                           // cannot be referenced from a static context
}

Cách sửa là biến phương thức thành phương thức instance (bỏ static để nó có this), hoặc truyền đối tượng vào một cách tường minh:

Chiều ngược lại thì không sao: một phương thức instance có thể thoải mái đọc thuộc tính static và gọi phương thức static, vì dữ liệu dùng chung ở cấp lớp luôn tồn tại.

Hằng số với static final

Kết hợp static với final là bạn có một hằng số: một giá trị dùng chung không bao giờ thay đổi được. Theo quy ước, hằng số được đặt tên bằng UPPER_SNAKE_CASE:

static final là cách viết chuẩn mực để diễn đạt "một giá trị cố định thuộc về kiểu", như Integer.MAX_VALUE hay Math.PI trong thư viện chuẩn. Đặt static nghĩa là bạn không lãng phí một bản sao cho mỗi đối tượng; đặt final nghĩa là không ai gán lại được.

Khối khởi tạo static

Một thuộc tính static đơn giản có thể được khởi tạo ngay tại chỗ. Khi việc thiết lập cần logic thật sự, như tạo bảng tra cứu bằng HashMap hay đọc cấu hình, hãy dùng một khối static. Nó chạy một lần, khi lớp được nạp lần đầu, trước khi có bất kỳ đối tượng nào:

Khối này chỉ chạy một lần dù bạn dùng lớp bao nhiêu lần, nên đây là nơi phù hợp cho việc thiết lập một lần, áp dụng cho cả lớp.

Khi nào nên dùng static

Một quy tắc nhanh:

  • Chỉ dùng thuộc tính static cho dữ liệu thật sự dùng chung giữa mọi instance: biến đếm, bộ nhớ đệm, hằng số. Nếu hai đối tượng có thể hợp lý giữ hai giá trị khác nhau, nó nên là thuộc tính instance.
  • Dùng phương thức static khi kết quả chỉ phụ thuộc vào đối số, không phụ thuộc vào trạng thái của đối tượng nào (các hàm tiện ích thuần túy).
  • Mặc định hãy dùng thành viên instance. Lạm dụng static sẽ âm thầm biến chương trình của bạn thành một đống trạng thái toàn cục, khó kiểm thử và khó suy luận. static là ngoại lệ, không phải điểm xuất phát.

Tiếp theo: enum

Một hằng số static final ổn cho một giá trị cố định, nhưng khi bạn có một tập hợp nhỏ, cố định các giá trị liên quan, như hướng đi, các ngày trong tuần, trạng thái đơn hàng, Java có một kiểu được thiết kế riêng, an toàn và giàu biểu đạt hơn các hằng số rời rạc. Đó là enum, nội dung của trang tiếp theo.

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

static trong Java nghĩa là gì?

static nghĩa là một thuộc tính hay phương thức thuộc về chính lớp, không thuộc về đối tượng riêng lẻ nào. Có đúng một bản sao của thuộc tính static, dùng chung cho mọi instance, và phương thức static được gọi trên lớp (Math.max(...)) mà không cần đối tượng. Ngược lại, các thành viên không static (instance) có một bản sao riêng cho mỗi đối tượng.

Biến static và biến instance trong Java khác nhau thế nào?

Biến instance có một giá trị cho mỗi đối tượng: hai đối tượng có thể giữ hai giá trị khác nhau. Biến static chỉ có một giá trị dùng chung cho mọi đối tượng của lớp, nên thay đổi nó qua một đối tượng (hoặc qua tên lớp) thì mọi đối tượng khác đều thấy. Dùng thuộc tính instance cho trạng thái riêng của từng đối tượng, và thuộc tính static cho dữ liệu thật sự mang tính toàn cục với lớp, như một biến đếm hay một hằng số.

Vì sao phương thức main trong Java là static?

JVM cần gọi main trước khi có bất kỳ đối tượng nào của lớp bạn. Vì phương thức static thuộc về lớp chứ không phải một instance, môi trường chạy có thể gọi thẳng Main.main(args) mà không cần tạo một Main trước. Đó là lý do chữ ký luôn là public static void main(String[] args).

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