Từ Khóa: static (Static Keywords)

"Trong Java, từ khóa static là một công cụ quan trọng, cho phép bạn định nghĩa các thành phần có thể truy cập mà không cần tạo đối tượng. Điều này giúp tối ưu hóa bộ nhớ và làm cho chương trình trở nên hiệu quả hơn. Dưới đây là những điều cơ bản bạn cần biết về từ khóa static trong Java."

Từ khóa static trong Java được sử dụng để quản lý bộ nhớ một cách hiệu quả. Khi một thành phần được khai báo là static thì nó thuộc về lớp chứ không phải thuộc về một đối tượng cụ thể nào của lớp đó. Từ khóa static có thể được áp dụng cho:

↳ Biến static.

↳ Phương thức static.

↳ Khối lệnh static.

Các ngữ cảnh của từ khóa static
Ảnh mô tả các ngữ cảnh của từ khóa static.

Từ khóa static là một công cụ mạnh mẽ trong Java, nhưng cần được sử dụng một cách hợp lý. Hiểu rõ các đặc điểm và ứng dụng của static sẽ giúp bạn viết code Java hiệu quả hơn.

Ⅰ. Biến static

Khi bạn khai báo một biến là static, thì biến đó được gọi là biến tĩnh, hay biến static.

Đặc điểm của biến static:

Thuộc về lớp, chứ không thuộc về đối tượng nào.

Có một bản sao duy nhất cho toàn bộ lớp, bất kể có bao nhiêu đối tượng được tạo ra.

Được khởi tạo khi lớp được tải vào bộ nhớ.

Cách sử dụng biến static:

Để lưu trữ các thông tin chung cho tất cả các đối tượng của lớp, ví dụ như số lượng đối tượng đã được tạo, các hằng số chung, tên công ty, tên trường học...

Truy cập trực tiếp thông qua tên lớp mà không cần tạo đối tượng.

Ví dụ 1:

Hãy tưởng tượng một công ty tên là ABC Corp có nhiều nhân viên. Tất cả nhân viên đều làm việc cho cùng một công ty, vì vậy tên công ty của họ giống nhau. Trong trường hợp này, tên công ty có thể là biến static trong lớp Employee. Dưới đây là một ví dụ cụ thể để bạn hiểu rõ hơn:

Không sử dụng biến static

Ví dụ: Employee.java

public class Employee {
    String companyName = "ABC Corp";
    int id;
    String name;

    public Employee(int id, String name) {
        this.id = id;
  this.name = name;
    }

    public void display() {
        System.out.println(id +". "+ name + " Làm việc tại " + companyName);
    }

    public static void main(String[] args) {
        Employee employee1 = new Employee("Vương");
        Employee employee2 = new Employee("Trường");

        employee1.display();
        employee2.display();
    }
}

Kết quả của chương trình là:

1. Vương Làm việc tại ABC Corp
2. Trường Làm việc tại ABC Corp

Trong ví dụ này, mỗi đối tượng Employee có một bản sao riêng của biến companyName. Điều này không hiệu quả về bộ nhớ vì companyName là thuộc tính chung của tất cả các đối tượng.

Sử dụng biến static

Ví dụ: Employee.java

public class Employee {
    static String companyName = "ABC Corp";
    int id;
    String name;

    public Employee(int id, String name) {
  this.id = id;
        this.name = name;
    }

    public void display() {
        System.out.println(id +". "+ name + " Làm việc tại " + companyName);
    }

    public static void main(String[] args) {
        Employee employee1 = new Employee("Vương");
        Employee employee2 = new Employee("Trường");

        employee1.display();
        employee2.display();
    }
}

Kết quả của chương trình là:

1. Vương Làm việc tại ABC Corp
2. Trường Làm việc tại ABC Corp

Trong ví dụ này, biến companyName được khai báo là static, do đó chỉ có một bản sao duy nhất của biến này được chia sẻ giữa tất cả các đối tượng Employee, giúp tiết kiệm bộ nhớ.

Thông tin chung của biến static
Ảnh mô tả thông tin chung của biến static.

Ví dụ 2:

Trong ví dụ này, biến static được dùng để đếm số lượng đối tượng được tạo từ một lớp, so sánh với cách không sử dụng biến static.

Không sử dụng biến static

Trong ví dụ này, mỗi đối tượng sẽ có biến count riêng của nó, do đó, biến đếm không hoạt động chính xác.

Ví dụ: ECounter.java

public class Counter {
    int count = 0; // Biến đếm không static

    public Counter() {
        count++;
        System.out.println("Biến đếm số lượng không dùng static: " + count);
    }

    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        Counter c3 = new Counter();
    }
}

Kết quả của chương trình là:

Biến đếm số lượng không dùng static: 1
Biến đếm số lượng không dùng static: 1
Biến đếm số lượng không dùng static: 1

Trong ví dụ này, mỗi đối tượng Counter có biến count riêng, nên giá trị của count không được chia sẻ giữa các đối tượng.

Sử dụng biến static

Trong ví dụ này, biến count được khai báo là static, do đó, nó được chia sẻ giữa tất cả các đối tượng và đếm chính xác số lượng đối tượng được tạo ra.

Ví dụ: Counter.java

public class Counter {
    static int count = 0; // Biến đếm dùng static

    public Counter() {
        count++;
        System.out.println("Biến đếm số lượng dùng static: " + count);
    }

    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        Counter c3 = new Counter();
    }
}

Kết quả của chương trình là:

Biến đếm số lượng dùng static: 1
Biến đếm số lượng dùng static: 2
Biến đếm số lượng dùng static: 3

Trong ví dụ này, biến count được chia sẻ giữa tất cả các đối tượng Counter, do đó, nó đếm chính xác số lượng đối tượng được tạo ra.

Bảng so sánh chi tiết giữa biến static và biến thể hiện (instance variable) trong Java.

Đặc điểmBiến static (Biến lớp)Biến thể hiện (Biến instance)
Thuộc vềLớpĐối tượng (instance) của lớp
Bộ nhớVùng nhớ chung của lớpVùng nhớ riêng của mỗi đối tượng
Số lượng bản saoChỉ có một bản sao cho toàn bộ lớpMỗi đối tượng có một bản sao riêng biệt
Cách truy cậpTruy cập trực tiếp qua tên lớpTruy cập qua đối tượng của lớp
Vòng đờiĐược tạo khi lớp được load và tồn tại cho đến khi chương trình kết thúcĐược tạo khi đối tượng được khởi tạo và bị hủy khi đối tượng bị thu hồi bộ nhớ
Sử dụngĐể lưu trữ thông tin chung cho tất cả các đối tượng của lớp, các hằng số, các phương thức tiện íchĐể lưu trữ dữ liệu riêng của từng đối tượng
Ví dụSố lượng đối tượng đã được tạo, giá trị Pi, tên chung…Tên của một người, tuổi, địa chỉ

Ⅱ. Phương thức static.

Nếu bạn áp dụng từ khóa static với bất cứ phương thức nào, thì phương thức đó được gọi là phương thức static.

Đặc điểm của phương thức static:

Phương thức static thuộc về lớp và có thể được gọi mà không cần tạo đối tượng của lớp đó.

Phương thức static thường được sử dụng trong các lớp tiện ích hoặc để thực hiện các tác vụ không liên quan đến các biến thể hiện (biến instance).

Phương thức static có thể truy cập các biến static khác và gọi các phương thức static khác trong cùng một lớp.

Cách sử dụng phương thức static:

Để thực hiện các tác vụ liên quan đến lớp chứ không phải đối tượng cụ thể, ví dụ như các phương thức tiện ích (utility methods).

Phương thức main luôn là một phương thức static.

Ví dụ 1: Phương thức static trong một lớp tiện ích.

Phương thức static thường được sử dụng trong các lớp tiện ích (utility classes) nơi mà các phương thức không cần đến trạng thái của đối tượng.

Ví dụ: Math.java

public class Math {
    // Phương thức static để tính tổng hai số
    public static int add(int a, int b) {
        return a + b;
    }

    // Phương thức static để tính hiệu hai số
    public static int subtract(int a, int b) {
        return a - b;
    }

    // Phương thức static để tính tích hai số
    public static int multiply(int a, int b) {
        return a * b;
    }

    public static void main(String[] args) {
        // Gọi các phương thức static mà không cần tạo đối tượng
        int tong = Math.add(10, 5);
        int hieu = Math.subtract(10, 5);
        int nhan = Math.multiply(10, 5);

        // In kết quả ra màn hình
        System.out.println("Tổng hai số: " + tong);
        System.out.println("Hiệu hai số: " + hieu);
        System.out.println("Nhân hai số: " + nhan);
    }
}

Kết quả của chương trình là:

Tổng hai số: 15
Hiệu hai số: 5
Nhân hai số: 50

Trong ví dụ này, các phương thức add, subtract và multiply là các phương thức static. Chúng có thể được gọi trực tiếp bằng tên lớp Math mà không cần tạo đối tượng của lớp này.

Ví dụ 2: Phương thức static trong một lớp thông thường.

Phương thức static cũng có thể được sử dụng trong một lớp thông thường để thực hiện các tác vụ không liên quan đến các biến thể hiện (instance variables):

Ví dụ: Counter.java

public class Counter {
    private static int count = 0;

    // Phương thức static để tăng giá trị của biến count
    public static void increment() {
        count++;
    }

    // Phương thức static để lấy giá trị hiện tại của biến count
    public static int getCount() {
        return count;
    }

    public static void main(String[] args) {
        // Gọi các phương thức static mà không cần tạo đối tượng
        Counter.increment();
        Counter.increment();
        Counter.increment();

        // In giá trị của biến count ra màn hình
        System.out.println("Count: " + Counter.getCount());
    }
}

Kết quả của chương trình là:

Tổng hai số: 15
Hiệu hai số: 5
Nhân hai số: 50

Trong ví dụ này, phương thức increment và getCount là các phương thức static. Chúng có thể được gọi trực tiếp bằng tên lớp Counter mà không cần tạo đối tượng của lớp này. Biến count là biến static và được chia sẻ giữa tất cả các đối tượng của lớp Counter.

Các hạn chế chính của phương thức static

1. Không thể truy cập trực tiếp biến instance:

Phương thức static không thể truy cập trực tiếp các biến instance (biến không phải static) của lớp. Điều này là do phương thức static không liên kết với bất kỳ đối tượng cụ thể nào, trong khi biến instance lại thuộc về một đối tượng nhất định.

2. Không thể gọi trực tiếp phương thức instance:

Tương tự như biến instance, phương thức static cũng không thể gọi trực tiếp các phương thức instance. Lý do là phương thức static không có ngữ cảnh của một đối tượng.

3. Không thể sử dụng từ khóa this và super:

Từ khóa this đại diện cho đối tượng hiện tại, còn super dùng để gọi đến phương thức của lớp cha. Do phương thức static không liên quan đến bất kỳ đối tượng cụ thể nào, nên không thể sử dụng hai từ khóa này trong phương thức static.

4. Không thể ghi đè (override):

Phương thức static không thể bị ghi đè bởi các lớp con. Điều này là do phương thức static thuộc về lớp chứ không thuộc về đối tượng, và việc ghi đè chỉ áp dụng cho các phương thức instance.

Ví dụ minh họa các hạn chế của phương thức static:

Ví dụ: Animal.java

class Animal {
    public static void sound() {
        System.out.println("Âm thanh của động vật");
    }
}

class Dog extends Animal {
    @Override
    public static void sound() {
        System.out.println("Tiếng chó sủa");
    }
}

public class Main {
    public static void main(String[] args) {   
        
        // Gọi phương thức static bằng tên lớp
        Dog.sound();
    }
}

Kết quả của chương trình là:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: The method sound() of type Dog must override or implement a supertype method

So sánh chi tiết giữa phương thức static và phương thức không static (instance method) trong Java:

Đặc điểmPhương thức staticPhương thức không static (instance method)
Thuộc vềLớpĐối tượng (instance) của lớp
Cách gọiGọi trực tiếp qua tên lớpGọi thông qua đối tượng của lớp
Truy cậpCó thể truy cập biến static và phương thức static khácCó thể truy cập cả biến static, biến instance và phương thức static, phương thức instance
Từ khóa thissuperKhông thể sử dụngCó thể sử dụng
Ghi đèKhông thể ghi đèCó thể ghi đè
Sử dụngCác phương thức tiện ích, hằng số, điểm vào của chương trình (main)Các hành động liên quan đến trạng thái của đối tượng

Ⅲ. Khối lệnh static.

Khối lệnh static được sử dụng để khởi tạo dữ liệu tĩnh. Nó được thực thi trước phương thức main tại lúc tải lớp.

Ví dụ: Example.java

public class Example {
    public static int number;

    static {
        number = 42;
  System.out.println("Xin chào, Java");
        System.out.println("Khối lệnh Static được thực thi");
    }

    public static void main(String[] args) {
	  System.out.println("\nXin chào, Main");
        System.out.println("Giá trị của number: " + Example.number);
    }
}

Kết quả của chương trình là:

Xin chào, Khối Static
Khối lệnh Static được thực thi

Xin chào, Main
Giá trị của number: 42

Câu hỏi: Tại sao phương thức main trong Java lại là static?

Câu trả lời ngắn gọn: Phương thức main trong Java được khai báo là static để JVM (Java Virtual Machine) có thể gọi trực tiếp nó mà không cần phải tạo ra một đối tượng nào của lớp chứa phương thức main.

Giải thích chi tiết:

↳ JVM và điểm khởi đầu: Khi bạn chạy một chương trình Java, JVM sẽ tìm đến phương thức main() để bắt đầu thực thi.

↳ Không cần tạo đối tượng: Để đơn giản hóa quá trình khởi động chương trình, JVM không cần phải tạo ra một đối tượng của lớp chứa phương thức main. Việc khai báo main là static cho phép JVM gọi trực tiếp phương thức này mà không cần qua các bước tạo đối tượng phức tạp.

↳ Tính độc lập: Phương thức static thuộc về lớp chứ không thuộc về bất kỳ đối tượng nào của lớp đó. Điều này có nghĩa là phương thức main có thể được gọi ngay cả khi chưa có bất kỳ đối tượng nào của lớp được tạo ra.

↳ Tính tiện lợi: Việc khai báo main là static giúp cho việc viết và chạy các chương trình Java trở nên đơn giản và trực quan hơn.

Ví dụ: Example.java

public class Example {
    public static void main(String[] args) {
        System.out.println("Xin chào, thế giới!");
    }
}

Trong ví dụ trên, khi bạn chạy chương trình này, JVM sẽ tìm đến lớp Example và gọi trực tiếp phương thức main() mà không cần tạo ra một đối tượng nào của lớp Example.

Các câu hỏi liên quan bạn có thể quan tâm:

Câu hỏi: Tại sao phương thức main phải là public?

Trả lời: Để JVM có thể truy cập được phương thức main từ bất kỳ lớp nào.

Câu hỏi: Tại sao phương thức main phải là void?

Trả lời: Vì phương thức main không cần trả về bất kỳ giá trị nào.

Câu hỏi: Có thể có nhiều phương thức main trong một lớp không?

Trả lời: Không, một lớp chỉ có thể có một phương thức main.

Câu hỏi: Phương thức main có thể được override không?

Trả lời: Không, phương thức static không thể bị override.

So sánh khối static và phương thức main trong Java nhé.

Đặc điểmKhối staticPhương thức main
Mục đíchKhởi tạo biến static, công việc chuẩn bịĐiểm bắt đầu của chương trình
Thời điểm thực thiKhi lớp được loadKhi chương trình được chạy
Tham sốKhông cóMảng String[] (đối số dòng lệnh)
Giá trị trả vềKhôngvoid (không có)
Có thể gọi trực tiếpKhôngCó (JVM gọi)

Câu Nói Truyền Cảm Hứng

“Không ai sinh ra đã giỏi. Mọi thành công đều bắt đầu từ một bước nhỏ.” – Lao Tzu

Không Gian Tích Cực

“Chúc bạn một ngày mới đầy năng lượng và sự sáng tạo, luôn tiến về phía trước.”