Giáo trình Lập trình hướng đối tượng với Java (Phần 1)

Lập trình là công đoạn quan trọng chủ chốt và không thể thiếu để tạo ra sản

phẩm phần mềm. Phần mềm càng trở nên đa dạng và ngành công nghiệp phần mềm

càng phát triển thì người ta càng thấy rõ tầm quan trọng của phương pháp lập trình.

Phương pháp lập trình tốt không chỉ đảm bảo tạo ra phần mềm tốt mà còn hỗ trợ

thiết kế phần mềm có tính mở và hỗ trợ khả năng sử dụng lại các mô đun. Nhờ đó

chúng ta có thể dễ dàng bảo trì, nâng cấp phần mềm cũng như giảm chi phí phát

triển phần mềm.

Trong những thập kỷ 1970, 1980, phương pháp phát triển phần mềm chủ yếu là

lập trình có cấu trúc (structured programming). Cách tiếp cận cấu trúc đối với việc

thiết kế chương trình dựa trên chiến lược chia để trị: Để giải một bài toán lớn, chúng

ta tìm cách chia nó thành vài bài toán nhỏ hơn và giải riêng từng bài; để giải mỗi bài,

hãy coi nó như một bài toán mới và có thể tiếp tục chia nó thành các bài toán nhỏ

hơn; cuối cùng, ta sẽ đi đến những bài toán có thể giải ngay được mà không cần phải

chia tiếp. Cách tiếp cận này được gọi là lập trình từ trên xuống (top-down

programming).

Lập trình từ trên xuống là một phương pháp tốt và đã được áp dụng thành công

cho phát triển rất nhiều phần mềm. Tuy nhiên, cùng với sự đa dạng và phức tạp của

phần mềm, phương pháp này bộc lộ những hạn chế. Trước hết, nó hầu như chỉ đáp

ứng việc tạo ra các lệnh hay là các quy trình để giải quyết một bài toán. Dần dần,

người ta nhận ra rằng thiết kế các cấu trúc dữ liệu cho một chương trình có tầm

quan trọng không kém việc thiết kế các hàm/thủ tục và các cấu trúc điều khiển. Lập

trình từ trên xuống không quan tâm đủ đến dữ liệu mà chương trình cần xử lý.

Thứ hai, với lập trình từ trên xuống, chúng ta khó có thể tái sử dụng các phần

của chương trình này cho các chương trình khác. Bằng việc xuất phát từ một bài toán

cụ thể và chia nó thành các mảnh sao cho thuận, cách tiếp cận này có xu hướng tạo

ra một thiết kế đặc thù cho chính bài toán đó. Chúng ta khó có khả năng lấy một

đoạn mã lớn từ một chương trình cũ lắp vào một dự án mới mà không phải sửa đổi

lớn. Việc xây dựng các chương trình chất lượng cao là khó khăn và tốn kém, do đó

những nhà phát triển phần mềm luôn luôn muốn tái sử dụng các sản phẩm cũ.

Thứ ba, môi trường hoạt động trong thực tế của các ứng dụng luôn thay đổi.

Dẫn đến việc yêu cầu phần mềm cũng phải liên tục thay đổi theo để đáp ứng nhu

cầu của người dùng nếu không muốn phần mềm bị đào thải. Do đó, một thiết kế

linh hoạt mềm dẻo là cái mà các nhà phát triển phần mềm mong muốn. Phương

pháp tiếp cận từ dưới lên (bottom-up) hỗ trợ tốt hơn cho tính linh hoạt mềm dẻo đó.

Trong thực tế, thiết kế và lập trình từ trên xuống thường được kết hợp với thiết

kế và lập trình từ dưới lên. Trong tiếp cận từ dưới lên, từ các vấn đề mà ta đã biết8

cách giải và có thể đã có sẵn các thành phần tái sử dụng được chúng ta xây dựng

dần theo hướng lên trên, hướng đến một giải pháp cho bài toán tổng.

pdf 102 trang yennguyen 2760
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Lập trình hướng đối tượng với Java (Phần 1)", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Giáo trình Lập trình hướng đối tượng với Java (Phần 1)

Giáo trình Lập trình hướng đối tượng với Java (Phần 1)
 1
Mục lục 
GIỚI THIỆU .............................................................................5 
Chương 1. MỞ ĐẦU ............................................................7 
1.1. KHÁI NIỆM CƠ BẢN ................................................ 12 
1.2. ĐỐI TƯỢNG VÀ LỚP................................................ 13 
1.3. CÁC NGUYÊN TẮC TRỤ CỘT ................................ 15 
Chương 2. NGÔN NGỮ LẬP TRÌNH JAVA ................... 20 
2.1. ĐẶC TÍNH CỦA JAVA .............................................. 20 
2.1.1. Máy ảo Java – Java Virtual Machine ............... 21 
2.1.2. Các nền tảng Java ............................................. 23 
2.1.3. Môi trường lập trình Java ................................ 23 
2.1.4. Cấu trúc mã nguồn Java .................................. 24 
2.1.5. Chương trình Java đầu tiên ............................. 25 
2.2. BIẾN ............................................................................. 27 
2.3. CÁC PHÉP TOÁN CƠ BẢN...................................... 28 
2.3.1. Phép gán ............................................................ 28 
2.3.2. Các phép toán số học........................................ 28 
2.3.3. Các phép toán khác .......................................... 29 
2.3.4. Độ ưu tiên của các phép toán .......................... 30 
2.4. CÁC CẤU TRÚC ĐIỀU KHIỂN ................................ 30 
2.4.1. Các cấu trúc rẽ nhánh....................................... 31 
2.4.2. Các cấu trúc lặp ................................................ 37 
2.4.3. Biểu thức điều kiện trong các cấu trúc điều khiển 43 
Chương 3. LỚP VÀ ĐỐI TƯỢNG .................................... 48 
3.1. TẠO VÀ SỬ DỤNG ĐỐI TƯỢNG ............................ 49 
3.2. TƯƠNG TÁC GIỮA CÁC ĐỐI TƯỢNG ................. 51 
Chương 4. BIẾN VÀ CÁC KIỂU DỮ LIỆU ...................... 57 
4.1. BIẾN VÀ CÁC KIỂU DỮ LIỆU CƠ BẢN ................. 58 
4.2. THAM CHIẾU ĐỐI TƯỢNG VÀ ĐỐI TƯỢNG ...... 59 
4.3. PHÉP GÁN .................................................................. 62 
4.4. CÁC PHÉP SO SÁNH ................................................ 63 
 2
4.5. MẢNG ......................................................................... 64 
Chương 5. HÀNH VI CỦA ĐỐI TƯỢNG ....................... 70 
5.1. PHƯƠNG THỨC VÀ TRẠNG THÁI ĐỐI TƯỢNG70 
5.2. TRUYỀN THAM SỐ VÀ GIÁ TRỊ TRẢ VỀ .............. 71 
5.3. CƠ CHẾ TRUYỀN BẰNG GIÁ TRỊ .......................... 73 
5.4. ĐÓNG GÓI VÀ CÁC PHƯƠNG THỨC TRUY NHẬP 75 
5.5. KHAI BÁO VÀ KHỞI TẠO BIẾN THỰC THỂ........ 79 
5.6. BIẾN THỰC THỂ VÀ BIẾN ĐỊA PHƯƠNG ........... 80 
Chương 6. SỬ DỤNG THƯ VIỆN JAVA ......................... 85 
6.1. ArrayList ..................................................................... 85 
6.2. SỬ DỤNG JAVA API ................................................. 87 
6.3. MỘT SỐ LỚP THÔNG DỤNG TRONG API ........... 88 
6.3.1. Math ................................................................... 88 
6.3.2. Các lớp bọc ngoài kiểu dữ liệu cơ bản ............ 89 
6.3.3. Các lớp biểu diễn xâu kí tự .............................. 90 
6.4. TRÒ CHƠI BẮN TÀU ................................................ 91 
Chương 7. THỪA KẾ VÀ ĐA HÌNH ............................. 103 
7.1. QUAN HỆ THỪA KẾ .............................................. 103 
7.2. THIẾT KẾ CÂY THỪA KẾ ...................................... 104 
7.3. CÀI ĐÈ – PHƯƠNG THỨC NÀO ĐƯỢC GỌI? ... 107 
7.4. CÁC QUAN HỆ IS-A VÀ HAS-A ........................... 108 
7.5. KHI NÀO NÊN DÙNG QUAN HỆ THỪA KẾ?.... 110 
7.6. LỢI ÍCH CỦA QUAN HỆ THỪA KẾ ..................... 110 
7.7. ĐA HÌNH .................................................................. 111 
7.8. GỌI PHIÊN BẢN PHƯƠNG THỨC CỦA LỚP CHA114 
7.9. CÁC QUY TẮC CHO VIỆC CÀI ĐÈ ....................... 115 
7.10. CHỒNG PHƯƠNG THỨC .................................... 116 
7.11. CÁC MỨC TRUY NHẬP ....................................... 117 
Chương 8. LỚP TRỪU TƯỢNG VÀ INTERFACE ........ 124 
8.1. MỘT SỐ LỚP KHÔNG NÊN TẠO THỰC THỂ .... 124 
8.2. LỚP TRỪU TƯỢNG VÀ LỚP CỤ THỂ ................. 126 
 3
8.3. PHƯƠNG THỨC TRỪU TƯỢNG .......................... 127 
8.4. VÍ DỤ VỀ ĐA HÌNH ................................................ 127 
8.5. LỚP Object ................................................................ 131 
8.6. ĐỔI KIỂU – KHI ĐỐI TƯỢNG MẤT HÀNH VI CỦA MÌNH 132 
8.7. ĐA THỪA KẾ VÀ VẤN ĐỀ HÌNH THOI.............. 135 
8.8. INTERFACE .............................................................. 137 
Chương 9. VÒNG ĐỜI CỦA ĐỐI TƯỢNG ................... 143 
9.1. BỘ NHỚ STACK VÀ BỘ NHỚ HEAP ................... 143 
9.2. KHỞI TẠO ĐỐI TƯỢNG ........................................ 145 
9.3. HÀM KHỞI TẠO VÀ VẤN ĐỀ THỪA KẾ ............ 149 
9.3.1. Gọi hàm khởi tạo của lớp cha ........................ 150 
9.3.2. Truyền đối số cho hàm khởi tạo lớp cha ...... 152 
9.4. HÀM KHỞI TẠO CHỒNG NHAU ........................ 153 
9.5. TẠO BẢN SAO CỦA ĐỐI TƯỢNG ....................... 154 
9.6. CUỘC ĐỜI CỦA ĐỐI TƯỢNG............................... 159 
Chương 10. THÀNH VIÊN LỚP VÀ THÀNH VIÊN THỰC THỂ 164 
10.1. BIẾN CỦA LỚP ...................................................... 164 
10.2. PHƯƠNG THỨC CỦA LỚP ................................. 165 
10.3. GIỚI HẠN CỦA PHƯƠNG THỨC LỚP ............. 167 
10.4. KHỞI TẠO BIẾN LỚP ........................................... 169 
10.5. MẪU THIẾT KẾ SINGLETON .............................. 170 
10.6. THÀNH VIÊN BẤT BIẾN – final .......................... 171 
Chương 11. NGOẠI LỆ ................................................... 174 
11.1. NGOẠI LỆ LÀ GÌ? .................................................. 175 
11.1.1. Tình huống sự cố .......................................... 175 
11.1.2. Xử lý ngoại lệ ................................................ 177 
11.1.3. Ngoại lệ là đối tượng .................................... 178 
11.2. KHỐI try/catch ........................................................ 179 
11.2.1. Bắt nhiều ngoại lệ ......................................... 179 
11.2.2. Hoạt động của khối try/catch ...................... 180 
11.2.3. Khối finally – những việc dù thế nào cũng phải làm 182 
 4
11.2.4. Thứ tự cho các khối catch ............................ 183 
11.3. NÉM NGOẠI LỆ ..................................................... 184 
11.4. NÉ NGOẠI LỆ ........................................................ 185 
11.5. NGOẠI LỆ ĐƯỢC KIỂM TRA VÀ KHÔNG ĐƯỢC KIỂM TRA 189 
11.6. ĐỊNH NGHĨA KIỂU NGOẠI LỆ MỚI ................. 190 
11.7. NGOẠI LỆ VÀ CÁC PHƯƠNG THỨC CÀI ĐÈ . 191 
Chương 12. CHUỖI HÓA ĐỐI TƯỢNG VÀ VÀO RA FILE 196 
12.1. QUY TRÌNH GHI ĐỐI TƯỢNG............................ 197 
12.2. CHUỖI HÓA ĐỐI TƯỢNG ................................... 199 
12.3. KHÔI PHỤC ĐỐI TƯỢNG .................................... 202 
12.4. GHI CHUỖI KÍ TỰ RA TỆP VĂN BẢN ............... 205 
12.4.1. Lớp File .......................................................... 206 
12.4.2. Bộ nhớ đệm ................................................... 207 
12.5. ĐỌC TỆP VĂN BẢN .............................................. 207 
12.6. CÁC DÒNG VÀO/RA TRONG Java API ............. 209 
Chương 13. LẬP TRÌNH TỔNG QUÁT VÀ CÁC LỚP COLLECTION 215 
13.1. LỚP TỔNG QUÁT ................................................. 217 
13.2. PHƯƠNG THỨC TỔNG QUÁT ........................... 219 
13.3. CÁC CẤU TRÚC DỮ LIỆU TỔNG QUÁT TRONG JAVA API 220 
13.4. ITERATOR VÀ VÒNG LẶP FOR EACH ............. 222 
13.5. SO SÁNH NỘI DUNG ĐỐI TƯỢNG ................... 224 
13.5.1. So sánh bằng ................................................. 224 
13.5.2. So sánh lớn hơn/nhỏ hơn ............................. 226 
13.6. KÍ TỰ ĐẠI DIỆN TRONG KHAI BÁO THAM SỐ KIỂU 228 
Phụ lục A. DỊCH CHƯƠNG TRÌNH BẰNG JDK .......... 233 
Phụ lục B. PACKAGE – TỔ CHỨC GÓI CỦA JAVA .... 236 
Phụ lục C. BẢNG THUẬT NGỮ ANH-VIỆT ................. 239 
Tµi liÖu tham kh¶o ............................................................... 241 
 5
Giíi thiÖu 
Phần mềm ngày càng lớn và phức tạp và đòi hỏi được cập nhật liên tục để đáp 
ứng những yêu cầu mới của người dùng. Phương pháp lập trình thủ tục truyền 
thống dần trở nên không đáp ứng được những đòi hỏi đó của ngành công nghiệp 
phần mềm. Lập trình hướng đối tượng đã ra đời trong bối cảnh như vậy để hỗ trợ sử 
dụng lại và phát triển các phần mềm qui mô lớn. 
Giáo trình này cung cấp cho sinh viên các kiến thức từ cơ bản cho đến một số kỹ 
thuật nâng cao về phương pháp lập trình hướng đối tượng. Giáo trình dùng cho 
sinh viên ngành Công nghệ thông tin đã có kiến thức căn bản về lập trình. Giáo trình 
sử dụng ngôn ngữ lập trình Java để minh họa và đồng thời cũng giới thiệu một số 
kiến thức căn bản của ngôn ngữ này. 
Các nội dung chính về phương pháp lập trình hướng đối tượng được trình bày 
trong giáo trình bao gồm lớp và đối tượng, đóng gói/che giấu thông tin, kế thừa và 
đa hình, xử lý ngoại lệ và lập trình tổng quát. Ngoài ra, giáo trình cũng trình bày các 
kiến thức về Java bao gồm các đặc trưng cơ bản của ngôn ngữ, các thư viện cơ bản 
và cách thức tổ chức vào/ra dữ liệu. 
Thay vì cách trình bày theo tính hàn lâm về một chủ đề rộng, để thuận tiện cho 
giảng dạy, giáo trình chọn cách trình bày theo các bài học cụ thể được sắp xếp theo 
trình tự kiến thức từ cơ sở đến chuyên sâu. Mỗi chủ đề có thể được giảng dạy với 
thời lượng 2~3 giờ lý thuyết và giờ thực hành tương ứng. Ch-¬ng 2 và Ch-¬ng 6, với 
nội dung là các kiến thức cơ bản về ngôn ngữ lập trình Java, tuy cần thiết nhưng 
không phải nội dung trọng tâm của môn học Lập trình hướng đối tượng. Các 
chương này, do đó, nên để sinh viên tự học. Chương 9 và Chương 10 không nhất 
thiết phải được dạy thành những chủ đề độc lập mà có thể được tách rải rác các nội 
dung kiến thức và giới thiệu kèm theo các khái niệm hướng đối tượng có liên quan, 
hoặc yêu cầu sinh viên tự đọc khi cần đến các kiến thức này trong quá trình thực 
hành. 
Tuy cuốn giáo trình này không trình bày sâu về lập trình Java, nhưng kiến thức 
về lập trình Java lại là cần thiết đối với sinh viên, ngay cả với mục đích thực hành 
môn học. Do đó, ngoài mục đích thực hành các nội dung liên quan đến lập trình 
hướng đối tượng, các bài tập thực hành của môn học này nên có thêm đóng vai trò 
định hướng và gợi ý giúp đỡ sinh viên tự học các chủ đề thuần túy Java mà giáo 
viên cho là cần thiết, chẳng hạn như học về vào ra dữ liệu đơn giản ngay từ tuần đầu 
tiên của môn học. Các định hướng này có thể được thể hiện ở những bài tập thực 
hành với những đoạn chương trình mẫu, hoặc yêu cầu tìm hiểu tài liệu API về một 
số lớp tiện ích. Một số bài tập cuối chương là ví dụ của dạng bài tập này. 
 6
Các thuật ngữ hướng đối tượng nguyên gốc tiếng Anh đã được chuyển sang 
tiếng Việt theo những cách khác nhau tùy các tác giả. Sinh viên cần biết thuật ngữ 
nguyên gốc tiếng Anh cũng như các cách dịch khác nhau đó để tiện cho việc sử 
dụng tài liệu tiếng Anh cũng như để liên hệ kiến thức giữa các tài liệu tiếng Việt. Vì 
lí do đó, giáo trình này cung cấp bảng thuật ngữ Anh-Việt với các cách dịch khác 
nhau tại Phụ lục C, bên cạnh Phụ lục A về công cụ lập trình JDK và Phụ lục B về tổ 
chức gói của ngôn ngữ Java. 
Các tác giả chân thành cảm ơn PGS. TS. Nguyễn Đình Hóa, TS. Trương Anh 
Hoàng, TS. Cao Tuấn Dũng, TS. Đặng Đức Hạnh, cũng như các đồng nghiệp và sinh 
viên tại Khoa Công nghệ thông tin, Trường Đại học Công nghệ đã đọc bản thảo giáo 
trình và có các góp ý quí báu về nội dung chuyên môn cũng như cách thức trình bày. 
Tuy vậy, giáo trình vẫn còn nhiều khiếm khuyết, các tác giả mong tiếp tục nhận 
được góp ý để hoàn thiện trong tương lai. 
 7
Ch−¬ng 1. më ®Çu 
Lập trình là công đoạn quan trọng chủ chốt và không thể thiếu để tạo ra sản 
phẩm phần mềm. Phần mềm càng trở nên đa dạng và ngành công nghiệp phần mềm 
càng phát triển thì người ta càng thấy rõ tầm quan trọng của phương pháp lập trình. 
Phương pháp lập trình tốt không chỉ đảm bảo tạo ra phần mềm tốt mà còn hỗ trợ 
thiết kế phần mềm có tính mở và hỗ trợ khả năng sử dụng lại các mô đun. Nhờ đó 
chúng ta có thể dễ dàng bảo trì, nâng cấp phần mềm cũng như giảm chi phí phát 
triển phần mềm. 
Trong những thập kỷ 1970, 1980, phương pháp phát triển phần mềm chủ yếu là 
lập trình có cấu trúc (structured programming). Cách tiếp cận cấu trúc đối với việc 
thiết kế chương trình dựa trên chiến lược chia để trị: Để giải một bài toán lớn, chúng 
ta tìm cách chia nó thành vài bài toán nhỏ hơn và giải riêng từng bài; để giải mỗi bài, 
hãy coi nó như một bài toán mới và có thể tiếp tục chia nó thành các bài toán nhỏ 
hơn; cuối cùng, ta sẽ đi đến những bài toán có thể giải ngay được mà không cần phải 
chia tiếp. Cách tiếp cận này được gọi là lập trình từ trên xuống (top-down 
programming). 
Lập trình từ trên xuống là một phương pháp tốt và đã được áp dụng thành công 
cho phát triển rất nhiều phần mềm. Tuy nhiên, cùng với sự đa dạng và phức tạp của 
phần mềm, phương pháp này bộc lộ những hạn chế. Trước hết, nó hầu như chỉ đáp 
ứng việc tạo ra các lệnh hay là các quy trình để giải quyết một bài toán. Dần dần, 
người ta nhận ra rằng thiết kế các cấu trúc dữ liệu cho một chương trình có tầm 
quan trọng không kém việc thiết kế các hàm/thủ tục và các cấu trúc điều khiển. Lập 
trình từ trên xuống không quan tâm đủ đến dữ liệu mà chương trình cần xử lý. 
Thứ hai, với lập trình từ trên xuống, chúng ta khó có thể tái sử dụng các phần 
của chương trình này cho các chương trình khác. Bằng việc xuất phát từ một bài toán 
cụ thể và chia nó thành các mảnh sao cho thuận, cách tiếp cận này có xu hướng tạo 
ra một thiết kế đặc thù cho chính bài toán đó. Chúng ta khó có khả năng lấy một 
đoạn mã lớn từ một chương trình cũ lắp vào một dự án mới mà không phải sửa đổi 
lớn. Việc xây dựng các chương trình chất lượng cao là khó khăn và tốn kém, do đó 
những nhà phát triển phần mềm luôn luôn muốn tái sử dụng các sản phẩm cũ. 
Thứ ba, môi trường hoạt động trong thực tế của các ứng dụng luôn thay đổi. 
Dẫn đến việc yêu cầu phần mềm cũng phải liên tục thay đổi theo để đáp ứng nhu 
cầu của người dùng nếu không muốn phần mềm bị đào thải. Do đó, một thiết kế 
linh hoạt mềm dẻo là cái mà các nhà phát triển phần mềm mong muốn. Phương 
pháp tiếp cận từ dưới lên (bottom-up) hỗ trợ tốt hơn cho tính linh hoạt mềm dẻo đó. 
Trong thực tế, thiết kế và lập trình từ trên xuống thường được kết hợp với thiết 
kế và lập trình từ dưới lên. Trong tiếp cận từ dưới lên, từ các vấn đề mà ta đã biết 
 8
cách giải và có thể đã có sẵn các thành phần tái sử dụng được chúng ta xây dựng 
dần theo hướng lên trên, hướng đến một giải pháp cho bài toán tổng. 
Các thành phần tái sử dụng được nên có tính mô-đun hóa cao nhất có thể. Mỗi 
mô-đun là một thành phần của một hệ thống lớn hơn, nó tương tác với phần còn lại 
của hệ thống theo một cách đơn giản và được quy ước chặt chẽ. Ý tưởng ở đây là 
một mô-đun có thể được "lắp vào" một hệ thống. Chi tiết về những gì xảy ra bên 
trong mô-đun không cần được xét đến đối với hệ thống nói chung, miễn là mô-đun 
đó hoàn thành tốt vai trò được giao. Đây gọi  ...  viện. Thay cho một lô các lớp đặt cùng một chỗ, các lớp được 
đặt vào các gói khác nhau tùy theo chức năng, chẳng hạn GUI, cấu trúc dữ liệu, hay 
cơ sở dữ liệu. Thứ hai, cấu trúc gói cho ta một không gian tên, giúp tránh trùng tên. 
Nếu một loạt lập trình viên tạo các lớp có tên giống nhau nhưng đặt tại các gói khác 
nhau thì máy ảo Java vẫn có thể được các lớp đó. Thứ ba, tổ chức gói cho ta một mức 
bảo mật (mức gói), ta có thể hạn chế mã ta viết trong một gói để chỉ có các lớp nằm 
trong gói đó mới có thể truy nhập. Ta sẽ nói kĩ hơn về vấn đề này sau. 
Sử dụng API bằng cách nào? 
Ta cần biết hai điều: (1) trong thư viện có những lớp nào, (2) khi đã tìm thấy một 
lớp, làm thế nào để biết nó có thể làm được gì. Để trả lời cho hai câu hỏi đó, ta có thể 
tra cứu một cuốn sách về Java hoặc tài liệu API. 
 88
Hình 6.2: Tài liệu API phiên bản Java 6, trang về ArrayList. 
Tài liệu API là nguồn tài liệu tốt nhất để tìm chi tiết về từng lớp và các phương 
thức của nó. Tại đó, ta có thể tìm và duyệt theo gói, tìm và tra cứu theo tên lớp. Với 
mỗi lớp, ta có đầy đủ thông tin mô tả lớp, các lớp liên quan, danh sách các phương 
thức, và đặc tả chi tiết của từng phương thức. 
6.3. MỘT SỐ LỚP THÔNG DỤNG TRONG API 
6.3.1. Math 
Math là lớp cung cấp các hàm toán học thông dụng. 
• Math.random() : trả về một giá trị kiểu double trong khoảng [0.0,..,1.0). 
• Math.abs() : trả về một giá trị double là giá trị tuyệt đối của đối số kiểu double, 
tương tự đối với đối số và giá trị trả về kiểu int. 
• Math.round() : trả về một giá trị int hoặc long (tùy theo đối số là kiểu float hay 
double) là giá trị làm tròn của đối số tới giá trị nguyên gần nhất. Lưu ý rằng các 
hằng kiểu float được Java hiểu là thuộc kiểu double trừ khi thêm kí tự f vào cuối, 
ví dụ 1.2f. 
• Math.min() : trả về giá trị nhỏ hơn trong hai đối số. Đối số có thể là int, long, 
float, hoặc double. 
• Math.max(): trả về giá trị lớn hơn trong hai đối số. Đối số có thể là int, long, float, 
hoặc double. 
Ngoài ra, Math còn các phương thức khác như sqrt(), tan(), ceil(), floor(), và 
sin(). Ta nên tra cứu chi tiết tại tài liệu API. 
 89
6.3.2. Các lớp bọc ngoài kiểu dữ liệu cơ bản 
Đôi khi, ta muốn đối xử với một giá trị kiểu cơ bản như là một đối tượng. Ví dụ, 
ở các phiên bản Java trước 5.0, ta không thể chèn thẳng một giá trị kiểu cơ bản vào 
trong một cấu trúc kiểu ArrayList. Các lời gọi tương tự như list.add(2) sẽ bị trình 
biên dịch báo lỗi do phương thức add lấy đối số là tham chiếu đối tượng. 
Trong những trường hợp như vậy, ta có các lớp bọc ngoài mỗi kiểu cơ bản 
(wrapper class). Các lớp bọc ngoài này có tên gần trùng với tên kiểu cơ bản tương 
ứng: Boolean, Character, Byte, Short, Integer, Long, Float, Double. Mỗi đối tượng 
thuộc các lớp trên bao bọc một giá trị kiểu cơ bản tương ứng, kèm theo các phương 
thức để thao tác với giá trị đó. Ví dụ: 
Hình 6.3: Sử dụng lớp Integer. 
Các lớp bọc ngoài khác cũng có cách sử dụng và các phương thức tiện ích tương 
tự như Integer. chẳng hạn mỗi đối tượng Boolean có phương thức booleanValue() 
trả về giá trị boolean chứa trong nó. 
Tóm lại, nếu dùng phiên bản Java trước 5.0 hay từ 5.0 trở đi, ta sẽ sử dụng 
ArrayList cho các giá trị int theo kiểu như sau: 
Với các phiên bản Java từ 5.0 trở đi, trình biên dịch tự động làm hộ ta các công 
việc bọc và gỡ các đối tượng bọc ngoài thuộc kiểu tương ứng. Nói cách khác, 
 90
ArrayList thực sự là danh sách của các đối tượng Integer, nhưng ta có thể coi như 
ArrayList lấy vào và trả về các giá trị int. Trình biên dịch không chỉ tự động bọc và 
gỡ bọc trong các tình huống sử dụng các cấu trúc dữ liệu tương tự ArrayList. Việc 
này còn xảy ra ở hầu hết các tình huống khác: 
• Đối số của phương thức: dù một phương thức khai báo tham số kiểu cơ bản hay 
kiểu lớp bọc ngoài thì nó vẫn chấp nhận đối số ở cả dạng cơ bản cũng như kiểu 
lớp bọc ngoài. 
• Giá trị trả về: dù một phương thức khai báo kiểu trả về kiểu cơ bản hay bọc 
ngoài thì lệnh return trong phương thức dùng giá trị ở cả dạng cơ bản cũng như 
bọc ngoài đều được. 
• Biểu thức boolean: ở những vị trí yêu cầu một biểu thức boolean, ta có thể dùng 
biểu thức cho giá trị boolean (chẳng hạn 2 < a), hoặc một biến boolean, hoặc một 
tham chiếu kiểu Boolean đều được. 
• Phép toán số học: ta có thể dùng tham chiếu kiểu bọc ngoài làm toán hạng của 
các phép toán số học, kể cả phép ++. 
• Phép gán: ta có thể dùng một tham chiếu kiểu bọc ngoài để gán trị cho một biến 
kiểu cơ bản và ngược lại. Ví dụ: Double d = 10.0; 
6.3.3. Các lớp biểu diễn xâu kí tự 
String và StringBuffer là hai lớp thông dụng để biểu diễn dữ liệu dạng xâu kí tự. 
String dành cho các chuỗi kí tự không thể sửa đổi nội dung. Tất cả các hằng xâu kí tự 
như "abc" đều được Java coi như các thực thể của lớp String. StringBuffer và 
StringBuilder cho phép sửa đổi nội dung chuỗi, sử dụng một trong hai lớp này sẽ 
hiệu quả hơn String nếu ta cần dùng nhiều thao tác sửa xâu. Từ Java 5.0, ta nên dùng 
StringBuilder thay vì String Buffer cho mục đích này, trừ khi ta cần chú ý tránh xung 
đột giữa các thao tác xử lý xâu tại các luồng khác nhau. 
String và StringBuffer/StringBuilder đều có các phương thức sau: 
• charAt (int index) trả về kí tự tại một vị trí 
• compareTo() so sánh giá trị với một đối tượng cùng loại. 
• các phương thức indexOf() tìm vị trí của một kí tự/xâu con theo chiều từ trái 
sang phải. 
• các phương thức lastIndexOf() tìm vị trí của một kí tự/xâu con theo chiều từ phải 
sang trái. 
• length() trả về độ dài của xâu. 
• substring(int start, int end) trả về đối tượng String là xâu con. 
Để nối xâu, ta dùng concat() cho String và append() cho StringBuffer/StringBuilder. 
Ngoài ra, String còn có thêm các tiện ích : 
 91
• valueOf() trả về biểu diễn kiểu String của một giá trị thuộc kiểu cơ bản, 
• split() để tách xâu thành các từ con theo một cú pháp cho trước, 
• replace(char old, char new) trả về một String mới là kết quả của việc thay thế hết 
các kí tự old bằng kí tự new 
• trim() trả về một String mới là kết quả của việc xóa các kí tự trắng ở đầu và cuối 
String hiện tại. 
StringBuffer và StringBuilder có các phương thức cung cấp các phương thức để 
chèn (insert), thay (replace), xóa một phần (delete), đảo xâu (reverse) tại đối 
tượng StringBuffer/StringBuilder hiện tại. 
Ta đã biết những cách đơn giản để lấy biểu diễn bằng xâu kí tự cho các giá trị 
số: 
int n = 302044; 
String s1 = "" + n; 
String s2 = Integer.toString(n); 
Đôi khi, ta cần biểu diễn các giá trị số một cách cầu kì hơn, chẳng hạn 302,044, 
hay quy định số chữ số nằm sau dấu phảy thập phân sẽ được in ra, biểu diễn dạng 
nhị phân, hệ cơ số 16... Phương thức format() của lớp String giúp chúng ta làm được 
việc này. Ví dụ: 
6.4. TRÒ CHƠI BẮN TÀU 
Trong mục này, ta sẽ làm một chương trình ví dụ: trò chơi bắn tàu SinkAShip6. 
Đây sẽ là một ứng dụng hoàn chỉnh minh họa việc sử dụng Java API, và cũng là một 
ứng dụng đủ lớn để minh họa rõ hơn sự tương tác giữa các đối tượng trong chương 
trình. 
Trò chơi bắn tàu được mô tả như sau: Máy tính có một số con tàu kích thước 1 x 
3 trên một vùng là lưới vuông 7 x 7, cho phép người chơi bắn mỗi lần một viên đạn, 
mỗi viên trúng ô nào sẽ làm cháy phần tàu nằm trong ô đó, nếu như ở đó có tàu. 
Người chơi không biết các con tàu đó ở đâu, nhưng có mục tiêu là bắn cháy hết tàu, 
nên phải đoán xem nên bắn vào đâu để tốn càng ít đạn càng tốt. 
6 Chỉnh sửa từ ví dụ DotComBust của cuốn Head First Java, 2nd Edition. 
 92
Khi bắt đầu một ván chơi, chương trình sẽ đặt ngẫu nhiên ba con tàu vào một 
lưới ảo kích thước 7x7, sau đó mời người chơi bắn phát đầu tiên. 
Ta chưa học lập trình giao diện đồ họa, do đó chương trình của chúng ta sẽ sử 
dụng giao diện dòng lệnh. Mỗi lần, chương trình sẽ mời người chơi nhập tọa độ một 
phát bắn, người chơi nhập một tọa độ có dạng "A5" hay "B1". Chương trình xử lý 
phát bắn, kiểm tra xem có trúng hay không rồi in ra màn hình một thông báo thuộc 
một trong các loại: "hit" (trúng), "miss" (trượt), hoặc "You sunk a ship" (khi một tàu 
vừa bị bắn cháy hết). Khi cả ba con tàu đều bị cháy hết, ván chơi kết thúc, chương 
trình thông báo điểm của người chơi. 
Tọa độ trong trò chơi có dạng "A4", trong đó kí tự thứ nhất là một chữ cái trong 
đoạn từ A đến G đại diện cho tọa độ dòng, kí tự thứ hai là một chữ số trong đoạn từ 
0 đến 6 đại diện cho tọa độ cột trong lưới vuông 7x7. 
Thiết kế mức cao cho hoạt động của chương trình: 
Bước tiếp theo là xác định ta cần đến các đối tượng nào. Ít nhất, ta sẽ cần đến 
ván chơi và các mô hình tàu, tương ứng với hai lớp SinkAShip và Ship. Khi viết một 
lớp, quy trình chung được gợi ý như sau: 
 93
• Xác định các nhiệm vụ và hoạt động của lớp 
• Liệt kê các biến thực thể và phương thức 
• Viết mã giả cho các phương thức để mô tả thuật toán/quy trình công việc 
của chúng. 
• Viết chương trình test cho các phương thức. 
• Cài đặt lớp 
• Test các phương thức 
• Tìm lỗi và cài lại nếu cần 
• Test với người dùng thực. 
Ta sẽ bỏ qua bước cuối cùng. 
Đầu tiên là lớp Ship, ta cần lưu hai thông tin chính: tọa độ các ô của tàu và tàu 
đã bị bắn cháy hết hay chưa. Dưới đây là thiết kế mà ta dễ dàng nghĩ đến. 
Nhưng thiết kế trên chưa tính đến trường hợp người chơi bắn hai phát vào cùng 
một ô, chưa phân biệt một phát đạn bắn vào ô chưa bị cháy với một phát đạn bắn 
vào ô đã cháy. Nếu người chơi bắn ba lần vào cùng một ô thì thuật toán trên sẽ cho 
là tàu đã bị bắn cháy, mặc dù thực tế vẫn còn hai ô chưa bị bắn. Ta có thể giải quyết 
vấn đề này bằng một mảng phụ chứa các giá trị boolean để đánh dấu các ô đã bị 
bắn, hoặc dùng giá trị int sẵn có tại mảng locationCells để mã hóa các trạng thái 
chưa bị bắn / đã bị bắn. Tuy nhiên, để có giải pháp vừa gọn gàng, vừa tận dụng thư 
viện Java, ta chọn cách dùng ArrayList để lưu danh sách các ô chưa bị bắn của con 
tàu. Mỗi khi ô nào bị bắn trúng, phần tử tương ứng sẽ bị xóa khỏi danh sách. Khi 
danh sách rỗng là khi tàu đã bị bắn cháy. Như vậy ta chỉ cần một đối tượng 
ArrayList là đủ dùng thay cho cả mảng int locationCells và biến đếm numOfHits. Ta 
có thiết kế như sau: 
 94
Cài đặt lớp Ship theo thiết kế trên: 
Lớp SinkAShip có các nhiệm vụ sau: 
• tạo ra ba con tàu, 
• cho mỗi con tàu một cái tên, 
• đặt ba con tàu vào lưới. Ở đây ta cần tính vị trí tàu một cách ngẫu nhiên, ta 
tạo một lớp GameHelper để cung cấp tiện ích này (sẽ nói đến Helper sau). 
• hỏi tọa độ bắn của người chơi, kiểm tra với cả ba con tàu rồi in kết quả. Lặp 
cho đến khi nào cả ba con tàu đều đã bị cháy. 
Như vậy, ta cần ba lớp: SinkAShip vận hành trò chơi, Ship đại diện cho tàu, và 
GameHelper cung cấp cho Sink các tiện ích trợ giúp như nhận input từ người chơi 
và sinh vị trí cho các con tàu. Ta cần một đối tượng SinkAShip, ba đối tượng Ship, và 
một đối tượng GameHelper. Ngoài ra còn có các đối tượng ArrayList chứa trong ba 
đối tượng Ship. 
 95
Vậy ai làm gì trong một ván SinkAShip? Các đối tượng trong chương trình bắn tàu 
hoạt động và tương tác với nhau theo từng giai đoạn như sau: 
1. Phương thức main() của lớp SinkAShip tạo một đối tượng SinkAShip, đối 
tượng này sẽ vận hành trò chơi. 
2. Đối tượng SinkAShip tạo một đối tượng GameHelper để nó làm 'trợ lí'. 
3. Đối tượng SinkAShip tạo một ArrayList để chuẩn bị lưu trữ ba đối tượng 
Ship. 
4. Đối tượng SinkAShip tạo ba đối tượng Ship và gắn vào ArrayList nói trên. 
5. Đối tượng SinkAShip yêu cầu 'trợ lí' sinh tọa độ cho từng đối tượng Ship, 
chuyển dữ liệu tọa độ nhận được cho các đối tượng Ship. Các đối tượng Ship 
cập nhật danh sách tọa độ tại ArrayList của mình. 
6. Đối tượng SinkAShip yêu cầu 'trợ lí' lấy tọa độ bắn của người chơi, 'trợ lí' hiển 
thị lời mời nhập tại giao diện dòng lệnh và nhận input của người chơi). Nhận 
được kết quả do 'trợ lí' cung cấp, đối tượng SinkAShip yêu cầu từng đối 
tượng Ship tự kiểm tra xem có bị bắn trúng hay không. Mỗi đối tượng Ship 
kiểm tra từng vị trí trong ArrayList của mình và trả về kết quả tương ứng 
 96
("miss", "hit", ). Bước này lặp đi lặp lại cho đến khi tất cả các con tàu đều bị 
bắn cháy. 
Như đã nói ở Chương 1, chương trình hướng đối tượng là một nhóm các đối tượng 
tương tác với nhau. Các ví dụ trước trong cuốn sách này đều nhỏ nên khó thấy rõ sự 
tương tác giữa các đối tượng. Ví dụ trò chơi bắn tàu này đủ lớn để minh họa được 
khía cạnh đó. 
Với hoạt động như đã mô tả, lớp SinkAShip được thiết kế như sau: 
 97
Lớp SinkAShip được cài đặt như sau: 
 98
 99
Cuối cùng là lớp GameHelper chứa các phương thức tiện ích cho SinkAShip sử 
dụng. Lớp này cung cấp hai phương thức. Phương thức getUserInput() nhận input 
của người chơi bằng cách hiển thị lời mời nhập tọa độ bắn và đọc chuỗi kí tự người 
dùng gõ vào từ dòng lệnh. Phương thức thứ hai, placeShip(), sinh tự động vị trí cho 
các con tàu. Trong mã nguồn, có một số lệnh System.out.print(ln) trong phương thức 
placeShip() đã được chuyển thành dòng chú thích. Đó là các lệnh hiển thị tọa độ của 
các con tàu. Nếu cho các lệnh này chạy, chúng sẽ cho phép ta biết tọa độ của tàu để 
chơi "ăn gian" hoặc để test chương trình. 
Do chỉ là một ví dụ minh họa, chương trình này tuy hoàn chỉnh nhưng được viết 
ở mức độ vắn tắt tối đa với giao diện tối thiểu. Bạn đọc có thể sửa để cải thiện phần 
giao diện đối với người dùng, chẳng hạn như hiển thị bản đồ vùng biển cùng với các 
thông tin về các tọa độ đã bắn trúng hoặc trượt để hỗ trợ người chơi, hoặc có thể sử 
dụng thư viện giao diện đồ họa của Java để tăng tính thẩm mỹ và tính thân thiện 
người dùng. 
 100
Hình 6.4: GameHelper, phần 1/2. 
 101
Hình 6.5: GameHelper, phần 2/2. 
 102
Bài tập 
1. Viết lớp Dice mô hình hóa xúc xắc và việc tung xúc xắc. Mỗi đối tượng Dice có 
một biến int lưu trạng thái hiện tại là mặt ngửa của lần gieo gần nhất (một giá trị 
trong khoảng từ 1 đến 6), một phương thức public roll() giả lập việc gieo xúc xắc 
và trả về giá trị của mặt ngửa vừa gieo được. Hãy sử dụng thư viện Math cho 
việc sinh số ngẫu nhiên. 
2. Viết lớp Card mô hình hóa các quân bài tú-lơ-khơ. Sử dụng ArrayList để xây 
dựng lớp CardSet mô hình hóa một xấp bài có quân không xác định. Cài phương 
thức shuffle() của lớp CardSet với nhiệm vụ tráo ngẫu nhiên các quân bài trong 
xấp bài. Viết lớp CardTestDrive để thử nghiệm hai lớp Card và CardSet nói trên. 
3. Có thể dùng một đối tượng thuộc lớp Scanner để đọc dữ liệu từ một file text 
tương tự như đọc dữ liệu từ bàn phím. Ví dụ: 
try { 
 Scanner input = 
 new Scanner (new File("C:\\Tmp\\test.txt")); 
 // đọc dữ liệu 
 int n = input.nextInt(); 
} catch (java.io.FileNotFoundException e) { } 
a) Hãy viết một chương trình Java đọc dữ liệu từ một file text và in từng từ ra màn 
hình. 
b) Sửa chương trình tại phần a để bỏ qua các dấu .,:.khi đọc các từ trong văn bản. 
Gợi ý: 
Lệnh sau đây đặt chế độ cho đối tượng Scanner coi tất cả các kí tự không phải a..z 
hay A..Z như các kí tự phân tách giữa các từ khi thực hiện lệnh đọc từng từ 
input.useDelimiter(Pattern.compile("[^a-zA-Z]")); 
Lệnh sau đây bỏ qua tất cả các kí tự không phải a..z hay A..Z cho đến khi gặp một kí 
tự trong khoản a..z hay A..Z 
input.skip("[^a-zA-Z]*"); 

File đính kèm:

  • pdfgiao_trinh_lap_trinh_huong_doi_tuong_voi_java_phan_1.pdf