So sánh giữa public static function and function năm 2024

Lưu ý: Chỉ có một param được đánh dấu là

fun doSomething(matter: String): String { }

30. Nếu param không phải là param cuối cùng, khi gọi hàm, ta phải chỉ định rõ các param sau đó

Như ở phần cuối của bài static và final trong PHP mình có nói là giữa static và self nó có khác nhau ở trong một số trường hợp. Và để chứng minh cho điều đó thì bài hôm nay mình sẽ giới thiệu với mọi người những sự khác nhau đó.

1, Tổng quan.

Nhìn chung thì cả selfstatic đều dùng để gọi các thành phần tĩnh trong đối tượng, nhưng nếu chỉ đơn thuần như trong nội bộ class thì cả 2 keywords này đều cho ra kết quả tốt.

VD:

<?php class ConNguoi {

private static $name = 'ConNguoi';
public static function getName()  
{  
    echo self::$name;  
    echo '<br>';  
    echo static::$name;  
}  
} ConNguoi::getName();

Kết Quả:

ConNguoi ConNguoi

Vậy liệu nó có cho ra các giá trị khác nhau khi chúng ta sử dụng tính kế thừa trong class? Để biết rõ hơn thì chúng ta sẽ tạ ra các ví dụ để so sánh nó.

2, So Sánh và kết luận.

Vẫn là class ConNguoi như trên nhưng chúng ta sẽ khai báo thêm một class

ConNguoi ConNguoi

0 kế thừa class ConNguoi và override lại thuộc tính

ConNguoi ConNguoi

2 như sau:

<?php class ConNguoi {

private static $name = 'ConNguoi';
public static function getName()  
{  
    echo self::$name;  
    echo '<br>';  
    echo static::$name;  
}  
} class NguoiLon extends ConNguoi {
private static $name = 'NguoiLon';  
} NguoiLon::getName();

Sau khi chạy dòng trên thì mình thu được kết quả như sau:

ConNguoi Fatal error: Cannot access private property NguoiLon::$name

-Như các bạn đã thấy: Đối với self thì kết quả chạy như bình thường, còn với static thì sao nó lại báo là không thể truy cập vào thuộc tính

ConNguoi ConNguoi

5 mà lại là

ConNguoi ConNguoi

6, phải chăng thằng static này đại diện cho đối tượng hiện tại nên không thể truy xuất được đến thuộc tính

ConNguoi ConNguoi

2. Thôi được rồi, để chắc ăn hơn thì mình thử đổi visibility của biến

ConNguoi ConNguoi

2 thành

ConNguoi ConNguoi

9 xem sao?

<?php class ConNguoi {

protected static $name = 'ConNguoi';
public function getName()  
{  
    echo self::$name;  
    echo '<br>';  
    echo static::$name;  
}  
} class NguoiLon extends ConNguoi {
protected static $name = 'NguoiLon';  
} // NguoiLon::getName(); $a = new NguoiLon(); $a->getName();

Và đây là kết quả mình nhận được:

ConNguoi NguoiLon

Giờ đã chạy được ngon lành, nhưng khi nó lại cho ra 2 kết quả khác nhau. Từ 2 ví dụ trên chúng ta tạm đưa ra kết luận là static nó có nguyên tắc gần như $this, là đều truy xuất đến đối tượng hiện tại.

Để ok hơn nữa thì mình sẽ tiếp tục tạo ra ví dụ sau và chạy nó:

<?php class ConNguoi {

protected static $name = 'ConNguoi';
public static function getSelf()  
{  
    return new self;  
    // or  
    // return new static();  
}
public static function getStatic()  
{  
    return new static;  
    // or  
    // return new static();  
}  
} class NguoiLon extends ConNguoi { } echo get_class(NguoiLon::getSelf()); //ConNguoi echo get_class(NguoiLon::getStatic()); //NguoiLon

Ok, nó đã chạy được và cho ra kết quả giống với kết luận ở trên. Lúc này thì chẳng cần phải ngại ngần gì nữa mà không dám kết luận khẳng định.

Kết Luận

  • Self: Truy xuất đến class khai báo nó.
  • Static: Truy xuất đến đối tượng hiện tại.

3, Lời kết.

-Như vậy mình đã giới thiệu xong đến mọi người về sự khác nhau của self và static trong PHP, ngoài ra các bạn có thể xem thêm bài so sánh giữa this và self trong PHP của mình tại đây.

Vậy static là gì? Sử dụng chúng trong trường hợp nào? Bài hôm nay mình sẽ giới thiệu với mọi người xung quanh từ khóa này.

Biến của lớp và phương thức của lớp

Thông thường, mỗi một phương thức hay một thuộc tính nào đó đều gắn chặt với một đối tượng cụ thể. Muốn truy cập tới các biến hay phương thức của đối tượng, ta thường đều phải gọi cho các đối tượng cụ thể.

Tuy nhiên, trong một số trường hợp, ta muốn có dữ liệu nào đó của lớp được chia sẻ giữa tất cả các đối tượng thuộc một lớp, các phương thức của lớp hoạt động độc lập với các đối tượng của lớp đó, thì giải pháp là các biến lớp và phương thức lớp.

1 - Biến của lớp(biến static)

Đôi khi, ta muốn một lớp có những biến dùng chung cho tất cả các đối tượng thuộc lớp đó. Ta gọi các biến dùng chung này là biến của lớp (class variable), hay gọi tắt là biến lớp. Chúng không gắn với bất cứ một đối tượng nào mà chỉ gắn với lớp đối tượng. Chúng được dùng chung cho tất cả các đối tượng trong lớp đó.

Để phân biệt giữa biến thực thể và biến lớp khi khai báo trong định nghĩa lớp, ta dùng từ khóa static cho các biến lớp. Vì từ khóa đó nên biến lớp thường được gọi là biến static.

Lấy ví dụ sau, bên cạnh biến thực thể name, lớp Cow còn có một biến lớp numOfCows với mục đích ghi lại số lượng các đối tượng Cow đã được tạo.

Mỗi đối tượng Cow có một biến name của riêng nó, nhưng numOfCows thì chỉ có đúng một bản dùng chung cho tất cả các đối tượng Cow.

numOfCows được khởi tạo bằng 0, mỗi lần một đối tượng Cow được tạo, biến này được tăng thêm 1 (tại hàm khởi tạo dành cho đối tượng đó) để ghi nhận rằng vừa có thêm một thực thể mới của lớp Cow.

So sánh giữa public static function and function năm 2024

Từ bên ngoài lớp, ta có thể dùng tên lớp để truy nhập biến static. Chẳng hạn, dùng

private static int numOfCows = 0;

6 để truy nhập numOfCows:

So sánh giữa public static function and function năm 2024

2 - Phương thức của lớp(hàm static)

Lại xét ví dụ trong phần 1, giả sử ta muốn numOfCows là biến

private static int numOfCows = 0;

9 để không cho phép ai đó sửa từ bên ngoài lớp Cow.

Nhưng ta vẫn muốn cho phép đọc giá trị của biến này từ bên ngoài, nên ta sẽ bổ sung một phương thức, chẳng hạn

static {
 numOfCows = 0;
}

1, để trả về giá trị của biến đó.

public int getCount() {
 return numOfCows;
}

Như các phương thức mà ta đã quen dùng, để gọi

static {
 numOfCows = 0;
}

1, người ta sẽ cần đến một tham chiếu kiểu Cow và kích hoạt phương thức đó cho một đối tượng Cow.

Tuy nhiên, sẽ có những vấn đề xảy ra như sau:

  • Cần đến một con bò để biết được có tất cả bao nhiêu con bò? Nghe có vẻ không được tự nhiên lắm.
  • static { numOfCows = 0; }

    1 không dùng đến một đặc điểm hay dữ liệu đặc thù nào của mỗi đối tượng Cow
  • Khi còn chưa có một đối tượng Cow nào được tạo thì không thể gọi được

    static { numOfCows = 0; }

    1

Phương thức

static {
 numOfCows = 0;
}

1không nên bị phụ thuộc vào các đối tượng Cow cụ thể như vậy.

Để giải quyết vấn đề này, ta có thể cho

static {
 numOfCows = 0;
}

1 làm một phương thức của lớp (class method), thường gọi tắt là phương thức lớp – hay phương thức static - để nó có thể tồn tại độc lập với các đối tượng và có thể được gọi thẳng từ lớp mà không cần đến một tham chiếu đối tượng nào.

Ta dùng từ khóa static khi khai báo phương thức lớp:

So sánh giữa public static function and function năm 2024

Đặc điểm độc lập đối với các đối tượng của phương thức static chính là lí do ta đã luôn luôn phải khai báo phương thức static`3 với từ khóa `static.

`static`3 được kích hoạt để khởi động chương trình - khi chưa có bất cứ đối tượng nào được tạo – nên nó phải được phép chạy mà không gắn với bất cứ đối tượng nào.

3 - Giới hạn của phương thức lớp

Đặc điểm về tính độc lập đó vừa là ưu điểm vừa là giới hạn cho hoạt động của các phương thức lớp.

Không được gắn với một đối tượng nào, nên các phương thức static của một lớp chạy mà không biết một chút gì về bất cứ đối tượng cụ thể nào của lớp đó.

Như đã thấy trong ví dụ phần 2,

static {
 numOfCows = 0;
}

1 chạy ngay cả khi không tồn tại bất cứ đối tượng Cow nào.

Kể cả khi gọi

static {
 numOfCows = 0;
}

1 từ một đối tượng cụ thể thì

static {
 numOfCows = 0;
}

1 cũng vẫn không biết gì về đối tượng Cow của đối tượng đó.

Vì khi đó, trình biên dịch chỉ dùng kiểu khai báo Cow để xác định nên chạy

static {
 numOfCows = 0;
}

1 của lớp nào, nó không quan tâm tới đối tượng nào.

Nếu một biến thực thể được dùng đến trong một phương thức lớp, trình biên dịch sẽ không hiểu ta đang nói đến biến thực thể của đối tượng nào, bất kể trong `name`3 đang có 10 hay chỉ có duy nhất một đối tượng thuộc lớp đó. Tương tự khi gọi các phương thức của thực thể trong các phương thức static

So sánh giữa public static function and function năm 2024
So sánh giữa public static function and function năm 2024

4 - Khởi tạo biến của lớp

Các biến static được khởi tạo khi lớp được nạp vào bộ nhớ. Một lớp được nạp khi máy ảo Java quyết định đến lúc cần nạp, chẳng hạn như khi ai đó định tạo thực thể đầu tiên của lớp đó, hoặc dùng biến static hoặc phương thức static của lớp đó.

Có hai đảm bảo về việc khởi tạo các biến static:

  • Các biến static trong một lớp được khởi tạo trước khi bất cứ đối tượng nào của lớp đó có thể được tạo
  • Các biến static trong một lớp được khởi tạo trước khi bất cứ phương thức static nào của lớp đó có thể chạy

Ta có hai cách để khởi tạo biến static. Thứ nhất, khởi tạo ngay tại dòng khai báo biến

private static int numOfCows = 0;

Cách thứ hai: Java cung cấp một cú pháp đặc biệt là khối khởi tạo static (static initialization block) – một khối mã được bọc trong cặp ngoặc { } và có tiêu đề là từ khóa static.

static {
 numOfCows = 0;
}

Một lớp có thể có vài khối khởi tạo static đặt ở bất cứ đâu trong định nghĩa lớp. Chúng được đảm bảo sẽ được kích hoạt theo đúng thứ tự xuất hiện trong mã.

Và quan trọng bậc nhất là chúng được đảm bảo sẽ chạy trước khi bất gì biến thành viên nào được truy nhập hay phương thức static nào được chạy.