Biến (field) trong Java được phân loại thành static (class variable) và non-static / instance variable tùy thuộc vào từ khóa static.
Static Variable (Biến lớp / Class Variable)
- Khai báo với từ khóa
static. - Thuộc về class, không thuộc instance nào.
- Chỉ có một bản sao (single copy) chung cho tất cả instance.
- Khởi tạo khi class được load (class loading), trước khi tạo bất kỳ object nào.
- Truy cập qua
ClassName.staticField(khuyến khích) hoặcinstance.staticField(có warning). - Đời sống = đời sống của class (cho đến khi class unload).
Non-Static Variable (Instance Variable / Biến thể hiện)
- Không có từ khóa
static. - Thuộc về instance (object) cụ thể.
- Mỗi
new ClassName()tạo ra bản sao riêng của biến này. - Khởi tạo khi object được tạo (sau constructor).
- Chỉ truy cập qua
instance.field. - Đời sống = đời sống của object (được GC khi object không còn tham chiếu).
Bảng so sánh
| STT | Tiêu chí | Static Variable | Non-Static Variable |
|---|---|---|---|
| 1 | Truy cập | Từ static method, non-static method, static block, instance method. | Chỉ từ instance method, constructor, instance block. Không thể truy cập trực tiếp từ static context. |
| 2 | Chia sẻ (Sharing) | Chia sẻ cho tất cả instance - thay đổi ở đâu thấy ở đâu. | Riêng biệt cho mỗi instance. |
| 3 | Cấp phát bộ nhớ | Một lần ở Metaspace/Heap (class metadata). | Mỗi lần new object (Heap). |
| 4 | Từ khóa | Bắt buộc static. |
Không cần từ khóa đặc biệt. |
| 5 | Khởi tạo | Static initializer block: static { ... }. |
Instance initializer block: { ... } hoặc constructor. |
| 6 | Serialization | Không được serialize (transient mặc định). | Được serialize mặc định. |
Ví dụ minh họa
public class JavaTester {
// Instance variable
public int counter = 0;
// Static variable
public static int staticCounter = 0;
public JavaTester() {
counter++; // Mỗi instance tăng counter riêng
staticCounter++; // Tất cả instance cùng tăng staticCounter
}
public static void main(String args[]) {
JavaTester t1 = new JavaTester();
JavaTester t2 = new JavaTester();
JavaTester t3 = new JavaTester();
// counter là instance variable -> mỗi object có bản sao riêng
System.out.println("t1.counter: " + t1.counter); // 1
System.out.println("t2.counter: " + t2.counter); // 1
System.out.println("t3.counter: " + t3.counter); // 1
// staticCounter là class variable -> chung
System.out.println("staticCounter (qua t3): " + t3.staticCounter); // 3
System.out.println("staticCounter (qua class): " + JavaTester.staticCounter); // 3
}
}
Kết quả
t1.counter: 1 t2.counter: 1 t3.counter: 1 staticCounter (qua t3): 3 staticCounter (qua class): 3
Lỗi thường gặp
public class ErrorDemo {
int instanceVar = 10;
static int staticVar = 20;
static void staticMethod() {
// System.out.println(instanceVar); // COMPILE ERROR: non-static variable cannot be referenced from static context
System.out.println(staticVar); // OK
}
void instanceMethod() {
System.out.println(instanceVar); // OK
System.out.println(staticVar); // OK
}
}
Best Practices
- Hằng số (constants):
static final(UPPER_SNAKE_CASE). Ví dụ:static final double PI = 3.14159; - Shared state: Dùng static cẩn thận (thread-safety, testing khó). Cân nhắc Dependency Injection.
- Singleton pattern: Static field hold instance.
- Utility class: Chỉ static method/field, private constructor.
- Tránh mutable static field trong multi-threaded env trừ khi synchronized/atomic.
Memory Model (JVM)
- Static variables: Lưu trong Metaspace (Java 8+) / PermGen (Java 7-), một phần của Heap cho class metadata.
- Instance variables: Lưu trong Heap như một phần của object.
- Local variables (trong method): Lưu trong Stack (frame của thread).