this và static Keywords
Sau bài này, bạn sẽ:
- Sử dụng this keyword để phân biệt instance variables và parameters, constructor chaining, và method chaining
- Hiểu sự khác biệt giữa static members (class-level) và instance members (object-level)
- Khai báo và sử dụng static fields, static methods, và static blocks
- Biết khi nào nên dùng static vs instance dựa trên yêu cầu của bài toán
- Áp dụng static import để truy cập static members mà không cần class name
Bài trước: Encapsulation và Access Modifiers — Đã học về 4 access modifiers và getters/setters. Bài này sẽ tìm hiểu về this và static keywords - hai khái niệm quan trọng trong Java OOP.
this Keyword
this là một reference (tham chiếu) đến object hiện tại (current object) đang gọi method hoặc constructor.
Hãy nghĩ về đại từ nhân xưng "tôi/mình":
- Khi bạn nói "Tôi đang học Java", "tôi" chỉ chính bạn
- Trong class,
thischỉ object đang thực thi code
Công dụng của this
- Phân biệt instance variable và parameter
- Gọi constructor khác (constructor chaining)
- Truyền current object làm tham số
- Trả về current object từ method
1. this để phân biệt Instance Variable vs Parameter
Khi tên parameter trùng với tên field, dùng this để phân biệt.
Ví dụ cơ bản
class Student {
private String name; // instance variable
private int age;
// Constructor
public Student(String name, int age) {
this.name = name; // this.name = instance variable
// name = parameter
this.age = age;
}
// Setter
public void setName(String name) {
this.name = name; // Gán parameter vào instance variable
}
}
Giải thích:
this.name: Instance variable của objectname: Parameter của constructor/method
Không dùng this sẽ như thế nào?
class Student {
private String name;
public void setName(String name) {
name = name; // ❌ Gán parameter vào chính nó - vô nghĩa!
// this.name vẫn là null!
}
}
Nếu tên parameter khác tên field, không bắt buộc dùng this:
public void setName(String n) {
name = n; // ✅ OK - không trung tên
}
Nhưng dùng this làm code rõ ràng hơn!
Ví dụ: Person Class
class Person {
private String firstName;
private String lastName;
private int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName; // this phân biệt field vs parameter
this.lastName = lastName;
this.age = age;
}
public void updateInfo(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
System.out.println("Info updated for " + this.firstName);
}
public void displayInfo() {
// Có thể bỏ this khi không trung tên, nhưng dùng để rõ ràng
System.out.println("Name: " + this.firstName + " " + this.lastName);
System.out.println("Age: " + this.age);
}
}
2. this() - Constructor Chaining
this() gọi constructor khác trong cùng class.
Quy tắc
- this() phải là câu lệnh đầu tiên trong constructor
- Tránh circular calls (A gọi B, B gọi A)
Ví dụ cơ bản
class Rectangle {
private double width;
private double height;
// Constructor 1: Chỉ nhận width (hình vuông)
public Rectangle(double side) {
this(side, side); // Gọi constructor 2
}
// Constructor 2: Nhận cả width và height
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
// Constructor 3: Default (1x1)
public Rectangle() {
this(1.0, 1.0); // Gọi constructor 2
}
}
// Sử dụng
Rectangle rect1 = new Rectangle(); // Constructor 3 → 2
Rectangle rect2 = new Rectangle(5); // Constructor 1 → 2
Rectangle rect3 = new Rectangle(4, 6); // Constructor 2 trực tiếp
Ví dụ: Product với nhiều Constructors
class Product {
private String productId;
private String name;
private double price;
private int quantity;
private String category;
// Constructor đầy đủ nhất (main constructor)
public Product(String productId, String name, double price, int quantity, String category) {
this.productId = productId;
this.name = name;
this.price = price;
this.quantity = quantity;
this.category = category;
}
// Constructor không có category (default: "General")
public Product(String productId, String name, double price, int quantity) {
this(productId, name, price, quantity, "General");
}
// Constructor không có quantity (default: 0)
public Product(String productId, String name, double price) {
this(productId, name, price, 0, "General");
}
// Constructor chỉ có id và name
public Product(String productId, String name) {
this(productId, name, 0.0, 0, "General");
}
public void displayInfo() {
System.out.println("Product: " + name + " (" + productId + ")");
System.out.println("Price: $" + price + ", Quantity: " + quantity);
System.out.println("Category: " + category);
}
}
- Đặt logic chính trong constructor có nhiều tham số nhất
- Các constructor khác gọi đến constructor chính qua
this() - Giảm code duplication
3. this làm tham số
Truyền current object vào method khác.
class Printer {
public void print(Document doc) {
System.out.println("Printing: " + doc.getTitle());
}
}
class Document {
private String title;
public Document(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void printDocument(Printer printer) {
printer.print(this); // Truyền current object (Document)
}
}
// Sử dụng
Document doc = new Document("Report.pdf");
Printer printer = new Printer();
doc.printDocument(printer); // Output: Printing: Report.pdf
4. this để return current object (Method Chaining)
Method chaining (fluent API) cho phép gọi nhiều methods liên tiếp.
class StringBuilder {
private String value = "";
public StringBuilder append(String str) {
value += str;
return this; // Trả về current object
}
public StringBuilder reverse() {
value = new StringBuilder(value).reverse().toString();
return this; // Trả về current object
}
public String build() {
return value;
}
}
// Sử dụng - method chaining
String result = new StringBuilder()
.append("Hello")
.append(" ")
.append("World")
.reverse()
.build();
System.out.println(result); // dlroW olleH
Ví dụ: Query Builder Pattern
class QueryBuilder {
private String table;
private String columns = "*";
private String whereClause = "";
private String orderBy = "";
public QueryBuilder from(String table) {
this.table = table;
return this;
}
public QueryBuilder select(String columns) {
this.columns = columns;
return this;
}
public QueryBuilder where(String condition) {
this.whereClause = " WHERE " + condition;
return this;
}
public QueryBuilder orderBy(String column) {
this.orderBy = " ORDER BY " + column;
return this;
}
public String build() {
return "SELECT " + columns + " FROM " + table + whereClause + orderBy;
}
}
// Sử dụng
String sql = new QueryBuilder()
.select("name, age")
.from("students")
.where("age > 18")
.orderBy("name")
.build();
System.out.println(sql);
// Output: SELECT name, age FROM students WHERE age > 18 ORDER BY name
static Keyword
static nghĩa là "thuộc về class", không thuộc về bất kỳ object nào.
Hãy nghĩ về trường học:
- Instance variable: Tên, tuổi của mỗi học sinh (khác nhau)
- Static variable: Tên trường, số lượng học sinh chung cho tất cả (giống nhau)
Static vs Instance
| Instance (non-static) | Static | |
|---|---|---|
| Thuộc về | Object cụ thể | Class |
| Truy cập qua | objectName.member | ClassName.member |
| Bộ nhớ | Mỗi object có bản copy riêng | Chỉ 1 copy duy nhất cho tất cả objects |
| Khởi tạo | Khi tạo object (new) | Khi class được load lần đầu |
Dùng this | ✅ Có | ❌ Không |
Static Fields (Class Variables)
Static field được chia sẻ bởi tất cả objects của class.
Ví dụ: Đếm số lượng objects
class Student {
private String name;
private static int studentCount = 0; // Static field - chung cho tất cả
public Student(String name) {
this.name = name;
studentCount++; // Tăng mỗi khi tạo object mới
}
public static int getStudentCount() {
return studentCount;
}
}
// Sử dụng
Student s1 = new Student("An");
Student s2 = new Student("Bình");
Student s3 = new Student("Cường");
System.out.println("Total students: " + Student.getStudentCount()); // 3
Giải thích:
studentCountlà static → chỉ có 1 biến duy nhất, không phải mỗi object có 1 copy- Mỗi lần tạo object mới,
studentCounttăng lên - Truy cập qua
ClassName.staticField(không cần object)
Ví dụ: Configuration Constants
class DatabaseConfig {
// Static constants (final)
public static final String DB_URL = "jdbc:mysql://localhost:3306/mydb";
public static final String DB_USER = "admin";
public static final int MAX_CONNECTIONS = 100;
public static final double TIMEOUT_SECONDS = 30.0;
// Static variable
private static int currentConnections = 0;
public static void incrementConnections() {
if (currentConnections < MAX_CONNECTIONS) {
currentConnections++;
}
}
public static int getCurrentConnections() {
return currentConnections;
}
}
// Sử dụng
System.out.println(DatabaseConfig.DB_URL);
System.out.println(DatabaseConfig.MAX_CONNECTIONS);
DatabaseConfig.incrementConnections();
static final thường dùng cho constants (hằng số):
- static: Chung cho tất cả
- final: Không thể thay đổi
- Convention: Tên viết HOA, ngăn cách bằng underscore (
MAX_VALUE)
Static Methods
Static method thuộc về class, không thuộc object.
Quy tắc Static Methods
- Không thể dùng
this(không có object hiện tại) - Chỉ truy cập được static members (fields và methods)
- Không thể override (sẽ học ở module kế thừa)
Ví dụ cơ bản
class MathUtils {
// Static method - không cần object
public static int add(int a, int b) {
return a + b;
}
public static int max(int a, int b) {
return (a > b) ? a : b;
}
public static double circleArea(double radius) {
return Math.PI * radius * radius;
}
}
// Sử dụng - không cần new object
int sum = MathUtils.add(5, 3); // 8
int maximum = MathUtils.max(10, 20); // 20
double area = MathUtils.circleArea(5); // 78.54...
Static method KHÔNG thể truy cập instance members
class Example {
private int instanceVar = 10;
private static int staticVar = 20;
public static void staticMethod() {
System.out.println(staticVar); // ✅ OK - static access static
// System.out.println(instanceVar); // ❌ Error - static không access instance
// System.out.println(this.instanceVar); // ❌ Error - không có 'this' trong static
}
public void instanceMethod() {
System.out.println(instanceVar); // ✅ OK - instance access instance
System.out.println(staticVar); // ✅ OK - instance access static
}
}
Ví dụ: Utility Class
class StringUtils {
// Private constructor - ngăn không cho new object
private StringUtils() {
throw new UnsupportedOperationException("Utility class");
}
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
public static String capitalize(String str) {
if (isEmpty(str)) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
}
public static String reverse(String str) {
if (isEmpty(str)) {
return str;
}
return new StringBuilder(str).reverse().toString();
}
public static int countWords(String str) {
if (isEmpty(str)) {
return 0;
}
return str.trim().split("\\s+").length;
}
}
// Sử dụng
System.out.println(StringUtils.capitalize("hello")); // Hello
System.out.println(StringUtils.reverse("Java")); // avaJ
System.out.println(StringUtils.countWords("Hello World")); // 2
// StringUtils utils = new StringUtils(); // ❌ Error - constructor private
Static Blocks (Static Initializers)
Static block được chạy một lần duy nhất khi class được load vào bộ nhớ.
Cú pháp
class ClassName {
static {
// Code chạy khi class load
}
}
Ví dụ: Khởi tạo Static Variables
class Configuration {
private static final String CONFIG_FILE = "config.properties";
private static String appName;
private static int maxUsers;
private static boolean initialized;
// Static block - chạy khi class load
static {
System.out.println("Loading configuration...");
appName = "MyApp";
maxUsers = 100;
initialized = true;
System.out.println("Configuration loaded!");
}
public static void displayConfig() {
System.out.println("App: " + appName);
System.out.println("Max users: " + maxUsers);
System.out.println("Initialized: " + initialized);
}
}
// Sử dụng
Configuration.displayConfig();
// Output:
// Loading configuration...
// Configuration loaded!
// App: MyApp
// Max users: 100
// Initialized: true
Nhiều Static Blocks
class Demo {
private static int value;
static {
System.out.println("Static block 1");
value = 10;
}
static {
System.out.println("Static block 2");
value += 5;
}
static {
System.out.println("Static block 3");
value *= 2;
}
public static void main(String[] args) {
System.out.println("Main method");
System.out.println("Value: " + value);
}
}
// Output:
// Static block 1
// Static block 2
// Static block 3
// Main method
// Value: 30
Thứ tự thực thi:
- Static blocks (theo thứ tự khai báo)
- main() method
Static Initialization Order Across Classes
Khi có nhiều classes, thứ tự khởi tạo static members phức tạp hơn.
class ClassA {
static {
System.out.println("ClassA static block");
}
static int valueA = getValueA();
static int getValueA() {
System.out.println("ClassA getValueA()");
return 10;
}
}
class ClassB {
static {
System.out.println("ClassB static block");
}
static int valueB = ClassA.valueA * 2; // Tham chiếu ClassA
static {
System.out.println("ClassB valueB = " + valueB);
}
}
public class StaticInitOrder {
public static void main(String[] args) {
System.out.println("Main started");
System.out.println("Accessing ClassB.valueB: " + ClassB.valueB);
System.out.println("Accessing ClassA.valueA: " + ClassA.valueA);
}
}
Output:
Main started
ClassB static block
ClassA static block
ClassA getValueA()
ClassB valueB = 20
Accessing ClassB.valueB: 20
Accessing ClassA.valueA: 10
Giải thích:
- Main chạy, truy cập
ClassB.valueB - ClassB load → chạy static block 1
- Khởi tạo
valueB→ cầnClassA.valueA - ClassA load → static block → getValueA()
- Quay lại ClassB → static block 2
- Truy cập
ClassA.valueA→ ClassA đã load, không chạy lại static
Ví dụ: Circular static dependency (bẫy nguy hiểm)
class ClassX {
static int x = ClassY.y + 1; // Tham chiếu ClassY
static {
System.out.println("ClassX static: x = " + x);
}
}
class ClassY {
static int y = ClassX.x + 1; // Tham chiếu ClassX
static {
System.out.println("ClassY static: y = " + y);
}
}
public class CircularInit {
public static void main(String[] args) {
System.out.println("Final x = " + ClassX.x);
System.out.println("Final y = " + ClassY.y);
}
}
Output (bất ngờ!):
ClassX static: x = 1
ClassY static: y = 2
Final x = 1
Final y = 2
Giải thích:
- Truy cập
ClassX.x - ClassX load → khởi tạo
x = ClassY.y + 1 - ClassY chưa load →
ymặc định = 0 x = 0 + 1 = 1- ClassY load → khởi tạo
y = ClassX.x + 1 - ClassX đã load →
x = 1 y = 1 + 1 = 2
Đừng để classes phụ thuộc lẫn nhau trong static initialization - rất khó debug!
Class Loading Mechanism (Cơ chế nạp class)
Phần này giới thiệu cơ chế class loading của JVM. Nếu bạn mới bắt đầu, có thể bỏ qua và quay lại sau. Chi tiết sẽ được trình bày ở Module JVM Internals.
Java sử dụng lazy loading - class chỉ được load khi cần thiết.
Khi nào class được load?
- Tạo instance với
new - Truy cập static member (field hoặc method)
- Reflection (Class.forName())
- JVM startup (main class)
- Subclass được load → superclass cũng load
class Parent {
static {
System.out.println("Parent loaded");
}
}
class Child extends Parent {
static {
System.out.println("Child loaded");
}
}
public class ClassLoadingDemo {
public static void main(String[] args) {
System.out.println("Main started");
// Class chưa được load
System.out.println("Before new Child()");
new Child(); // Load Child → tự động load Parent trước
}
}
Output:
Main started
Before new Child()
Parent loaded
Child loaded
Class loading phases:
- Loading: Đọc .class file vào memory
- Linking: Verify, prepare, resolve
- Initialization: Chạy static blocks và khởi tạo static fields
Singleton Pattern Basics
Singleton đảm bảo một class chỉ có đúng 1 instance duy nhất.
Hãy nghĩ về Tổng thống:
- Một quốc gia chỉ có 1 tổng thống tại một thời điểm
- Ai cũng truy cập cùng 1 người (cùng instance)
Singleton với Static
public class Database {
// 1. Static instance - chỉ có 1 instance duy nhất
private static Database instance;
// 2. Private constructor - ngăn new từ bên ngoài
private Database() {
System.out.println("Database connection created");
}
// 3. Public static method - truy cập instance
public static Database getInstance() {
if (instance == null) {
instance = new Database();
}
return instance;
}
public void query(String sql) {
System.out.println("Executing: " + sql);
}
}
// Sử dụng
public class SingletonDemo {
public static void main(String[] args) {
// Database db = new Database(); // ❌ Compile error - private constructor
Database db1 = Database.getInstance(); // Tạo instance lần đầu
Database db2 = Database.getInstance(); // Lấy instance đã tạo
System.out.println(db1 == db2); // true - cùng 1 instance
db1.query("SELECT * FROM users");
}
}
Output:
Database connection created
true
Executing: SELECT * FROM users
Eager vs Lazy Singleton
// Eager initialization - tạo ngay khi class load
class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
// Lazy initialization - tạo khi cần (có thể thread-unsafe)
class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
Phần này thảo luận về thread safety trong Singleton pattern. Nếu bạn mới bắt đầu, có thể bỏ qua và quay lại sau khi học Module Multithreading.
// Thread-safe lazy (Double-checked locking)
class ThreadSafeSingleton {
private static volatile ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
public static ThreadSafeSingleton getInstance() {
if (instance == null) {
synchronized (ThreadSafeSingleton.class) {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
}
Chi tiết về Singleton pattern sẽ được học trong Module 15: Design Patterns.
Static Context Restrictions
Static methods có những hạn chế quan trọng.
Tại sao static method không thể dùng this?
class Example {
private int instanceVar = 10;
private static int staticVar = 20;
// Instance method - có 'this'
public void instanceMethod() {
System.out.println(this.instanceVar); // ✅ OK - this tồn tại
System.out.println(instanceVar); // ✅ OK
System.out.println(staticVar); // ✅ OK
}
// Static method - KHÔNG có 'this'
public static void staticMethod() {
// System.out.println(this.instanceVar); // ❌ 'this' không tồn tại
// System.out.println(instanceVar); // ❌ Cần object để access
System.out.println(staticVar); // ✅ OK - static access static
// Muốn access instance member → cần object
Example obj = new Example();
System.out.println(obj.instanceVar); // ✅ OK - có object
}
}
Giải thích:
this= reference đến object hiện tại- Static method không thuộc object nào → không có
this - Static method chạy được mà không cần tạo object
Static không thể gọi instance members trực tiếp
class Calculator {
private int lastResult = 0; // Instance variable
public int add(int a, int b) { // Instance method
lastResult = a + b;
return lastResult;
}
public static int multiply(int a, int b) { // Static method
// return add(a, b); // ❌ COMPILE ERROR - static không gọi instance method
// lastResult = a * b; // ❌ COMPILE ERROR - static không access instance field
// ✅ Phải tạo object
Calculator calc = new Calculator();
return calc.add(a, b);
}
}
Quy tắc:
- ✅ Instance → instance: OK
- ✅ Instance → static: OK
- ✅ Static → static: OK
- ❌ Static → instance: KHÔNG (phải qua object)
class Parent {
public static void display() {
System.out.println("Parent static");
}
public void show() {
System.out.println("Parent instance");
}
}
class Child extends Parent {
// Static method hiding (NOT overriding!)
public static void display() {
System.out.println("Child static");
}
// Instance method overriding
@Override
public void show() {
System.out.println("Child instance");
}
}
public class HidingVsOverriding {
public static void main(String[] args) {
Parent p1 = new Parent();
Parent p2 = new Child(); // Upcast
Child c = new Child();
// Static method hiding - gọi theo TYPE của reference
p1.display(); // Parent static
p2.display(); // Parent static (!!!) - hiding, not polymorphism
c.display(); // Child static
// Instance method overriding - polymorphism
p1.show(); // Parent instance
p2.show(); // Child instance (✅ polymorphism works!)
c.show(); // Child instance
}
}
Kết luận OCP:
- Static methods KHÔNG thể override (chỉ hiding)
- Static method resolution dựa trên compile-time type (reference type)
- Instance method resolution dựa trên runtime type (actual object)
- @Override với static method → compile warning/error
Static import pitfalls
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import static java.lang.System.out;
public class StaticImportDemo {
public static void main(String[] args) {
out.println(PI); // 3.141592653589793
out.println(sqrt(16)); // 4.0
}
// ❌ Bẫy: Shadowing
private static final double PI = 3.14; // Che khuất Math.PI
public static void test() {
System.out.println(PI); // 3.14 (local PI, not Math.PI!)
}
}
- Chỉ dùng cho constants rõ ràng (Math.PI, Color.RED...)
- Tránh import * (ambiguity)
- Tránh shadowing
Khi nào dùng static vs instance?
Dùng static khi:
-
Utility methods không phụ thuộc vào object state
Math.max(), Arrays.sort(), Collections.shuffle() -
Constants chung cho tất cả
public static final double PI = 3.14159; -
Factory methods tạo objects
LocalDate.now(), Integer.valueOf(), Optional.of() -
Counters đếm số lượng objects
private static int instanceCount = 0; -
Main method - entry point
public static void main(String[] args)
Dùng instance khi:
-
Mỗi object có state riêng
class Student {
private String name; // Mỗi sinh viên có tên khác nhau
private int age;
} -
Behavior phụ thuộc vào object state
public void deposit(double amount) {
this.balance += amount; // Phụ thuộc vào balance của object
}
Ví dụ tổng hợp: Bank System
class BankAccount {
// Static members - chung cho tất cả accounts
private static int totalAccounts = 0;
private static double totalBalance = 0.0;
private static final double INTEREST_RATE = 0.05; // 5%
// Instance members - riêng cho mỗi account
private String accountNumber;
private String ownerName;
private double balance;
// Static block
static {
System.out.println("Bank System Initialized");
System.out.println("Interest Rate: " + (INTEREST_RATE * 100) + "%");
}
// Constructor
public BankAccount(String accountNumber, String ownerName, double initialBalance) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = initialBalance;
totalAccounts++;
totalBalance += initialBalance;
}
// Instance method
public void deposit(double amount) {
if (amount > 0) {
this.balance += amount;
totalBalance += amount; // Cập nhật static variable
System.out.println("Deposited: $" + amount);
}
}
// Instance method
public void withdraw(double amount) {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
totalBalance -= amount;
System.out.println("Withdrawn: $" + amount);
} else {
System.out.println("Insufficient funds!");
}
}
// Instance method
public void applyInterest() {
double interest = this.balance * INTEREST_RATE;
this.balance += interest;
totalBalance += interest;
System.out.println("Interest applied: $" + interest);
}
// Static method - không cần object
public static int getTotalAccounts() {
return totalAccounts;
}
public static double getTotalBalance() {
return totalBalance;
}
public static void displayBankStats() {
System.out.println("=== Bank Statistics ===");
System.out.println("Total Accounts: " + totalAccounts);
System.out.println("Total Balance: $" + totalBalance);
System.out.println("Interest Rate: " + (INTEREST_RATE * 100) + "%");
}
// Instance method
public void displayAccountInfo() {
System.out.println("=== Account Info ===");
System.out.println("Account: " + this.accountNumber);
System.out.println("Owner: " + this.ownerName);
System.out.println("Balance: $" + this.balance);
}
}
public class BankDemo {
public static void main(String[] args) {
// Static method - không cần object
BankAccount.displayBankStats();
System.out.println("\n" + "=".repeat(40) + "\n");
// Tạo accounts
BankAccount acc1 = new BankAccount("ACC001", "Nguyễn Văn An", 1000);
BankAccount acc2 = new BankAccount("ACC002", "Trần Thị Bình", 2000);
BankAccount acc3 = new BankAccount("ACC003", "Lê Văn Cường", 1500);
// Instance methods
acc1.deposit(500);
acc2.withdraw(300);
acc1.applyInterest();
System.out.println("\n" + "=".repeat(40) + "\n");
// Display account info
acc1.displayAccountInfo();
System.out.println("\n" + "=".repeat(40) + "\n");
// Static method
BankAccount.displayBankStats();
}
}
static import
static import cho phép truy cập static members mà không cần tên class.
Cú pháp
import static package.ClassName.staticMember;
import static package.ClassName.*; // Import all static members
Ví dụ
// Không dùng static import
import java.lang.Math;
public class Demo {
public static void main(String[] args) {
double result = Math.sqrt(16) + Math.pow(2, 3);
System.out.println(Math.PI);
}
}
// Dùng static import
import static java.lang.Math.*;
public class Demo {
public static void main(String[] args) {
double result = sqrt(16) + pow(2, 3); // Không cần Math.
System.out.println(PI); // Không cần Math.
}
}
Đừng lạm dụng static import! Có thể gây nhầm lẫn khi không rõ method/variable đến từ đâu.
import static java.lang.Math.*;
import static com.myapp.MathUtils.*;
// max() đến từ Math hay MathUtils? 🤔
int result = max(5, 10);
Bài tập thực hành
Bài 1: Counter Class
Tạo class Counter với:
- Static field:
totalCount(đếm tổng số lần tất cả counters đếm) - Instance field:
count(đếm riêng cho mỗi object) - Methods:
void increment(): Tăng cảcountvàtotalCountvoid reset(): Resetcountvề 0static void resetAll(): ResettotalCountvề 0- Getters cho cả hai
Bài 2: ID Generator
Tạo class Product tự động generate ID:
- Static field:
nextId(bắt đầu từ 1) - Instance field:
id,name,price - Constructor tự động gán
id = nextId++ - Static method
getNextId()trả về ID sẽ được gán tiếp theo
Bài 3: MathUtils Utility Class
Tạo utility class MathUtils với:
- Private constructor (ngăn tạo object)
- Static methods:
int factorial(int n)boolean isPrime(int n)int gcd(int a, int b)(greatest common divisor)double average(int... numbers)(varargs)
Bài 4: Method Chaining
Tạo class Person hỗ trợ method chaining:
Person person = new Person()
.setName("An")
.setAge(20)
.setEmail("[email protected]")
.build();
Tổng kết
Key Takeaways
this Keyword:
- Reference đến current object
- Phân biệt instance variable vs parameter
- Constructor chaining:
this() - Method chaining:
return this; - Truyền current object:
method(this)
static Keyword:
- Static field: Chung cho tất cả objects
- Static method: Thuộc class, không cần object
- Static block: Chạy khi class load
- static final: Constants
- Không dùng
thistrong static context
Khi nào dùng static:
- Utility methods
- Constants
- Counters
- Factory methods
Khi nào dùng instance:
- Mỗi object có state riêng
- Behavior phụ thuộc object state
Module hoàn thành
Chúc mừng! Bạn đã hoàn thành Module 2: OOP Cơ bản. Bạn đã học:
- ✅ Giới thiệu OOP và 4 tính chất
- ✅ Class và Object
- ✅ Constructor và Methods
- ✅ Encapsulation và Access Modifiers
- ✅ this và static Keywords
Module tiếp theo
Tiếp tục hành trình với Module 3: OOP Nâng cao để tìm hiểu về Inheritance, Polymorphism, Abstract Classes, và Interfaces.
Đọc thêm
"Static methods are like functions in procedural programming - they don't need an object to work. Instance methods are the heart of object-oriented programming - they operate on object state." — Effective Java