Static member thuộc về class, không phải instance.
Hình dung: private int balance là số dư riêng từng tài khoản; static int totalAccounts là một biến duy nhất dùng chung cho mọi Account.
class Counter {
static int total = 0; // dùng chung
int instanceCount = 0; // riêng từng object
Counter() { total++; instanceCount++; }
static int getTotal() { return total; } // gọi: Counter.getTotal()
}Khi nên dùng:
- Utility/helper: Math.abs(), Collections.sort().
- Hằng số: static final int MAX_SIZE = 100;.
- Factory method: LocalDate.now(), List.of(...).
Bẫy cần tránh:
- Khó test: static khó mock — phải dùng mockStatic (chậm, tăng coupling).
- Không polymorphic: static method không override được, chỉ hide.
- Trạng thái toàn cục: mutable static = global state → khó debug, dễ leak giữa test.
- Memory leak: static Map cache không có evict → phình mãi.
Quy tắc: static phù hợp cho pure function + immutable constant. Nghi ngờ thì chuyển sang DI để dễ test.
Static members belong to the class, not individual instances.
Picture: private int balance is each account's own balance; static int totalAccounts is a single variable shared by every Account.
class Counter {
static int total = 0; // shared
int instanceCount = 0; // per object
Counter() { total++; instanceCount++; }
static int getTotal() { return total; } // call: Counter.getTotal()
}When to use:
- Utilities/helpers: Math.abs(), Collections.sort().
- Constants: static final int MAX_SIZE = 100;.
- Factory methods: LocalDate.now(), List.of(...).
Pitfalls:
- Hard to test: static is hard to mock — needs mockStatic (slow, more coupling).
- Not polymorphic: static methods cannot be overridden, only hidden.
- Global state: mutable statics → hard to debug, leak across tests.
- Memory leak: static Map cache without eviction grows forever.
Rule: static fits pure functions + immutable constants. If in doubt, switch to DI for easier testing.