Java - ĐH Công Nghệ - 21

Bài tập

1. Các phát biểu sau đây đúng hay sai?

a) khi một đối tượng thuộc lớp con được khởi tạo, hàm khởi tạo của lớp cha phải được gọi một cách tường minh.

b) nếu một lớp có khai báo các hàm khởi tạo, trình biên dịch sẽ không tạo hàm khởi tạo mặc định cho lớp đó.

c) lớp con được thừa kế hàm khởi tạo của lớp cha. Khi khởi tạo đối tượng lớp con, hàm khởi tạo của lớp cha luôn luôn được gọi tự động để khởi tạo phần được thừa kế.

2. Từ khóa new dùng để làm gì? Giải thích chuyện xảy ra khi dùng từ khóa này trong một ứng dụng.

3. Hàm khởi tạo mặc định là gì? Các biến thực thể của một đối tượng được khởi tạo như thế nào nếu lớp đó không có hàm khởi tạo nào do lập trình viên viết.

4. Tìm lỗi biên dịch nếu có của các hàm khởi tạo trong cài đặt sau đây của lớp SonOfBoo.


Có thể bạn quan tâm!

Xem toàn bộ 251 trang tài liệu này.

5. Cho cài đặt lớp Foo ở cột bên trái, nếu bổ sung vào vị trí A một trong các dòng mã ở cột bên phải, dòng nào sẽ làm cho một đối tượng bị mất dấu và sẽ bị garbage collector thu hồi bất cứ lúc nào?

Java - ĐH Công Nghệ - 21

Chương 10. Thành viên lớp và thành viên thực thể


Ta đã biết đối với các biến thực thể, mỗi đối tượng đều có một bản riêng của mỗi biến. Chẳng hạn, nếu khai báo lớp Cow có biến thực thể name, thì mỗi đối tượng Cow đều có một biến name của riêng nó nằm trong vùng bộ nhớ được cấp phát cho đối tượng đó. Hầu hết những phương thức ta đã thấy trong các ví dụ đều có hoạt động chịu ảnh hưởng của giá trị các biến thực thể. Nói cách khác, chúng có hành vi tùy thuộc từng đối tượng cụ thể. Khi gọi các phương thức, ta cũng đều phải gọi cho các đối tượng cụ thể. Nói tóm lại, đó là các phương thức thuộc về đối tượng.

Nếu 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.


10.1. BIẾN CỦA LỚP


Đô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ụ trong Hình 10.1, 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.

@ (

" &


: " 9 / "

@2:

" ! @

" R'@ ( # 3&

/ "


@ ( "

# &


R'@ ( ))&

W . 0 L

@ 5: *

) % GCI Q d

- , @


7 7 8@ ( J8) R'@ ( )8 78 &



@ (I A

"EF "

9 ,

9 (

9 # (

@ ( 2 # ( @ ( &

@ ( 1 # ( @ ( &


Hình 10.1: Biến lớp - biến static.


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 Cow.numOfCows để truy nhập numOfCows:



10.2. PHƯƠNG THỨC CỦA LỚP


Lại xét ví dụ trong Hình 10.1, giả sử ta muốn numOfCows là biến private để 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 (các chương trình dùng đến Cow có thể muốn biết có bao nhiêu đối tượng Cow đã được tạo), nên ta sẽ bổ sung một phương thức, chẳng hạn getCount(), để 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 getCount(), 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. 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. Vả lại, gọi getCount() từ bất cứ đối tượng Cow nào thực ra cũng như nhau cả, vì getCount() 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 (nó không truy nhập biến thực thể nào). Hơn nữa, khi còn chưa có một đối tượng Cow nào được tạo thì không thể gọi được getCount()!

Phương thức getCount() khô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 getCount() 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:

public static int getCount() { return numOfCows;

}

Các phương thức thông thường mà ta đã biết, ngoại trừ main(), được gọi là các phương thức của thực thể (instance method) – hay các phương thức không static. Các phương thức này phụ thuộc vào từng đối tượng và phải được gọi từ đối tượng.

Hình 10.2 là bản sửa đổi của ví dụ trong Hình 10.1. Trong đó bổ sung phương thức static getCount() và trình diễn việc gọi phương thức đó từ tên lớp cũng như từ tham chiếu đối tượng. Lần này, ta có thể truy vấn số lượng Cow ngay từ khi chưa có đối tượng Cow nào được tạo. Lưu ý rằng có thể gọi getCount() từ tên lớp cũng như từ một tham chiếu kiểu Cow.

public cl ss Cow {

pri te tring n me;

pri te st tic int numOfCows # 3;


public Cow( tring t e me) { n me # t e me; numOfCows));

}


public static int getCount() { return numOfCows;

}


public tring get me() { return n me;


2 3 4 @2

" ! A / " : " 9 L


@ "

I Q . -

9 "

#

}

}


public cl ss CountCows {

public st tic oi m in( tringEF rgs) {

stem7out7println(Cow7getCount()); Cow c2 # new Cow();

stem7out7println(Cow7getCount()); Cow c1 # new Cow();

stem7out7println(c17getCount());

}


1 / - @2


< 1 /

}


Hình 10.2. Phương thức lớp.


Đặ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 main() với từ khóa static. main() đượ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.


10.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ụ Hình 10.2, getCount() 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 getCount() từ tham chiếu c2 thì getCount() cũng vẫn không biết gì về đối tượng Cow mà c2 đang chiếu tới. Vì khi đó, trình biên dịch chỉ dùng kiểu khai báo của c2 để xác định nên chạy getCount() của lớp nào, nó không quan tâm c2 đang chiếu tới đối tượng nào. Cow.getCount() hay c2.getCount() chỉ là hai cách gọi phương thức, và với cách nào thì getCount() cũng vẫn là một phương thức static.



Hình 10.3: Phương thức lớp không thể truy nhập biến thực thể.


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 heap đang có 10 hay chỉ có duy nhất một đối tượng thuộc lớp đó. Ví dụ, chương trình trong Hình 10.3 bị lỗi biên dịch vì phương thức main() cố truy nhập biến name. Do main() là phương thức static, trình biên dịch không hiểu name mà main() đang nói đến là biến thực thể name của đối tượng nào. Lời thông báo lỗi có nội dung: biến thực thể name không thể được gọi đến từ một ngữ cảnh static. Ta dễ thấy rằng tham chiếu this cũng không thể sử dụng trong một phương thức lớp, bởi nó không hiểu đối tượng 'này' là đối tượng nào.

Hiệu ứng dây chuyền của việc các phương thức static không thể dùng biến thực thể là chúng cũng không thể gọi đến các phương thức thực thể (phương thức thường) của lớp đó. Các phương thức thực thể được quyền dùng biến thực thể, gọi đến các phương thức thực thể đồng nghĩa với việc gián tiếp sử dụng biến thực thể.


Hình 10.4: Phương thức lớp không thể gọi phương thức thực thể.


Ví dụ trong Hình 10.4 cũng gặp lỗi tương tự lỗi biên dịch trong Hình 10.3.

..... Xem trang tiếp theo?
⇦ Trang trước - Trang tiếp theo ⇨

Ngày đăng: 24/12/2023