
Chào các "coder nhí" của thế kỷ 21! Hôm nay, anh Creyt sẽ "tám" với các em về một thằng cha hơi bị "lạnh lùng" nhưng lại cực kỳ quyền lực trong Java: từ khóa static. Nghe cái tên đã thấy nó đứng im, không nhúc nhích rồi đúng không? Nhưng đừng để vẻ ngoài đánh lừa, nó chính là chìa khóa để điều khiển những "tài sản chung" của cả một lớp học đấy!
1. static Keyword: "Tài Sản Chung" của Cả Lớp, Không Của Riêng Ai
Thôi, nói lý thuyết khô khan quá các em lại gật gù mất. Để anh Creyt kể chuyện này: Tưởng tượng lớp mình là một cái Class tên là HocSinh. Mỗi đứa học sinh trong lớp – thằng Tèo, con Tí, thằng Bin – là một Object (đối tượng) của cái Class HocSinh đó. Mỗi đứa có cái cặp sách riêng (biến instance), có quyền tự do đi chơi riêng (phương thức instance).
Nhưng mà, trong lớp mình có cái Bảng Đen đúng không? Hay cái Loa Thông Báo của trường? Mấy cái đó có phải của riêng thằng Tèo hay con Tí không? KHÔNG! Nó là của cả lớp, ai cũng dùng được, ai cũng thấy được, và nếu một đứa viết lên bảng thì cả lớp đều thấy. Đấy, cái Bảng Đen hay cái Loa Thông Báo chính là những thứ mang tính chất static đấy các em ạ!
Nói tóm lại, khi một thứ gì đó được gắn mác static:
- Nó không thuộc về một đối tượng cụ thể nào (như thằng Tèo hay con Tí).
- Nó thuộc về chính cái Class đó.
- Chỉ có DUY NHẤT MỘT BẢN SAO của nó tồn tại trong bộ nhớ, bất kể em tạo bao nhiêu đối tượng đi chăng nữa.
2. static trong Java: Hẹn Hò Với "Tài Sản Chung" Của Class
static có thể được dùng với:
a. Biến (Variables - Fields)
Khi một biến được khai báo là static, nó trở thành biến của lớp (class variable), chứ không phải biến của đối tượng (instance variable). Tất cả các đối tượng của lớp đó đều chia sẻ cùng một bản sao của biến này. Nếu một đối tượng thay đổi giá trị của nó, giá trị đó sẽ thay đổi với tất cả các đối tượng khác.
Metaphor: Cái Bảng Đen trong lớp. Thằng Tèo viết "I love Creyt" lên bảng, cả lớp đều thấy. Con Tí xóa đi, cả lớp đều biết nó bị xóa.
b. Phương Thức (Methods)
Khi một phương thức được khai báo là static, nó trở thành phương thức của lớp (class method). Em không cần tạo một đối tượng của lớp để gọi phương thức này. Nó thường được dùng để thao tác với các biến static hoặc thực hiện các chức năng tiện ích không cần dữ liệu riêng của từng đối tượng.
Metaphor: Cái Loa Thông Báo của trường. Thầy hiệu trưởng (Class) dùng nó để thông báo cho toàn bộ học sinh (Objects), không cần phải gọi riêng từng đứa học sinh lên để thông báo.
c. Khối (Blocks)
Khối static là một khối code đặc biệt chỉ chạy DUY NHẤT MỘT LẦN khi lớp được load vào bộ nhớ lần đầu tiên. Nó thường được dùng để khởi tạo các biến static có giá trị phức tạp hoặc cần logic đặc biệt để thiết lập.
Metaphor: Lễ Khai Giảng đầu năm học. Chỉ diễn ra một lần, để chuẩn bị cho cả năm học, thiết lập mọi thứ sẵn sàng cho lớp học hoạt động.
d. Lớp Lồng (Nested Classes)
Một lớp lồng (nested class) có thể được khai báo là static. Một static nested class không yêu cầu một thể hiện (instance) của lớp bên ngoài để được khởi tạo. Nó giống như một lớp độc lập nhưng được gói gọn về mặt logic bên trong lớp khác.
Metaphor: Một phòng học bộ môn (ví dụ: phòng Lab) nằm trong khuôn viên trường. Em có thể vào thẳng phòng Lab mà không cần phải đi qua một lớp học cụ thể nào đó trước. Nó độc lập, nhưng vẫn là một phần của tổng thể trường học.

3. Code Ví Dụ Minh Họa: Xây Dựng "Lớp Học Mẫu"
Giờ thì chúng ta cùng "xây" một cái Class HocSinh để xem static hoạt động như thế nào nhé!
class HocSinh {
// Biến static: Số lượng học sinh trong lớp (chung cho cả lớp)
static int soLuongHocSinh = 0;
// Biến instance: Tên của từng học sinh (riêng của mỗi học sinh)
String ten;
// Khối static: Chạy khi Class HocSinh được load vào bộ nhớ
static {
System.out.println("--- Lớp học đã được mở! Chuẩn bị đón học sinh ---");
}
// Constructor: Khởi tạo một đối tượng HocSinh mới
public HocSinh(String ten) {
this.ten = ten;
soLuongHocSinh++; // Mỗi khi có học sinh mới, tăng biến static lên
System.out.println(this.ten + " đã nhập học. Tổng số: " + soLuongHocSinh);
}
// Phương thức instance: Học sinh tự giới thiệu
public void tuGioiThieu() {
System.out.println("Chào các bạn, mình là " + this.ten + ".");
}
// Phương thức static: Thông báo chung của lớp
public static void thongBaoChungCuaLop() {
System.out.println("\n--- Thông báo từ Ban Giám Hiệu ---");
System.out.println("Tổng số học sinh hiện tại của lớp là: " + soLuongHocSinh + " em.");
// Lưu ý: Không thể truy cập biến 'ten' ở đây vì nó là biến instance
// System.out.println("Tên học sinh đầu tiên: " + ten); // Lỗi biên dịch!
}
// Static Nested Class: Lớp Cán Bộ Lớp
static class CanBoLop {
String chucVu = "Lớp trưởng";
public void thongBaoCanBo() {
System.out.println("\n--- Cán bộ lớp thông báo ---");
System.out.println(chucVu + " nhắc nhở các bạn đi học đầy đủ.");
}
}
}
public class BaiHocStatic {
public static void main(String[] args) {
System.out.println("Bắt đầu bài học về Static Keyword\n");
// Gọi phương thức static mà KHÔNG CẦN tạo đối tượng
HocSinh.thongBaoChungCuaLop(); // Kết quả: Tổng số học sinh hiện tại của lớp là: 0 em.
// Tạo các đối tượng HocSinh
HocSinh hs1 = new HocSinh("Tèo");
HocSinh hs2 = new HocSinh("Tí");
HocSinh hs3 = new HocSinh("Bin");
// Gọi phương thức instance của từng đối tượng
hs1.tuGioiThieu();
hs2.tuGioiThieu();
// Gọi lại phương thức static sau khi tạo đối tượng
// soLuongHocSinh đã được tăng lên qua constructor của từng HocSinh
HocSinh.thongBaoChungCuaLop(); // Kết quả: Tổng số học sinh hiện tại của lớp là: 3 em.
// Truy cập trực tiếp biến static
System.out.println("\nSố lượng học sinh truy cập trực tiếp: " + HocSinh.soLuongHocSinh);
// Tạo đối tượng của Static Nested Class
HocSinh.CanBoLop lopTruong = new HocSinh.CanBoLop();
lopTruong.thongBaoCanBo();
System.out.println("\nKết thúc bài học về Static Keyword");
}
}
Output khi chạy code:
--- Lớp học đã được mở! Chuẩn bị đón học sinh ---
Bắt đầu bài học về Static Keyword
--- Thông báo từ Ban Giám Hiệu ---
Tổng số học sinh hiện tại của lớp là: 0 em.
Tèo đã nhập học. Tổng số: 1
Tí đã nhập học. Tổng số: 2
Bin đã nhập học. Tổng số: 3
Chào các bạn, mình là Tèo.
Chào các bạn, mình là Tí.
--- Thông báo từ Ban Giám Hiệu ---
Tổng số học sinh hiện tại của lớp là: 3 em.
Số lượng học sinh truy cập trực tiếp: 3
--- Cán bộ lớp thông báo ---
Lớp trưởng nhắc nhở các bạn đi học đầy đủ.
Kết thúc bài học về Static Keyword
Thấy chưa? Biến soLuongHocSinh tăng lên cho cả lớp, và phương thức thongBaoChungCuaLop() có thể được gọi mà không cần tạo đối tượng HocSinh nào cả. Quá "đỉnh của chóp"!
4. Mẹo Nhớ Nhanh và Best Practices của Giảng Viên Creyt
- Mẹo nhớ:
static= Share (chia sẻ), Single (duy nhất), Class-level (cấp độ lớp). Cứ nhớ "3 S" là thuộc bài! - Khi nào thì dùng
static?- Hằng số (Constants): Khi em có một giá trị không đổi và cần được chia sẻ bởi tất cả các đối tượng (ví dụ:
Math.PItrong Java). Luôn kết hợp vớifinalđể tạostatic final. - Bộ đếm (Counters): Để đếm số lượng đối tượng đã được tạo ra (như ví dụ
soLuongHocSinhcủa chúng ta). - Phương thức tiện ích (Utility methods): Các hàm không cần dữ liệu riêng của một đối tượng để hoạt động (ví dụ:
Math.sqrt(),Integer.parseInt()). - Khởi tạo phức tạp: Dùng
static blockđể thiết lập các biếnstaticcần nhiều bước xử lý.
- Hằng số (Constants): Khi em có một giá trị không đổi và cần được chia sẻ bởi tất cả các đối tượng (ví dụ:
- Cảnh báo từ Creyt:
staticmethod không thể gọi non-staticmethod hoặc truy cập non-staticvariable trực tiếp. Vì sao? Đơn giản là phương thứcstaticthuộc về lớp, nó không biết đối tượng nào đang được nói đến để truy cập cái biến riêng của nó cả. Giống như cái loa thông báo của trường không thể biết thằng Tèo hôm nay ăn sáng món gì!- Đừng lạm dụng
static! Dùngstaticquá nhiều có thể làm cho code khó kiểm thử, giảm tính linh hoạt và mất đi vẻ đẹp của Lập trình Hướng đối tượng (OOP). Nó tạo ra "global state" – trạng thái toàn cục, dễ dẫn đến các lỗi khó lường.
5. Ứng Dụng Thực Tế: "Static" Quanh Ta
static không phải là cái gì xa vời đâu các em, nó có mặt khắp nơi trong các ứng dụng mà các em vẫn dùng hàng ngày:
- Thư viện tiện ích của Java:
Math.PIvàMath.random(): Hằng số và hàm toán học không cần tạo đối tượngMath.System.out.println():outlà một biếnstatictrong lớpSystemcủa Java, đại diện cho luồng output chuẩn.Arrays.sort(): Phương thứcstaticđể sắp xếp mảng mà không cần tạo đối tượngArrays.
- Các thư viện tiện ích khác: Ví dụ như
StringUtilstrong Apache Commons Lang, chứa hàng loạt các phương thứcstaticđể xử lý chuỗi một cách tiện lợi. - Mẫu thiết kế Singleton: Một mẫu thiết kế để đảm bảo chỉ có DUY NHẤT MỘT thể hiện của một lớp tồn tại trong toàn bộ ứng dụng. Thường sử dụng
staticđể quản lý việc tạo và truy cập thể hiện duy nhất đó (ví dụ: một lớp quản lý kết nối cơ sở dữ liệu). - Cấu hình ứng dụng: Các biến
static finalthường được dùng để lưu trữ các giá trị cấu hình chung của ứng dụng (ví dụ:DATABASE_URL,API_KEY).
6. Thử Nghiệm và Hướng Dẫn Nên Dùng Cho Case Nào
Thử nghiệm nhỏ:
Hãy thử tạo một phương thức static và trong đó, cố gắng truy cập một biến không static của lớp. Các em sẽ thấy ngay "lời nhắc nhở" từ trình biên dịch (compiler error) đấy. Đây là cách tốt nhất để hiểu rõ ranh giới giữa static và non-static.
Vậy khi nào thì "nên" dùng static?
- Khi dữ liệu hoặc chức năng không phụ thuộc vào trạng thái cụ thể của bất kỳ đối tượng nào. Ví dụ, hàm
Math.max()luôn trả về giá trị lớn hơn của hai số, nó không cần biết đối tượngMathnào đang gọi nó. - Khi bạn muốn một giá trị hoặc hành vi được chia sẻ và nhất quán trên tất cả các đối tượng của một lớp. Ví dụ, một hằng số
COMPANY_NAMEcho tất cả các nhân viên. - Khi bạn cần một bộ đếm toàn cục cho số lượng đối tượng đã được tạo.
- Khi bạn tạo các lớp tiện ích (utility classes) mà chủ yếu chứa các hàm độc lập, không cần quản lý trạng thái.
Nhớ nhé, static là một công cụ mạnh mẽ, nhưng cũng giống như "siêu năng lực" vậy, phải dùng đúng lúc, đúng chỗ thì mới phát huy hiệu quả tối đa. Lạm dụng là dễ "gây họa" lắm đấy! Anh Creyt tin rằng với bài giảng này, các em đã "thấm" được cái sự "lạnh lùng" nhưng "đầy quyền năng" của static rồi chứ gì? Cứ thực hành nhiều vào, rồi mọi thứ sẽ "ngấm" thôi!
Thuộc Series: Java – OOP
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!