Chúng tôi tiếp tục giới thiệu về nâng cấp đối tượng trong java trong khóa học java này


Các biến lớp

Để tạo một biến lớp, ta dùng từ khóa static khi khai báo:
PHP:
accessSpecifier static  variableName[=  initialValue ];
JRE tạo một bản sao của các biến cá thể của lớp cho mọi cá thể của lớp đó. JRE chỉ sinh duy nhất một bản sao cho mỗi biến lớp, không phụ thuộc vào số lượng cá thể, khi lần đầu tiên nó gặp lời gọi lớp trong chương trình. Tất cả các cá thể sẽ chia sẻ chung (và có thể sửa đổi) bản sao riêng lẻ này. Điều này làm cho các biến lớp trở thành một lựa chọn tốt để chứa các hằng số mà tất cả các cá thể đều có thể sử dụng.
Ví dụ, chúng ta đang dùng các số nguyên để mô tả “tờ giấy bạc” trong wallet của Adult. Điều đó hoàn toàn chấp nhận được, nhưng sẽ thật tuyệt nếu ta đặt tên cho các giá trị nguyên này để chúng ta có thể dễ dàng hiểu con số đó biểu thị cho cái gì khi ta đọc mã lệnh. Hãy khai báo một vài hằng số để làm điều này, ở chính ngay nơi ta khai báo các biến cá thể trong lớp của mình:
PHP:
protected static final int ONE_DOLLAR_BILL 1;
protected static final 
int FIVE_DOLLAR_BILL 5;
protected static final 
int TEN_DOLLAR_BILL 10;
protected static final 
int TWENTY_DOLLAR_BILL 20;
protected static final 
int FIFTY_DOLLAR_BILL 50;
protected static final 
int ONE_HUNDRED_DOLLAR_BILL 100;
Theo quy ước, các hằng số của lớp đều được viết bằng chữ in hoa, các từ phân tách nhau bằng dấu gạch dưới. Ta dùng từ khóa static để khai báo chúng như là các biến lớp, và ta thêm từ khóa final vào để đảm bảo là không một cá thể nào có thể thay đổi chúng được (nghĩa là biến chúng trở thành hằng số). Bây giờ ta có thể biến đổi main() để thêm một ít tiền cho Adult của ta, sử dụng các hằng được đặt tên mới:
PHP:
public static void main(String[] args) {Adult myAdult = new Adult();myAdult.addMoney(new int[] { Adult.ONE_DOLLAR_BILLAdult.FIVE_DOLLAR_BILL });System.out.println(myAdult);
}
Đọc đoạn mã này sẽ giúp làm sáng tỏ những gì ta bổ sung vào wallet wallet.

Các phương thức lớp

Như ta đã biết, ta gọi một phương thức cá thể như sau:
PHP:
variableWithInstance.methodName();
Chúng ta đã gọi phương thức trên một biến có tên, biến đó chứa một cá thể của lớp. Khi bạn gọi một phương thức lớp, bạn sẽ gọi như sau:
PHP:
ClassName.methodName();

Chúng ta không cần đến một cá thể để gọi phương thức này. Chúng ta đã gọi nó thông qua chính bản thân lớp. Phương thức main() mà ta đang dùng chính là một phương thức lớp. Hãy nhìn chữ ký của nó. Chú ý rằng nó được khai báo với từ khóa public static. Chúng ta đã biết định tố truy nhập này từ trước đây. Còn từ khóa static chỉ ra rằng đây là một phương thức lớp, đây chính là lý do mà các phương thức kiểu này đôi khi được gọi là cácphương thức static. Chúng ta không cần có một cá thể của Adult để gọi phương thức main().
Chúng ta có thể xây dựng các phương thức lớp cho Adult nếu ta muốn, mặc dù thực sự không có lý do để làm điều đó trong trường hợp này. Tuy nhiên, để minh họa cách làm, ta sẽ bổ sung thêm một phương thức lớp tầm thường:
PHP:
public static void doSomething() {System.out.println("Did something");
}
Thêm dấu chú thích vào các dòng lệnh hiện có của main() để loại bỏ chúng và bổ sung thêm dòng sau:
PHP:
Adult.doSomething();Adult myAdult = new Adult();myAdult.doSomething();
Khi bạn chạy mã lệnh này, bạn sẽ thấy thông điệp tương ứng trên màn hình hai lần. Lời gọi thứ nhất gọi doSomething() theo cách điển hình khi gọi một phương thức lớp. Bạn cũng có thể gọi chúng thông qua một cá thể của lớp, như ở dòng thứ ba của mã lệnh. Nhưng đó không phải là cách hay. Eclipse sẽ cảnh báo cho bạn biết bằng cách dùng dòng gạch chân dạng sóng màu vàng và đề nghị bạn nên truy cập phương thức này theo “cách tĩnh” (static way), nghĩa là trên lớp chứ không phải là trên cá thể.
So sánh các đối tượng với toán tử ==
Có hai cách để so sánh các đối tượng trong ngôn ngữ Java:
* Toán tử ==
* Toán tử equals()
Cách đầu tiên, và là cách cơ bản nhất, so sánh các đối tượng theo tiêu chí ngang bằng đối tượng (object equality). Nói cách khác, câu lệnh:
PHP:
== b
sẽ trả lại giá trị true nếu và chỉ nếu a và b trỏ tới chính xác cùng một cá thể của một lớp (tức là cùng một đối tượng). Các kiểu nguyên thủy là ngoại lệ riêng. Khi ta so sánh hai kiểu nguyên thủy bằng toán tử ==, môi trường chạy thi hành của Java sẽ so sánh các giá trị của chúng (hãy nhớ rằng dù gì thì chúng cũng không phải là đối tượng thực sự). Hãy thử ví dụ này trong main() và xem kết quả trên màn hình.
PHP:
int int1 1;int int2 1;Integer integer1 = new Integer(1);Integer integer2 = new Integer(1);Adult adult1 = new Adult();Adult adult2 = new Adult();
 
System.out.println(int1 == int2);System.out.println(integer1 == integer2);integer2 integer1;System.out.println(integer1 == integer2);System.out.println(adult1 == adult2);
Phép so sánh đầu tiên trả lại giá trị true, vì ta đang so sánh hai kiểu nguyên thủy có cùng giá trị. Phép so sánh thứ hai trả lại giá trị false, vì hai biến không tham chiếu đến cùng một đối tượng cá thể. Phép so sánh thứ ba trả lại giá trị true, vì bây giờ hai biến trỏ đến cùng một cá thể. Hãy thử với lớp của chúng ta, ta cũng nhận được giá trị false vì adult1 và adult2 không chỉ đến cùng một cá thể.

So sánh các đối tượng bằng equals()

Bạn gọi phương thức equals() trên một đối tượng như sau:
PHP:
a.equals(b);

Phương thức equals() là một phương thức của lớp Object, vốn là lớp cha của mọi lớp trong ngôn ngữ Java. Điều đó có nghĩa là bất cứ lớp nào bạn xây dựng nên cũng sẽ thừa kế hành vi cơ sở equals() từ lớp Object. Hành vi cơ sở này không khác so với toán tử ==. Nói cách khác, mặc định là hai câu lệnh này cùng sử dụng toán tử == và trả lại giá trị false:
PHP:
== b;a.equals(b);
Hãy nhìn lại phương thức spendMoney() của lớp Adult. Chuyện gì xảy ra đằng sau khi ta gọi phương thức contains()của đối tượng wallet của ta? Ngôn ngữ Java sử dụng toán tử == để so sánh các đối tượng trong danh sách với một đối tượng mà ta yêu cầu. Nếu Java thấy khớp, phương thức sẽ trả lại giá trị true; các trường hợp khác trả lại giá trị false. Bởi vì ta đang so sánh các kiểu nguyên thủy, Java có thể thấy sự trùng khớp dựa theo giá trị của các số nguyên (hãy nhớ rằng toán tử == so sánh các kiểu nguyên thủy dựa trên giá trị của chúng).
Thật tuyệt với đối với các kiểu nguyên thủy, nhưng liệu sẽ thế nào nếu ta so sánh nội dung của các đối tượng? Toán tử == không thể làm việc này. Để so sánh nội dung của các đối tượng, chúng ta phải đè chồng phương thức equals() của lớp mà a là cá thể của lớp đó. Điều đó có nghĩa là bạn tạo ra một phương thức có cùng chữ ký chính xác như chữ ký của phương thức của một trong các lớp bậc trên (superclasses), nhưng bạn sẽ triển khai thực hiện phương thức này khác với phương thức của lớp bậc trên. Nếu làm như vậy, bạn có thể so sánh nội dung của hai đối tượng để xem liệu chúng có giống nhau không chứ không phải là chỉ kiểm tra xem liệu hai biến đó có trỏ tới cùng một cá thể không.
Hãy thử ví dụ này trong main(), và xem kết quả trên màn hình:
PHP:
Adult adult1 = new Adult();Adult adult2 = new Adult();
 
System.out.println(adult1 == adult2);System.out.println(adult1.equals(adult2));
 
Integer integer1 = new Integer(1);Integer integer2 = new Integer(1);
 
System.out.println(integer1 == integer2);System.out.println(integer1.equals(integer2));
Phép so sánh đầu tiên trả lại giá trị false vì adult1 và adult2 trỏ đến các cá thể khác nhau của lớp Adult. Phép so sánh thứ hai cũng trả lại giá trị false vì triển khai mặc định của equals() đơn giản là so sánh hai biến để xem liệu chúng có trỏ tới cùng một cá thể không. Nhưng hành vi mặc định này của equals() thường không phải là cái ta mong muốn. Chúng ta muốn so sánh nội dung của hai Adult để xem liệu chúng có giống nhau không. Ta có thể đè chồng phương thức equals() để làm điều này. Như bạn thấy kết quả của hai phép so sánh cuối cùng trong ví dụ trên, lớp Integer đè chồng lên phương thức này sao cho toán tử ==trả lại giá trị false, nhưng equals() lại so sánh các giá trị int đã bao bọc để xem có bằng nhau không. Chúng ta sẽ làm tương tự với Adult trong phần tiếp theo.

0 nhận xét:

Đăng nhận xét

 
Top