[Tự học lập trình Java] Hiểu thêm về các lớp.

 



Thêm về các lớp (More on Classes)

Giới thiệu.

Trong phần này sẽ quan tâm đến các khía cạnh của lớp (class) phụ thuộc vào việc sử dụng đối tượng tham chiếu và toán tử dấu chấm (.) mà bạn đã được học trong phần trước trên các đối tượng:

·        Các giá trị trả về từ các phương thức.

·        Từ khóa this.

·        Lớp và các thể hiện của các thành viên.

·        Điều khiển truy cập.

Trả về một giá trị từ một phương thức.

Một phương thức trả về nơi mã nguồn gọi nó khi:

·        Hoàn thành tất cả các câu lệnh trong phương thức.

·        Chạy đến câu lệnh return hoặc

·        ném một ngoại lệ (sẽ đề cập sau).

Sẽ xem xét cái nào xảy ra trước.

Bạn định nghĩa một kiểu trả về của phương thức trong khai báo phương thức. Trong thân phương thức, sử dụng câu lệnh return để trả về giá trị.

Bất kỳ khai báo phương thức void đều không trả về một giá trị. Nó không cần có trong nội dung câu lệnh return, nhưng cũng có thể có. Trong trường hợp như vậy, một câu lệnh return có thể được sử dụng để chi nhánh ra một khối điểu khiển luồng và thoát khỏi phương thức và đơn giản là sử dụng như này:

return;

Nếu bạn cố gắng trả về một giá trị từ một phương thức đã được khai báo void, bạn sẽ gặp lỗi của trình biên dịch.

Bất kỳ phương thức nào không khai báo void phải có trong nội dung phương thức một câu lệnh return trả về giá trị tương ứng. Như thế này:

return returnValue;

Kiểu dữ liệu trả về phải khớp với kiểu dữ liệu trả về ở khai báo phương thức, bạn không thể trả về một giá trị kiểu số nguyên int từ một phương thức được khai báo là trả về một boolean.

Phương thức getArea() trong lớp Rectangle đã được bàn trong phần đối tượng trả về một số nguyên:

    // a method for computing the area of the rectangle
    public int getArea() {
        return width * height;
    }

Phương thức getArea trả về một kiểu nguyên thủy. Phương thức có thể trả về một kiểu tham chiếu. Ví dụ, trong chương trình đối tượng thao tác Bicycle, chúng ta có thể thấy phương thức này:Phương thức này trả về một số nguyên là tính toán của biểu thức width*height trả về.

public Bicycle seeWhosFastest(Bicycle myBike, Bicycle yourBike,
                              Environment env) {
    Bicycle fastest;
    // code to calculate which bike is 
    // faster, given each bike's gear 
    // and cadence and given the 
    // environment (terrain and wind)
    return fastest;
}

Trả về một lớp (class) hoặc giao diện lập trình (interface).

Nếu phần này làm bạn bối rối, hãy bỏ qua nó và trở lại sau khi bạn đã học xong phần giao diện và kế thừa.

Khi một phương thức sử dụng một tên lớp là kiểu dữ liệu trả về của nó, như phươn thức seeWhoFastest ở trên, lớp của kiểu dữ liệu được trả về đối tượng phải là lớp con hoặc chính xác lớp là kiểu dữ liệu trả về. Giả sử bạn có một lớp kế thừa là lớp ImaginaryNumber là lớp con của lớp java.lang.Number, đó là lần lượt một lớp con của lớp Object, như minh họa trong Hình

Lớp ImaginaryNumber kế thừa lớp Object - tinhocoban.net
Lớp kế thừa của ImaginaryNumber, bây giờ giả sử bạn có khai báo phương thức trả về một Number:

public Number returnANumber() {
    ...
}

Phương thức returnAnumber có thể trả về một ImaginaryNumber nhưng nó không là một Object. ImaginaryNumber là một Number, bởi vì  nó là một lớp con của Number. Tuy nhiên một Object thì không nhất thiết là một Number, nó có thể là một String hay một kiểu khác.

Bạn có thể đè một phương thức và định nghĩa nó trả về một lớp con của lớp ban đầu như sau:

public ImaginaryNumber returnANumber() {
    ...
}

Chú ý: Bạn có thể sử dụng tên interface như kiểu trả về. Trong trường hợp này, đối tượng trả về phải thuộc lớp thực thi interface đó.Kỹ thuật này, được gọi là kiểu trả về phương sai (covariant return type), nghĩa là nó trả về kiểu cho phép thay đổi trực tiếp như lớp con.

Sử dụng từ khóa this.

Trong một phương thức thể hiện hoặc một phương thức khởi tạo, this là một tham chiếu đến đối tượng hiện tại – đối tượng mà phương thức hoặc hàm khởi tạo đang được gọi. Bạn có thể tham chiếu bất kỳ thành viên nào của đối tượng hiện tại trong chính phương thức hoặc hàm tạo đó bằng cách sử dụng từ khóa this.

Sử dụng this với một thuộc tính.

Lý do phổ biến để dùng từ khóa this là bởi vì một thuộc tính bị che bởi phương thức hoặc tham số hàm khởi tạo.

Ví dụ lớp Point đã được viết như sau:

public class Point {
    public int x = 0;
    public int y = 0;
        
    //constructor
    public Point(int a, int b) {
        x = a;
        y = b;
    }
}

Nhưng có thể được viết như sau:

public class Point {
    public int x = 0;
    public int y = 0;
        
    //constructor
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Mỗi đối số của hàm khởi tạo ánh xạ một thuộc tính của đối tượng – bên trong hàm khởi tạo, x là giá trị sao chép địa phương của đối số đầu của hàm khởi tạo. Để tham chiếu đến thuộc tính x của lớp Point, hàm khởi tạo phải sử dụng this.x.

Sử dụng this với một hàm khởi tạo

Từ bên trong hàm khởi tạo, bạn có thể sử dụng từ khóa this để gọi đến một hàm khởi tạo khác trong cùng một lớp (class). Làm việc đó được gọi là lời gọi hàm tạo rõ ràng. Ở đây, một lớp Rectangle khác, với một thực thi khác từ một lớp là một phần đối tượng.

public class Rectangle {
    private int x, y;
    private int width, height;
        
    public Rectangle() {
        this(0, 0, 1, 1);
    }
    public Rectangle(int width, int height) {
        this(0, 0, width, height);
    }
    public Rectangle(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
    ...
}

Trong lớp này, có một tập hợp các hàm khởi tạo. Mỗi hàm khởi tạo khởi tạo vài hoặc tất cả các biến thành viên của lớp hình chữ nhật. Các hàm khởi tạo cung cấp một giá trị mặc định cho biến thành viên bất kỳ mà ban đầu không được cung cấp bởi một đối số. Ví dụ, hàm khởi tạo không đối số, tạo một hình chữ nhật Rectangle có kích thước 1x1 ở tọa độ 0,0. Hàm khởi tạo hai đối số gọi hàm khởi tạo bốn đối số, truyền vào giá trị độ rộng width và chiều cao height, nhưng sử dụng tọa độ 0,0. Trước đó, trình biên dịch quyết định hàm khởi tạo nào được gọi, dựa trên số và kiểu đối số.

Nếu có, thì lời gọi hàm khởi tạo phải là dòng đầu tiên của hàm khởi tạo.

Kiểm soát truy cập vào các thành viên của một lớp.

Bổ ngữ mức độ truy cập quyết định một lớp khác có thể truy cập vào một thuộc tính hoặc gọi phương thức cụ thể. Có hai mức độ điều khiển truy cập:

·        Mức độ cao nhất – public, hoặc package-private (không có bổ ngữ rõ ràng).

·        Mức độ thành viên truy cập – public, private, protected, hoặc package-private (không có bổ ngữ rõ ràng).

Một lớp có thể được khai báo với bổ ngữ public, trong trường hợp đó lớp hiển thị rõ với tất cả các lớp ở mọi nơi. Nếu một lớp không có bổ ngữ (là mặc định, cũng hiểu là package-private), nó sẽ chỉ hiển thị với các lớp thuộc gói đó (gói có tên là nhóm liên quan đến lớp đó – bạn sẽ học về chúng trong các bài học sau).

Các mức độ thành viên, bạn có thể sử dụng bổ ngữ public hoặc không có bổ ngữ (package-private) chỉ với mức độ cao nhất của lớp, và với cùng một ý nghĩa. Cho các thành viên, có hai cách thêm bổ ngữ: privateprotected. Bổ ngữ private là các thành viên chỉ có thể truy cập được từ bên trong lớp đó. Bổ ngữ protected nghĩa là các thành viên chỉ được truy cập bên trong gói đó (giống như package-private) và, ngoài ra các lớp con của lớp đó trong các gói khác.

Dưới đây hiển thị quyền truy cập vào các thành viên cho mỗi bổ ngữ:


Modifier

Class

Package

Subclass

World

public

Y

Y

Y

Y

protected

Y

Y

Y

N

no modifier

Y

Y

N

N

private

Y

N

N

N

Cột dữ liệu đầu tiên xác định bản thân lớp đó có quyền truy cập vào các thành viên được định nghĩa bởi mức độ truy cập. Như bạn có thể thấy, một lớp luôn có quyền truy cập vào các thành viên của mình. Cột thứ hai quyết định lớp trong cùng gói của lớp đó (bất kể huyết thống) có quyền truy cập vào các thành viên hay không. Cột thứ ba quyết định một lớp con của lớp được khai báo bên ngoài gói có quyền truy cập vào các thành viên hay không. Cột thứ tư quyết định tất cả các lớp khác có quyền truy cập vào các thành viên của lớp hay không.

Mức độ truy cập có ảnh hưởng đến bạn theo hai cách. Thứ nhất, khi bạn sử dụng lớp đến từ một lớp ở mã nguồn khác, như là lớp trong nền tảng Java, mức độ truy cập quyết định các thành viên của chúng có được truy cập bởi lớp của bạn đang cần sử dụng. Thứ hai, khi bạn viết một lớp, bạn cần quyết định mức độ truy cập của tất cả các biến thành viên và tất cả các phương thức trong lớp của bạn có.

Hãy nhìn tập hợp một lớp và nhìn xem tại sao mức độ truy cập ảnh hưởng đến việc hiển thị của chúng. Trong Hình dưới  thể hiện bốn lớp trong ví dụ và chúng liên quan với nhau như thế nào.

Minh họa mức độ truy cập trong lớp và gói - tinhoccoban.net

Trong Bảng dưới thể hiện nơi nào các thành viên của lớp Alpha được hiển thị với các mức độ truy cập có thể áp dụng với chúng.

Bảng dưới Hiển thị các lớp theo mức độ truy cập trong Hình trên

Modifier

Alpha

Beta

Alphasub

Gamma

public

Y

Y

Y

Y

protected

Y

Y

Y

N

no modifier

Y

Y

N

N

private

Y

N

N

N


Chú ý: Thủ thuật chọn một mức độ truy cập:

Chú ý: Thủ thuật chọn một mức độ truy cập:

Nếu một chương trình sử dụng lớp của bạn, bạn muốn chắc chắn rằng lỗi sử dụng sai sẽ không xảy ra. Mức độ truy cập có thể giúp bạn điều này:

Sử dụng cấp độ truy cập hạn chế nhất phù hợp với từng thành viên cụ thể. Sử dụng private trừ khi bạn có lý do chính đáng để không sử dụng.

Tránh public thuộc tính ngoại trừ các hằng. (Nhiều ví dụ trong hướng dẫn sử dụng các thuộc tính công khai. Điều này có thể giúp minh họa một vài điểm ngắn gọn, nhưng nó không được khuyến khích cho việc tạo mã nguồn.) Các thuộc tính công khai có khuynh hướng liên kết bạn với thực thi cụ thể và giới hạn sự uyển chuyển trong việc chuyển đổi mã nguồn của bạn.

Hiểu về các thành viên của lớp.

Trong phần này, chúng ta bàn về sử dụng từ khóa static cho việc tạo các thuộc tính và các phương thức, hơn là một thể hiện của một lớp.

Class Variables

Khi một số của đối tượng được tạo ra từ các bản thiết kế lớp (blueprint), mỗi đối tượng trong chúng đều là bản sao riêng biệt của các biến. Trong trường hợp của lớp Bicycle, các biến instance variable là cadence, gear và speed. Mỗi đối tượng Bicycle có giá trị cho mỗi biến đó được lưu trữ trong các vị trí bộ nhớ khác nhau.

Đôi khi, bạn muốn có các biến chung cho tất cả các đối tượng. Việc này sẽ hoàn thành với bổ ngữ static. Các thuộc tính có bổ ngữ static trong khai báo được gọi là thuộc tính tĩnh (static fields hoặc class variables). Chúng liên kết với lớp, hơn là với bất kỳ đối tượng nào. Tất cả các thể hiện của lớp chia sẻ một biến lớp (class variable), nó nằm ở một vị trí cố định trong bộ nhớ. Bất kỳ đối tượng nào có thể thay đổi giá trị cho một biến kiểu này, nhưng nó có thể được sử dụng mà không cần khởi tạo một thể hiện (instance) của lớp.

Ví dụ, giả sử bạn muốn tạo một số đối tượng Bicycle và gán các đối tượng đó một dãy số, bắt đầu từ 1 cho đối tượng đầu tiên. Số ID này là duy nhất cho mỗi đối tượng và là một thể hiện của lớp. Trong cùng thời gian, bạn cần một thuộc tính theo dõi bao nhiêu đối tượng Bicycle đã được tạo ra, cũng như bạn biết ID được gán cho đối tượng tiếp theo. Như một thuộc tính không liên quan cụ thể đối tượng, nhưng lại có liên quan đối với cả lớp. Ví dụ bạn cần một biến lớp (class variable), numberOfBicycles như sau:

public class Bicycle {
        
    private int cadence;
    private int gear;
    private int speed;
        
    // add an instance variable for the object ID
    private int id;
    
    // add a class variable for the
    // number of Bicycle objects instantiated
    private static int numberOfBicycles = 0;
        ...
}

Class variable được tham chiếu đến tên lớp của nó như sau:

Bicycle.numberOfBicycles

Thể hiện rõ nhất đây là biến class variable.

Chú ý: Bạn có thể cũng tham chiếu đến các thuộc tính tĩnh với một tham chiếu đối tượng như:

myBike.numberOfBicycles

Nhưng cách này không được khuyến khích vì rõ ràng chúng là biến class variable.

Bạn có thể sử dụng hàm khởi tạo Bicycle để thiết lập biến thể hiện id và tăng giá trị cho biến lớp numberOfBicycles.

public class Bicycle {
        
    private int cadence;
    private int gear;
    private int speed;
    private int id;
    private static int numberOfBicycles = 0;
        
    public Bicycle(int startCadence, int startSpeed, int startGear){
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
 
        // increment number of Bicycles
        // and assign ID number
        id = ++numberOfBicycles;
    }
 
    // new method to return the ID instance variable
    public int getID() {
        return id;
    }
        ...
}

Class Methods.

Ngôn ngữ lập trình Java hỗ trợ các phương thức tĩnh (static methods) được biết đến giống như biến tĩnh (static variables). Phương thưc này cũng được gọi là Class method là phương thức không đòi hỏi khởi tạo object trước khi gọi, class method không thể truy cập vào các instance variables.  Các phương thức tĩnh, có bổ ngữ là static trong khai báo của chúng, được gọi với tên lớp, không cần đến việc tạo một cá thể (instance) của lớp, như trong:

ClassName.methodName(args)

Chú ý: Bạn có thể tham chiếu đến các phương thức tĩnh với một đối tượng giống như:

instanceName.methodName(args)

Nhưng điều này không được khuyến khích bởi vì không làm cho rõ ràng chúng là các phương thức bình thường của lớp.

Cách sử dụng chung cho các phương thức tĩnh (static method) là truy cập các thuộc tính tĩnh (static fiels). Ví dụ, chúng ta có thể thêm một phương thức tĩnh cho lớp Bicycle để truy cập vào thuộc tính tĩnh numberOfBicycles như:

public static int getNumberOfBicycles() {
    return numberOfBicycles;
}

Các phương thức còn lại trong class được gọi là Instance method là phương thức mà đòi hỏi phải khởi tạo object trước khi gọi, instance method có thể truy cập vào các instance variables

Như vậy, Không phải tất cả các sự kết hợp của biến cá thể và biến lớp và các phương thức đều được phép:

·        Các instance methods có thể truy cập vào các instance variable instance method trực tiếp.

·        Các instance methods có thể truy cập các class variableclass method trực tiếp.

·        Các class methods có thể truy cập vào class variableclass method trực tiếp.

·        Các class methods không thể truy cập vào các instance variable hoặc instance methods trực tiếp – chúng phải sử dụng một đối tượng tham chiếu. Do vậy, class methods không thể sử dụng từ khóa this bởi vì không có instance nào để this tham chiếu tới.

Các hằng (Constants).

Bổ ngữ static, trong mối quan hệ với bổ ngữ final, cũng được sử dụng để định nghĩa các hằng. Bổ ngữ final chỉ ra rằng giá trị của thuộc tính là không thể thay đổi.

Ví dụ, định nghĩa khai báo biến một hằng PI, giá trị xấp xỉ cho pi (tỉ lệ của đường tròn trên đường kính của nó).

static final double PI = 3.141592653589793;

Định nghĩa các hằng theo cách này không thể đăng ký lại, và trình biên dịch sẽ báo lỗi chương trình của bạn nếu bạn làm điều đó. Quy ước, tên của hằng được viết bằng chữ hoa. Nếu tên nhiều hơn một từ thì, các từ sẽ được phân cách nhau bởi một dấu gạch dưới (_).

Chú ý: Nếu một kiểu dữ liệu nguyên thủy hoặc một chuỗi được định nghĩa cho một hằng và giá trị đã biết tại thời điểm biên dịch, trình biên dịch thay thế tên hằng ở khắp nơi trong mã nguồn bằng giá trị. Việc nay được gọi là hằng số thời gian biên dịch (compile-time constant). Nếu giá trị của hằng vượt ra ngoài phạm vi (ví dụ, nếu theo luật cho rằng pi thực sự là 3.975), bạn sẽ cần biên dịch lại bất kỳ lớp nào sử dụng hằng này để nhận giá trị hiện tại.

Lớp Bicycle.

Sau tất cả những thay đồi trong phần này, lớp Bicycle bây giờ như sau:

public class Bicycle {
        
    private int cadence;
    private int gear;
    private int speed;
        
    private int id;
    
    private static int numberOfBicycles = 0;
 
        
    public Bicycle(int startCadence,
                   int startSpeed,
                   int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
 
        id = ++numberOfBicycles;
    }
 
    public int getID() {
        return id;
    }
 
    public static int getNumberOfBicycles() {
        return numberOfBicycles;
    }
 
    public int getCadence() {
        return cadence;
    }
        
    public void setCadence(int newValue) {
        cadence = newValue;
    }
        
    public int getGear(){
        return gear;
    }
        
    public void setGear(int newValue) {
        gear = newValue;
    }
        
    public int getSpeed() {
        return speed;
    }
        
    public void applyBrake(int decrement) {
        speed -= decrement;
    }
        
    public void speedUp(int increment) {
        speed += increment;
    }
}

Khởi gán các thuộc tính.

Giống như bạn đã thấy, bạn có thể thường cung cấp giá trị ban đầu cho một thuộc tính trong định nghĩa sau.

public class BedAndBreakfast {
 
    // initialize to 10
    public static int capacity = 10;
 
    // initialize to false
    private boolean full = false;
}

Nó làm việc khi khởi gán giá trị cho biến ban đầu có thể đặt trên một dòng. Tuy nhiên, hình thức khởi tọa có những hạn chế bởi vì sự đơn giản của nó. Nếu quá trình khởi tạo yêu cầu khởi tạo một vài logic (ví dụ, xử lý lỗi hoặc một vòng lặp for để điền một mảng phức tạp), gán đơn giản là không thỏa đáng. Biến Intances variable có thể được khởi gán trong hàm khởi tạo, nơi mà việc xử lý lỗi hoặc có logic khác có thể được sử dụng. Để cung cấp giống như khả năng của biến class variable, ngôn ngữ lập trình Java có các khối khởi gán tĩnh (static initialization blocks).

Các khối khởi gán tĩnh (Static Initialization Blocks)

Một khối khởi gán tĩnh là một khối bình thường của mã nguồn, bắt đầu và kết thúc trong dấu mở/ đóng ngoặc kép, {}, và trước khối này có từ khòa static. Như ví dụ sau:

static {
    // whatever code is needed for initialization goes here
}

Một lớp có thể có nhiều khối khởi gán tĩnh, và chúng có thể xuất hiện ở bất kỳ đâu trong thân của lớp. Hệ thống thời gian chạy đảm bảo rằng các khối khởi gán tĩnh được gọi theo thứ tự mà chúng xuất hiện trong mã nguồn.

Ở đây có một thay thế cho khối tĩnh – bạn có thể viết một phương thức riêng tư private:

class Whatever {
    public static varType myVar = initializeClassVariable();
        
    private static varType initializeClassVariable() {
 
        // initialization code goes here
    }
}

Nâng cao cho phương thức tĩnh riêng tư là chúng có thể sử dụng lại sau đó nếu bạn cần khởi tạo các class variable.

Khởi gán các thành viên Instance.

Bình thường, bạn nên đặt mã nguồn khởi gán cho một biến instance variable trong một hàm khởi tạo. Có hai sự thay thế sử dụng hàm khởi tạo để khởi gán cho các biến instance variable: khối khởi gán và các final methods.

Các khối khởi gán dùng cho instance variable trông giống như khối khởi gán tĩnh, nhưng không có từ khóa static:

{

    // whatever code is needed for initialization goes here
}

Trình biên dịch Java chép các khối khởi gán trong tất cả các hàm khởi tạo. Do vậy, cách tiếp cận này có thể sử dụng cho chia sẻ một khối mã nguồn với nhiều hàm khởi tạo.

Một final method không được ghi đè trong một lớp con. Việc này sẽ bàn trong giao diện và kế thừa. Ở đây là một ví dụ sử dụng final method cho khởi gán một biến instance variable.

class Whatever {
    private varType myVar = initializeInstanceVariable();
        
    protected final varType initializeInstanceVariable() {
 
        // initialization code goes here
    }
}

Việc này là đặc biệt hữu ích nếu các lớp con có thể muốn sử dụng lại các phương thức khởi tạo. Phương thức là final bởi vì gọi phương thức không final trong khởi tạo biến instance có thể gây ra vấn đề.

Tổng kết về tạo và sử dụng lớp và đối tượng.

Một khai báo lớp thì đặt tên cho lớp và thân lớp bắt đầu bằng một dầu mở ngoặc nhọn và kết thúc thân lớp bằng dấu đóng ngoặc nhọn. Tên lớp có thể được đặt sau các bổ ngữ. Thân lớp bao gồm các thuộc tính, các phương thức, và các hàm khởi tạo trong các lớp. Một lớp sử dụng các thuộc tính để lưu trữ thông tin troạng thái và sử dụng các phương thức để triển khai các hành vi. Hàm khởi tạo khởi tạo một cá thể cho một lớp sử dụng tên của lớp và nhìn giống các phương thức nhưng không có một giá trị trả về.

Bạn điều khiển truy cập  cho các lớp và các thành viên giống như cách: sử dụng một bổ ngữ như public trong khai báo.

Bạn chỉ định một biến class variable hoặc một phương thức class method bằng cách sử dụng từ khóa static trong khai báo thành viên. Các biến class variable được chia sẻ bởi tất cả các instance trong một lớp và có thể truy cập thông qua tên lớp giống như một tham chiếu. Các phiên bản của một lớp nhận bản sao chép của mỗi biến instance variable, phải truy cập thông qua tham chiếu cụ thể.

Bạn tạo một đối tượng từ một lớp sử dụng toán tử new và một hàm khởi tạo. Toàn tử new trả về một tham chiếu đến đối tượng vừa được tạo ra. Bạn có thể gán tham chiếu cho một biến hoặc sử dụng nó trực tiếp.

Các biến Instance variable và các phương thức có khả năng truy cập ở mã nguồn bên ngoài lớp mà chúng đã được định nghĩa có thể được tham chiếu thông qua tên đầy đủ. Tên đầy đủ của một biến instance trông giống như:

objectReference.variableName

Tên đầy đủ của một phương thức instance method trông giống như:

objectReference.methodName(argumentList)

Hoặc

objectReference.methodName()
Bộ thu dọn rác sẽ tự động quét dọn những đối tượng không sử dụng. Một đối tượng không được sử dụng nếu chương trình không có tham chiếu đến nó. Bạn có thể xóa một tham chiếu bằng cách thiết lập biến đó được tham chiếu đến null.
Mới hơn Cũ hơn

Biểu mẫu liên hệ