LỜI NÓI ĐẦU

Hiện nay có nhiều ngôn ngữ lập trình được sử dụng để viết các ứng dụng trên máy tính như: C++, C#, VB, VB.Net…. Mỗi ngôn ngữ đều có một thế mạnh riêng, nếu

biết lựa chọn một ngôn ngữ phù hợp để xây dựng ứng dụng sẽ giúp cho các chuyên gia

lập trình tích kiệm được thời gian, công sức và giúp xử lý kỹ thuật lập trình đơn giản

và thuận tiện hơn.

Java là ngôn ngữ lập trình hướng đối tượng thuần túy với nhiều đặc trưng ưu việt

so với các ngôn ngữ lập trình hướng đối tượng khác như tính độc lập với nên, Tính Đa

nhiệm - đa luồng tính bảo mật, khả năng đóng gói, giao tiếp….

Khác với các ngôn ngữ lập trình hướng đối tượng khác như c++ hay c# là ngay

từ khi ra đời mục tiêu của java là phát triển một ngôn ngữ đơn giản, nhỏ nhẹ và có thể

chạy trên nhiều môi trường khác nhau, do đó Java đặc biệt thích hợp để viết chương

trình cho các ứng dụng trên mạng, trên các thiết bị di động, …, việc việt các ứng dụng

này bằng Java dễ dàng hơn nhiều so với các ngôn ngữ khác.

Chính vì vậy trong rất nhiều ngôn ngữ lập trình đang được các chuyên gia sử

dụng để viết nên các ứng dụng trên máy tính, trong doanh nghiệp, trên các thiết bị cầm

tay... Java luôn là ngôn ngữ được các chuyên gia lựa chọn ở mức ưu tiên số 1. Java đã

được ứng dụng rộng rãi và trở thành một ngôn ngữ lập trình được ưa chuộng bậc nhất

hiện nay. Ở Việt Nam, Java đang rất phát triển các doanh nghiệp đã sử dụng Java

trong các sản phẩm Game, phần mềm, ngân hàng, thiết bị cầm tay, ứng dụng

web,…Những chuyên gia thành thạo ngôn ngữ này luôn đứng trước sự săn đón của

hàng trăm nghìn việc làm và cơ hội thăng tiến.

Tập bài giảng này được xây dựng gồm có 6 chương trình bày chi tiết về lập trình

Java cơ bản, bám sát theo khung chương trình môn lập trình Java của Trường Đại học

Sư phạm Kỹ thuật Nam Định. Nhằm trang bi cho sinh viên những kiến thức cơ bản về lập trình Java tạo nền tảng để sinh viên tiếp cận với ngôn ngữ này cũng như học các học phần khác trong chương trình như thực tập lập trình trên thiết bị di động. Tập bài giảng được sử dụng làm tài liệu giảng dạy cho các giáo viên, giảng viên và tài liệu học tập cho học sinh – sinh viên thuộc Trường Đại học Sư phạm Kỹ thuật Nam Định. Ngoài ra có thể làm tài liệu tham khảo cho mọi đối tượng quan tâm đến lập trình Java.

i

NHÓM TÁC GIẢ

MỤC LỤC

CHƢƠNG 1: LẬP TRÌNH JAVA CƠ BẢN ...................................................... 1

1.1. Giới thiệu về Java và cài đặt ..................................................................... 1

1.1.1. Lịch sử Java .......................................................................................... 1

1.1.2. Đặc trưng của Java ................................................................................ 1

1.1.3. Các kiểu chương trình Java .................................................................. 5

1.1.4. Máy ảo Java .......................................................................................... 6

1.1.5. Bộ công cụ phát triển JDK .................................................................... 8

1.1.6. Cài đặt JDK ......................................................................................... 11

1.2. Ngôn ngữ lập trình Java .......................................................................... 17

1.2.1. Mở đầu lập trình với Java ................................................................... 17

1.2.2. Cấu trúc chương trình Java ................................................................. 17

1.2.3. Kiểu dữ liệu cơ sở, hằng, biến ............................................................ 20

1.2.4. Kiểu dữ liệu Mảng (Array) ................................................................. 27

1.2.5. Toán tử và biểu thức ........................................................................... 29

1.2.6. Các cấu trúc điều khiển ....................................................................... 32

1.2.7. Hàm – Phương thức (Function – Method) .......................................... 38

1.2.8. Nhập xuất dữ liệu ................................................................................ 39

Câu hỏi và bài tập chƣơng 1 .......................................................................... 43

CHƢƠNG 2: LẬP TRÌNH HƢỚNG ĐỐI TƢỢNG TRONG JAVA ............ 45

2.1. Giới thiệu về lập trình hƣớng đối tƣợng ................................................ 45

2.2. Trừu tƣợng hoá dữ liệu ........................................................................... 47

2.3. Lớp và định nghĩa lớp trong Java .......................................................... 50

2.4. Đối tƣợng .................................................................................................. 62

2.5. Thiết lập và hủy ....................................................................................... 63

2.5.1. Thiết lập .............................................................................................. 63

2.5.2. Hủy ...................................................................................................... 65

2.6. Các đặc tính của lớp ................................................................................ 66

ii

2.6.1. Tính bền vững ..................................................................................... 66

2.6.2. Tính thừa kế ........................................................................................ 68

2.6.3. Tính đa thừa kế ................................................................................... 72

2.6.4. Tính đa hình ........................................................................................ 73

2.6.6. Lớp trừu tượng .................................................................................... 76

2.7. Xử lý ngoại lệ ............................................................................................ 80

Câu hỏi và bài tập chƣơng 2 .......................................................................... 84

CHƢƠNG 3: CÁC GÓI VÀ LẬP TRÌNH VÀO RA ....................................... 92

3.1. Các gói trong Java ................................................................................... 92

3.1.1. Giới thiệu ............................................................................................ 92

3.1.2. Các giao diện ...................................................................................... 92

3.1.3. Các gói ................................................................................................ 95

3.1.4. Gói và điều khiển truy xuất ................................................................ 98

3.1.5. Gói Java.lang ...................................................................................... 99

3.2. Các dòng (Stream) ................................................................................. 115

3.3. Gói Java.io .............................................................................................. 115

3.3.1. Lớp InputStream ............................................................................... 115

3.3.2. Lớp OutputStream ............................................................................ 117

3.3.3. Vào ra mảng byte .............................................................................. 117

3.3.4. Vào ra tập tin .................................................................................... 119

3.3.5. Nhập xuất lọc .................................................................................... 121

3.3.6. Vào ra có sử dụng bộ đệm ................................................................ 122

3.3.7. Lớp Reader và Writer ....................................................................... 125

3.3.8. Nhập xuất chuỗi ................................................................................ 125

3.3.9. Lớp PrinterWriter ............................................................................. 128

3.3.10. Giao diện DataInput ........................................................................ 129

3.3.11. Giao diện DataOutput ..................................................................... 129

3.3.12. Lớp RandomAccessFile .................................................................. 130

3.4. Gói java.awt.print .................................................................................. 132

iii

Câu hỏi và bài tập chƣơng 3 ........................................................................ 133

CHƢƠNG 4: LẬP TRÌNH ĐỒ HOẠ AWT ................................................... 136

4.1. Giới thiệu về AWT ................................................................................. 136

4.2. Container (vật chứa) .............................................................................. 137

4.2.1. Giới thiệu .......................................................................................... 137

4.2.2. Frame ................................................................................................ 137

4.2.3. Panel.................................................................................................. 138

4.2.4. Dialog................................................................................................ 139

4.3. Thành phần (Component) ..................................................................... 140

4.3.1. Giới thiệu .......................................................................................... 140

4.3.2. Nhãn .................................................................................................. 140

4.3.3. Ô văn bản .......................................................................................... 142

4.3.4. Vùng văn bản .................................................................................... 144

4.3.5. Nút .................................................................................................... 145

4.3.6. Checkbox và Radio Button .............................................................. 147

4.3.7. Danh sách lựa chọn ........................................................................... 149

4.4. Quản lý cách trình bày .......................................................................... 151

4.4.1. FlowLayout manager ....................................................................... 152

4.4.2. BorderLayout Manager .................................................................... 153

4.4.3. Card Layout Manager ....................................................................... 154

4.4.4. GridLayout Manager ........................................................................ 156

4.4.5. GridBagLayout Manager .................................................................. 158

4.5. Xử lý các sự kiện .................................................................................... 163

4.6. Thực đơn ................................................................................................. 169

Câu hỏi và bài tập chƣơng 4 ........................................................................ 173

CHƢƠNG 5: JAVA APPLET VÀ SWING .................................................... 175

5.1. Java Applet ............................................................................................. 175

5.1.1. Giới thiệu về Applet ......................................................................... 175

5.1.2. Cấu trúc của một Applet ................................................................... 175

iv

5.1.3. Chu trình sống của một Applet ......................................................... 178

5.1.4. Truyền tham số cho Applet ............................................................... 179

5.1.5. Lớp Graphics .................................................................................... 181

5.1.6. Điều khiển màu ................................................................................. 190

5.1.7. Điều khiển Font ................................................................................ 191

5.1.8. Lớp FontMetrics ............................................................................... 192

5.1.9. Chọn chế độ để vẽ ............................................................................. 196

5.2. Java Swing .............................................................................................. 198

5.2.1 Giới thiệu về Swing ........................................................................... 198

5.2.2. Các biểu tượng và nhãn .................................................................... 199

5.2.3. Các trường văn bản ........................................................................... 200

5.2.4. Nút nhấn ........................................................................................... 201

5.2.5. Lớp JButton ...................................................................................... 202

5.2.6. Hộp kiểm tra ..................................................................................... 202

5.2.7. Radio Button ..................................................................................... 203

5.2.8. ComboBox ........................................................................................ 203

5.2.9. Tabbed Pane ...................................................................................... 203

5.2.10. Scroll Pane ...................................................................................... 204

5.2.11. Cây .................................................................................................. 205

5.2.12. Bảng ................................................................................................ 207

Câu hỏi và bài tập chƣơng 5 ........................................................................ 210

CHƢƠNG 6: LẬP TRÌNH ĐA LUỒNG ........................................................ 224

6.1. Giới thiệu về lập trình luồng ................................................................. 224

6.2. Luồng và đa luồng ................................................................................. 224

6.3. Tạo và quản lý luồng ............................................................................. 225

6.4. Vòng đời của luồng ................................................................................ 228

6.5. Trạng thái của luồng và các phƣơng thức của lớp Thread ............... 228

6.6. Thời gian biểu luồng .............................................................................. 229

6.7. Luồng chạy ngầm ................................................................................... 231

v

6.8. Đa luông với Applet ............................................................................... 232

6.9. Nhóm luồng ............................................................................................ 233

6.10. Sự đồng bộ luồng.................................................................................. 234

6.10.1. Đồng bộ mã ..................................................................................... 235

6.10.2. Sử dụng khối đồng bộ (Synchronized Block) ................................ 237

6.10.3. Ưu điểm của các phương thức đồng bộ .......................................... 240

6.11. Cơ chế đợi thông báo ........................................................................... 240

6.12. Khoá chết (Deadlock) .......................................................................... 244

6.13. Thu dọn rác .......................................................................................... 246

Câu hỏi và bài tập chƣơng 6 ........................................................................ 250

vi

TÀI LIỆU THAM KHẢO ............................................................................ 254

DANH MỤC CÁC HÌNH VẼ

Bảng 1.1. Bảng các ký hiệu ghi chú ..................................................................... 18

Bảng 1.2. Các từ khóa của Java ............................................................................ 20

Bảng 1.3. Các kiểu số nguyên .............................................................................. 21

Bảng 1.4. Các phép toán trên kiểu số nguyên....................................................... 21

Bảng 1.5. Các loại số thực .................................................................................... 22

Bảng 1.6. Các phép toán trên kiểu số thực ........................................................... 22

Bảng 1.7. Giá trị mặc định .................................................................................... 23

Bảng 1.8. Hằng số nguyên .................................................................................... 25

Bảng 1.9. Các hằng số thực .................................................................................. 26

Bảng 1.10. Toán tử 2 ngôi .................................................................................... 30

Bảng 1.11. Danh sách các toán tử đơn ................................................................. 30

Bảng 1.12. Danh sách các toán tử gán .................................................................. 31

Bảng 1.13. Danh sách các toán tử trên kiểu số thực ............................................. 31

Bảng 1.14. Danh sách các phép toán logic ........................................................... 31

Bảng 2.1. Một ví dụ về hai phương pháp giải quyết OOP và Structured ............. 47

Bảng 2.2. Sử dụng các bổ nghĩa ........................................................................... 54

Bảng 2.3. Các exception thường gặp .................................................................... 83

Bảng 2.4. Các phương thức thông dụng của Throwable ...................................... 84

Bảng 3.1. Truy cập đến các thành phần của lớp ................................................... 99

Bảng 3.2. Các lớp trình bao bọc cho các kiểu dữ liệu nguyên thủ y. .................... 99

Bảng 3.3. Lớp Runtime ....................................................................................... 110

Bảng 3.4. Lớp System. ........................................................................................ 111

Bảng 3.5. Lớp Object. ......................................................................................... 114

Bảng 3.6. Các phương thức của lớp InputStream ............................................... 116

Bảng 3.7. Các phương thức lớp OutputStream ................................................... 117

Bảng 3.8. Các phương thức của giao diện DataInput ......................................... 129

Bảng 3.9. Các phương thức của giao diện DataOutput ...................................... 130

vii

Bảng 4.1. Các phương thức của Label ................................................................ 141

Bảng 4.2. Các phương thức của TextField ......................................................... 143

Bảng 4.3. Các phương thức của TextArea .......................................................... 144

Bảng 4.4. Các biến thành viên của lớp GridBagConstraints .............................. 159

Bảng 4.5. Các biến thành viên dữ liệu tĩnh của biến fill .................................... 159

Bảng 4.6. các sự kiện của AWT ......................................................................... 164

Bảng 5.1. Các phương thức của một applet ........................................................ 176

Bảng 5.2. Phạm vi giá trị của các thành phần màu ............................................. 190

Bảng 5.3. Các giá trị RGB .................................................................................. 190

Bảng 5.4. Các màu thường gặp ........................................................................... 191

Bảng 5.5. Các lớp thành phần Swing .................................................................. 199

Bảng 5.6. Các hàm sử dụng của JLabel .............................................................. 200

Bảng 5.7. Các hằng số ........................................................................................ 205

viii

Bảng 6.1. Các phương thức của một lớp luồng .................................................. 229

DANH MỤC CÁC HÌNH VẼ

Hình 1.1. Cách biên dịch truyền thống ................................................................... 3

Hình 1.2. Dịch chương trình Java ........................................................................... 3

Hình 1.3. Trang web tải bộ công cụ Java ............................................................. 12

Hình 1.4. Biểu tượng Java Download ................................................................... 12

Hình 1.5. Lựa chọn bản quyền .............................................................................. 12

Hình 1.6. Bộ JDK cho Windows bản 64 bit ......................................................... 12

Hình 1.7. Màn hình cài đặt đầu tiên ...................................................................... 13

Hình 1.8. Màn hình Custom Setup ....................................................................... 13

Hình 1.9. Màn hình Description Folder ................................................................ 13

Hình 1.10. Màn hình Description Folder .............................................................. 14

Hình 1.11. Đường dẫn của thư mục cài đặt Java .................................................. 14

Hình 1.12. Màn hình Computer Properties ........................................................... 15

Hình 1.13. Màn hình System Properties ............................................................... 15

Hình 1.14. Màn hình Environment Variables ....................................................... 16

Hình 1.15. Màn hình Edit System Variable .......................................................... 16

Hình 1.16. Màn hình cửa sổ run ........................................................................... 16

Hình 1.17. Màn hình Command Prompt .............................................................. 16

Hình 3.1. Minh họa khái niệm của String Pool. ................................................. 101

Hình 3.2. Kết quả chạy ví dụ 3.22 ...................................................................... 112

Hình 3.3. Kết quả chạy ví dụ 3.23 ...................................................................... 114

Hình 3.4. Kết quả chạy ví dụ 3.25 ...................................................................... 119

Hình 3.5. Kết quả chạy ví dụ 3.26 ...................................................................... 121

Hình 3.6. Kết quả chạy ví dụ 3.27 ...................................................................... 125

Hình 3.7. Kết quả chạy ví dụ 3.28 ...................................................................... 127

Hình 3.8. Kết quả chạy ví dụ 3.29 ...................................................................... 128

Hình 3.9. Kết quả chạy ví dụ 3.30 ...................................................................... 132

Hình 4.1. Hệ thống cây phân cấp lớp AWT ....................................................... 137

ix

Hình 4.2. Kết quả chạy ví dụ 4.1 ........................................................................ 138

Hình 4.3. Kết quả chạy ví dụ 4.2 ........................................................................ 139

Hình 4.4. Các lớp thành phần ............................................................................. 140

Hình 4.5. Kết quả chạy ví dụ 4.3 ........................................................................ 142

Hình 4.6. Kết quả chạy ví dụ 4.4 ........................................................................ 143

Hình 4.7. Kết quả chạy ví dụ 4.5 ........................................................................ 145

Hình 4.8. Kết quả chạy ví dụ 4.6 ........................................................................ 147

Hình 4.9. Kết quả chạy ví dụ 4.7 ........................................................................ 149

Hình 4.10. Kết quả chạy ví dụ 4.8 ...................................................................... 151

Hình 4.11. Kết quả chạy ví dụ 4.9 ...................................................................... 153

Hình 4.12. BorderLayout .................................................................................... 154

Hình 4.13. Kết quả chạy ví dụ 4.10 .................................................................... 156

Hình 4.14. Kết quả chạy ví dụ 4.10 .................................................................... 158

Hình 4.15. Kết quả chạy ví dụ 4.12 .................................................................... 162

Hình 4.16. Kết quả chạy ví dụ 4.13 .................................................................... 167

Hình 4.17. Gói Event .......................................................................................... 167

Hình 4.18. Event Listener ................................................................................... 168

Hình 4.19. Action Listener ................................................................................. 168

Hình 4.20. Item Listener ..................................................................................... 168

Hình 4.21. Window Listener .............................................................................. 169

Hình 4.22. Các Component ................................................................................ 169

Hình 4.23. Kết quả chạy ví dụ 4.14 .................................................................... 172

Hình 4.24. Pop-up menu ..................................................................................... 173

Hình 5.1. Kết quả chạy ví dụ 5.1 ........................................................................ 178

Hình 5.2. Chu trình sống của một applet ............................................................ 178

Hình 5.3. Kết quả chạy ví dụ 5.2 ........................................................................ 180

Hình 5.4. Kết quả chạy ví dụ 5.3 ........................................................................ 184

Hình 5.5. Kết quả chạy ví dụ 5.4 ........................................................................ 188

Hình 5.6. Kết quả chạy ví dụ 5.5 ........................................................................ 189

x

Hình 5.7. Kết quả chạy ví dụ 5.6 ........................................................................ 194

Hình 5.8. Kết quả chạy ví dụ 5.7 ........................................................................ 196

Hình 5.9. Kết quả chạy ví dụ 5.8 ........................................................................ 198

Hình 6.1. Kết quả thực hiện ví dụ 6.2 ................................................................. 227

Hình 6.2. Vòng đời của luồng ............................................................................. 228

Hình 6.3. Kết quả chạy ví dụ 6.3 ........................................................................ 231

Hình 6.4. Kết quả chạy ví dụ 6.4 ........................................................................ 233

Hình 6.5. Kết quả chạy ví dụ 6.5 ........................................................................ 237

Hình 6.6. Kết quả hiển thị của ví dụ 6.5 không có sự đồng bộ .......................... 237

Hình 6. 7. Kết quả sau mỗi lần kích chuột .......................................................... 243

Hình 6.8. Kết quả thực hiện ví dụ 6.8 ................................................................. 246

xi

Hình 6. 9. Kết quả chạy ví dụ 6.9 ....................................................................... 249

CHƢƠNG 1: LẬP TRÌNH JAVA CƠ BẢN

1.1. Giới thiệu về Java và cài đặt

1.1.1. Lịch sử Java

Java là ngôn ngữ lập trình hướng đối tượng (tựa C++) doSun Microsystem đưa ra

vào giữa thập niên 90.

Chương trình viết bằng ngôn ngữ lập trình Java có thể chạytrên bất kỳ hệ thống

nào có cài máy ảo Java (Java VirtualMachine).

Ngôn ngữ lập trình Java do James Gosling và các công sựcủa Công ty Sun

Microsystem phát triển.

Đầu thập niên 90, Sun Microsystem tập hợp các nhà nghiêncứu thành lập nên nhóm đặt tên là Green Team. Nhóm GreenTeam có trách nhiệm xây dựng công nghệ

mới cho ngành điệntử tiêu dùng. Để giải quyết vấn đề này nhóm nghiên cứu pháttriển

đã xây dựng một ngôn ngữ lập trình mới đặt tên là Oaktương tự như C++ nhưng loại

bỏ một số tính năng nguy hiểmcủa C++ và có khả năng chạy trên nhiều nền phần cứng

khácnhau. Cùng lúc đó WorldWide Web bắt đầu phát triển và Sun đãthấy được tiềm

năng của ngôn ngữ Oak nên đã đầu tư cải tiếnvà phát triển. Sau đó không lâu ngôn ngữ

mới với tên gọi làJava ra đời và được giới thiệu năm 1995.

Đến quý II năm 1995, Sun Microsystems công bố chính thức Java và nó trở

thành một công cụ mạnh mẽ cho việc phát triển các ứng dụng trên Internet.

Java được xây dựng chủ yếu trong bộ công cụ phát triển JDK (Java Development

Kit) nó gồm các trình biên dịch, thông dịch, giúp đỡ, soạn tài liệu, thư viện chuẩn…

sau đó các nhà phát triển phần mềm đã xây dựng thêm rất nhiều nhánh mới như Java

Mail (Java-Thư tín), Java TAPI (Java-Viễn thông)…

Bắt đầu với JDK 1.0 vào 1995, đến năm 1996 Sun đưa ra phiên bản JDK 1.1,

năm 1998 ra đời phiên bản JDK 1.2 và hiện nay đã có phiên bản JDK 1.7.

Java là tên gọi của một hòn đảo ở Indonexia, Đây là nơinhóm nghiên cứu phát triển đã chọn để đặt tên cho ngôn ngữlập trình Java trong một chuyến đi tham quan và làm việc trênhòn đảo này. Hòn đảo Java này là nơi rất nổi tiếng với nhiềukhu vườn trồng cafe, đó chính là lý do chúng ta thường thấybiểu tượng ly cafe trong nhiều sản

phẩm phần mềm, công cụ lậptrình Java của Sun cũng như một số hãng phần mềm khác đưara.

1.1.2. Đặc trƣng của Java

1

 Đơn giản

 Hướng đối tượng  Độc lập phần cứng và hệ điều hành  Mạnh  Bảo mật  Phân tán  Đa luồng  Động

a) Đơn giản

Những người thiết kế mong muốn phát triển một ngôn ngữ dễ học và quen thuộc

với đa số người lập trình. Do vậy Java loại bỏ các đặc trưng phức tạp của C và C++ như thao tác con trỏ, thao tác định nghĩa chồng toán tử (operator overloading),… Java

không sử dụng lệnh “goto” cũng như file header (.h). Cấu trúc “struct” và “union”

cũng được loại bỏ khỏi Java.

b) Hƣớng đối tƣợng

Java được thiết kế xoay quanh mô hình hướng đối tượng. Vì vậy trong Java, tiêu

điểm là dữ liệu và các phương pháp thao tác lên dữ liệu đó. Dữ liệu và các phương

pháp mô tả trạng thái và cách ứng xử của một đối tượng trong Java.

c) Độc lập phần cứng và hệ điều hành

Đây là khả năng một chương trình được viết tại một máy nhưng có thể chạy được

bất kỳ đâu. Chúng được thể hiện ở mức mã nguồn và mức nhị phân.

Ở mức mã nguồn, người lập trình cần mô tả kiểu cho mỗi biến. Kiểu dữ liệu

trong Java nhất quán cho tất cả các hệ điều hành và phần cứng khác nhau. Java có

riêng một thư viện các lớp cơ sở. Vì vậy chương trình Java được viết trên một máy có

thể dịch và chạy trơn tru trên các loại máy khác mà không cần viết lại.

Tính độc lập ở mức nhị phân, một chương trình đã biên dịch có thể chạy trên

2

nhiều nền (phần cứng, hệ điều hành) khác mà không cần dịch lại mã nguồn. Tuy vậy cần có phần mềm máy ảo Java (sẽ đề cập đến ở phần sau) hoạt động như một trình thông dịch tại máy thực thi.

Hình 1.1. Cách biên dịch truyền thống

Đối với các chương trình viết bằng C, C++ hoặc một ngôn ngữ nào khác, trình

biên dịch sẽ chuyển tập lệnh thành mã máy (Machine code),hay lệnh của bộ vi xử lý.

Những lệnh này phụ thuộc vào CPU hiện tại trên máy bạn. Nên khi muốn chạy trên

loại CPU khác, chúng ta phải biên dịch lại chương trình. Hình 1.2 thể hiện quá trình để

thực thi chương trình viết bằng C++ trên các loại máy khác nhau.

Hình 1.2 Quá trình thực thi chương trình viết bằng Java trên các loại máy khác

nhau.

Hình 1.2. Dịch chương trình Java

Môi trường phát triển của Java được chia làm hai phần: Trình biên dịch và trình

thông dịch. Không như C hay C++, trình biên dịch của Java chuyển mã nguồn thành dạng bytecode độc lập với phần cứng mà có thể chạy trên bất kỳ CPU nào.

Nhưng để thực thi chương trình dưới dạng bytecode, tại mỗi máy cần phải có trình thông dịch của Java hay còn gọi là máy ảo Java. Máy ảo Java chuyển bytecode thành mã lệnh mà CPU thực thi được.

d) Mạnh mẽ

3

Java là ngôn ngữ yêu cầu chặt chẽ về kiểu dữ liệu. Phải khai báo kiểu dữ liệu tường minh khi viết chương trình. Java kiểm tra lúc biên dịch và cả trong thời gian thông dịch vì vậy Java loại bỏ một số loại lỗi lập trình nhất định.

Java không sử dụng con trỏ và các phép toán con trỏ. Java kiểm tra tất cả các truy

nhập đến mảng, chuỗi khi thực thi để đảm bảo rằng các truy nhập đó không ra ngoài giới hạn kích thước. Java kiểm tra sự chuyển đổi kiểu dữ liệu từ dạng này sang dạng

khác lúc thực thi.

Trong các môi trường lập trình truyền thống, lập trình viên phải tự mình cấp phát

bộ nhớ. Trước khi chương trình kết thúc thì phải tự giải phóng bộ nhớ đã cấp. Vấn đề nảy sinh khi lập trình viên quên giải phóng bộ nhớ đã xin cấp trước đó. Trong chương

trình Java, lập trình viên không phải bận tâm đến việc cấp phát bộ nhớ. Quá trình cấp

phát, giải phóngđược thực hiện tự động, nhờ dịch vụ thu nhặt những đối tượng không

còn sử dụng nữa (garbage collection).Cơ chế bẫy lỗi của Java giúp đơn giản hóa quá trình xử lý lỗi và hồi phục sau lỗi.

e) Bảo mật

Viruses là nguyên nhân gây ra sự lo lắng trong việc sử dụng máy tính. Trước khi

có Java, các lập trình viên phải quétVirus các tệp trước khi tải về hay thực hiện chúng.

Thông thường việc này cũng không loại trừ hoàn toàn Virus. Ngoài ra chương trình

khi thực thi có khả năng tìm kiếm và đọc các thông tin nhạy cảm trên máy của người

sử dụng mà người sử dụng không hề hay biết.

Java cung cấp một môi trường quản lý thực thi chương trình. Nó cho rằng không

có một đoạn mã nào là an toàn cả. Vì vậy Java không chỉ là ngôn ngữ lập trình thuần tuý mà còn cung cấp nhiều mức để kiểm soát tính an toàn khi thực thi chương trình.

Ở lớp đầu tiên, dữ liệu và các phương thức được đóng gói bên trong lớp. Chúng

chỉ được truy xuất thông qua các giao diện mà lớp cung cấp. Java không hỗ trợ con trỏ

vì vậy không cho phép truy xuất bộ nhớ trực tiếp. Nó cũng ngăn chặn không cho truy

xuất thông tin bên ngoài kích thước của mảng bằng kỹ thuật tràn và cũng cung cấp kỹ

thuật dọn rác trong bộ nhớ. Các đặc trưng này tạo cho Java an toàn tối đa và có khả

năng cơ động cao.

Trong lớp thứ hai, trình biên dịch kiểm soát để đảm bảo mã là an toàn, và tuân

theo các nguyên tắc của Java.

Lớp thứ ba được đảm bảo bởi trình thông dịch. Chúng kiểm tra xem bytecode có

đảm bảo các qui tắc an toàn trước khi thực thi.

Lớp thứ tư kiểm soát việc nạp các lớp vào bộ nhớ để giám sát việc vi phạm giới

hạn truy xuất trước khi nạp vào hệ thống.

4

f) Phân tán

Java có thể dùng để xây dựng các ứng dụng có thể làm việc trên nhiều phần

cứng, hệ điều hành và giao diện đồ họa. Java được thiết kế hỗ trợ cho các ứng dụng chạy trên mạng. Vì vậy chúng được sử dụng rộng rãi như là công cụ phát triển trên

Internet, nơi sử dụng nhiều nền tảng khác nhau.

g) Đa luồng

Chương trình Java đa luồng(Multithreading) để thực thi các công việc đồng thời. Chúng cũng cung cấp giải pháp đồng bộ giữa các luồng. Đặc tính hỗ trợ đa luồngnày

cho phép xây dựng các ứng dụng trên mạng chạy hiệu quả.

h) Động

Java được thiết kế như một ngôn ngữ động để đáp ứng cho những môi trường mở. Các chương trình Java chứa rất nhiều thông tin thực thi nhằm kiểm soát và truy

nhập đối tượng lúc chạy. Điều này cho phép khả năng liên kết động mã.

1.1.3. Các kiểu chƣơng trình Java

Chúng ta có thể xây dựng các loại chương trình Java như sau:

a) Applets

Applet là chương trình được tạo ra để sử dụng trên Internet thông qua các trình

duyệt hỗ trợ Java như IE hay Netscape. Bạn có thể dùng Java để xây dựng Applet.

Applet được nhúng bên trong trang Web. Khi trang Web hiển thị trong trình duyệt,

Applet sẽ được tải về và thực thi tại trình duyệt.

b) Ứng dụng thực thi qua dòng lệnh

Các chương trình này chạy từ dấu nhắc lệnh và không sử dụng giao diện đồ họa.

Các thông tin nhập xuất được thể hiện tại dấu nhắc lệnh.

c) Ứng dụng đồ họa

Đây là các chương trình Java chạy độc lập cho phép người dùng tương tác qua

giao diện đồ họa.

d) Servlet

Java thích hợp để phát triển ứng dụng nhiều lớp. Applet là chương trình đồ họa chạy trên trình duyệt tại máy trạm. Ở các ứng dụng Web, máy trạm gửi yêu cầu tới

5

máy chủ. Máy chủ xử lý và gửi kết quả trở lại máy trạm. Các Java API chạy trên máy chủ chịu trách nhiệm xử lý tại máy chủ và trả lời các yêu cầu của máy trạm. Các Java API chạy trên máy chủ này mở rộng khả năng của các ứng dụng Java API chuẩn. Các ứng dụng trên máy chủ này được gọi là các Servlet. hoặc Applet tại máy chủ. Xử lý Form của HTML là cách sử dụng đơn giản nhất của Servlet. Chúng còn có thể được

dùng để xử lý dữ liệu, thực thi các giao dịch và thường được thực thi thông qua máy

chủ Web.

e) Ứng dụng cơ sở dữ liệu

Các ứng dụng này sử dụng JDBC API để kết nối tới cơ sở dữ liệu. Chúng có thể

là Applet hay ứng dụng, nhưng Applet bị giới hạn bởi tính bảo mật.

1.1.4. Máy ảo Java

Máy ảo là một phần mềm mô phỏng một máy tính thật. Nó có tập hợp các lệnh

logic để xác định các hoạt động của máy tính và có một hệ điều hành ảo. Người ta có

thể xem nó như một máy tính thật. Nó thiết lập các lớp trừu tượng cho: Phần cứng bên

dưới, hệ điều hành, mã đã biên dịch.

Trình biên dịch chuyển mã nguồn thành tập các lệnh của máy ảo mà không phụ

thuộc vào phần cứng cụ thể. Trình thông dịch trên mỗi máy sẽ chuyển tập lệnh này

thành chương trình thực thi. Máy ảo tạo ra một môi trường bên trong để thực thi các

lệnh bằng cách:

 Nạp các file .class  Quản lý bộ nhớ  Dọn “rác”

Việc không nhất quán của phần cứng làm cho máy ảo phải sử dụng ngăn xếp để

lưu trữ các thông tin sau:

 Các “Frame” chứa các trạng thái của các phương thức.  Các toán hạng của mã bytecode.  Các tham số truyền cho phương thức.  Các biến cục bộ.

Khi máy ảo Java(JVM) thực thi mã, một thanh ghi cục bộ có tên “Program

Counter” được sử dụng. Thanh ghi này trỏ tới lệnh đang thực hiện. Khi cần thiết, có

thể thay đổi nội dung thanh ghi để đổi hướng thực thi của chương trình. Trong trường hợp thông thường thì từng lệnh một nối tiếp nhau sẽ được thực thi.

Một khái niệm thông dụng khác trong Java là trình biên dịch “Just In Time- JIT”. Các trình duyệt thông dụng như Netscape hay IE đều có JIT bên trong để tăng tốc độ thực thi chương trình Java. Mục đích chính của JIT là chuyển tập lệnh bytecode thành mã máy cụ thể cho từng loại CPU. Các lệnh này sẽ được lưu trữ và sử dụng mỗi khi gọi đến.

a) Quản lý bộ nhớ và dọn rác

6

Trong C, C++ hay Pascal người lập trình sử dụng phương pháp trực tiếp để cấp phát và thu hồi bộ nhớ ở vùng “Heap”. Heap là vùng bộ nhớ lớn được phân chia cho tất cả các luồng.

Để quản lý Heap, bộ nhớ được theo dõi qua các danh sách sau:

 Danh sách các vùng nhớ chưa sử dụng.  Danh sách các vùng đã cấp.

Khi có một yêu cầu về cấp phát bộ nhớ, hệ thống xem xét trong danh sách chưa cấp phát để lấy ra khối bộ nhớ đầu tiên có kích cỡ sát nhất với lượng bộ nhớ cần thiết . Kỹ thuật cấp phát này giảm tối thiểu việc phân mảnh của heap.

“Coalescing” là kỹ thuật khác cũng giảm thiểu việc phân mảnh của heap bằng

cách gom lại các vùng nhớ chưa dùng liền nhau thành một khối lớn hơn. Còn kỹ thuật

sắp xếp lại các phần đã dùng để tạo vùng nhớ chưa sử dụng lớn hơn gọi là

“Compaction”.

Java sử dụng hai heap riêng biệt cho cấp phát vùng nhớ tĩnh và vùng nhớ động.

Một heap (heap tĩnh) chứa các định nghĩa về lớp, các hằng và danh sách các phương

thức. Heap còn lại (heap động) được chia làm hai phần được cấp phát theo hai chiều

ngược nhau. Một bên chứa đối tượng còn một bên chứa con trỏ trỏ đến đối tượng đó.

“Handle” là cấu trúc bao gồm hai con trỏ. Một trỏ đến bảng phương thức của đối

tượng, con trỏ thứ hai trỏ đến chính đối tượng đó. Chú ý rằng khi “Compaction” cần

cập nhật lại giá trị con trỏ của cấu trúc “handle”.

Thuật toán dọn rác có thể áp dụng cho các đối tượng đặt trong heap động. Khi có

yêu cầu về bộ nhớ, trình quản lý heap trước tiên kiểm tra danh sách bộ nhớ chưa cấp

phát. Nếu không tìm thấy khối bộ nhớ nào phù hợp (về kích cỡ) thì trình dọn rác sẽ

được kích hoạt khi hệ thống rỗi. Nhưng khi đòi hỏi bộ nhớ cấp bách thì trình dọn rác

sẽ được kích hoạt ngay.

Trình dọn rác gọi phương thức finalize của đối tượng trước khi dọn dẹp đối tượng. Hàm này sẽ dọn dẹp các tài nguyên bên ngoài như các file đang mở. Công việc

này không được trình dọn rác thực thi.

b) Quá trình kiểm tra file .class

Việc kiểm tra được áp dụng cho tất cả các file .class sắp được nạp lên bộ nhớ để đảm bảo tính an toàn. Trình “Class Loader” sẽ kiểm tra tất cả các file .class không thuộc hệ điều hành với mục đích giám sát sự tuân thủ các nghi thức để phát hiện các file .class có nguy cơ gây hư hỏng đến bộ nhớ, hệ thống file cục bộ, mạng hoặc hệ điều hành. Quá trình kiểm tra sẽ xem xét tổng thể tính nguyên vẹn của một lớp.

File .class bao gồm ba phần logic là:

 Bytecode  Thông tin về Class như phương thức, giao diện và các giá trị hằng số được

7

tập hợp trong quá trình biên dịch.

 Các thuộc tính về lớp.

Các thông tin của file .class được xem xét riêng rẽ trong các bảng sau:

 Bảng Field chứa các thuộc tính  Bảng Method chứa các hàm của class  Bảng Interface và các hằng số.

Quá trình kiểm tra file .class được thực hiện ở bốn mức:

 Mức đầu tiên thực hiện việc kiểm tra cú pháp để đảm bảo tính cấu trúc và

tính toàn vẹn cú pháp của file .class được nạp.

 Mức thứ hai sẽ xem xét file .class để đảm bảo các file này không vi phạm

các nguyên tắc về sự nhất quán ngữ nghĩa.

 Mức thứ ba sẽ kiểm tra bytecode. Trong bước này sẽ kiểm tra số thông số truyền vào phương thức, khả năng truy xuất sai chỉ số của mảng, chuỗi, biểu

thức.

 Mức thứ tư sẽ kiểm tra trong thời gian thực thi để giám sát các việc còn lại mà ba bước trên chưa làm. Ví dụ như liên kết tới các lớp khác trong khi thực

thi, hay kiểm tra quyền truy xuất. Nếu mọi điều thỏa mãn, lớp sẽ được khởi

tạo.

1.1.5. Bộ công cụ phát triển JDK

Sun Microsystem đưa ra ngôn ngữ lập trình Java qua sản phẩm có tên là Java Development Kit (JDK). Từ khi phát triển phiên bản đầu tiên từ năm 1995 đến nay, bộ

công cụ Java đã được phát triển qua nhiều phiên bản và cũng có nhiều sự thay đổi, sau

đây là một số phiên bản đã được phát hành:

JDK 1.0

JDK 1.1 (19 tháng 2, 1997)

JDK 1.1.5 (Pumpkin) 03 tháng 12, 1997

JDK 1.1.6 (Abigail) 24 tháng 4, 1998

JDK 1.1.7 (Brutus) 28 tháng 9, 1998

JDK 1.1.8 (Chelsea) 08 tháng 4, 1999

J2SE 1.2 (Playground) 08 tháng 12, 1998

J2SE 1.2.1 (không có) 30 tháng 3, 1999

J2SE 1.2.2 (Cricket) 08 tháng 7, 1999

8

J2SE 1.3 (Kestrel) 08 tháng 5, 2000

J2SE 1.3.1 (Ladybird) 17 tháng 5, 2001

J2SE 1.4.0 (Merlin) 06 tháng 02, 2002

J2SE 1.4.1 (Hopper) 16 tháng 9, 2002

J2SE 1.4.2 (Mantis) 26 tháng 6, 2003

J2SE 5 (1.5.0) (Tiger) 30 tháng 9, 2004

Java SE 6(còn gọi là Mustang), được công bố 11 tháng 12 năm 2006. Các bản

cập nhật 2 và 3 được đưa ra vào năm 2007, bản cập nhật 4 đưa ra tháng 1 năm 2008.

JDK 6.18, 2010

Java SE 7(còn gọi là Dolphin), được bắt đầu từ tháng 8 năm 2006 và công bố

ngày 28 tháng 7 năm 2011.

Hiện tại đang phát triển và thử nghiệm bản Java JDK 8.

JDK bao gồm Java Plug-In, chúng cho phép chạy trực tiếp Java Applet hay

JavaBean bằng cách dùng JRE thay cho sử dụng môi trường thực thi mặc định của

trình duyệt.

JDK chứa các công cụ sau:

Trình biên dịch, 'javac'

Cú pháp:

javac [options] sourcecodename.java

Trình thông dịch, 'java'

Cú pháp:

java [options] classname

Trình dịch ngƣợc, 'javap'

javap dịch ngược bytecode và in ra thông tin về các thuộc tính (các trường), các

phương thức của một lớp.

Cú pháp:

javap [options] classname

Công cụ sinh tài liệu, 'javadoc'

Tiện ích này cho phép ta tạo ra tệp HTML dựa trên các lời giải thích trong mã

chương trình (phần nằm trong cặp dấu /*.... */).

9

Cú pháp:

javadoc [options] sourcecodename.java

Chƣơng trình tìm lỗi - Debug, „jdb‟

Cú pháp:

jdb [options] sourcecodename.java

hay

jdb -host -password [options] sourcecodename.java

Chƣơng trình xem Applet , „appletviewer‟

Cú pháp:

appletviewer [options] url

Java Core API

Nhân Java API được thay thế bởi phiên bản JFC 1.1.

Một số package thông dụng được:

java.lang

Chứa các lớp quan trọng nhất của ngôn ngữ Java. Chúng bao gồm các kiểu dữ

liệu cơ bản như ký tự, số nguyên,… Chúng cũng chứa các lớp làm nhiệm vụ xử lý lỗi

và các lớp vào ra chuẩn. Một vài lớp quan trọng khác như String hay StringBuffer.

java.applet

Đây là package nhỏ nhất chứa một mình lớp Applet. Các Applet nhúng trong

trang Web hay chạy trong appletviewer đều thừa kế từ lớp này.

java.awt

Package này được gọi là Abstract Window Toolkit (AWT). Chúng chứa các lớp

dùng để tạo giao diện đồ họa. Một số lớp bên trong là: Button, GridBagLayout,

Graphics.

java.io

Cung cấp thư viện vào ra chuẩn. Chúng cho phép tạo và quản lý dòng dữ liệu

theo nhiều cách.

java.util

Package này cung cấp một số công cụ hữu ích. Một vài lớp của package này là:

Date, Hashtable, Stack, Vector và StringTokenizer.

10

java.net

Cung cấp khả năng giao tiếp với máy từ xa. Cho phép tạo và kết nối tới Socket

hoặc URL.

java.awt.event

Chứa các lớp, giao diện dùng để xử lý các sự kiện trong chương trình như chuột,

bàn phím.

java.rmi

Công cụ để gọi hàm từ xa. Chúng cho phép tạo đối tượng trên máy khác và sử

dụng các đối tượng đó trên máy cục bộ.

java.security

Cung cấp các công cụ cần thiết để mã hóa và đảm bảo tính an toàn của dữ liệu

truyền giữa máy trạm và máy chủ.

java.sql

Package này chứa Java DataBase Connectivity (JDBC), dùng để truy xuất cơ sở

dữ liệu quan hệ như Oracle, SQL Server,....

1.1.6. Cài đặt JDK

Để cài đặt bộ công cụ Java Development Kit (JDK) thực hiện theo 3 bước sau:

1) Tải bộ công cụ về máy tính cá nhân

Bộ công cụ JDK của Sun System là một bộ công cụ miễn phí, có thể download

trực tiếp từ hãng về qua địa chỉ website:

http://www.oracle.com/technetwork/java/javase/downloads/index.html

Tùy theo cấu hình của máy tính và phiên bản hệ điều hành mà có thể lựa chọn

gói phần mềm phù hợp, rồi download về máy tính.

Ví dụ 1.1: Máy tính chạy hệ điều hành Windows 7 bản 64 bit ta thực hiện như

sau:

Bước 1: Mở trình duyệt Web và truy nhậpvào địa chỉ sau:

11

http://www.oracle.com/technetwork/java/javase/downloads/index.html

Hình 1.3. Trang web tải bộ công cụ Java

Bước 2: Kích chọn biểu tượng Java Download như hình sau:

Hình 1.4. Biểu tượng Java Download

Bước 3: Kích chọn AcceptLicense Agreement

Hình 1.5. Lựa chọn bản quyền

Bước 4: Lựa chọn góijdk-7u45-windows-x64.exe rồi tải về máy như hình sau:

Hình 1.6. Bộ JDK cho Windows bản 64 bit

2) Cài đặt JDK

Sau khi đã tải được file chương trình về máy tính, chạy file chương trình đãdownload được và thực hiện theo các chỉ dẫn của chương trình cho đến khi hoàn

thành thì sẽ được chương trình JDK cài trên máy tính.

Ví dụ 1.2: Hướng các bước cài đặt JDK cho Windows 7 64 bit

Bước 1: Chạy file jdk-7u45-windows-x64.exe đã tải về máy tính được màn hình

12

Setup như sau:

Hình 1.7. Màn hình cài đặt đầu tiên

Bước 2: Kích chuột vào Next để tiếp tục cài đặt, khi đó được màn hình custom

setup như hình sau:

Hình 1.8. Màn hình Custom Setup

Bước 3:Lựa chọn các thành phần cài đặt trong Feature và lựa chọn thư mục chứa

chương trình cài đặt trong mục Install to, sau đó chọn Next để tiếp tục cài đặt sẽ được màn hình Description Folder

13

Hình 1.9. Màn hình Description Folder

Bước 4: Trong màn hình này có thể thay đổi thư mục cài đặt trong change của

mục Install to theo mặc định đường dẫn thư mục cài đặt sẽ là c:\Program Files\Java, chọn Next để tiếp tục, khi đó được màn hình Progress

Hình 1.10. Màn hình Progress

Bước 5: Cho đến khi tiến trình cài đặt hoàn thành và kích chọn Finish để hoàn

thành.

3) Cấu hình JDK

Sau khi đã cài đặt thành công JDK, người lập trình nên cấu hình class path để

thiết lập biến môi trường cho Java có thể chạy được trên bất kỳ thư mục chứa chương

trình nào. Tùy theo hệ điều hành được cài đặt trên máy tính mà có cách vào chế độ cấu

hình khác nhau.

Trong ví dụ 1.3 dưới đây sẽ hướng dẫn cách cài đặt và cấu JDK trên hệ điều hành

Windows 7 bản 64 bit.

Ví dụ1.3: Cấu hình JDK trên Windows 7 bản 64 bit.

Bước 1: Mở Explore trên Windows và copy tên đường dẫn đến thư mục chứa

chương trình cài đặt của Java như hình sau:

Hình 1.11. Đường dẫn của thư mục cài đặt Java

14

Bước 2: Kích chuột phải vào My Computer chọn Properties được màn hình sau:

Hình 1.12. Màn hình Computer Properties

Bước 3: Chọn Avanced system settings trong cửa sổ trên sẽ được cửa sổ màn

hình System Properties như sau:

Hình 1.13. Màn hình System Properties

15

Bước 4: ChọnEnvironment Variables khi đó được màn hình:

Hình 1.14. Màn hình Environment Variables

Bước 5: Trong mục System Avariables chọn Path rồi chọn edit được màn hình

Edit System Variable. Dán (Pate) đường dẫn đã copy ở bước 1 vào phần cuối của mục

Variable value rồi chọn ok

Hình 1.15. Màn hình Edit System Variable

Bước 6: Kiểm tra quá trình cấu hình bằng cách vào run nhận lệnh cmd để mở cửa

số Command Prompt, trong cửa sổ này ở thư mục bất kỳ nhập lệnh java – version nếu

xuất hình như hình 1.7. dưới đây là quá trình cài cấu hình thành công.

Hình 1.16. Màn hình cửa sổ run

16

Hình 1.17. Màn hình Command Prompt

1.2. Ngôn ngữ lập trình Java

1.2.1. Mở đầu lập trình với Java

Java là một ngôn ngữ lập trình hướng đối tượng. Tất cả các lớp trong Java đều

dựa trên lớp Object, là cha của tất cả các lớp. Các lớp có liên quan được tổ chức thành

các gói (packages). Các gói cũng có thể chứa các gói khác hoặc các giao diện. Một

giao diện là một loạt các phương pháp mà các lớp Java có thể thực thi.

Các chương trình mã nguồn Java (*.java) đầu tiên được biên dịch thành các file

bytecode (tập tin mã nhị phân) (*.class). Sau đó trình thông dịch Java sẽ chuyển đổi

các bytecode thành các mã máy theo bộ xử lý cụ thể.

1.2.2. Cấu trúc chƣơng trình Java

Về cơ bản cấu trúc chung của một chương trình Java có dạng như sau:

import packages //chỉ định các gói hay lớp sẽ được dùng trong chương trình

class Classname

{ /* Đây là dòng chú thích */

int num1, num2; //khai báo biến

Methodname()

// Thân phương thức {

statements; // kết thúc lệnh bằng dấu ;

}

}

Trong đó phần đầu của một chương trình Java, “import packages” xác định thông tin môi trường. Để làm được việc này, chương trình được chia thành các lớp

hoặc các gói riêng biệt. Những gói này sẽ được chỉ dẫn trong chương trình. Thông tin

này được chỉ ra với sự trợ giúp của lệnh nhập “import”. Mỗi chương trình có thể có

nhiều hơn một lệnh nhập. Dưới đây là một ví dụ về lệnh nhập:

import java. awt.*;

Lệnh này nhập gói „awt‟. Gói này dùng để tạo các đối tượng GUI. Ở đây java là

tên của thư mục chứa gói „awt‟. Ký hiêu “*” chỉ tất cả các lớp thuộc gói này.

Trong java, tất cả các mã, bao gồm các biến và cách khai báo nên được thực hiện trong phạm vi một lớp. Bởi vậy, từng khai báo lớp được tiến hành sau lệnh nhập. Một

chương trình đơn giản có thể chỉ có một vài lớp. Những lớp này có thể mở rộng thành các lớp khác. Mỗi lệnh đều được kết thúc bởi dấu chấm phảy “;”. Chương trình còn có

thể bao gồm các ghi chú, chỉ dẫn. Khi dịch, chương trình dịch sẽ tự loại bỏ các ghi chú

17

này.

Các ghi chú dùng để gợi nhớ lại quá trình suy nghĩ, kiểm nghiệm các lập luận

hoặc khi cần bổ sung, sửa chữa, nâng cấp chương trình. Java hỗ trợ 3 cách ghi chú sau:

Bắt đầu Kết thúc Mục đích

/* */ Nội dung ở giữa là phần ghi chú

// (không có) Phần dòng còn lại là ghi chú

/** */ Nội dung ở giữa là phần ghi chú dùng

cho JavaDoc

Bảng 1.1. Bảng các ký hiệu ghi chú

Có thể phân chia các thành phần trong một chương trình Java làm 5 loại sau:

- Các tên định danh (identifiers): là tên các biến, các phương thức và các lớp để trình biên dịch định danh duy nhất. Tên định danh gồm các mẫu tự, dấu _ hoặc dấu $,

các ký tự đặc biệt không khác được dùng.

- Các từ khoá (keywords): là các tên định danh sẵn được Java dành riêng, không

được dùng các từ khóa để dùng làm các tên định danh. Các từ khoá của Java gồm:

Từ khóa Ý nghĩa

abstract dùng để thông báo một phương thức trừu tượng, cần có một

phương thức con không trừu tượng lấp lên. Một phương thức là

trừu tượng sẽ không có phần mã lệnh (MethodBody)

boolean kiểu biến logic, chỉ có hai giá trị đúng, sai (TRUE, FALSE).

Chúng khác với C/C++ biến kiểu này chỉ chiếm 1 bit trong bộ nhớ.

từ khoá dùng để thoát ra khỏi vòng lặp break

kiểu dữ liệu có giá trị 8 bit byte

một trường hợp trong cấu trúc switch case

catch

sử dụng khi nhận về các kết quả lỗi, được sử dụng trong xử lý ngoại lệ

char kiểu dữ liệu ký tự

class Khai báo Lớp

const khai báo hằng

continue dùng để nhảy về đầu vòng lặp

18

default giá trị mặc định trong cấu trúc lựa chọn

nằm trong câu lệnh vòng lặp (do … while) do

nằm trong cấu trúc lựa chọn (if … else …) else

extends

dẫn xuất, được sử dụng để khai báo một lớp được kế thừa từ một lớp khác

final Dùng để khai báo hằng số, phương thức không thể ghi đè, hoặc

lớp không thể kế thừa

phương thức kết thúc finally

kiểu số thực float

dùng để thực hiện một vòng lặp xác định for

nằm trong cặp lệnh điều khiển if ... then … else if

implements cài đặt, dùng để khai báo một lớp là cài đặt của một giao diện

import cho biết dùng các thư viện nào

instanceof dùng trong kiểu định danh thời gian chạy(Runtime Type

Identification) kiểm tra một đối tượng có phải là một thể hiện của

lớp hay không

int kiểu biến nguyên

interface giao diện, sử dụng để khai báo giao diện

long kiểu số nguyên

nguồn, gốc: Khai báo phương thức được viết bằng ngôn ngữ biên native

dịch C++

new dùng để tạo một đối tượng mới

package gói, đóng gói

private từ khóa khai đối tượng tiếp sau là cục bộ

protected không bị chồng, sử dụng trong các lớp dẫn xuất

đối tượng khai báo tiếp theo sau là biến toàn cục public

return trở về lệnh gọi

short kiểu biến nguyên

statictis đối tượng khai báo tiếp theo sau là tĩnh

19

father đối tượng cha

swith dùng trong cấu trúc lựa chọn (switch case)

synchronized đồng bộ, sử dụng để khai báo một phương thức độc quyền truy

xuất trên một đối tượng

đối tượng hiện đang xét this

trả về lỗi throw

throws

cho biết phương thức hay biến sẽ trả về (báo) khi có lỗi (dùng trong khai báo phương thức)

transient Giá trị của biến được khai báo kiểu này sẽ không được lưu giữ như

trước khi một đối tượng (chứa biến đó) được lưu trữ

try Sử dụng để bắt ngoại lệ

void Dùng để khai báo một phương thức không trả về giá trị

volatile Giá trị của biến được khai báo kiểu này có thể được thay đổi đột

xuất bởi các phần khác của chương trình

while dùng trong cặp lệnh vòng lặp

Bảng 1.2. Các từ khóa của Java

- Các dấu cách (separators): thông báo cho trình biên dịch java cách xác định các

thành phần ngữ nghĩa của chương trình.

- Các hằng (literals): là các giá trị bất biến trong một chương trình. Các hằng có

thể là các số, các chuỗi, các ký tự, hoặc các giá trị Boolean.

- Các toán tử (operators): chỉ định một đánh giá hoặc một phép toán được thực

hiện trên dữ liệu hay các đối tượng. Ví dụ : +, -, *, /, %

1.2.3. Kiểu dữ liệu cơ sở, hằng, biến

a) Các kiểu dữ liệu cơ sở:

Java cung cấp các kiểu dữ liệu cơ sở. Các kiểu dữ liệu này được dùng để tạo các

kiểu dữ liệu dẫn xuất

- Kiểu số nguyên, Java có bốn loại số nguyên: byte, short, int, long

Kiểu Kích thước Giá trị nhỏ nhất Giá trị lớn nhất

byte 7 bits -128 127

Short 16 bits -32768 32767

20

int 30 bits -2.147.483.648 2.147.483.647

long 62 bits -9.223.372.036.854 9.223.372.036.854

Bảng 1.3. Các kiểu số nguyên

Các phép tính trên biến số nguyên gồm:

Toán Phép toán

tử

Bằng nhau ==

Không bằng !=

Lớn hơn >

Nhỏ hơn <

Lớn hơn hoặc bằng >=

Nhỏ hơn hoặc bằng <=

Cộng +

Trừ -

Nhân *

Chia /

Lấy phần dư của phép chia %

Tăng 1 ++

Giảm 1 --

Phép toán phủ định trên bit ~

Phép toán AND trên bit &

Phép toán OR trên bit |

Phép toán XOR trên bit ^

Dịch chuyển sang trái n bit <<

Dịch chuyển sang phải n bit >>

>>> Dịch chuyển sang phải và điền 0 vào bit

trống

Bảng 1.4. Các phép toán trên kiểu số nguyên

Nếu hai toán hạng đều dạng long thì kết quả sẽ là dạng long 64 bits. Nếu có một

21

toán hạng không phải dạng long thì sẽ được chuyển sang dạng long trước khi thực hiện

phép toán. Nếu cả hai toán hạng không phải là long (byte, short) thì sẽ được đổi sang

int trước khi thực hiện phép toán. Trong Java không thể chuyển biến boolean sang int như các ngôn ngữ khác.

-Kiểu số thực, Java có 2 loại số thực là: float và double

Kiểu Kích cỡ Giá trị nhỏ nhất Giá trị lớn nhất

float 32 bits -3.40292347E+38 3.40292347E+38

double 64 bits -1.79769313486231570E+308 1.79769313486231570E+308

Bảng 1.5. Các loại số thực

Các phép toán trên kiểu số thực gồm:

Toán tử Phép toán

== Bằng nhau

!= Không bằng

> Lớn hơn

< Nhỏ hơn

>= Lớn hơn hoặc bằng

<= Nhỏ hơn hoặc bằng

+ Cộng

- Trừ

* Nhân

/ Chia

% Lấy phần dư

++ Tăng 1

-- Giảm 1

Bảng 1.6. Các phép toán trên kiểu số thực

Biến kiểu float và double có thể được chuyển sang các kiểu dữ liệu số khác nhưng không thể chuyển sang kiểu dữ liệu boolean.Khi thực hiện phép tính java sẽ

chuyển các toán hạng về kiểu dữ liệu cao nhất rồi mới tính toán. Java đưa ra một số thực NaN (Not a Number) dùng cho các giá trị không thể xác định được trong khoảng từ vô cực âm đến vô cực dương. Nó giải quyết lỗi phép chia cho 0.

22

-Kiểu dữ liệu char:

Char là kiểu dữ liệu về ký tự, một biến char sẽ có giá trị là một ký tự Unicode, có

kích thước 16 bits từ „\u0000‟ đến „\uFFFF‟.

-Kiểu dữ liệu boolean:

Boolean là kiểu dữ liệu chỉ có hai giá trị là True và False dùng để xác định kết quả một điều kiện và có kích thước là một bit. Do đó ta không thể chuyển kiểu giữa

boolean sang int hay ngược lại .

Giá trị mặc định:

Một lỗi hay gặp phải khi lập trình là sử dụng những biến chưa khởi tạo. Điều này

tạo ra những lỗi không thể đoán trước (như định dạng lại đĩa, chép đè lên CMOS), bởi

vì chúng có thể nhận bất kỳ giá trị nào trong vùng nhớ khi chương trình bắt đầu chạy.

Java đã tránh được lỗi trên bằng cách luôn khởi tạo giá trị mặc định cho các biến. Các

giá trị được xác định phụ thuộc vào kiểu của biến theo bảng sau:

Giá trị mặc định Kiểu

Byte 0

Short 0

Int 0

Long 0L

float 0.0f

double 0.0d

char null

boolean false

null Các biến dẫn xuất

Bảng 1.7. Giá trị mặc định

-Chuyển kiểu (type casting):

Trong một biểu thức của chương trình không phải lúc nào các biến cũng thuộc về cùng một kiểu dữ liệu. Vì vậy để các biến thuộc về cùng một kiểu dữ liệu nào đó Java cho phép chuyển kiểu đối với một biến ở 2 hướng: chuyển kiểu hẹp (Narrowing conversion – từ kiểu lớn về kiểu nhỏ) và chuyển kiểu nới rộng (Widening conversion - từ kiểu nhỏ về kiểu lớn)

Ví dụ 1.4: class TypeCast { public static void main (String args[]) {

23

float a = (float) 12; //chuyển kiểu số nguyên 12 sang float

System.out.println (“a = “+a); // a = 12.0 float b = 15.682f; int c = (int) b + 20; System.out.println (“c = “+c); // c = 35

}

}

chuyển kiểu mở rộng(Widening) – quá trình làm tròn số theo hướng mở rộng

không làm mất thông tin về độ lớn của mỗi giá trị.Biến đổi theo hướng mở rộng chuyển một giá trị sang một dạng khác có độ rộng phù hợp hơn so với nguyên bản.

Biến đổi theo hướng lại thu nhỏ lại (Narrowing) làm mất thông tin về độ lớn của giá trị

được chuyển đổi. Chúng không được thực hiện khi thực hiện phép gán. Ở ví dụ trên

giá trị thập phân sau dấu phảy sẽ bị mất.

b) Đặt tên (Identifier)

Một tên là một chuỗi các ký tự gồm các chữ, số, dấu gạch dưới (_), và dấu dollar

($).

Việc đặt tên cho một hằng và biến cần theo những quy tắc sau:

- Ký tự đầu phải là ký tự chữ, dấu _, hay dấu $.Không thể bắt đầu bởi một số

- Sau ký tự đầu, có thể dùng các ký tự chữ, ký số, dấu _, dấu $

- Không được có khoảng trắng

- Không được trùng với các từ khóa

c) Hằng (literal):

Hằng là một giá trị thực được sử dụng trong chương trình, được biểu diễn như

chính nó chứ không phải là một giá trị của một biến hay một kết quả của một biểu

thức. Ví dụ hằng 3.14159 thay cho số Pi.

-Khai báo hằng:

Để khai báo hằng ta dùng từ khoá final đặt trước kiểu dữ liệu và tên hằng cú pháp

như sau:

Cú pháp:

final datatype identifier =value [, identifier=value[,…] ];

Trong đó

- datatype là kiểu dữ liệu

- identifier là tên hằng

- value là giá trị của hằng

Ví dụ sau sẽ khai báo một hằng tên PI thuộc kiểu số thực (float) có giá trị là

24

3.14159

final float PI = 3.14159;

- Có các loại hằng sau:

Hằng số nguyên:

Hằng số nguyên có thể được biểu diễn dưới dạng thập phân (decimal), bát phân

(octal), thập lục phân (hexadecimal).

Integer Long Octal Hexadecimal

0 0L 0 0x0

1 1L 01 0x1

10 10L 012 0xA

15 15L 017 0xF

16 16L 020 0x10

0x64 100 100L 0144

Bảng 1.8. Hằng số nguyên

Hằng số thực:

Các hằng thực có thể biểu diễn bằng dạng thập phân như 5.1693 hay dạng mũ

như 6.2e23. Để chỉ rõ hằng kiểu float ta thêm “f” hay “F” phía sau hằng, hoặc hằng

thực ta thêm “d” hay “D”.

Hằng ký tự:

Một hằng ký tự là một ký tự đơn hay một chuỗi ESCAPE, được đặt trong hai dấu

nháy đơn. Chuỗi ESCAPE được dùng để biểu diễn các ký tự đặc biệt như tab („\t‟) hay một động tác đặc biệt như xuống dòng („\n‟). Ta có bảng liệt kê các chuỗi ESCAPE

thường dùng:

Chuỗi Ý nghĩa

\b Xóa lùi

\t Tab ngang

\n Xuống hàng

\f Đẩy trang

\r Dấu enter

25

\” Dấu nháy kép

\‟ Dấu nháy đơn

\\ Dấu sổ ngược

\uxxxx Ký tự Unicode

Bảng 1.9. Các hằng số thực

Hằng chuỗi ký tự:

Mặc dù Java không cung cấp kiểu dữ liệu cơ sở string, ta vẫn có thể khai báo một hằng chuỗi trong chương trình. Một hằng chuỗi được đặt giữa 2 dấu nháy kép, nó có thể là hằng chuỗi rỗng hay có nhiều ký tự.

Khi sử dụng một hằng chuỗi ký tự, ta xem như đã tạo ra một đối tượng của lớp String và không cần chú ý đến các thao tác về bộ nhớ như tạo và xóa các vùng nhớ cho các hằng chuỗi ký tự. Java có một hệ thống quản lý vùng nhớ tự động làm việc này.

d. Biến

Các ứng dụng sử dụng các biến để lưu trữ các dữ liệu cần thiết hoặc các dữ liệu

được tạo ra trong quá trình thực thi chương trình. Các biến được xác định bởi một tên

biến và có một phạm vi tác động. Phạm vi tác động của biến được xác định một cách

rõ ràng trong chương trình.

Việc khai báo một biến bao gồm 3 thành phần: kiểu biến, tên của nó và giá trị ban đầu được gán cho biến (không bắt buộc). Để khai báo nhiều biến ta sử dụng dấu

phẩy để phân cách các biến. Khi khai báo biến, luôn nhớ rằng Java phân biệt chữ

thường và chữ in hoa (case -sensitive).

Cú pháp:

Datatype identifier [=value] [, identifier[=value]… ];

Để khai báo một biến nguyên (int) có tên là counter dùng để lưu giá trị ban đầu

là 1, ta có thể thực hiện như sau:

int counter = 1;

Java có những yêu cầu hạn chế đặt tên biến mà bạn có thể gán giá trị vào. Những hạn chế này cũng giống các hạn chế khi đặt tên cho các định danh mà ta đã thảo luận ở các phần trước của chương này.

Java cho phép khai báo biến khá linh hoạt như sau:

char ch; //khai báo một biến kiểu char

int count, num ; //khai báo hai biến trên cùng dòng

int count =10; char ch =‟A‟; //khai báo và khởi tạo biến

int num = 012; //gán giá trị bát phân

int num = 0x18; // gán giá trị thập lục phân

26

float dec=1.0F; //khai báo và gán giá trị cho biến kiểu float

char ch=‟\u0020‟; //khai báo và khởi tạo một giá trị Unicode cho biến ch

int a=5, b=10; //khai báo và khởi tạo nhiều biến cùng lúc

int c=a+b; //khởi tạo biến bằng biểu thức

e) Phạm vi hoạt động của hằng và biến

Tất cả các biến trong ứng dụng Java đều có một phạm vi (scope), hay là các đặc

trưng xác định nơi có thể truy cập biến chỉ bằng tên của nó. Nếu biến nằm trong vùng phạm vi, thì có thể tương tác với nó bằng tên. Nếu biến nằm ngoài vùng phạm vi thì

điều này là không thể.

Có nhiều mức phạm vi trong ngôn ngữ Java, được xác định bởi vị trí khai báo

public class SomeClass {

member variable scope

public void SomeMethod(parameters ) {

method parameter scope

local variable declaration(s)

local scope

someStatementWithACodeBlock {

block scope

}

}

}

của biến ở đâu. Đoạn mã dưới đây chỉ ra vị trí khai báo các biến:

Phạm vi của một biến trải rộng từ đầu đoạn (hoặc đầu khối) cho đến cuối đoạn

(hoặc cuối khối) mã lệnh mà nó được khai báo trong đó.

1.2.4. Kiểu dữ liệu Mảng (Array)

Mảng được dùng để lưu trữ các khoản mục (items) cùng kiểu dữ liệu liền kề nhau trong bộ nhớ. Mỗi lần ta khai báo kích thước của một mảng, nó sẽ không thể thay đổi. Dữ liệu trên mảng có thể là kiểu dữ liệu nguyên thủy hoặc đối tượng. Cũng như các biến, ta có thể gán các giá trị vào mảng tại các phần tử được tạo ra trong mảng. Nếu không, Java sẽ gán giá trị mặc định vào tất cả các phần tử của mảng, giá trị mặc định

phụ thuộc vào kiểu dữ liệu. Ví dụ nếu kiểu dữ liệu là nguyên (int) thì giá trị mặc định ban đầu sẽ là 0.

27

Khai báo mảng

Đặc trưng của mảng là một cặp dấu ngoặc vuông ([]), một mảng được khai báo

bằng hai cách :

Cặp ngoặc vuông đặt sau tên biến

Cặp ngoặc vuông đặt sau kiểu dữ liệu

Cú pháp như sau:

Datatype identifier[]; hoặc Datatype[] identifier;

Ví dụ 1.5:

int arrInt[]; hoặc int[] arrInt;

int[] arrInt1, arrInt2, arrInt3;

Cấp phát bộ nhớ cho mảng

Sau khi khai báo, bản thân mảng chưa được xác định (chưa được cấp phát bộ

nhớ), bởi vì Java không cho phép chỉ định số phần tử khi khai báo. Để định vị cho một

mảng trong bộ nhớ ta cần dùng từ khóa new.

Cú pháp như sau:

Identifier = new datatype[n];

Trong đó Identifier là tên biến mảng đã được khai báo, datatype là kiểu dữ liệu

của mảng, n là số phần tử của mảng.

Ví dụ 1.6: để cấp phát một vùng nhớ cho một mảng arrInt đã khai báo ở trên gồm

100 phần tử kiểu int ta thực hiên như sau:

int arrInt = new int[100];

Tuy nhiên Java cũng cho phép thực hiện việc cấp phát vùng nhớ cho mảng trong

khi khai báo theo cú pháp như sau:

Datatype identifier[] = new Datatype[n];

Ví dụ 1.7:

char ch[] = new char [10]; // khai báo và cấp phát bộ nhớ bằng từ khóa new

Lệnh này sẽ tạo ra mảng ch có 10 phần tử kiểu char được đánh số từ 0 đến 9.

Khởi tạo mảng

Chúng ta có thể khởi tạo giá trị ban đầu cho các phần tử của mảng khi nó được

khai báo.

28

Ví dụ 1.8:

int arrInt[] = {1, 2, 3};

chararrChar[] = {„a‟, „b‟, „c‟};

string arrStrng[] = {“ABC”, “EFG”, „GHI‟};

Truy cập mảng

Chỉ số mảng trong Java bắt đầu tư 0. Vì vậy phần tử đầu tiên có chỉ số là 0, và

phần tử thứ n có chỉ số là n-1. Các phần tử của mảng được truy xuất thông qua chỉ số

của nó đặt giữa cặp dấu ngoặc vuông ([]).

int arrInt[] = {1, 2, 3};

int x = arrInt[0]; // x sẽ có giá trị là 1.

int y = arrInt[1]; // y sẽ có giá trị là 2.

int z = arrInt[2]; // z sẽ có giá trị là 3.

Ví dụ 1.9: khai báo mảng

1.2.5. Toán tử và biểu thức

a) Toán tử

Toàn tử là các ký hiệu sử dụng cho các phép toán, thường được tham gia vào

việc thành lập các biểu thức.

Trong Java có các loại toán tử sau:

Toán tử hai ngôi:

< nhỏ hơn a < b a nhỏ hơn b

<= nhỏ hơn hoặc bằng a <= b a nhỏ hơn hoặc bằng

> lớn hơn a > b a lớn hơn b

>= lớn hơn hoặc bằng a >= b a lớn hơn hoặc bằng b

+ phép cộng a + b a cộng b

– phép trừ a – b a trừ b

* phép nhân a * b a nhân b

/ phép chia a / b a chia b

% phép tính modulo a % b a modulo b

<< a << n dịch số a sang trái n bit

dịch chuyển bit sang trái

29

>> dịch chuyển bit sang a >> b dịch số a sang phải n bit

phải

>>> dịch chuyển bit a >>> b dịch a sang phải n bit và

điền 0 vào các bit trống

sang phải và điền 0 vào các bit trống

& Phép AND trên bit a & b 4 & 2 = 0

Phép OR trên bit a | b 4 | 2 = 6 |

Phép XOR trên bit a ^ b 4 ^ 2 = 1 ^

Bảng 1.10. Toán tử 2 ngôi

Toán tử đơn:

Toán tử Phép toán Ví dụ Ghi chú

– đổi dấu a =10, –a = –10 – a

~ phép đảo bit a =6, ~a = 1 ~ a

++ Tăng a++ gán a = a+1

–– giảm a––/– gán a= a–1

Bảng 1.11. Danh sách các toán tử đơn

Toán tử gán :

Toán tử gán Ví dụ Ý nghĩa

+= a += 3 a = a+3

–= a –= 4 a = a – 4

*= a *= 2 a = (a*2)

/= a /= 5 a = (a/5)

&= a &= b a = (a&b)

|= a |= b a = (a | b)

^= a ^= b a = (a ^ b)

%= a %= b a = (a % b)

<<= a <<= n a = (a << n)

>>= a >>= n a = (a >> n)

30

>>>= a >>>= n a = (a >>> n)

Bảng 1.12. Danh sách các toán tử gán

Toán tử trên số thực:

Toán tử Phép toán Ví dụ Ghi chú

= Phép gán a = 10 Gán cho a giá trị 10

== So sánh bằng a == b a bằng b

!= Không bằng a != b a khác b

< nhỏ hơn a < b a nhỏ hơn b

<= a <= b a nhỏ hơn hoặc bằng

nhỏ hơn hoặc bằng

> lớn hơn a > b a lớn hơn b

>= lớn hơn hoặc a >= b a lớn hơn hoặc bằng

bằng

+ phép cộng a + b a cộng b

– phép trừ a – b a trừ b

* phép nhân a * b a nhân b

/ phép chia a / b a chia b

Bảng 1.13. Danh sách các toán tử trên kiểu số thực

Toán tử trên kiểu Boolean:

Toán tử Phép toán Ví dụ Ghi chú

! phép đảo !a lấy giá trị ngược lại với a

&& kiện a && b thỏa đồng thời 2 điều kiện a và điều

AND b

|| điều kiện OR a || b thoả a hay ba3 hai

== phép bằng a == b xét a có bằng b không

!= phép not a != b xét a có khác b không

?: chuỗiđiềukiện a ? expr1 nếu a đúng thì lấy kết quả

: expr2 expr1, ngược lại lấy expr2

Bảng 1.14. Danh sách các phép toán logic

31

b)Biểu thức:

Biểu thức là một sự kết hợp giữa các toán tử và các toán hạng theo đúng một trật

tự nhất định, để diễn đạt một công thức toán học nào đó. Mỗi biểu thức có sẽ có một giá trị.

Mỗi toán hạng có thể là một hằng, một biến hoặc một biểu thức khác.

Trong trường hợp, biểu thức có nhiều toán tử, ta dùng cặp dấu ngoặc tròn () để

chỉ định toán tử nào được thực hiện trước

Như vậy hằng, biến, phần tử mảng và hàm cũng được xem là biểu thức.

Ví dụ 1.10: sum = 5 + 4;

Có 3 loại biểu thức chính là:

Biểu thức số liên kết các biến số, các hằng bằng các phép toán số, kết quả của loại

biểu thức này là một giá trị số.

Biểu thức gán dùng để gán giá trị cho một biến, một hằng.

Biểu thức logic chỉ cho ra kết quả là true hay false.

Các biểu thức phức tạp là do lồng ghép các biểu thức đơn giản vào nhau ta có thể

phân tích chúng dựa vào dấu ngoặc tròn (()).

1.2.6. Các cấu trúc điều khiển

Cấu trúc điều khiển là các lệnh kiểm tra và điều khiển quá trình thực hiện các câu

lệnh sao cho chương trình thực hiện đúng và đạt được yêu cầu đặt ra.

a) Cấu trúc lựa chọn if:

Câu lệnh if kiểm tra kết quả của một điều kiện và thực thi một thao tác phù hợp

trên cơ sở kết quả đó. Lệnh if có các dạng sau:

Dạng 1:

Cú pháp:

if ()

Trong đó:

- Biểu thứ điều kiện là một biểu thức logic

- Câu lệnh là một câu lệnh nào đó muốn thực hiện khi biểu thức logic trả về kết

quả là true.

Ý nghĩa của câu lệnh là: nếu biểu thức logic đúng thì thực hiện câu lệnh

Trong trường hợp muốn có nhiều câu lệnh cùng thực hiện khi biểu thức logic

đúng (true) thì các lệnh này được bao trong một khối lệnh.

Cú pháp như sau:

32

if (){}

Ví dụ 1.11:

import java.util.Date;

Date today = new Date();

if (today.getDay == 0) System.out.println(“It is Sunday.”);

Dạng 2:

Cú pháp:

if () else

Trong đó:

- biểu thứ điều kiện là một biểu thức logic

- Câu lệnh 1 là một câu lệnh nào đó muốn thực hiện khi biểu thức logic trả về kết

quả là đúng.

- Câu lệnh 2 là một câu lệnh nào đó muốn thực hiện khi biểu thức logic trả về kết

quả là sai.

Ý nghĩa của câu lệnh là nếu biểu thức logic đúng thì thực hiện câu lệnh 1 trong

trường hợp ngược lại (biểu thức logic sai) thì thực hiện lệnh 2.

Trong trường hợp muốn có nhiều câu lệnh cùng thực hiện khi biểu thức logic

đúng (true) hoặc sai (false) thì các lệnh này phải là một khối lệnh. Trong trường hợp

đầy đủ ta có thể viết lệnh theo cú pháp như sau:

if (){[]} else {[]}

import java.util.Date;

Date today = new Date();

if (today.getDay == 0)

System.out.println(“It is Sunday.”);

else

System.out.println(“It is not Sunday.”);

Ta cũng có thể dùng các cấu trúc if lồng nhau

if

else if

else if

else ;

Ví dụ 1.13:

import java.util.Date;

Date today = new Date();

33

Ví dụ 1.12:

if (today.getDay == 0) System.out.println(“It is Sunday.”);

else if (today.getDay ==1) System.out.println(“It is Monday.”);

else if (today.getDay ==2) System.out.println(“It is Tuesday.”);

else if (today.getDay ==3) System.out.println(“It is Wednesday.”);

else if (today.getDay ==4) System.out.println(“It is Thursday.”);

else if (today.getDay ==5) System.out.println(“It is Friday.”);

else System.out.println(“It must be Saturday.”);

Cấu trúc lựa chọn Switch:

Switch

{ case : ;

case : ;

case : ;

default : ;

}

import java.util.Date;

Date today = new Date();

Switch (today.getDay)

{ case 0: //Sunday

case 3: //Wednesday

case 6: //Saturday

System.out.println(“It‟s Football day!”);

break;

case 2: //Tuesday

System.out.println(“It‟s Tennis day!”);

case 1: //Monday

case 4: //Thursday

case 5: //Friday

System.out.println(“It‟s Golf day!”); break;

}

Ví dụ 1.14:

b) Cấu trúc lặp:

Vòng lặp for:

Câu lệnh for cho phép thực hiện lặp lại công việc cho đến khi biểu thức điều kiện sai.

for ([];[];[])

34

Trong đó:

- Khởi tạo: là một biểu thức dùng để gán giá trị khởi tạo cho một biến điều khiển

lặp

- Biểu thức kiểm tra: là một biểu thức logic dùng để kiểm tra việc lặp nếu biểu

thức đúng thì cho phép lặp lại ngược lại sẽ kết thúc vòng lặp.

- Bước nhảy: dùng để thiết lập bước nhảy cho mỗi lần lặp

for (int count = 0; count <100; count ++)

System.out.println(“Count = ” + count);

Ví dụ 1.15:

Trong trường hợp trong vòng lặp for muốn thực hiện nhiều lệnh thì các câu lệnh phải là một khối lệnh. Trong trường hợp đầy đủ ta có thể viết lệnh theo cú pháp như sau:

for (; ; )

{[]}

Vòng lặp While:

Cú pháp:

while

;

Trong đó:

- biểu thức boolean: là một biểu thức logic trả lại một trong 2 giá trị đúng (true) hoặc sai (false)

- khối lệnh: là tập các câu lệnh sẽ được thực hiện trong vòng lặp

Ý nghĩa của câu lệnh như sau: Trong khi biểu thức logic còn đúng thì lặp lại các công việc trong khối lệnh.

int count = 0;

while (count < 100)

{ System.out.println (“Count = “+count);

count++;

}

Ví dụ 1.16:

Vòng lặp do … while:Vòng lặp này tương tự như vòng lặp while do tuy nhiên khối lệnh sẽ được thực hiện trước khi khiểm tra biểu thức boolean.

do

{ ;

} while ()

public void ShowYears(int year)

{ do {System.out.println(“Year is “+ year);

35

Ví dụ 1.17:

} while (year <2000)

}

c) Các lệnh nhảy:

Lệnh break: dùng để thoát khỏi cấu trúc switch hoặc thoát khỏi vòng lặp hiện tại.

while

{ lệnh 1;

lệnh 2;

if

break;

lệnh 3;

}

lệnh 4;

Ví dụ 1.18: xét đoạn chương trình tìm số nguyên x trong một mảng số nguyên cho

class BreakExam

{ public static void main (String args[]) throws Exception

{ char ch[] = {'h','e','l','l','o',' ','w','o','r','l','d'};

char c = ' '; int i=0;

boolean flag=false;

System.out.print("Nhap ky tu can tim: ");

c = (char)System.in.read();

for (i=0; i<=10;i++)

{ if (ch[i] == c)

{ flag = true;

break;

}

}

if (flag == false) System.out.println("Ky tu "+c+" khong tim thay");

else System.out.println("Ky tu "+c+" nam o vi tri thu "+(i+1));

}

}

trước như sau:

Lệnh continue: lệnh continue điều khiển chương trình nhảy về đầu vòng lặp, bỏ

qua các lệnh trong khối lặp sau nó.

while

{ lệnh 1;

36

lệnh 2;

if

continue;

lệnh 3;

}

lệnh 4;

class Continue

{ public static void main(String args[])

{

for (int i=0; i<10; i++)

{ System.out.print(i++" ");

if ((i%2)==0) continue;

System.out.println("");

}

}

}

Ví dụ 1.19: xét đoạn chương trình in ra các số từ 0 đến 9 như sau:

Nhãn (Label):

Java không hỗ trợ lệnh goto, mặc dù lệnh goto có trong bộ từ khóa. Java thực hiện việc kết hợp lệnh break hay continue với nhãn để thay thế lệnh goto trong việc xử lý lặp như sau:

public void paint (Graphics g)

{ int line=1;

outsideLoop:

for (int out =0; out<3; out ++)

{ g.drawString(“out = “+ out, 5, line * 20);

line ++;

for (int inner=0; inner < 5; inner++)

{ // tạo biến ngẫu nhiên

double randNum = Math.random();

g.drawString(Double.toString(randNum), 15, line * 20);

line++;

if (randNum < 0.1

{ g.drawString(“break to outsideLoop”, 25, line * 20);

line++;

break outsideLoop;

}

37

Ví dụ 1.20:

if (randNum < 0.6)

{ g. g.drawString(“continue-outsideLoop, 25, line * 20);

line++;

continue outsideLoop;

}

}

g.drawString(“All done”, 50, line *20);

}

1.2.7. Hàm – Phƣơng thức (Function – Method)

Khái niệm

Hàm hay phương thức trong Java chính là các khối lệnh đơn vị thực hiện những chức năng riêng biệt. Vì trong Java không có một hàm nào là toàn cục mà luôn phải

gắn với một đối tượng cụ thể, thể hiện hành vi ứng xử của đối tượng đó. Nên trong

ngôn ngữ Java sẽ dùng khái niệm phương thức thay cho khái niệm hàm.

Định nghĩa một phương thức:

Cấu trúc

| integer | … > (<đối số 1>, <đối số 2>, … )

{ }

Cách truy xuất

private : phương thức này chỉ được truy xuất bên trong lớp chứa nó.

public : có thể truy xuất từ bất kỳ lớp bên ngoài nào.

protected : chỉ các lớp dẫn xuất của lớp chứa phương thức này mới truy xuất

được nó.

Cách cập nhật

static: là phương thức có thể được gọi mà không cần đến đối tượng. Nó chỉ được

sử dụng đối với các dữ liệu và các phương thức tĩnh khác.

absttract : phương thức đơn giản nhất, không cài đặt gì ở trong lớp khai báo nó. Nhưng phương thức này sẽ được phát triển trong các lớp là dẫn xuất của lớp hiện

hành.

38

final: phương thức loại này được bảo vệ không cho các lớp dẫn xuất khai báo và cài đặt lại. (overriding).

native : là phương thức được viết bằng ngôn ngữ khác Java. Giống như phương

thức abstract, phương thức native chỉ có tên gọi và khối lệnh rỗng. Nhưng khác với abstract nó được cài đặt trong các thư viện dùng chung viết bằng ngôn ngữ khác.

synchronized: phương thức loại này đảm bảo dữ liệu sẽ không bị sai lạc khi cùng

một lúc hai phương thức truy cập cùng một dữ liệu, thường được sử dụng trong kỹ

thuật luồng (thread).

Giá trị trả về

void : là loại phương thức không trả về giá trị nào.

integer, boolean, float, … : kiểu dữ liệu của giá trị mà phương thức trả về.

Gọi một phƣơng thức

Khi muốn một phương thức được thực hiện, ta chỉ cần gọi tên của phương thức

và gán các đối số là các giá trị hay các biến có cùng kiểu với các đối số đã được khai báo.

class CallMethod

{ static public int hinhvuong(int x)

{ return (x*x); }

public static void main(String args[])

{ int canh = 0, len = 0; String chuoi = "";

char buff[] = new char[10];

System.out.print("Nhap canh hinh vuong : ");

try { len = System.in.read(buff);

String s = String(buff, 0, len-1);

chuoi = s;

canh = Integer(chuoi).intValue();

} catch(Exception e) {}

System.out.println("Dien tich hinh vuong: "+hinhvuong(canh));

}

}

Ví dụ 1.21:

1.2.8. Nhập xuất dữ liệu

Việc nhập xuất dữ liệu với java sẽ được đề cập chi tiết trong chương 3 của tài liệu này. Tuy nhiên để ứng dụng lý thuyết của chương vào giải quyết bài toán cũng

như cài đặt các thử nghiệm để hiểu sau hơn về lý thuyết thì ta cần phải biết những thủ

tục nhập xuất cơ bản với java. Vì vậy trong phần này sẽ trình bày một số cách thức để

39

nhập xuất dữ liệu với java qua cửa số lệnh (console).

Nhập dữ liệu là tác vụ đưa các dữ liệu cụ thể vào cho biến trong chương trình.

Như vậy, phải có một nguồn chứa dữ liệu (bàn phím, tập tin, biến khác).

Xuất dữ liệu là tác vụ đưa trị cụ thể của biến trong chương trình ra một nơi chứa

(màn hình hay file hay biến khác).

Nhập/xuất dữ liệu là các phương tiện mà chương trình tương tác với người dùng

và thường không thể thiếu trong đa số các ứng dụng. Thường có hai cơ chế nhập xuất dữ liệu có tương tác với người dùng là:

 Nhập xuất dữ liệu trong các ứng dụng console application  Nhập/xuất dữ liệu thông qua các phần tử trên GUI. Cách 1 thường dùng trong các ứng dụng chạy theo cơ chế tuần tự còn cách 2 được áp dụng trong các ứng dụng hướng cửa sổ.

Việc nhập/xuất dữ liệu với java sẽ được đề cập chi tiết trong chương 3 của tài

liệu này. Trong phần này chỉ trình bày một số cách thức đơn giản để nhập xuất dữ liệu trong các ứng dụng console application.

a) Xuất dữ liệu

Java cung cấp class System mô tả hệ thống trong đó có chứa 2 đối tượng mặc

định dùng để nhập và xuất dữ liệu đó là System.in và System.out.

Để xuất dữ liệu ra màn ta hình dùng 2 phương thức của đối tượng System.out đó

là:

 System.out.print()  System.out.println([Dữ liệu xuất]);

Trong đó dữ liệu xuất có thể là : ký tự, số, chuỗi,…print là xuất ra màn hình mà

không xuống dòng, còn println là xuất ra màn hình và con trỏ sẽ tự động xuống dòng.

class OutputExam{

public static void main(String args[])

{

int a=10,b=30;

System.out.print(“Tong cua ” + a + “ + ” + b + “ = ” + (a+b));

}

}

Ví dụ 1.22: sau đây sẽ xuất ra màn hình một xâu và giá trị của biến

Kết quả chạy chương trình là:

40

Tong cua 10 + 30 = 40

b) Nhập dữ liệu

Nhập dữ liệu từ bàn phím khá phức tạp vì với mỗi dữ liệu có cách nhập khác nhau: Ký tự thì chỉ cần 1 phím, số nguyên, số thực có thể nhập với nhiều phím nên các

phím nhập cần giữ lại bộ đệm (Buffer), có thể cần kiểm tra phím nhập.

Java cung cấp 2 gói dữ liệu java.io và java.util chứa các lớp cho việc nhập xuất

Gói dữ java.io có các liệu

lớp Reader, BufferedReader, InputStream, OutputStrean,…, có chứa các phương thức cho việc nhập dữ liệu (chi tiết sẽ được

nghiên cứu ở chương 3).

Ví dụ 1.23: Sau sẽ giúp nhập xuất với các kiểu dữ liệu khác nhau sử dụng các đối

import java.io.*; // file InOutDemo.java

class InOutDemo

{ public static void main(String args []) throws java.io.IOException

{

Reader inputChar_Obj = new InputStreamReader(System.in);

System.out.print("Input a character:");

char c = (char)inputChar_Obj.read() ;

System.out.println(" character read :" + c);

input_Obj=

new BufferedReader(new

InputStreamReader(

BufferedReader System.in));

System.out.print("Input an Integer:");

int n= Integer.valueOf( input_Obj.readLine()).intValue() ;

System.out.println(" integer read :" + n);

System.out.print("Input a Double:");

double x= Double.valueOf( input_Obj.readLine()).doubleValue() ;

System.out.println(" Double read :" + x);

System.out.print("Input a string:");

String s = input_Obj.readLine();

System.out.println(" String read :" + s);

System.out.print("Input a character:");

int m = System.in.read() ;

41

tượng của gói java.io

System.out.println(" Code of this character :" + m);

}

}

Kết quả thực hiện chương trình

Input a character:r

character read :r

Input an Integer:123

integer read :123

Input a Double:12.908

Double read :12.908

Input a string:Hello

String read :Hello

Input a character:A

Code of this character:65

Gói java.util cũng cung cấp lớp Scanner cũng có các phương thức cho việc nhập

xuất các loại dữ liệu như: nextLine, next, nextInt, nextFloat, nextDouble,…,

import java.util.Scanner;

public class InOutScan {

public static void main(String[] args) {

int tuoi;

String ten;

Scanner nhapDuLieu = new Scanner(System.in);

System.out.print("Nhập Tên: ");

ten = nhapDuLieu.nextLine();

System.out.print("Nhập Tuổi: ");

tuoi = nhapDuLieu.nextInt();

System.out.println("\nTên Vừa Nhập:" + ten+"\n");

System.out.println("Tuổi Vừa Nhập: " + tuoi);

}

42

Ví dụ 1.24:Sau đây sử dụng lớp Scanner để nhập dữ liệu

}

Kết quả chạy chương trình:

Câu hỏi và bài tập chƣơng 1

1. Viết chương trình thực hiện các công việc sau:

a. Nhập vào 3 số nguyên dương a, b, c

b. Kiểm tra nếu a, b, c, là 3 cạnh của một tam giác thì tính diện tích, chu vi của

tam giác tạo thành bởi 3 cạnh a, b, c đó. Ngược lại thông báo không phải 3 cạnh của

tam giác.

2. Viết chương trình thực hiện các công việc sau:

a. Nhập vào một số nguyên n

b. Kiểm tra xem có phải n>0 không, nếu đúng thì tính n! và in kết quả ra màn

hình, ngược lại thì thông báo ra màn hình n <0.

3.Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím 2 số thực a, b và một ký tự là một phép toán (pheptoan)

b. Kiểm tra phép toán nhập vào có phải là một trong các phép toán: +, -, *, /

không. Nếu đúng tính giá trị của biểu thức a pheptoan b (ví dụ phép toán là + thì tính

a+b) và hiển thị kết quả ra màn hình ngược lại thông báo lỗi (chú ý yêu cầu kiểm soát cả lỗi chia cho 0).

4.Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím 2 số nguyên là tử số và mẫu số của một phân số.

b. Thực hiện tính toán để tìm phân số tối giản của phân số đã nhập và in kết quả

ra màn hình.

5. Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím 2 số nguyên a và b.

43

b. Tìm nghiệm của phương trình bậc nhất ax+b=0 và in kết quả ra màn hình

6. Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím 3 số nguyên a, b và c.

b. Tìm nghiệm của phương trình bậc hai ax2+bx +c=0 và in kết quả ra màn hình

7. Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím một số nguyên dương n.

b. Tính tổng s = 1/2+1/4+...+1/2*n và in kết quả s ra màn hình.

8. Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím một số nguyên dương n.

b. Tính tổng s = 1+1/3+...+1/(2*n+1) và in kết quả s ra màn hình.

9. Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím một số nguyên dương n.

b. Tính tổng s = 1+1/2-1/3+...+(-1)n*1/n và in kết quả s ra màn hình.

10. Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím một số nguyên n (0

nguyên.

b. Hiển thị mảng đã nhập ra màn hình

c. Tìm phần tử lớn nhất trong mảng và in vị trí của phần tử đó ra màn hình.

d. Sắp xếp mảng theo thứ tự tăng dần rồi in mảng sau sắp xếp ra màn hình.

11. Viết chương trình thực hiện các công việc sau:

a. Nhập vào từ bàn phím một số nguyên n (0

nguyên.

b. Hiển thị mảng đã nhập ra màn hình

c. Tìm phần tử lớn nhất trong mảng và in vị trí của phần tử đó ra màn hình.

44

d. Sắp xếp mảng theo thứ tự giảm dần rồi in mảng sau sắp xếp ra màn hình.

CHƢƠNG 2: LẬP TRÌNH HƢỚNG ĐỐI TƢỢNG TRONG JAVA

2.1. Giới thiệu về lập trình hƣớng đối tƣợng

Lập trình hướng đối tượng (OOP) là một phương pháp thiết kế và phát triển phần

mềm. Những ngôn ngữ OOP không chỉ bao gồm cú pháp và một trình biên dịch

(compiler) mà còn có một môi trường phát triển toàn diện. Môi trường này bao gồm

một thư viện được thiết kế tốt, thuận lợi cho việc sử dụng các đối tượng.

Đối với một ngôn ngữ lập trình hỗ trợ OOP thì việc triển khai kỹ thuật lập trình

hướng đối tượng sẽ dễ dàng hơn. Kỹ thuật lập trình hướng đối tượng cải tiến việc phát

triển các hệ thống phần mềm. Kỹ thuật ấy đề cao nhân tố chức năng (functionality) và

các mối quan hệ dữ liệu.

OOP là phương thức tư duy mới để giải quyết vấn đề bằng máy tính. Để đạt kết

quả, lập trình viên phải nắm vấn đề thành một thực thể quen thuộc với máy tính. Cách

tiếp cận hướng đối tượng cung cấp một giải pháp toàn vẹn để giải quyết vấn đề.

Hãy xem xét một tình huống cần được triển khai thành một hệ thống trên máy vi

tính: việc mua bán xe hơi. Vấn đề vi tính hóa việc mua bán xe hơi bao gồm những gì?

Những yếu tố rõ ràng nhất liên quan đến việc mua bán xe hơi là:

1) Các kiểu xe hơi (model). 2) Nhân viên bán hàng. 3) Khách hàng.

Những hoạt động liên quan đến việc mua bán:

1) Nhân viên bán hàng đưa khách hàng tham quan phòng trưng bày. 2) Khách hàng chọn lựa một xe hơi. 3) Khách hàng đặt hóa đơn. 4) Khách hàng trả tiền. 5) Chiếc xe được trao cho khách hàng.

Mỗi vấn đề được chia ra thành nhiều yếu tố, được gọi là các Đối tượng (Objects)

hoặc các Thực thể (Entities). Chẳng hạn như ở ví dụ trên, khách hàng, xe hơi và nhân

viên bán hàng là những đối tượng hoặc thực thể.

Lập trình viên luôn luôn cố gắng tạo ra những kịch bản (scenarios) thật quen thuộc với những tình huống thực tiễn. Bước thứ nhất trong phương pháp này là làm cho máy tính liên kết với những đối tượng thế giới thực.

Tuy nhiên, máy tính chỉ là một cỗ máy. Nó chỉ thực hiện những công việc được

45

lập trình mà thôi. Vì thế, trách nhiệm của lập trình viên là cung cấp cho máy tính những thông tin theo cách thức mà nó cũng nhận thức được cùng những thực thể như chúng ta nhận thức.

Đó chính là vai trò của kỹ thuật hướng đối tượng. Chúng ta sử dụng kỹ thuật

hướng đối tượng để ánh xạ những thực thể chúng ta gặp phải trong đời sống thực thành những thực thể tương tự trong máy tính.

Phát triển phần mềm theo kỹ thuật lập trình hướng đối tượng có khả năng giảm

thiểu sự lẫn lộn thường xảy ra giữa hệ thống và lĩnh vực ứng dụng.

Lập trình hướng đối tượng đề cập đến dữ liệu và thủ tục xử lý dữ liệu theo quan điểm là một đối tượng duy nhất. Lập trình hướng đối tượng xem xét dữ liệu như là một

thực thể hay là một đơn vị độc lập, với bản chất riêng và những đặc tính của thực thể

ấy. Bây giờ chúng ta hãy khảo sát những thuật ngữ „đối tượng‟ (object), „dữ liệu‟

(data) và „phương thức‟ (method).

Có nhiều loại đối tượng khác nhau. Chúng ta có thể xem các bộ phận khác nhau

trong một cơ quan là các đối tượng. Điển hình là một cơ quan có những bộ phận liên

quan đến việc quản trị, đến việc kinh doanh, đến việc kế toán, đến việc tiếp thị … Mỗi

bộ phận có nhân sự riêng, các nhân sự được giao cho những trách nhiệm rõ ràng. Mỗi

bộ phận cũng có những dữ liệu riêng chẳng hạn như thông tin cá nhân, bảng kiểm kê,

những thể thức kinh doanh, hoặc bất kỳ dữ liệu nào liên quan đến chức năng của bộ

phận đó. Rõ ràng là một cơ quan được chia thành nhiều bộ phận thì việc quản trị nhân

sự và những hoạt động doanh nghiệp dễ dàng hơn. Nhân sự của cơ quan điều khiển và

xử lý dữ liệu liên quan đến bộ phận của mình.

Chẳng hạn như bộ phận kế toán chịu trách nhiệm về lương đối với cơ quan. Nếu

một người ở đơn vị tiếp thị cần những chi tiết liên quan đến lương của đơn vị mình,

người ấy chỉ cần liên hệ với bộ phận kế toán. Một người có thẩm quyền trong bộ phận

kế toán sẽ cung cấp thông tin cần biết, nếu như thông tin ấy có thể chia sẻ được. Một

người không có thẩm quyền từ một bộ phận khác thì không thể truy cập dữ liệu, hoặc

không thể thay đổi làm hư hỏng dữ liệu. Ví dụ này minh chứng rằng các đối tượng là

hữu dụng trong việc phân cấp và tổ chức dữ liệu.

Khái niệm về đối tượng có thể được mở rộng đến hầu hết các lãnh vực đời sống, và hơn nữa, đến thế giới lập trình. Bất kỳ ứng dụng nào đều có thể được định nghĩa theo thuật ngữ thực thể hoặc đối tượng để tạo ra tiến trình xử lý mô phỏng theo tiến trình xử lý mà con người nghĩ ra.

Phương pháp giải quyết „top-down‟ (từ trên xuống) cũng còn được gọi là „lập trình

46

hướng cấu trúc‟ (structured programming). Nó xác định những chức năng chính của một chương trình và những chức năng này được phân thành những đơn vị nhỏ hơn cho đến mức độ thấp nhất. Bằng kỹ thuật này, các chương trình được cấu trúc theo hệ thống phân cấp các module. Mỗi một module có một đầu vào riêng và một đầu ra

riêng. Trong mỗi module, sự điều khiển có chiều hướng đi xuống theo cấu trúc chứ

không có chiều hướng đi lên.

Phương pháp OOP cố gắng quản lý việc thừa kế phức tạp trong những vấn đề

thực tế. Để làm được việc này, phương pháp OOP che giấu một vài thông tin bên trong

các đối tượng. OOP tập trung trước hết trên dữ liệu. Rồi gắn kết các phương thức thao

tác trên dữ liệu, việc này được xem như là phần thừa kế của việc định nghĩa dữ liệu. Bảng 2.1 minh họa sự khác biệt giữa hai phương pháp:

Phương pháp Top-Down OOP

Chúng ta sẽ xây dựng một khách Chúng ta sẽ xây dựng một tòa nhà 10 tầng

sạn.

với những dãy phòng trung bình, sang trọng, và một phòng họp lớn.

Chúng ta sẽ thiết kế các tầng lầu, các Chúng ta sẽ xây dựng một khách sạn với

phòng và phòng họp. những thành phần trên.

Bảng 2.1. Một ví dụ về hai phương pháp giải quyết OOP và Structured

2.2. Trừu tƣợng hoá dữ liệu

Khi một lập trình viên phải phát triển một chương trình ứng dụng thì không có

nghĩa là người ấy lập tức viết mã cho ứng dụng ấy. Trước hết, người ấy phải nghiên

cứu ứng dụng và xác định những thành phần tạo nên ứng dụng. Kế tiếp, người ấy phải

xác định những thông tin cần thiết về mỗi thành phần.

Hãy khảo sát chương trình ứng dụng cho việc mua bán xe hơi nói trên. Chương

trình phải xuất hóa đơn cho những xe hơi đã bán cho khách hàng. Để xuất một hóa

đơn, chúng ta cần những thông tin chi tiết về khách hàng. Vậy bước thứ nhất là xác

định những đặc tính của khách hàng.

Một vài đặc tính gắn kết với khách hàng là:

 Tên.  Địa chỉ.  Tuổi.  Chiều cao.  Màu tóc.

Từ danh sách kể trên, chúng ta xác định những đặc tính thiết yếu đối với ứng

dụng. Bởi vì chúng ta đang đề cập đến những khách hàng mua xe, vì thế những chi tiết

47

thiết yếu là:  Tên.  Địa chỉ.

Còn những chi tiết khác (chiều cao, màu tóc …) là không quan trọng đối với ứng

dụng. Tuy nhiên, nếu chúng ta phát triển một ứng dụng hỗ trợ cho việc điều tra tội phạm thì những thông tin chẳng hạn như màu tóc là thiết yếu.

Bên cạnh những chi tiết về khách hàng, những thông tin sau cũng cần thiết:

 Kiểu xe được bán.  Nhân viên nào bán xe.

Bên cạnh những đặc tính của khách hàng, xe hơi và nhân viên bán hàng, chúng ta

cũng cần liệt kê những hành động được thực hiện.

Công việc xuất hóa đơn đòi hỏi những hành động sau:

 Nhập tên của khách hàng.  Nhập địa chỉ của khách hàng.  Nhập kiểu xe.  Nhập tên của nhân viên bán xe.  Xuất hóa đơn với định dạng đòi hỏi.

Khung thông tin bên dưới cho thấy những thuộc tính và những hành động liên

quan đến một hóa đơn:

Các thuộc tính

Tên của khách hàng

Địa chỉ của khách

hàng

Kiểu xe bán

Nhân viên bán xe

Các hành động

Nhập tên

Nhập địa chỉ

Nhập kiểu xe

Nhập tên nhân viên

bán xe

Xuất hóa đơn

48

Định nghĩa

Sự trừu tƣợng hóa dữ liệu là quá trình xác định và nhóm các thuộc tính và các

hành động liên quan đến một thực thể cụ thể, xét trong mối tương quan với ứng dụng đang phát triển.

Tiếp theo, chúng ta muốn ứng dụng tính toán tiền hoa hồng cho nhân viên bán

hàng.

Những thuộc tính liên kết với nhân viên bán hàng có tương quan với ứng dụng

này là:

 Tên.  Số lượng xe bán được.  Tiền hoa hồng.

Những hành động đòi buộc đối với công việc này là:

 Nhập tên nhân viên bán xe.  Nhập số lượng xe bán được.  Tính tiền hoa hồng kiếm được.

Những thuộc tính

Tên

Số lượng xe bán được

Tiền hoa hồng

Những hành động

Nhập tên

Nhập số lượng xe bán

được

Tính tiền hoa hồng

Như thế, việc trừu tượng hóa dữ liệu tra đặt ra câu hỏi „Đâu là những thuộc tính

và những hành động cần thiết cho một vấn đề đặt ra?‟

Những ƣu điểm của việc trừu tƣợng hóa

Những ưu điểm của việc trừu tượng hóa là:

49

 Tập trung vào vấn đề.  Xác định những đặc tính thiết yếu và những hành động cần thiết.  Giảm thiểu những chi tiết không cần thiết.

Việc trừu tượng hóa dữ liệu là cần thiết, bởi vì không thể mô phỏng tất cả các

hành động và các thuộc tính của một thực thể. Vấn đề màu chốt là tập trung đến những hành vi cốt yếu và áp dụng chúng trong ứng dụng.

Chẳng hạn như khách hàng hoặc nhân viên bán hàng cũng có thể thực hiện

những hành động sau:  Người ấy đi lại.  Người ấy nói chuyện.

Tuy nhiên, những hành động này không liên quan đến ứng dụng. Việc trừu tượng

hóa dữ liệu sẽ loại bỏ chúng.

2.3. Lớp và định nghĩa lớp trong Java

Lớp được xem là một khuôn mẫu (template) của đối tượng. Lớp bao gồm các

vùng dữ liệu của đối tượng và các phương thức tác động lên các dữ liệu đó.

Lớp có tính kế thừa (inheritance); một lớp (subclass) có thể thứa kế tất cả các

vùng dữ liệu và các phương thức của một lớp khác (superclass).

Lớp còn có tính đa hình (polymorphism) cho phép cài đặt các lớp dẫn xuất rất

khác nhau từ cùng một lớp nguồn.

Bạn còn có thể định nghĩa một lớp bên trong một lớp khác. Đây là lớp xếp lồng

nhau, các thể hiện (instance) của lớp này tồn tại bên trong thể hiện của một lớp che

phủ chúng. Nó chi phối việc truy nhập đến các thành phần của thể hiện bao phủ chúng.

Định nghĩa lớp:

Một lớp được định nghĩa theo dạng sau:

[] [] class [extends ]

[implements ] {

}

Trong đó:

- Phần đầu của lớp là phần khai báo cho một lớp bao gồm:

+ dùng để thiết lập mức truy xuất cho lớp, trong Java có nhiều

mức truy xuất khác nhau như public, private…, là một thành phần

50

lựa chọn có thể khai báo hoặc không, trong trường hợp không khai báo cụ thể thì lớp sẽ được gán mức truy xuất mặc định.

+là dùng để khai báo một số chức năng cho lớp như lớp tượng

abstract, hay lớp hoàn tất việc cài đặt cho một lớp đã có Final. là một thành phần lựa chọn có thể khai báo hoặc không.

+ class là từ khóa bắt buộc

+ là tên của một lớp cần tạo, tên lớp phải được đặt là một tên

chuẩn như đã trình bày ở trên.

+ extends dùng để khai báo lớp là được kế thừa từ một lớp

cha.

+ implements ] dùng để khai báo khi muốn lớp là một thể

hiện từ một lớp nào đó.

- Phần thân của lớp nằm trong cặp ngoặc nhọn “{}”, trong đó

thành phần>là dùng để định nghĩa các thành phần như: Các trường, các phương thức

của lớp đó.

Các thuộc tính

Các thuộc tính (properties) hãy các vùng dữ liệu của lớp (field) bản chất chúng là

các biến được khai báo ở mức lớp, các thuộc tính được khai báo theo cú pháp sau:

[] [=value];

Trong đó:

- Access Modifier dùng để khai báo giới hạn phạm vi của thuộc tính, có 3 dạng giới hạn phạm vi cho thuộc tính là :public, private và protected. Thành phần này là

một lựa chọn có thể khai báo hoặc không.

- Datatype là kiểu dữ liệu của thuộc tính

- identifier là tên của thuộc tính, phải được đặt theo quy định về tên của java

- value là giá trị khởi tạo cho thuộc tính, thành phần này là một lựa chọn có thể

có hoặc không.

Phƣơng thức

Phương thức được định nghĩa như một hành động hoặc một tác vụ thật sự của đối tượng. Nó còn được định nghĩa như một hành vi mà trên đó các thao tác cần thiết được

thực thi. Trong mỗi lớp có thể định nghĩa phương thức theo cú pháp như sau:

[] [< None Access

Modifier>]([])

51

{

//body of method

}

Trong đó:

- Access Modifier dùng để khai báo giới hạn phạm vi của thuộc tính, có 3

dạng giới hạn phạm vi cho thuộc tính là : public, private và protected. Thành phần

này là một lựa chọn có thể khai báo hoặc không.

- None Access Modifier: cho phép thiết lập các thuộc tính của phương thức.

Java cung cấp các bổ nghĩa sau: static, abstract, final, native, synchronized, volatile.

- datatype: Kiểu dữ liệu mà phương thức trả về. Nếu không có một giá trị nào

được trả về, kiểu dữ liệu có thể là void.

- method_name: Tên của phương thức

- parameter_list: Chứa tên của tham số được sử dụng trong phương thức và

kiểu dữ liệu. Dấu phẩy được dùng để phân cách các tham số.

- Thân của phương thức nằm trong cặp dấu ngoặc nhọn “{}” là nơi viết đoạn

mã chương trình xử lý của phương thức đó.

Đoạn mã sau đây định nghĩa lớp Temp chứa một giá trị nguyên (int). Lớp này

chứa hai phương thức là: show() và main(). Cả hai phương thức đều có khả năng truy

cập bên ngoài lớp khi chúng được khai báo như public. Nếu chúng không trả về một

giá trị nào, kiểu dữ liệu trả về được định nghĩa như kiểu void.

Phương thức show() hiển thị một giá trị của biến x. Ở phương thức main(), hai

thí dụ của đối tượng thuộc lớp Temp được khai báo. Đối tượng thứ nhất gồm giá trị

mặc định của biến x. Nó được hiển thị ngay khi gọi phương thức show() lần đầu tiên.

Giá trị của xđược thay đổi dùng cho đối tượng thứ hai. Nó được hiển thị khi ta gọi

phương thức show() lần thứ hai.

class Temp

{

static int x=10;//variable

public static void show()//method

{ System.out.println(x);}

public static void main(String args[])

{

Temp t = new Temp();// object 1

t.show();//method call

Temp t1=new Temp();// object 2

52

Ví dụ 2.1: Định nghĩa lớp Temp chứa hai phương thức là: show() và main()

t1.x=20;

t1.show();

}

}

Các chỉ định truy xuất(Access Modifier)

Các chỉ định truy xuất dùng để giới hạn khả năng truy nhập vào một phương

thức, một thuộc tính hoặc một lớp đối tượng. Java cung cấp các chỉ định truy xuất sau

đây:

 Công cộng (Public): Chỉ định truy xuất public có thể được nhìn thấy từ mọi

gói hoặc mọi lớp.

 Bảo vệ (Protected): Các lớp mở rộng từ lớp hiện hành trong cùng một gói, hoặc tại các gói khác nhau có thể truy cập các phương thức, thuộc tính loại

này.

 Riêng tư (Private): Phương thức, thuộc tính riêng tư chỉ có thể được truy cập trực tiếp từ một phương thức trong cùng lớp hoặc truy cập gián tiếp qua

phương thức công cộng trong cùng một lớp.

Các bổ nghĩa phƣơng thức (None Access Modifier)

Các bổ nghĩa phương thức cho phép ta thiết lập các thuộc tính của phương thức.

Java cung cấp các bổ nghĩa sau:

 Tĩnh (static): phương thức có thể được gọi mà không cần đến đối tượng. Nó

chỉ được sử dụng đối với các dữ liệu và các phương thức tĩnh khác.

 Trừu tƣợng (abstract): Ngụ ý rằng phương thức không có một mã (code) và nó sẽ được bổ sung ở các lớp con (subclass). Loại phương thức này được

sử dụng trong các lớp kế thừa.

 Kết thúc (final): Phương thức không thể được thừa kế hoặc ghi đè

(Overridden).

 Tự nhiên (native): Chỉ ra rằng phần thân của phương thức được viết trên các

ngôn ngữ khác Java ví dụ C, hoặc C++.

 Đồng bộ (synchronized): Sử dụng với phương thức trong quá trình thực thi threads. Nó cho phép chỉ một thread được truy cập vào khối mã tại một thời điểm.

 Linh hoạt (volatile): Được sử dụng với các biến để thông báo rằng giá trị của biến có thể được thay đổi vài lần khi thực thi chương trình và giá trị của nó không được đặt vào thanh ghi.

53

Bảng dưới đây chỉ ra nơi mà các bổ nghĩa được sử dụng:

Bổ nghĩa Phƣơng thức Biến Lớp

public Yes Yes Yes

private Yes Yes (Nested Yes

class)

protected Yes Yes (Nested

Yes class)

abstract Yes No Yes

final Yes Yes Yes

native Yes No No

volatile No Yes No

Bảng 2.2. Sử dụng các bổ nghĩa

Từ khóa this

Thông thường bên trong thân của một phương thức ta có thể tham chiếu đến các

thuộc tính của đối tượng đó, tuy nhiên trong một số tình huống đặc biệt như tên của

tham số trùng với tên của thuộc tính, lúc đó để chỉ các thành viên của đối tượng đó ta

dùng từ khoá this, từ khoá this dùng để chỉ đối tượng này.

Ví dụ2.2: sau chỉ ra cho ta thấy trong tình huống này bắt buộc phải dùng từ khoá

class HSBColor {

int hue, saturation, brightness;

HSBColor (int hue, int saturation, int brightness) {

this.hue = hue;

this.saturation = saturation;

this.brightness = brightness;

}

}

this vì tên tham số của phương thức tạo dựng lại trùng với tên của thuộc tính

Ở ví dụ trên có các biên hue, saturation, brightness trung tên vì vậy để phân biệt

chúng là thuộc tính hay tham số thì ta phải dùng từ khóa this.

54

Getter và setter

Trong Java cũng như các ngôn ngữ lập trình hướng đối tượng khác thường thì

các thuộc tính không được thiết lập ở chế độ truy cập public vì làm như vậy sẽ làm mất tính bao đóng của các lớp đối tượng và khả năng proteced sẽ bị hạn chế. Chính vì vậy

thông thường các thuộc tính được khai báo là private. Tuy nhiên điều này làm cho việc

lấy giá trị, cũng như thiết lập các giá trị cho thuộc tính không thể được thực hiện từ các

lớp khác ngoài nó. Để giải quyết vấn đề này chúng ta dùng các phương thức getter và setter gọi là các phương thức truy cập.

Các phương thức truy cập là các phương thức giống như những phương thức

khác nhưng chúng thường tuân thủ theo một quy ước đặt tên riêng. Để cung cấp giá trị

một biến cá thể cho đối tượng khác, hãy tạo ra một phương thức có tên là getVariableName(). Tương tự như thế, để cho phép các đối tượng khác thiết đặt các

biến cá thể của đối tượng của bạn, hãy tạo ra phương thức setVariableName().

Trong cộng đồng Java, các phương thức truy cập này thường được gọi là các

getter và các setter vì tên chúng bắt đầu bằng get và set.

Đây là một số đặc tính chung của các getter và setter:

- Định tố truy cập của các getter và setter điển hình là public. - Các getter điển hình là không nhận tham số nào. - Các setter điển hình là chỉ nhận một tham số, đó là giá trị mới cho biến cá thể

mà chúng thiết đặt.

- Kiểu trả về của getter điển hình là cùng kiểu với biến cá thể mà nó báo lại

giá trị.

- Kiểu trả lại của setter điển hình là void, nghĩa là chúng không trả lại gì hết

(chúng chỉ đặt giá trị cho biến cá thể).

Ví dụ 2.3:Định nghĩa một lớp học sinh và xây dựng các phương thức getter và

class HocSinh { private String hoTen; private String lop;

private float diemTb; public void setHoTen(String hoTen1) {

//hoTen1 là biến cục bộ nhập vào, thường để trùng tên thuộc tính như các hàm setter phía dưới

this.hoTen = hoTen1; } public void setLop(String lop) {

this.lop = lop;

55

setter để truy cập vào các thuộc tính của lớp này.

}

public String getHoTen() { return hoTen;

} public String getLop() {

return lop; }

public float getDiemTb() { return diemTb;

} public void setDiemTb(float diemTb) {

this.diemTb = diemTb; }

} public class JavaDemo{

public static void main(String[] args) { HocSinh a = new HocSinh();

a.setHoTen("Vu Van T"); a.setLop("At7a");

a.setDiemTb(7.5f); System.out.println("Họ tên: " + a.getHoTen());

System.out.println("Lớp: " + a.getLop()); System.out.println("Điểm Tb: " + a.getDiemTb());

} }

Supper

Khi một lớp được kế thừa từ lớp cha trong cả lớp cha và lớp con đều có mô

phương thức trùng tên nhau, thế thì làm thế nào có thể gọi phương thức trùng tênđó

của lớp cha, java cung cấp cho ta từ khoá Super dùng để chỉ đối tượng của lớpcha

ví dụ 2.4:Xây dựng 2 lớp, một lớp cha và một lớp con có cùng một phương thức

class ASillyClass {

boolean aVariable;

void aMethod() {

aVariable = true;

}

}

56

và sử dụng Supper để phân biệt giữa các phương thứ này.

class ASillierClass extends ASillyClass {

boolean aVariable;

void aMethod() {

aVariable = false;

super.aMethod();

System.out.println(aVariable);

System.out.println(super.aVariable);

}

}

trong ví dụ trên ta thấy trong lớp cha có phương thức tên là aMethod trong lớp

concũng có một phương thức cùng tên, ta còn thấy cả hai lớp này cùng có một thuộc tính tên aVariable để có thể truy cập vào các thành viên của lớp cha ta phải dùngtừ

khoá super.

Trả về giá trị từ phƣơng thức

Khai báo kiểu giá trị trả về từ lúc khai báo phương thức, bên trongthân của

phương thức ta phải sử dụng phát biểu return value; để trả về kết quả,nếu hàm được khai báo kiểu void thì ta chỉ sử dụng phát biểu return; mệnh đềreturn đôi khi còn được

dùng để kết thúc một phương thức.

Truyền tham số cho phương thức

Khi ta viết các phương thức, một số phương thức yêu cầu phải có một sốtham số,

các tham số của một phương thức được khai báo trong lời khai báophương thức, chúng

phải được khai báo chi tiết có bao nhiêu tham số, mỗi tham sốcần phải cung cấp cho

chúng một cái tên và kiểu dữ liệu của chúng.

Ví dụ 2.5: Tạo một phương thức dùng để tính tổng của hai số, phương thức này

được

public double tongHaiSo(double a, double b){

return (a + b);

}

khai báo như sau:

Truyền theo tham trị

57

Khi gọi một phương thức mà tham số của phương thức có kiểu nguyênthủy, thì bản sao giá trị của tham số thực sự sẽ được chuyển đến phương thức, đâylà đặc tính

truyền theo trị ( pass- by – value ), nghĩa là phương thức không thể thayđổi giá trị của

các tham số truyền vào.

public class TestPassByValue {

public static void test(int t) {

t++;

System.out.println("Gia tri cua cac bien trong ham sau khi tang len 1 la " + t);

}

public static void main(String[] args) {

int t = 10;

System.out.println("Gia tri cua t truockhi goi ham = " + t);

test(t);

System.out.println("Gia tri cua t sau khi goi ham = " + t);

}

}

Ví dụ 2.6: Truyền theo tham trị

ta se nhận được kết quả ra như sau:

Gia tri của t truoc khi gọi ham = 10

Gia tri của t bên trong ham sau khi tang len 1 la 11

Gia tri của t sau khi gọi ham = 10

Như vậy giá trị của t trước và sau khi gọi là như nhau, mặc dù ta đã thay đổi giá

trị của t trong chương trình nhưng khi thoát khỏi chương trình giá trị của t không thay

đổi

Truyền theo tham biến

Trong Java không hỗ trợ việc truyền tham biến cho các tham số thuộc các kiểu dữ liệu nguyên thủy. Để truyền theo tham biến thì tham số phải thuộc về một kiểu dữ liệu tham chiếu nào đó như mảng, đối tượng …

package example;

publicclass Start {

publicstaticvoid example(int m[], int n) {

for(int i = 0; i < 10; ++i) {

58

Ví dụ 2.7: Sẽ truyền theo tham biến cho tham số là một mảng

m[i] = 1;

}

n = n + 1;

}

Publicstaticvoid main(String[] args) {

int n = 10;

int m[] = newint[10];

for(int i = 0; i < 10; ++i){

m[i] = i;

}

example(m, n);

System.out.print(“n = ” + n +”\n”);

for(int i = 0; i < 10; ++i){

System.out.print(“m[" + i + "] = ” + m[i] + “\n”);

}

}

}

Kết quả như sau:

n = 10

m[0] = 1

m[1] = 1

m[2] = 1

m[3] = 1

m[4] = 1

m[5] = 1

m[6] = 1

m[7] = 1

m[8] = 1

59

m[9] = 1

Qua kết quả cho thấy giá trị của các phần tử của mảng đã bị thay đổi sau khi ta

thay đổi trong phương thức example(m, n)

Nạp chồng (overloading) và ghi đè (overriding) phƣơng thức

Những phương thức được nạp chồng (overload) là những phương thức trong

cùng một lớp, có cùng một tên song có danh sách các tham số khác nhau. Sử dụng việc

nạp chồng phương thức để thực thi các phương thức giống nhau đối với các kiểu dữ liệu khác nhau.Ví dụ phương thức swap() có thể bị nạp chồng (overload) bởi các tham

số của kiểu dữ liệu khác nhưinteger, double và float

Phương thức được ghi đè (overriden) là phương thức có mặt ở lớp cha

(superclass) cũng như ở các lớp kế thừa. Phương thức này cho phép một lớp tổng quát chỉ định các phương thức sẽ là phương thức chung trong các lớp con.Ví dụ lớp xác

định phương thức tổng quát „area()‟. Phương thức này có thể được hiện thực trong một

lớp con để tìm diện tích một hình cụ thể như hình chữ nhật, hình vuông …

Phương thức nạp chồng là một hình thức đa hình (polymorphism) trong quá trình

biên dịch (compiler). Còn phương thức ghi đè là một hình thức đa hình trong quá trình

thực thi (runtime).

Đoạn chương trình sau mô tả nạp chồng phương thức được thực hiện như thế nào

//defined once

protected void perfomTask(double salary){

……….

System.out.prinln(“Salary is : ” + salary);

….

}

//overloaded –defined the second time with different parameters

protected void performTask(double salary, int bonus){

……

System.out.println(“Total Salary is: ” + salary+bonus);

….

}

60

Phương thức khởi tạo (Contructor) của lớp có thể bị nạp chồng (overload)

Phương thức ghi đè (Overriden) được định nghĩa lại ở các lớp con. Đoạn mã sau

đây mô tả phương thức ghi đè.

Ở đây ta dùng từ khoá “this” biểu thị đối tượng hiện hành, trong khi đó „super‟

được sử dụng để chỉ đối tượng lớp cha.

Phương thức ghi đè không phải là phương thức tĩnh (static). Nó là loại non-static.

{

int a;

SupperClass() // constuctor {

}

SupperClass(int b) //overloaded constructor

{

a=b;

}

public void message()

System.out.println("In the super class");

{

}

} class SubClass Extends SupperClass {// derriving a class

int a; SubClass(int a){//subclass constructor

this.a;

}

public void message(){ // overiding the base class message()

System.out.prinln(“In the sub class”);

}

}

Các đoạn mã sau đây mô tả việc thực thi ghi đè phương thức trong Java. class SupperClass // Tạo lớp cơ bản

Bây giờ tạo ra một đối tượng lớp cha và gán một lớp nhỏ tham chiếu đến nó như

sau:

SuperClasss spObj=new SubClass(22);

61

Câu lệnh „spObj.message()‟ thuộc phương thức lớp của SubClass.Ở đây kiểu đối tượng được gán cho „spObj‟ sẽ chỉ được xác định khi chương trình thực thi. Điều này được biết dưới khái niệm „liên kết động‟ (dynamic binding).

2.4. Đối tƣợng

Như đã biết một lớp đối tượng được xây dựng từ một tập các đối tượng có cùng chung thuộc tính và phương thức hay lớp đối tượng một thiết kế của các đối tượng, vì

vậy về cơ bản một đối tượng được tạo ra từ một lớp đối tượng. Ta có thể tạo được

những đối tượng của lớp này bằng câu lệnh:

ClassName ObjectName = new ClassName();

Trong đó:

- ClassName tên của lớp đối tượng

- ObjectName tên của đối tượng muốn tạo

- new từ khóa dùng để tạo đối tượng

- ClassName() hàm tạo của lớp đối tượng đó

Như vậy trong khai báo trên thấy rằng tạo một đối tượng gồm 3 phần

- Phần khai báo (Declaration): phần này khai báo một biến tham chiếu kiểu đối

tượng. Cũng có thể tách riêng phần này để tạo ra các biến tham chiếu kiểu đối tượng

như sau:

ClassName ObjectName1, ObjectName2, …;

Trong đó ObjectName1,ObjectName2 … là tên của các biến đối tượng muốn tạo

từ lớp đối tượng ClassName.

- Phần tạo (Instantiation): Sử dụng từ khóa newlà một phép toán trong Java để

tạo ra đối tượng.

- Phần khởi tạo (Initialization):Phép toán new cho phép gọi đến một hàm tạo để

khởi tạo giá trị cho một đối tượng.

Phần tạo và khởi tạo cũng có thể tách riêng để tạo các đối tượng như sau:

ObjectName = new ClassName();

public class Point {

public int x = 0;

public int y = 0;

public Point(int a, int b) {

x = a;

y = b;

62

Ví dụ 2.8:Tạo ra một lớp Point và tạo ra một đối tượng từ lớp đó

}

}

Tạo một đối tượng point1 từ lớp Point như sau

Point point1 = new Point(23,34);

Hoặc

Point point1;

Point1 = new Point(23,34);

2.5. Thiết lập và hủy

2.5.1. Thiết lập

Phương thức tạo dựng là một phương thức của lớp thường dùng để khởi tạo một đối tượng mới. Thông thường người ta thường sửdụng hàm tạo để khởi gán giá trị cho

các thuộc tính của đối tượng và có thể thựchiện một số công việc cần thiết khác nhằm

chuẩn bị cho đối tượng mới.

Đặc điểm của phƣơng thức tạo (Construtor)

 Hàm tạo có tên trùng với tên của lớp  Phương thức tạo không bao giờ trả về kết quả  Phương thức tạo được java gọi tự động khi một đối tượng của lớp được tạo

ra

 Phương thức tạo có thể có đối số như các phương thức thông thường khác  Trong một lớp có thể có nhiều phương thức

public class PhepTinh {

private int ToanHang1;

private int ToanHang2;

private char ToanTu;

public void setToanHang1(int ToanHang1) {

this.ToanHang1 = ToanHang1;

}

public void setToanHang2(int ToanHang2) {

this.ToanHang2 = ToanHang2;

}

63

Ví dụ 2.9: Tạomột lớp phép tính và sử dụng 2 hàm tạo

public void setToanTu(char ToanTu) {

this.ToanTu = ToanTu;

}

public int getToanHang1() {

return ToanHang1;

}

public int getToanHang2() {

return ToanHang2;

}

public char getToanTu() {

return ToanTu;

}

public PhepTinh() {

this.ToanHang1=0;

this.ToanHang2=0;

this.ToanTu='+';

}

public PhepTinh(int ToanHang1, int ToanHang2, char ToanTu) {

this.ToanHang1 = ToanHang1;

this.ToanHang2 = ToanHang2;

this.ToanTu = ToanTu;

}

public double TinhToan()

{

double giatri;

switch(ToanTu)

{

case '+':

giatri = ToanHang1 + ToanHang2 ;

break;

64

case '-':

giatri = ToanHang1 - ToanHang2 ;

break;

case '*':

giatri = ToanHang1 * ToanHang2 ;

break;

default:

giatri = (double)ToanHang1 / ToanHang2 ;

break;

}

return giatri;

}

}

Bây giờ tạo ra 2 đối tượng như sau:

PhepTinh pt1 = new PhepTinh();

double a = pt1.TinhToan();

PhepTinh pt2 = new PhepTinh(7,6,‟-‟);

double b = pt2.TinhToan();

System.out.print(“a =” + a + “b=” +b);

Chạy chương trình cho kết quả là: a=0 b=1

Hàm tạo mặc định (default constructor)

Nếu như trong class không có hàm khởi tạo thì máy ảo java sẽ làm nhiệm vụ gọi

ra một default constructor để tạo ra một đối tượng thể hiện của class. Hàm tạo này thực chất không làm gì cả,nếu trong lớp đã có ít nhất một hàm tạo thì hàm tạo mặc định sẽ không được tạora.

2.5.2. Hủy

Java không có phương thức hủy bỏ. Phương thức finalize tương tự nhưphương

thức hủy bỏ của C++, tuy nhiên nó không phải là phương thức hủy bỏ. Sởdĩ nó không phải là phương thức hủy bỏ vì khi đối tượng được hủy bỏ thì phươngthức này chưa

65

chắc đã được gọi đến. Phương thức này được gọi đến chỉ khi bộ thurác của Java được

khởi động và lúc đó đối tượng không còn được sử dụng nữa. Dovậy phương thức

finalize có thể không được gọi đến

Người lập trình C++ thường sử dụng toán tử new để cấp phát động một đốitượng,

nhưng lại thường quên gọi toán tử delete để giải phóng vùng nhớ này khikhông còn

dùng đến nữa, điều này làm rò rỉ bộ nhớ đôi khi dẫn đến chương trìnhphải kết thúc một

cách bất thường, quả thật đâu là một điều tồi tệ. Trong java takhông cần quan tâm đến điều đó, java có một cơ chế thu rác tự động, nó đủ thôngminh để biết đối tượng nào

không dùng nữa, rồi nó tự động thu hồi vùngnhớ dành cho đối tượng đó.

Trong ngôn ngữ C++ khi một đối tượng bị hủy, sẽ có một hàm đượcgọi tự động,

hàm này được gọi là huy tử hay còn gọi là hàm huy, thông thườnghàm hủy mặc định là đủ để dọn dẹp, tuy nhiên trong một số trường hợpthì hàm hủy mặc định lại không thể

đáp ứng được, do vậy người lập trình C++,phải viết ra hàm hủy riêng để làm việc đó,

tuy nhiên java lại không có khái niệmhàm hủy.

2.6. Các đặc tính của lớp

2.6.1. Tính bền vững

Cơchế đóng gói trong lập trình hướng đối tượng giúp chocác đối tượng dấu đi

một phần các chi tiết cài đặt, cũng nhưphần dữ liệu cục bộ của nó, và chỉ công bố ra

ngoài những gìcần công bố để trao đổi với các đối tượng khác. Hay có thể nói đối

tượng là một thành tố hỗ trợ tính đóng gói.

Đơn vị đóng gói cơ bản của ngôn ngữ java là class. Mộtclass định nghĩa hình

thức của một đối tượng. Một class định rõnhững thành phần dữ liệu và các đoạn mã

cài đặt các thao tácxử lý trên các đối tượng dữ liệu đó. Java dùng class để xâydựng

những đối tượng. Những đối tượng là những thể hiện(instances) của một class.

Một lớp bao gồm thành phần dữ liệu và thành phần xử lý.Thành phần dữ liệu của

một lớp thường bao gồm các biến thànhviên và các biến thể hiện của lớp. Thành phần

xử lý là các thaotác trên các thành phần dữ liệu, thường trong java người gọi làphương thức.

public class EncapTest{

private String name;

private String idNum;

private int age;

66

Ví dụ 2.10: Tạo ra một lớp mà các thuộc tính của nó là private, nghĩa là các thuộc tính không thể được truy xuất trực tiếp từ bất kỳ đối tượng nào thuộc các lớp nằm ngoài nó.

public int getAge(){

return age;

}

public String getName(){

return name;

}

public String getIdNum(){

return idNum;

}

public void setAge( int newAge){

age = newAge;

}

public void setName(String newName){

name = newName;

}

public void setIdNum( String newId){

idNum = newId;

}

}

Từ một lớp bên ngoài chỉ có thể truy nhập vào các thuộc tính của các đối tượng

public class RunEncap{

public static void main(String args[]){

EncapTest encap = new EncapTest();

encap.setName("James");

encap.setAge(20);

encap.setIdNum("12343ms");

System.out.print("Name : " + encap.getName()+

" Age : "+ encap.getAge());

}

67

này thông qua phương thức getters and setters như sau:

}

Các lợi ích của tính đóng gói là

- Có thể tạo ra các thuộc tính của lớp là chỉ đọc(Read-only) hoặc chỉ ghi (Write-

only)

- Một lớp có toàn quyền kiểm soát những gì được lưu trữ trong các thuộc tính mà

lớp khác không được phép tác động.

- Người sử dụng lớp đó không biết được dữ liệu được lưu trữ như thế nào. Một lớp có thể thay đổi kiểu dữ liệu của các thuộc tính mà người sử dụng không cần thay

đổi lại mã nguồn của họ

2.6.2. Tính thừa kế

Thừa kế là việc tạo một lớp mới từ một lớp đã biết sao cho các thành phần

(fields và methods) của lớp cũ cũng sẽ thành các thành phần (fields và methods)

của lớp mới. Khi đó ta gọi lớp mới là lớp thừa kế hay lớp dẫn xuất (derived class)

từ lớp cũ (superclass). Có thể lớp cũ cũng là lớp được dẫn xuất từ một lớp nào đấy,

nhưng đối với lớp mới vừa tạo thì lớp cũ đó là một lớp siêu lớp trực tiếp

(immediate supperclass).

Trong Java để tạo một lớp mới là thừa kế từ một lớp nào đó dùng từ khóa extends

cú pháp như sau:

class A extends B

{

// …

}

Trong đó lớp A là lớp con được thừa kế từ lớp B

public class Shape {

private String Mau;

private boolean FillMau;

public shape() {

this.Mau = "red";

this.FillMau=true;

}

68

Ví dụ 2.11: Tạo ra một lớp shape và một lớp tam giác kế thừa từ lớp shape

public boolean isFillMau() {

return FillMau;

}

public String getMau() {

return Mau;

}

public void setFillMau(boolean FillMau) {

this.FillMau = FillMau;

}

public void setMau(String Mau) {

this.Mau = Mau;

}

public void Inkq()

{

System.out.println("Mau = "+ getMau());

System.out.println("Dổ màu = "+ isFillMau());

}

}

public class TamGiac extends Shape{

private double canh1;

private double canh2;

private double canh3;

public void setCanh1(double canh1) {

this.canh1 = canh1;

}

public void setCanh2(double canh2) {

this.canh2 = canh2;

}

public void setCanh3(double canh3) {

69

Tạo lớp tam giác kế thừa lớp shape

this.canh3 = canh3;

}

public double getCanh1() {

return canh1;

}

public double getCanh2() {

return canh2;

}

public double getCanh3() {

return canh3;

}

public TamGiac() {

this.canh1=0;

this.canh2=0;

this.canh3=0;

}

// @Override

public boolean kiemtra() {

if(((canh1>0)&(canh2>0)&(canh3>0))&((canh1 + canh2> canh3)&(canh3 + canh2>

canh1)&(canh3 + canh1> canh2)))

return true;

else

return false;

}

//@Override

public double tinhdientich() {

double p = canh1+canh2+canh2;

return (Math.sqrt(p*(p-canh1)*(p-canh2)*(p-canh3)));

}

// @Override

public double tinhchuvi() {

70

return(canh1+canh2+canh2);

}

@Override

public void Inkq(){

System.out.println("Các thuộc tính của Tam giác là:");

super.Inkq();

System.out.println("Cạnh 1 = "+getCanh1());

System.out.println("Cạnh 2 = "+getCanh2());

System.out.println("Cạnh 3 = "+getCanh3());

}

public static void main(String[] args) {

TamGiac tb1 =new TamGiac();

tb1.setCanh1(3);

tb1.setCanh2(4);

tb1.setCanh3(5);

tb1.setFillMau(true);

tb1.setMau("đỏ");

tb1.Inkq();

if(tb1.kiemtra())

System.out.print("dien tich = " + tb1.tinhchuvi() + " chu vi = " + tb1.tinhdientich());

else

System.out.print("khong phai la tam giac");

}

}

Kết quả chạy chương trình được:

Các thuộc tính của Tam giác là:

Mau = đỏ

Dổ màu = true

Cạnh 1 = 3.0

71

Cạnh 2 = 4.0

Cạnh 3 = 5.0

dien tich = 11.0 chu vi = 60.794736614282655

Như vậy kết quả cho thấy lớp tam giác kế thừa thuộc tính màu, đổ màu, và

phương thức Inkq từ lớp cha shape của nó.

2.6.3. Tính đa thừa kế

Trong tất cả các ví dụ trên, một lớp thừa kế chỉ từ một lớp. Trường hợp như thế

gọi là „thừa kế đơn‟ (single inheritance).

Trong „đa thừa kế‟, một lớp con thừa kế từ hai hay nhiều lớp cha.

Hãy khảo sát ví dụ sau:

Lớp Đƣờng thẳng

Lớp Đƣờng tròn

Lớp Hình ảnh

Lớp Vẽ một hình

Khởi điểm

Bán kính

Hình ảnh

Nhận hình vẽ

Tâm điểm

Vẽ hình ảnh

Vẽ hình

Điểm tận cùng Vẽ đường thẳng

Vẽ đường tròn

+ + =

Trong hình trên, chúng ta đã xây dụng một lớp „Vẽ một hình‟, lớp này thừa

hưởng ba lớp: „Đường thẳng‟, „Đường tròn‟, „Hình ảnh‟. Như thế lớp „Vẽ một hình‟

kết hợp chức năng của ba lớp trên thêm vào chức năng được định nghĩa bên trong nó.

Lớp „Vẽ một hình‟ là một ví dụ về tính đa thừa kế.

Có thể sử dụng tính đa thừa kế để xây dựng một lớp mới, lớp này dẫn xuất chức

năng của nó từ một vài lớp khác. Như thế, xét theo góc cạnh của người sử dụng lớp

mới này, chỉ cần một lớp mà cung cấp tất cả các chức năng. Như vậy, họ không cần

phải sử dụng nhiều đối tượng khác nhau.

Sự thuận lợi quan trọng nhất của tính thừa kế là nó thúc đẩy việc tái sử dụng mã

chương trình.

Trong ví dụ trên, chúng ta có ba lớp „Đường thẳng‟, „Đường tròn‟ và „Hình ảnh‟. Giả thiết rằng ba người khác nhau xây dựng ba lớp này riêng biệt. Bây giờ, người sử dụng cần xây dựng một lớp để vẽ đường thẳng, vẽ đường tròn cũng như hiển thị hình ảnh. Vì thế họ tìm kiếm xem có lớp nào đáp ứng một hoặc tất cả các yêu cầu đó. Nếu có những lớp cung cấp chức năng thỏa yêu cầu thì người sử dụng sẽ thừa kế những lớp đó để tạo một lớp mới.

72

Giờ đây người sử dụng chỉ còn phải viết mã chương trình cho những đặc tính chưa có sau tiến trình thừa kế. Người sử dụng có thể sử dụng chính ba lớp trên. Tuy nhiên, sự thừa kế cung cấp một bó những chức năng hỗn độn trong một lớp.

Để đảm bảo tính dễ hiểu và hạn chế xung đột ngôn ngữ Java không có đa thừa

kế, nhưng chúng có khái niệm giao diện Interface. Với Interface, ta có thể có hầu hết các lợi ích mà đa thừa kế mang lại. Chi tiết về giao diện và cài đặt về giao diện sẽ

được trình bày chi tiết trong chương 3.

2.6.4. Tính đa hình

Java cũng hỗ trợ tính đa hình trong lập trình hướng đối tượng, tức là Java cũng cho phép các lớp kế thừa từ một lớp đã có cùng sử dụng một phương thức, nhưng khi

tạo ra các đối tượng khác nhau ở các lớp cùng gọi đến phương thức đó sẽ cho các kết

quả khác nhau.

Tạo lớp Shape:

public class Shape {

private String Mau;

private boolean FillMau;

public shape() {

this.Mau = "red";

this.FillMau=true;

}

public Shape(String Mau, boolean FillMau) {

this.Mau = Mau;

this.FillMau = FillMau;

}

public boolean isFillMau() {

return FillMau;

}

public String getMau() {

return Mau;

}

public void setFillMau(boolean FillMau) {

73

Ví dụ 2.12: Tạo lớp shape, lớp tam giác và lớp Test như sau

this.FillMau = FillMau;

}

public void setMau(String Mau) {

this.Mau = Mau;

}

public void Inkq()

{

System.out.println("Các thuộc tính của shape là ");

System.out.println("Mau = "+ getMau());

System.out.println("Dổ màu = "+ isFillMau());

}

}

Tạo lớp tam giác:

public class TamGiac extends shape{

private double canh2;

private double canh3;

public void setCanh1(double canh1) {

this.canh1 = canh1;

}

public void setCanh2(double canh2) {

this.canh2 = canh2;

}

public void setCanh3(double canh3) {

this.canh3 = canh3;

}

public double getCanh1() {

return canh1;

}

public double getCanh2() {

74

private double canh1;

return canh2;

}

public double getCanh3() {

return canh3;

}

public TamGiac() {

this.canh1=0;

this.canh2=0;

this.canh3=0;

}

public TamGiac(double canh1, double canh2, double canh3) {

this.canh1 = canh1;

this.canh2 = canh2;

this.canh3 = canh3;

}

public void Inkq(){

System.out.println("Các thuộc tính của Tam giác là:");

System.out.println("Cạnh 1 = "+getCanh1());

System.out.println("Cạnh 2 = "+getCanh2());

System.out.println("Cạnh 3 = "+getCanh3());

}

}

Tạo lớp Test:

public class Test {

public static void main(String[] args) {

TamGiac tb1 =new TamGiac(3,4,5);

tb1.Inkq();

shape s1 = new shape("Xanh",true);

s1.Inkq();

}

75

}

Kết quả chạy lớp Test

Các thuộc tính của Tam giác là:

Cạnh 1 = 3.0

Cạnh 2 = 4.0

Cạnh 3 = 5.0

Các thuộc tính của shape là

Mau = Xanh

Đổ màu = true

Qua kết quả cho thấy cả hai đối tượng tb1 và s1 cùng gọi đến phương thức Inkq()

nhưng lại cho hiển thị 2 giá trị khác nhau. Đó chính là do tính đa hình trong Java

2.6.6. Lớp trừu tƣợng

Trong trường hợp ta định nghĩa một lớp mà chưa xác định các vùng dữ liệu và

phương thức của nó, ta khai báo lớp đó là một lớp trừu tượng. Các vùng dữ liệu và

phương thức cụ thể ta sẽ khai báo ở lớp dẫn xuất. Để khai báo một lớp là abstract ta

đặt từ khoá abstract trước từ khóa class theo dạng sau:

abstract class X

{

}

Trong đó X là tên của lớp trừu tượng

Ta cũng có thể khai báo một phương thức là trừu tượng trong một lớp trừu tượng

(và chỉ được khai báo trong các lớp trừu tượng) các phương thức này sẽ được khai báo

chồng ở các lớp dẫn xuất và giải quyết các công việc cụ thể của lớp này. Ta có thể khai

báo các phương thức trừu tượng theo dạng sau:

abstract ([])

Một phương thức static hay private không được định nghĩa là trừu tượng. Vì khi khai báo là static hay private các phương thức này sẽ không bị khai báo chồng bởi các lớp dẫn xuất.

Ví dụ 2.13: Xây dựng lớp trừu tượng giảng viên và lớp con cán bộ kế thừa lớp

public abstract class GiangVien {

76

giảng viên

private String Ma;

private String HoTen;

private String DiaChi;

public String getDiaChi() {

return DiaChi;

}

public String getHoTen() {

return HoTen;

}

public String getMa() {

return Ma;

}

public void setDiaChi(String DiaChi) {

this.DiaChi = DiaChi;

}

public void setHoTen(String HoTen) {

this.HoTen = HoTen;

}

public void setMa(String Ma) {

this.Ma = Ma;

}

public GiangVien() {

this.Ma ="";

this.HoTen="";

this.DiaChi ="";

}

public GiangVien(String Ma, String HoTen, String DiaChi) {

this.Ma = Ma;

this.HoTen = HoTen;

this.DiaChi = DiaChi;

77

}

abstract public void Nhap();

abstract public void HienThi();

}

Lớp con cán bộ kế thừa lớp giảng viên

import java.util.Scanner;

public class CanBo extends GiangVien{

private String ChucVu;

private double HeSo;

static Scanner s=new Scanner(System.in);

public String getChucVu() {

return ChucVu;

}

public double getHeSo() {

return HeSo;

}

public void setChucVu(String ChucVu) {

this.ChucVu = ChucVu;

}

public void setHeSo(double HeSo) {

this.HeSo = HeSo;

}

public CanBo() {

}

public CanBo(String ChucVu, double HeSo, String Ma, String HoTen, String DiaChi)

{

super(Ma, HoTen, DiaChi);

this.ChucVu = ChucVu;

this.HeSo = HeSo;

}

78

@Override

public void Nhap() {

System.out.print("Nhap Ma:");String Ma = s.nextLine();

System.out.print("Nhap Ten:");String Ten = s.nextLine();

System.out.print("Diachi:");String DiaChi = s.nextLine();

System.out.print("Chuc vu:");String Chucvu = s.nextLine();

System.out.print("He so:");double HeSo = s.nextDouble();

this.setMa(Ma);

this.setHoTen(Ten);

this.setDiaChi(DiaChi);

this.setChucVu(Chucvu);

this.setHeSo(HeSo);

}

@Override

public void HienThi() {

System.out.println("

| " +

this.getMa() + "

| "+this.getHoTen()+"

|

"+this.getDiaChi()+" | "+ this.getChucVu()+"|"+this.getHeSo()+"|");

}

public boolean KiemTra(String HoTen)

{

if(this.getHoTen().equals(HoTen))

return true;

else

return false;

}

public static void main(String[] args) {

CanBo[] dscb = new CanBo[100];

char tiep='y'; int k=0;

while(tiep=='y')

{

79

CanBo cb = new CanBo();

cb.Nhap();

dscb[k]=cb;

k++;

System.out.print("Nhap tiep(y/n)");tiep=s.next().charAt(0);

String s1 = s.nextLine();

}

for(int i=0;i

{

CanBo cb = new CanBo();

cb = dscb[i];

cb.HienThi();

}

System.out.print("Nhap ho ten can bo can tim");String Hoten =s.nextLine();

for(int i=0;i

{

CanBo cb = new CanBo();

cb = dscb[i];

if(cb.KiemTra(Hoten))

cb.HienThi();

}

}

}

Ở đây ta thấy có 2 phương thức Nhap() và HienThi() là các phương thức trừu

tượng được tạo ở lớp GiangVien và nó được định nghĩa chồng ở lớp cán bộ và để định nghĩa nó là một cài đặt của một phương thức trừu tượng ta dùng từ khóa @Override ở trước phương thức đó.

2.7. Xử lý ngoại lệ

Đối với người lập trình họ có thể gặp một trong các lỗi sau:

- Lỗi cú pháp (syntax error)

80

- Lỗi logic thuật toán

- Lỗi lúc thực thi ( runtime error)

- Đối với lỗi cú pháp người lập trình có thể phát hiện và sửa lỗi, dựa vào trình biên dịch, đây là lỗi dễ phát hiện và sửa chữa, tuy nhiên đây cũng là lỗi gây khó khăn

và chán nản đối với người mới học lập trình.

- Đối với lỗi thuật toán, đây là lỗi khó phát hiện và sửa chữa nhất

- Đối với lỗi lúc thực thi, ta hoàn toàn có thể kiểm soát được chúng, thông thường lỗi runtime thường do nguyên nhân khách quan như: truy cập vào một ổ đĩa

nhưng ổ đĩa này lại chưa sẵn sàng, hay thực hiện phép chia nhưng mẫu số lại bằng 0,

kết nối với máy tính ở xa nhưng máy đó lại không tồn tại…, khi một lỗi runtime

xảy ra JVM sẽ phát sinh một ngoại lệ, nếu một chương trình không cung cấp mã sử lý ngoại lệ có thể kết thúc không bình thường.

- Mọi lớp ngoại lệ trong java đều được dẫn xuất từ lớp cơ sở Throwable, ta có thể

tạo ra lớp ngoại lệ riêng bằng cách mở rộng lớp Throwable

Việc trả về ngoại lệ của Java gọi là throwing, còn việc nắm bắt đối tượng trả về

của chương trình gọi là catching.

Một ngoại lệ (exception) trong chương trình Java là dấu hiệu chỉ ra rằng

có sự xuất hiện một điều kiện không bình thường nào đó.

Khi một ngoại lệ xảy ra, đối tượng tương ứng với ngoại lệ đó được tạo ra. Đối

tượng này sau đó được truyền cho phương thức là nơi mà ngoại lệ xảy ra. Đối tượng này chứa thông tin chi tiết về ngoại lệ. Thông tin này có thể được nhận về và được xử

lý. Các ngoại lệ này có thể là một ngoại lệ chuẩn của Java hoặc có thể là một ngoại lệ

do ta tạo ra. Lớp „Throwable‟ được Java cung cấp là cha của tất cả các ngoại lệ trong

Java (lớp đầu tiên trong cây thừa kế).

Sau khi bạn đã biết cách khai báo và ném ra biệt lệ, thì phần việc quan

trọng nhất là bắt và xử lý biệt lệ.

try{

// Các lệnh của chương trình

}

catch ( TypeException1 ex){

81

Vấn đề đối với người lập trình java là phải biết được đoạn mã nào của anh ta có thể gây ra lỗi. Khi họ đã khoanh vùng được đoạn mã có thể gây ra lỗi họ sẽ đặt đoạn mã, có khả năng gây ra lỗi đó trong khối try ( thử làm), và đặt đoạn mã xử lý lỗi trong khối catch ( bắt giữ). Khuôn dạng tổng quát như sau:

// mã được thực thi khi một ngoại lệ TypeException1 được phát hiện

}

catch ( TypeException2 ex){

// mã được thực thi khi một ngoại lệ TypeException2 được phát hiện

}

...

catch ( TypeExceptionN ex){

// // mã được thực thi khi một ngoại lệ TypeExceptionN được phát hiện

} finally{

// khối này luôn được thực hiện cho dù có phát sinh ngoại lệ hay không

}

Sơ đồ thực hiện của xử lý ngoại lệ

Hình 2.1. Sơ đồ thực hiện của xử lý ngoại lệ

class TryClass{

public static void main(String args[]) {

int n=0;

82

Ví dụ 2.14: Xử lý ngoại lệ bắt lỗi chia cho 0

try{

System.out.println(1/n);

}

catch(ArithmeticException ex){

System.out.println(“Loi chia cho 0”);

}

}

}

Khi chạy chương trình này ta se thu được một dòng in ra màn hình như sau:

Loi chia cho 0

Bảng liệt kê các exception thƣờng gặp:

Exception Lý do

ArithmeticException Lỗi do tính toán, thường là chia cho 0

ArrayIndexOutOfBoundsException Lỗi do định chỉ số các phần tử của dãy

ArrayStoreException Chương trình lưu dữ liệu không đồng

kiểu trên dãy

FileNotFoundException Truy cập một tập tin không có (theo

đường dẫn)

IOException Lỗi nhập xuất tổng quát, thường là truy

cập một tập tin bị cấm

NullPointerException Tham khảo đến một đối tượng rỗng

NumberFormatException Lỗi khi chuyển đổi ký tự thành số

OutOfMemoryException Không đủ bộ nhớ

SecurityException

Một Applet thực hiện một thao tác bị trình duyệt cấm

StackOverflowException Hệ thống bị tràn

StringIndexOutOfBoundsException Chương trình truy cập đến ký tự không

có trong chuỗi.

83

Bảng 2.3. Các exception thường gặp

Trong lớp Throwable có 3 phương thức rất thông dụng cho phép ta hiểu được chi

tiết của từng ngoại lệ:

Phương thức Ý nghĩa

getMessage() Nhận về một chuỗi là thông tin chi tiết của

ngoại lệ

toString() Chuyển đối tượng thành chuỗi

PrintStackTrace() Trình bày hệ thống việc gọi các phương

thức cho phép dò tìm ngoại lệ

Bảng 2.4. Các phương thức thông dụng của Throwable

Câu hỏi và bài tập chƣơng 2

1. Cho lớp Shape và lớp Tamgiac gồm các thuộc tính và phương thức như sau:

a) Viết chương trình tạo lớp ảo Shape trong đó:

- Các thuộc tính Mau - màu của đối tượng, FillMau- đổ màu cho đối tượng.

- Các phương thức gồm: các setters/getters cho các thuôc tính; các Constructor và

Kiemtra(), tinhdientich(), tichchuvi() là 3 phương thức ảo.

b) Viết chương trình tạo lớp Tamgiac kế thừa từ lớp Shape trong đó:

- Các thuộc tính canh1, canh2, canh3 là độ dài các cạnh của tam giác.

- Các phương thức gồm: các setters /getters cho các thuôc tính, các Constructor,

84

phương thức KiemTra() đùng để kiểm tra 3 cạnh có tạo thành một tam giác không, phương thức TinhDienTich() để tính diện tích của tàm giác và TinhChuVi() để tính chu vi của tam giác.

c) Viết lớp Test chạy thử nghiệm cho các lớp vừa tạo để nhập vào các thuộc tính

của một tam giác, tính điện tích, chu vi nếu 3 cạnh tạo thành tam giác và hiển thị kết quả ra màn hình.

2.Cho lớp HocVien và lớp Test gồm các thuộc tính và phương thức như sau:

a) Viết chương trình tạo lớp HocVien trong đó:

- Các thuộc tính gồm: Mahv là mã học viên, Hoten là họ tên học viên, Quequan

là quê quán của học viên, DiemThi là diểm thi của học viên.

- Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor.

b) Xây dựng lớp thử nghiệm Test để tạo danh sách học viên(dshv) gồm:

- Các thuộc tính:

 n đùng để lưu trữ độ dài của danh sách 0

 k dùng để lưu trữ số lượng học viên hiện có trong danh sách.

 dshv dùng để lưu trữ danh sách học viên.

 s là biến Scanner dùng trong nhập dữ liệu.

- Tạo các phương thức:

 NhapHocVien() để nhập một học viên vào danh sách.

 HienThiHocVien() để hiển thịcác thông tindanh sách học viên.

 TimKiemHocVien() để tìm kiếm một học viên theo họ tên.

c) Viết phương thức main() chạy thử nghiệm các phương thức trên sao cho lặp lại nhập học viên vào danh sách cho đến khi ấn phín „n‟ để kết thúc; hiển thị lại danh sách

học viên; nhập vào tên một học viên, tìm kiếm rồi hiển thị lại các thông tin về học viên

đó.

85

3.Cho 2 lớp MatHang và lớp Tivi gồm các thuộc tính và phương thức như sau:

a) Viết chương trình tạo lớp ảo (abstract) MatHang trong đó:

- Các thuộc tính gồm: Mamh là mã mặt hàng, Ten là tên, dongia là đơn giá của

mặt hàng.

- Các phương thức bao gồm: các setters/getters cho các thuộc tính, các

Constructor và 2 phương thức ảo Nhap() và HienThi().

b) Tạo lớp Tivi kế thừa từ lớp MatHang trong đó:

- Các thuộc tính LoaiTV là loại ti vi, ManHinh là độ lớn màn hình, s là biến

scanner dùng để nhập dữ liệu.

-Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor,

phương thức Nhap() dùng để nhập vào một ti vi và HienThi() dùng để hiển thị một

tivi.

c) Tạo hàm thử nghiệm main() nhập vào một danh sách các ti vi cho đến khi ấn

phím n thì kết thúc, hiển thị lại danh sách các Ti vi đã nhập.

4.Cho 2 lớp GiangVien và lớp CanBo gồm các thuộc tính và phương thức như sau:

86

a) Viết chương trình tạo lớp ảo (abstract) GiangVien trong đó:

- Các thuộc tính gồm: Ma là mã, HoTen là Họ và tên, DiaChi là địa chỉ của giảng

viên.

- Các phương thức bao gồm: các setters/getters cho các thuộc tính,

cácConstructorvà 2 phương thức ảo Nhap() và HienThi().

b)Tạo lớp CanBo kế thừa từ lớp GiangVien trong đó:

- Các thuộc tính ChucVu là chức vụ, HeSo là hệ số chức vụ, s là biến scanner

dùng để nhập dữ liệu.

-Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor,

Nhap() dùng để nhập một cán bộ và HienThi() dùng để hiển thị một cán bộ.

c) Tạo hàm thử nghiệm main() nhập vào một danh sách các cán bộ cho đến khi ấn phím n thì kết thúc; hiển thị lại danh sách các cán bộ đã nhập; nhập vào họ tên cán

bộ, tìm kiếm thông tin theo họ tên cán bộ đó và hiển thị thông tin các cán bộ tìm được

ra màn hình.

5.Cho lớp NhanVien và lớp DLNhanVien gồm các thuộc tính và phương thức như sau:

a) Viết chương trình tạo lớp NhanVien trong đó:

- Các thuộc tính gồm: Manv là mã nhân viên, Hoten là họ tên nhân viên, Que là

quê quán của nhân viên, Hesoluong là hệ số lương của nhân viên

- Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor.

b) Xây dựng lớp thử nghiệm DLNhanVien để tạo danh sách nhân viên(dsnv)

gồm:

- Các thuộc tính:

 n đùng để lưu trữ độ dài của danh sách 0

 k dùng để lưu trữ số lượng học viên hiện có trong danh sách.

87

 dsnv dùng để lưu trữ danh sách nhân viên.

 s là biến Scanner dùng trong nhập dữ liệu.

- Tạo các phương thức:

 NhapNhanVien() để nhập một nhân viên vào danh sách.

 HienThiNhanVien() để hiển thi danh sách nhân viên.

 TimKiemNhanVien() để tìm kiếm một nhân viên theo họ tên.

c) Viết phương thức main() chạy thử nghiệm các phương thức trên sao cho lặp lại

nhập nhân viên vào danh sách cho đến khi ấn phín „n‟ để kết thúc; hiển thị lạidanh

sách nhân viên đã nhập; nhập vào họ tên nhân viên tìm kiếm và hiển thị lại thông tin

các nhân viên có họ tên đó.

6. Cho lớp GiaiThua gồm các thuộc tính, phương thức như sau:

a) Viết chương trình tạo lớp GiaiThua trong đó:

- Thuộc tính n là số cần tính.

- Các phương thức gồm: các setters/getters cho các thuộc tính, Các Constructor,

phương thức KiemTra() đùng để kiểm tra giá trị nhập vào n có phải là một số nguyên

dương không, TinhGT() để tính n!.

b)Tạo lớp Test để thực hiện thử nghiệm lớp GiaiThua ở trên sao cho: Thông báo cho người dùng nhập vào một số n, kiểm tra giá trị của n nhập vào cho đến khi giá trị n đúng, tính và hiển thị kết quả ra màn hình.

88

7. Cho lớp PhepTinh gồm các thuộc tính, phương thức như sau:

a) Viết chương trình tạo lớp PhepTinh trong đó:

-Các thuộc tính ToanHang1, ToanHang2 là các toán hạng, ToanTu là các phép

toán +, -, *, /.

- Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor,

thức KiemTra() đùng để kiểm tra giá trị nhập vào có phải là một số nguyên không và

TinhToan() để tính giá trị của biểu thức.

b)Tạo một lớp Test trong đó sử dụng lớp PhepTinh đã tạo để thực hiện các tính

toán sao cho: chương trình lặp lại các yêu cầu người dùng nhập vào các toán hạng và

phép toán cho đến khi dữ liệu nhập vào đúng, thực hiện tính toán và in kết quả ra màn

hình.

8. Cho lớp PhanSo gồm các thuộc tính, phương thức như mô tả ở hình sau:

a) Viết chương trình tạo lớp PhanSo trong đó:

89

- Các thuộc tính TuSo, MauSo là tử số và mẫu số của phân số.

- Các phương thứcgồm: các setters/getters cho các thuộc tính, các Constructor,

KiemTra() đùng để kiểm tra giá trị nhập vào có phải là một số nguyên không, RutGon() đùng để tính và trả về phân số rút gọn của phân số đã cho và TinhToan() để

tính giá trị phân số.

b)Tạo một lớp Test trong đó sử dụng lớp PhanSo đã tạo để thực hiện các tính

toán sao cho: chương trình lặp lại các yêu cầu người dùng nhập vào các tử số và mẫu số của phân số cho đến khi dữ liệu nhập vào đúng, thực hiện tính toán và in kết quả ra

màn hình.

9. Cho lớp XeMay gồm các thuộc tính, phương thức như hình sau:

a) Viết chương trình tạo lớp XeMay trong đó:

- Các thuộc tính gồm: Biensolà biển số xe, TenXe là tên xe, Mau là màu của xe,

HangSX là hãng sản xuất xe.

- Các phương thức bao gồm: các setters/getters cho các thuộc tính,

cácConstructor.

b)Tạo một lớp Test trong đó sử dụng lớpXeMay đã tạo để viết chương trình thực

hiện các công việc sau:

- Nhập vào một số n (0

lại danh sách đã nhập

- Nhập vào một vị trí k ( 0

danh sách sau khi xóa.

- Nhập vào một biển số xe, tìm và hiển thị thông tin về xe đó nếu có, ngược lại

90

thông báo không có xe đó.

10. Cho lớp Sach gồm các thuộc tính, phương thức mô tả như hình sau:

a) Viết chương trình tạo lớp Sach trong đó:

- Các thuộc tính gồm: Ten là tên sách, Tacgia là tác giả, gia là giá, nam là năm

xuất bản cuốn sách.

- Các phương thức gồm: các setters/getters cho các thuộc tính và các Constructor.

b)Tạo một lớp Test trong đó sử dụng lớpSach đã tạo để viết chương trình thực

hiện các công việc sau:

- Nhập vào một số n (0

thị lại danh sách đã nhập.

- Nhập vào một vị trí k ( 0

đó, hiển thị lại danh sách sau khi thêm.

- Nhập vào một tên tác giả, tìm và hiển thị thông tin về các cuốn sách của tác giả

91

đó nếu có, ngược lại thông báo không có sách của tác giả đó.

CHƢƠNG 3: CÁC GÓI VÀ LẬP TRÌNH VÀO RA

3.1. Các gói trong Java

3.1.1. Giới thiệu

Gói và giao diện là hai thành phần chính của chương trình Java. Các gói được lưu

trữ theo kiểu phân cấp, và được nhập (import) một cách tường minh vào những lớp

mới được định nghĩa. Các giao diện có thể được sử dụng để chỉ định một tập các phương thức. Các phương thức này có thể được hiện thực bởi một hay nhiều lớp.

Một tập tin nguồn Java có thể chứa một hoặc tất cả bốn phần sau đây:

- Một câu lệnh khai báo gói (package). - Những câu lệnh nhập thêm các gói hoặc các lớp khác vào chương trình

(import).

- Một khai báo lớp công cộng (public) - Một số các lớp dạng riêng tư (private) của gói.

Một tập tin nguồn Java sẽ có khai báo lớp public đơn. Tất cả những phát biểu

khác tuỳ chọn. Chương trình nên được viết theo thứ tự: đặt tên gói (package), lệnh

nhập các gói (import), và định nghĩa lớp (class).

3.1.2. Các giao diện

Giao diện là một trong những khái niệm quan trọng nhất của ngôn ngữ Java. Nó

cho phép một lớp có nhiều lớp cha (superclass). Các chương trình Java có thể thừa kế chỉ một lớp tại một thời điểm, nhưng có thể hiện thực hàng loạt giao diện. Giao diện

được sử dụng để thay thế một lớp trừu tượng, không có một sự kế thừa mã thực thi

nào. Giao diện tương tự như các lớp trừu tượng. Sự khác nhau ở chỗ một lớp trừu

tượng có thể có những hành vi cụ thể, nhưng một giao diện thì không thể có một

phương thức cụ thể nào có hành vi của riêng mình. Các giao diện cần được hiện thực.

Một lớp trừu tượng có thể được thừa kế, nhưng không thể tạo ra được thể hiện (đối

tượng).

Có thể hiểu một cách đơn giản một giao diện là một lớp chứa một tập các phương thức trừu tượng (các phương thức chưa được đinh đĩa). Các phương thức này được kế thừa và cài đặt ở lớp cài đặt giao diện này.

Tạo một giao diện tương tự như tạo một lớp nhưng nó có điểm khác với lớp là

một lớp mô tả các thuộc tính và phương thức của đối tượng, còn một giao diện thì có chứa các phương thức mà một lớp cài đặt.

Về cơ bản một giao diện khác với một lớp thông như sau:

92

- Không có các cài đặt khởi tạo trong một giao diện

- Một giao diện không chứa đựng bất kỳ constructor nào

- Tất cả các phương thức trong một giao diện là trừu tượng

- Một giao diện chỉ chứa các thuộc tính được khai báo là cả static và final

- Một giao diện không thể được kế thừa từ một lớp; mà nó được cài đặt từ một

lớp

- Một giao diện có thể được kế thừa từ các giao diện khác

Định nghĩa giao diện

Từ khóa interface được sử dụng để khai báo một giao diện. Để tạo một giao diện

có thể thực hiện theo cấu trúc sau:

import packages; //chỉ định các gói hay lớp sẽ được dùng trong chương trình

public interface Interface_Name

{

//Any number of final, static fields

//Any number of abstract method declarations\

}

Trong đó:

- interface là từ khóa tạo giao diện, mặc định một giao diện là một lớp trừu tượng

mà không cần sử dụng từ khóa abstract

- Interface_Name là tên của giao diện muốn tạo

- Các phương thức và thuộc tính của giao diện được khai báo trong phần thân của

nó nằm giữa cặp ngoặc nhọn “{}”.

- Các phương thức của giao diện mặc định là trừu tượng nên không cần sử dụng

từ khóa abstract khi khai báo. Các phương thức phải được khai báo là public.

Public interface Animal {

public void eat();

public void travel();

}

Ví dụ 3.1: Tạo một giao diện Animal có 2 phương thức eat và travel như sau

Lưu ý: chương trình trên phải được lưu trữ với tên Animal.java

93

Cài đặt giao diện

Một lớp sử dụng từ khóa implementsđể cài đặt một giao diện, từ khóa

implementsđược khai báo sau phần khai báo kế thừa nếu lớp đó có cả kế thừa.

/* File name : MammalInt.java */

public class MammalInt implements Animal{

public void eat(){

System.out.println("Mammal eats");

}

public void travel(){

System.out.println("Mammal travels");

}

public int noOfLegs(){

return 0;

}

public static void main(String args[]){

MammalInt m = new MammalInt();

m.eat();

m.travel();

}

}

Ví dụ 3.2: Tạo lớp MammalInt cài đặt giao diện Animal đã tạo trong ví dụ trên

Kết quả thực hiện chương trình:

Mammal eats

Mammal travels

Giao diện kế thừa

Cũng giống như một lớp được kế thừa từ một lớp khác. Một giao diện cũng có thể được kế thừa từ một hoặc nhiều giao diện khác. Tuy nhiên giao diện chỉ được kế thừa từ giao diện mà không được kế thừa từ một lớp không phải là một giao diện.

Ví dụ 3.3: Tạo ra hai giao diện Sports và Football trong đó giao diện Football lại

//Filename: Sports.java

public interface Sports

94

được kế thừa từ giao diện Sports

{

public void setHomeTeam(String name);

public void setVisitingTeam(String name);

}

//Filename: Football.java

public interface Football extends Sports

{

public void homeTeamScored(int points);

public void visitingTeamScored(int points);

public void endOfQuarter(int quarter);

}

Trong trường hợp một giao diện kế thừa từ nhiều giao diện thì các giao diện được

khai báo cánh nhau bởi dấu phẩy.

3.1.3. Các gói

Gói được coi như các thư mục, đó là nơi tổ chức các lớp và các giao diện. Các

chương trình Java được tổ chức như những tập của các gói. Mỗi gói gồm có nhiều lớp,

và/hoặc các giao diện được coi như là các thành viên của nó. Đó là một phương án

thuận lợi để lưu trữ các nhóm của những lớp có liên quan với nhau dưới một cái tên cụ thể. Khi đang làm việc với một chương trình ứng dụng, tạo ra một số lớp. Các lớp đó

cần được tổ chức một cách hợp lý. Điều đó trở nên dễ dàng khi ta tổ chức các tập tin

lớp thành các gói khác nhau. Hãy tưởng tượng rằng mỗi gói giống như một thư mục

con. Tất cả các điều mà bạn cần làm là đặt các lớp và các giao diện có liên quan với

nhau vào các thư mục riêng, với một cái tên phản ánh được mục đích của các lớp.

Nói tóm lại, các gói có ích cho các mục đích sau:

 Chúng cho phép bạn tổ chức các lớp thành các đơn vị nhỏ hơn (như là các thư mục), và làm cho việc xác định vị trí trở nên dễ dàng và sử dụng các tập tin của lớp một cách phù hợp.

 Giúp đỡ để tránh cho việc đặt tên bị xung đột (trùng lặp tên). Khi bạn làm việc với một số các lớp bạn sẽ cảm thấy khó để quyết định đặt tên cho các

95

lớp và các phương thức. Đôi lúc bạn muốn sử dụng tên giống nhau mà tên đó liên quan đến lớp khác. Các gói giấu các lớp để tránh việc đặt tên bị xung đột.

 Các gói cho phép bạn bảo vệ các lớp, dữ liệu và phương thức ở mức rộng

hơn trên một nền tảng class-to-class.

 Các tên của gói có thể được sử dụng để nhận dạng các lớp.

Các gói cũng có thể chứa các gói khác.

Tạo một gói

Để tạo một gói sử dụng lệnh package ở dòng đầu tiền của file mã nguồn muốn tạo gói. Điều này chỉ ra rằng lớp được định nghĩa trong tập tin là một phần của gói xác

định. Mỗi file mã nguồn chỉ có thể có duy nhất một lệnh package.

Cú pháp:

package ;

trong đó package_name là tên của gói muốn tạo

ví dụ 3.4: tạo ra 2 lớp MammalInt và Animal thuộc cùng một gói Aminals

package Animals;

public interface Animal {

public void eat();

public void travel();

}

package Animals;

/* File name : MammalInt.java */

public class MammalInt implements Animal{

@Override

public void eat() {

System.out.println("Mammal eats");

}

@Override

public void travel() {

System.out.println("Mammal travels");

}

public int noOfLegs(){

return 0;

96

/* File name : Animal.java */

}

public static void main(String args[]){

MammalInt m = new MammalInt();

m.eat();

m.travel();

}

}

Sử dụng các gói

Để một lớp thao khảo đến một thành phần của một lớp khác nằm trong một gói

nào đó thì sử dụng câu lệnh import để nhập vào một gói đã được tạo.

Cú pháp

import package;

trong đó package là tên của một gói và lớp của gói đó, trong trường hợp muốn sử

dụng tất cả các lớp thì sử dụng ký hiệu “*” để thay thế.

Ví dụ 3.5: Trong một lớp Dog sử dụng lớp MammalInt trong gói aminals đã tạo ở

import Animals.MammalInt;

public class Dog {

public void Dog_Travel(MammalInt e)

{

e.travel();

System.out.println("The dog travels to");

}

public static void main(String args[]){

MammalInt m = new MammalInt();

Dog d = new Dog();

d.Dog_Travel(m);

}

}

trên như sau:

97

Kết quả chạy chương trình là:

Mammal travels

The dog travels to

Trong trường hợp chúng ta muốn sử dụng cả 2 lớp là Animal và MammalInt thì

trong lớp dog thì ta có thể khai báo như sau:

import Animals.*;

Thiết lập đƣờng dẫn cho lớp (classpath)

Chương trình dịch và chương trình thông dịch tìm kiếm các lớp trong thư mục

hiện hành, và tập tin nén (zip) chứa các lớp của JDK. Điều này có nghĩa các tập tin

nén chứa các lớp của JDK và thư mục hiện hành chứa mã nguồn tự động được đặt vào

classpath. Tuy nhiên, trong một vài trường hợp, bạn cần phải tự thiết lập classpath.

Classpath là một danh sách các thư mục, danh sách này trợ giúp để tìm kiếm các

tập tin .class tương ứng. Thông thường, ta không nên thiết lập môi trường classpath lâu

dài. Nó chỉ thích hợp khi thiết lập classpath để chạy chương trình, chỉ thiết lập đường

dẫn cho việc thực thi hiện thời. Ví dụ sau thiết lập classpath cho môt thư mục c:\temp:

javac –classpath c:\temp Packagedemo.java

Thứ tự của các mục trong classpath rất quan trọng. Khi bạn thực thi đoạn mã của

bạn, máy ảo Java sẽ tìm kiếm các mục trong classpath theo thứ tự các thư mục trong

classpath, cho đến khi nó tìm thấy lớp cần tìm.

3.1.4. Gói và điều khiển truy xuất

Các gói chứa các lớp và các gói con. Các lớp chứa dữ liệu và đoạn mã. Java cung

cấp nhiều mức độ truy cập thông qua các lớp, các gói và các chỉ định truy cập. Bảng

sau đây sẽ tóm tắt quyền truy cập các thành phần của lớp:

public protected No modifier private

Yes Yes Yes Cùng lớp Yes

Yes Yes Yes No

Cùng gói- lớp thừa kế (Subclass)

Yes Yes Yes No

Cùng gói-không thừa kế (non-Subclass)

Khác gói-lớp thừa kế Yes Yes No No

(subclass)

Yes No No No

98

Khác gói-không thừa kế (non-Subclass)

Bảng 3.1. Truy cập đến các thành phần của lớp

3.1.5. Gói Java.lang

Mặc định, mỗi chương trình java đều nhập gói java.lang. Vì thế, không cần lệnh

nhập gói java.lang này trong chương trình.

Lớp bao bọc (wrapper class)

Các kiểu dữ liệu nguyên thủy thì không phải là các đối tượng. Vì thế, chúng

không thể tạo ra hay truy cập bằng phương thức. Để tạo và thao tác kiểu dữ liệu nguyên thủy, ta sử dụng “wrapper class” tương ứng với. Bảng sau liệt kê các lớp trình

bao bọc (wrapper).

Kiểu dữ liệu Lớp trình bao bọc

boolean Boolean

byte Byte

char Character

double Double

float Float

int Integer

long Long

short Short

Bảng 3.2. Các lớp trình bao bọc cho các kiểu dữ liệu nguyên thủy.

Boolean wrapBool = new Boolean(“false”);

Integer num1 = new Integer(“31”);

Integer num2 = new Integer(“3”);

Int sum = num1.intValue()*num2.intValue();

Ví dụ 3.6: Một vài phương thức của lớp wrapper:

//intValue() là một hàm của lớp trình bao bọc Integer.

class CmdArg

{

public static void main(String args[])

{

99

Ví dụ 3.7:Minh họa cách sử dụng lớp wrapper cho kiểu dữ liệu int

int sum = 0;

for(int i = 0;i

sum+= Integer.parseInt(args[i]);

System.out.println(“Tổng là: ”+sum);

}

}

Vòng lặp for được sử dụng để tìm tổng của các số được truyền vào từ dòng lệnh.

Các số đó được lưu trữ trong mảng String args[]. Thuộc tính “length” xác định số các

phần tử trong mảng args[]. Mảng args[] là kiểu String. Vì thế, các phần tử phải được

đổi sang kiểu dữ liệu int trước khi cộng chúng. Quá trình chuyển đổi được thực hiện với sự giúp đỡ của lớp trình bao bọc “Integer”. Phương thức “parseInt()” trong lớp

“Integer” thực hiện quá trình chuyển đổi của kiểu dữ liệu chuỗi sang kiểu dữ liệu số

nguyên.

Tất cả các lớp trình bao bọc, ngoại trừ lớp “Character” có một phương thức tĩnh

“valueOf()” nhận một chuỗi, và trả về một giá trị số nguyên được. Các lớp bao bọc của

byte, int, long, và short cung cấp các hằng số MIN_VALUE và MAX_VALUE. Các

lớp bao bọc của double và long cũng cung cấp các hằng POSITIVE_INFINITY và

NEGATIVE_INFINITY.

a) Lớp String (lớp chuỗi)

Chuỗi là một dãy các ký tự. Lớp String cung cấp các phương thức để thao tác với

String str1 = new String( );

//str1 chứa một chuỗi rỗng.

String str2 = new String(“Hello World”);

//str2 chứa “Hello World”

char ch[] = {„A‟,‟B‟,‟C‟,‟D‟,‟E‟};

String str3 = new String(ch);

//str3 chứa “ABCDE”

String str4 = new String(ch,0,2);

//str4 chứa “AB” vì 0- tính từ ký tự bắt đầu, 2- là số lượng ký tự kể từ ký tự bắt đầu.

các chuỗi. Nó cung cấp các phương thức khởi tạo (constructor) khác nhau:

100

Toán tử “+” được sử dụng để cộng chuỗi khác vào chuỗi đang tồn tại. Toán tử “+” này được gọi như là “nối chuỗi”. Ở đây, nối chuỗi được thực hiện thông qua lớp “StringBuffer”. Chúng ta sẽ thảo luận về lớp này trong phần sau. Phương thức

“concat( )” của lớp String cũng có thể thực hiện việc nối chuỗi. Không giống như toán

tử “+”, phương thức này không thường xuyên nối hai chuỗi tại vị trí cuối cùng của chuỗi đầu tiên. Thay vào đó, phương thức này trả về một chuỗi mới, chuỗi mới đó sẽ

chứa giá trị của cả hai. Điều này có thể được gán cho chuỗi đang tồn tại.

String strFirst, strSecond, strFinal;

StrFirst = “Charlie”;

StrSecond = “Chaplin”;

//….bằng cách sử dụng phương thức concat( ) để gán với một chuỗi đang tồn tại.

StrFinal = strFirst.concat(strSecond);

Phương thức concat( ) chỉ làm việc với hai chuỗi tại một thời điểm.

Ví dụ 3.5:

b) Chuỗi mặc định (String pool)

Một chương trình Java có thể chứa nhiều chuỗi. “String Pool” đại diện cho tất cả

các chữ được tạo trong chương trình. Mỗi khi một chuỗi được tạo, String Pool tìm

kiếm trong nó, nếu tìm thấy nếu chuỗi đã tồn tại thì không tạo thể hiện mà chỉ gán thể

tìm thấy cho chuỗi mới. Việc này tiết kiệm rất nhiều không gian bộ nhớ.

Ví dụ 3.8: Tạo chuỗi String pool

String day = “Monday”;

String weekday = “Monday”;

Ở đây, một thể hiện cho biến “day”, biến đó có giá trị là “Monday”, được tạo

trong String Pool. Khi chuỗi bằng chữ “weekday” được tạo, có giá trị giống như của

biến “day”, một thể hiện đang tồn tại được gán đến biến “weekday”. Vì cả hai biến

“day” và “weekday” cũng đều nhằm chỉ vào chuỗi giống hệt nhau trong String Pool. Hình ảnh sau minh hoạ khái niệm của “String Pool”.

1 Sunday

day 2 Monday

3 Hello Weekday 4 Aptech

N Worl Hình 3.1. Minh họa khái niệm của String Pool.

d c) Các phƣơng thức của lớp String

101

Trong phần này, chúng ta sẽ xem xét các phương thức của lớp String.

 charAt( )

Phương thức này trả về một ký tự tại một vị trí trong chuỗi.

String name = new String(“Java Language”);

char ch = name.charAt(5);

Ví dụ 3.9: Sử dụng charAt để lấy về một ký tự

Biến “ch” chứa giá trị “L”, từ đó vị trí các số bắt đầu từ 0.

 startsWith( )

Phương thức này trả về giá trị kiểu logic (Boolean), phụ thuộc vào chuỗi có bắt

đầu với một chuỗi con cụ thể nào đó không.

String strname = “Java Language”;

boolean flag = strname.startsWith(“Java”);

Ví dụ 3.10: Sử dụng startsWith

Biến “flag” chứa giá trị true.

 endsWith( )

Phương thức này trả về một giá trị kiểu logic (boolean), phụ thuộc vào chuỗi kết

thúc bằng một chuỗi con nào đó không.

String strname = “Java Language”;

boolean flag = strname.endsWith(“Java”);

Ví dụ 3.11: Sử dụng endsWith

Biến “flag” chứa giá trị false.

 copyValueOf( )

Phương thức này trả về một chuỗi được rút ra từ một mảng ký tự được truyền

như một đối số. Phương thức này cũng lấy hai tham số nguyên. Tham số đầu tiên chỉ định vị trí từ nơi các ký tự phải được rút ra, và tham số thứ hai chỉ định số ký tự được rút ra từ mảng.

char name[] = {„L‟,‟a‟,‟n‟,‟g‟,‟u‟,‟a‟,‟g‟,‟e‟};

String subname = String .copyValueOf(name,5,2);

Bây giờ biến “subname” chứa chuỗi “ag”.

Ví dụ 3.12: Sử dụng hàm copyValueOf

102

 toCharArray( )

Phương thức này chuyển chuỗi thành một mảng ký tự.

Ví dụ 3.13: Sử dụng hàm toCharArray

String text = new String(“Hello World”);

char textArray[] = text.toCharArray( );

 indexOf( )

Phương thức này trả về thứ tự của một ký tự nào đó, hoặc một chuỗi trong phạm

String day = new String(“Sunday”);

int index1 = day.indexOf(„n‟);

//chứa 2

int index2 = day.indexOf(„z‟,2);

//chứa –1 nếu “z” không tìm thấy tại vị trí 2.

int index3 = day.indexOf(“Sun”);

//chứa mục 0

vi một chuỗi. Các câu lệnh sau biểu diễn các cách khác nhau của việc sử dụng hàm.

 toUpperCase( )

String lower = new String(“good morning”);

System.out.println(“Uppercase: ”+lower.toUpperCase( ));

Phương thức này trả về chữ hoa của chuỗi.

 toLowerCase( )

String upper = new String(“APTECH”);

System.out.println(“Lowercase: “+upper.toLowerCase( ));

Phương thức này trả về chữ thường của chuỗi.

 trim()

Phương thức này cắt bỏ khoảng trắng hai đầu chuỗi. Hãy thử đoạn mã sau để

String space = new String(“ Spaces “);

System.out.println(space);

System.out.println(space.trim()); //Sau khi cắt bỏ khoảng trắng

thấy sự khác nhau trước và sau khi cắt bỏ khoảng trắng.

 equals()

103

Phương thức này so sánh nội dung của hai đối tượng chuỗi.

String name1 = “Aptech”, name2 = “APTECH”;

boolean flag = name1.equals(name2);

Biến “flag” chứa giá trị false.

d) Lớp StringBuffer

Lớp StringBuffer cung cấp các phương thức khác nhau để thao tác một đối tượng

dạng chuỗi. Các đối tượng của lớp này rất mềm dẻo, đó là các ký tự và các chuỗi có

thể được chèn vào giữa đối tượng StringBuffer, hoặc nối thêm dữ liệu vào tại vị trí cuối. Lớp này cung cấp nhiều phương thức khởi tạo.

Ví dụ 3.14: Minh hoạ cách sử dụng các phương thức khởi tạo khác nhau để tạo ra

class StringBufferCons

{

public static void main(String args[])

{

StringBuffer s1 = new StringBuffer();

StringBuffer s2 = new StringBuffer(20);

StringBuffer s3 = new StringBuffer(“StringBuffer”);

System.out.println(“s3 = “+ s3);

System.out.println(s2.length()); //chứa 0

System.out.println(s3.length()); //chứa 12

System.out.println(s1.capacity()); //chứa 16

System.out.println(s2.capacity()); //chứa 20

System.out.println(s3.capacity()); //chứa 28

}

}

các đối tượng của lớp này.

“length()” và “capacity()” của StringBuffer là hai phương thức hoàn toàn khác nhau. Phương thức “length()” đề cập đến số các ký tự mà đối tượng thực chứa, trong khi “capacity()” trả về tổng dung lượng của một đối tượng (mặc định là 16) và số ký

tự trong đối tượng StringBuffer.

Dung lượng của StringBuffer có thể thay đổi với phương

104

thức“ensureCapacity()”. Đối số int đã được truyền đến phương thức này và dung lượng mới được tính toán như sau:

NewCapacity = OldCapacity * 2 + 2

Trước khi dung lượng của StringBuffer được đặt lại, điều kiện sau sẽ được kiểm

tra:

 Nếu dung lượng(NewCapacity) mới lớn hơn đối số được truyền cho phương

thức “ensureCapacity()”, thì dung lượng mới (NewCapacity) được đặt.

 Nếu dung lượng mới nhỏ hơn đối số được truyền cho phương thức “ensureCapacity()”, thì dung lượng được đặt bằng giá trị tham số truyền vào.

class test{

public static void main(String args[]){

StringBuffer s1 = new StringBuffer(5);

System.out.println(“Dung lượng của bộ nhớ đệm = “+s1.capacity());

//chứa 5

s1.ensureCapacity(8);

System.out.println(“Dung lượng của bộ nhớ đệm = “+s1.capacity());

//chứa 12

s1.ensureCapacity(30);

System.out.println(“Dung lượng của bộ nhớ đệm = “+s1.capacity());

//chứa 30

}

}

Ví dụ 3.15: Minh hoạ dung lượng được tính toán và được đặt như thế nào.

Trong đoạn mã trên, dung lượng ban đầu của s1 là 5. Câu lệnh

s1.ensureCapacity(8);

Thiết lập dung lượng của s1 đến 12 =(5*2+2) bởi vì dung lượng truyền vào là 8

nhỏ hơn dung lượng được tính toán là 12 .

s1.ensureCapacity(30);

Thiết lập dung lượng của “s1” đến 30 bởi vì dung lượng truyền vào là 30 thì lớn

hơn dung lượng được tính toán (12*2+2).

e) Các phƣơng thức lớp StringBuffer

Trong phần này, chúng ta sẽ xem xét các phương thức của lớp StringBuffer với

105

một chương trình.

 append()

Phương thức này nối thêm một chuỗi hoặc một mảng ký tự vào cuối cùng của

đối tượng StringBuffer.

StringBuffer s1 = new StringBuffer(“Good”);

s1.append(“evening”);

Ví dụ 3.16: Sử dụng hàm append

Giá trị trong s1 bây giờ là “goodevening”.

 insert()

Phương thức này có hai tham số. Tham số đầu tiên là vị trí chèn. Tham số thứ hai

có thể là một chuỗi, một ký tự (char), một giá trị nguyên (int), hay một giá trị số thực (float) được chèn vào. Vị trí chèn sẽ lớn hơn hay bằng 0, và nhỏ hơn hay bằng chiều

dài của đối tượng StringBuffer. Bất kỳ đối số nào, trừ ký tự hoặc chuỗi, được chuyển

sang chuỗi và sau đó mới được chèn vào.

StringBuffer str = new StringBuffer(“Java sion”);

str.insert(1,‟b‟);

Ví dụ 3.17: Sử dụng hàm insert

Biến “str” chứa chuỗi “Jbava sion”.

 charAt()

Phương thức này trả về một giá trị ký tự trong đối tượng StringBuffer tại vị trí

được chỉ định.

Ví dụ 18: Sử dụng hàm charAt

StringBuffer str = new StringBuffer(“James Gosling”);

char letter = str.charAt(6); //chứa “G”

 setCharAt()

Phương thức này được sử dụng để thay thế ký tự trong một StringBuffer bằng

StringBuffer name = new StringBuffer(“Jawa”);

name.setCharAt(2,‟v‟);

một ký tự khác tại một vị trí được chỉ định.

Biến “name” chứa “Java”.

106

 setLength()

Phương thức này thiết lập chiều dài của đối tượng StringBuffer. Nếu chiều dài

được chỉ định nhỏ hơn chiều dài dữ liệu hiện tại của nó, thì các ký tự thừa sẽ bị cắt bớt. Nếu chiều dài chỉ định nhiều hơn chiều dài dữ liệu thì các ký tự null được thêm vào

StringBuffer str = new StringBuffer(10);

str.setLength(str.length() +10);

phần cuối của StringBuffer

 getChars()

Phương thức này được sử dụng để trích ra các ký tự từ đối tượng StringBuffer, và

sao chép chúng vào một mảng. Phương thức getChars() có bốn tham số sau:

Chỉ số đầu: vị trí bắt đầu, từ nơi mà ký tự được lấy ra.

Chỉ số kết thúc: vị trí kết thúc

Mảng: Mảng đích, nơi mà các ký tự được sao chép.

Vị trí bắt đầu trong mảng đích: Các ký tự được sao chép vào mảng đích từ vị trí

này.

StringBuffer str = new StringBuffer(“Leopard”);

char ch[] = new char[10];

str.getChars(3,6,ch,0);

Ví dụ 3.19: Sử dụng hàm getChars

Bây giờ biến “ch” chứa “par”

 reverse()

Phương thức này đảo ngược nội dung của một đối tượng StringBuffer, và trả về

một đối tượng StringBuffer khác.

StringBuffer str = new StringBuffer(“devil”);

StringBuffer strrev = str.reverse();

Ví dụ 3.20: Sử dụng hàm reverse

Biến “strrev” chứa “lived”.

f) Lớp java.lang.Math

Lớp này chứa các phương thức tĩnh (static) để thực hiện các thao tác toán học.

Chúng được mô tả như sau:

Cú pháp là Math.

107

 abs()

Phương thức này trả về giá trị tuyệt đối của một số. Đối số được truyền đến nó có

thể là kiểu int, float, double, hoặc long. Kiểu dữ kiệu byte và short được chuyển thành kiểu int nếu chúng được truyền tới như là một đối số.

Ví dụ 3.21: Sử dụng hàm abs

int num = -1;

Math.abs(num) //trả về 1.

 ceil()

Phương thức này tìm thấy số nguyên nhỏ nhất lớn hơn hoặc bằng đối số được

truyền vào.

 floor()

Phương thức này trả về số nguyên lớn nhất nhỏ hơn hoặc bằng đối số được

System.out.println(Math.ceil(8.02)); //trả về 9.0

System.out.println(Math.ceil(-1.3)); //trả về -1.0

System.out.println(Math.ceil(100)); //trả về 100.0

System.out.println(Math.floor(-5.6)); //trả về -6.0

System.out.println(Math.floor(201.1)); //trả về 201

System.out.println(Math.floor(100)); //trả về 100

truyền vào.

 max()

Phương thức này tìm giá trị lớn nhất trong hai giá trị được truyền vào. Các đối số

được truyền vào có thể là kiểu int, long, double, và float.

min()

Phương thức này tìm giá trị nhỏ nhất trong hai giá trị được truyền vào. Các đối số

được truyền vào có thể là kiểu int, long, double và float.

 round()

Phương thức này làm tròn đối số có dấu phẩy động. Ví dụ câu lệnh

Math.round(34.5) trả về 35.

 random()

Phương thức này trả về một số ngẫu nhiên kiểu double giữa 0.0 và 1.0.

108

 sqrt()

Phương thức này trả về căn bậc hai của một số. Ví dụ câu lệnh Math.sqrt(144) trả

về 12.0.

 sin()

Phương thức này trả về sin của một số, nếu góc được truyền đến bằng radian.

Ví dụ 3.23: Math.sin(Math.PI/2) trả về 1.0, giá trị của sin 450.

PI/2 radian = 90 độ. Giá trị của “PI” được định nghĩa trong lớp Math (Math.PI).

 cos()

Phương thức này trả về cosine của một góc tính bằng radian.

 tan()

Phương thức này trả về tan của một góc tính bằng radian.

g) Lớp Runtime (Thời gian thực hiện chƣơng trình)

Lớp Runtime chứa thông tin về môi trường thực thi. Lớp này được sử dụng cho

việc quản lý bộ nhớ, và việc thực thi của các quá trình xử lý khác. Mỗi chương trình

Java có một thể hiện của lớp này, để cho phép ứng dụng giao tiếp với môi trường. Nó

không thể được khởi tạo, một ứng dụng không thể tạo ra một thể hiện của thuộc lớp

này. Tuy nhiên, chúng ta có thể tham chiếu thể hiện trong lúc thực hiện chương trình

từ việc dùng phương thức getRuntime().

Bây giờ, chúng ta biết rằng việc thu gom các dữ liệu không thích hợp trong Java

là một tiến trình tự động, và chạy một cách định kỳ. Để kích hoạt một cách thủ công bộ thu thập dữ liệu không còn được sử dụng ta gọi phương thức gc() trên đối tượng

Runtime hiện thời. Để xem chi tiết việc cấp phát bộ nhớ, sử dụng các phương thức

totalMemory() và freeMemory().

Runtime r = Runtime.getRunTime();

…..

long freemem = r.freeMemory();

long totalmem = r.totalMemory();

r.gc();

…..

109

Bảng sau trình bày một vài phương thức của lớp này:

Phương thức Ý nghĩa

exit(int) Dừng việc thực thi, và trả về giá trị của

chương trình cho hệ điều hành. Nếu thoát bình thường thì trả về 0; giá trị khác 0 cho

thoát không bình thường.

freeMemory() Trả về kích thước bộ nhớ chưa sử dụng tính

bằng byte

getRuntime() Trả về thể hiện Runtime

gc() Gọi bộ phận thu thập rác.

totalMemory() Trả về kích thước bộ nhớ tính bằng byte.

exec(String) Chạy chương trình ở môi trường bên ngoài

Bảng 3.3. Lớp Runtime

class RuntimeDemo

{

public static void main(String args[])

{

Runtime r = Runtime.getRuntime();

Process p = null;

try {

p = r.exec(“calc.exe”);

}

catch(Exception e)

{

System.out.println(“Error executing calculator”);

}

}

}

Ví dụ 3.24: Sử dụng hàm exec

Bạn có thể tham chiếu đến Runtime hiện hành thông qua phương thức

110

Runtime.getRuntime().

Sau đó, bạn có thể chạy chương trình calc.exe và tham chiếu đến calc.exe trong

đối tượngProcess.

h) Lớp System

Lớp System cung cấp các tiện ích như là, dòng vào, dòng ra chuẩn và dòng lỗi.

Nó cũng cung cấp phương thức để truy cập các thuộc tính liên quan đến hệ thống

Runtime của Java, và các thuộc tính môi trường khác nhau như là, phiên bản (version), đường dẫn, hay các dịch vụ, ...Các trường của lớp này là in, out, và err, các trường

này tiêu biểu cho dòng vào, ra và lỗi chuẩn tương ứng.

Bảng sau mô tả các phương thức của lớp này:

Phương thức Mục đích

exit(int) Dừng việc thực thi, và trả về giá trị của đoạn

mã. 0 cho biết có thể thoát ra một cách bình

thường.

gc() Gọi bộ phận thu thập rác.

getProperties() Trả về thuộc tính của hệ thống thời gian chạy

Java.

setProperties() Thiết lập các thuộc tính hệ thống hiện hành.

currentTimeMillis() Trả về thời gian hiện tại bằng mili giây (ms),

được tính từ lúc 0 giờ ngày 01 tháng 01 năm 1970.

arrayCopy(Object, int, Sao chép mảng.

Object, int, int)

Bảng 3.4. Lớp System.

Lớp System không thể tạo thể hiện (instance) được.

class SystemDemo

{

public static void main(String args[])

{

System.out.println(System.getProperty(“java.class.path”));

System.out.println(System.getProperty(“java.home”));

System.out.println(System.getProperty(“java.class.version”));

111

Ví dụ 3.22: Đọc và hiển thị một vài các thuộc tính môi trường Java.

System.out.println(System.getProperty(“java.specification.vendor”));

System.out.println(System.getProperty(“java.specification.version”));

System.out.println(System.getProperty(“java.vendor”));

System.out.println(System.getProperty(“java.vendor.url”));

System.out.println(System.getProperty(“java.version”));

System.out.println(System.getProperty(“java.vm.name”));

}

}

Mỗi thuộc tính cần in ra cần được cung cấp như một tham số (dạng chuỗi) đến

phương thức System.getProperty(). Phương thức này sẽ trả về thông tin tương ứng và phương thức System.out.println() in ra màn hình.

Kết quả chương trình trên như sau:

Hình 3.2. Kết quả chạy ví dụ 3.22

i) Lớp Class

Các thể hiện của lớp này chứa trạng thái thời gian thực hiện của một đối tượng

trong ứng dụng Java đang chạy. Điều này cho phép chúng ta truy cập thông tin về đối

tượng trong thời gian chạy.

Chúng ta có thể lấy một đối tượng của lớp này, hoặc một thể hiện bằng một trong

ba cách sau:

Sử dụng phương thức getClass() của đối tượng.

Sử dụng phương thức tĩnh forName() của lớp để lấy một thể hiện của lớp thông

qua tên của lớp đó.

Sử dụng một đối tượng ClassLoader để nạp một lớp mới.

112

Lớp Class không có phương thức xây dựng (constructor).

Ví dụ 3.23: Minh hoạ cách sử dụng phương thức của một lớp để truy cập thông

interface A

{

final int id = 1;

final String name = “Diana”;

}

class B implements A

{

int deptno;

}

class ClassDemo

{

public static void main(String args[])

{

A a = new B();

B b = new B();

Class x;

x = a.getClass();

System.out.println(“a is object of type: ”+x.getName());

x= b.getClass();

System.out.println(“b is object of type: ”+x.getName());

x=x.getSuperclass();

System.out.println(x.getName()+ “is the superclass of b.”);

}

}

tin của lớp đó:

113

Kết quả chạy chương trình được mô tả như hình dưới đây:

Hình 3.3. Kết quả chạy ví dụ 3.23

j) Lớp Object

Lớp Object là một lớp cha của tất cả các lớp. Dù là một lớp do người dùng định

nghĩa không thừa kế lại bất kỳ một lớp nào khác, theo mặc định nó thừa kế lớp Object.

Một vài các phương thức của lớp Object được biểu diễn bên dưới:

Phƣơng thức Mục đích

equals(Object) So sánh đối tượng hiện tại với đối tượng khác.

finalize() Phương thức cuối cùng. Thông thường bị định nghĩa đè ở

lớp con.

notify() Thông báo cho Thread (luồng) mà hiện thời trong trạng thái

đang chờ.

notifyAll() Thông báo tất cả các Thread (luồng) hiện hành trong trạng

thái chờ.

toString() Trả về một chuỗi đại diện cho đối tượng.

wait() Đưa Thread (luồng) vào trạng thái chờ.

Bảng 3.5. Lớp Object.

Trong chương trình sau, chúng ta không khai báo bất kỳ lớp hoặc gói nào. Bây

giờ, chúng ta có thể tạo bằng cách sử dụng phương thức equals(). Bởi vì theo mặc định

lớp ObjectDemo mở rộng lớp Object.

Class ObjectDemo

{

public static void main(String args[])

{

if (args[0].equals(“Aptech”))

System.out.println(“Yes, Aptech is the right choice!”);

}

114

Ví dụ 3.24: Lớp ObjectDemo

}

3.2. Các dòng (Stream)

Theo thuật ngữ chung, stream là một dòng lưu chuyển. trong thuật ngữ về kỹ thuật dòng là một lộ trình mà dữ liệu được truyền trong chương trình. Một ứng dụng

về các dòng ma ta đã quen thuộc đó là dòng nhập System.in.

Dòng là những ống (pipelines) để gửi và nhận thông tin trong các chương trình

java. Khi một dòng dữ liệu được gửi hoặc nhận, ta tham chiếu nó như đang “ghi” và “đọc” một dòng tương ứng. Khi một dòng được đọc hay ghi, các luồng khác bị có nhu

cầu đọc/ghi dòng đó đều phải tạm dừng. Nếu có một lỗi xảy ra khi đọc hay ghi dòng,

một ngoại lệ kiểu IOException được tạo ra. Do vậy, các câu lệnh thao tác dòng phải bao gồm khối try-catch.

Lớp „java.lang.System‟ định nghĩa các dòng nhập và xuất chuẩn. chúng là các

lớp chính của các dòng byte mà java cung cấp. Chúng ta cũng đã sử dụng các dòng

xuất để xuất dữ liệu và hiển thị kết quả trên màn hình. Dòng vào/ra bao gồm:

 Lớp System.out: Dòng xuất chuẩn dùng để hiển thị kết quả trên màn hình.  Lớp System.in: Dòng nhập chuẩn thường đến từ bàn phím và được dùng để

đọc các ký tự dữ liệu.

 Lớp System.err: Đây là dòng lỗi chuẩn.

Các lớp „InputStream‟ và „OutputStream‟ cung cấp nhiều khả năng vào/ra khác

nhau. Cả hai lớp này có các lớp thừa kế để thực hiện I/O thông qua các vùng bộ nhớ

đệm, các tập tin và ống (pipeline). Các lớp con của lớp InputStream thực hiện vào,

trong khi các lớp con của lớp OutputStream thực hiện ra.

3.3. Gói Java.io

Các luồng hệ thống rất có ích. Tuy nhiên, chúng không đủ mạnh để dùng khi ứng

phó với I/O thực tế. Gói java.io phải được nhập khẩu vì mục đích này. Chúng ta sẽ

thảo luận tìm hiểu về các lớp thuộc gói java.io.

3.3.1. Lớp InputStream

Lớp InputStream là một lớp trừu tượng. Nó định nghĩa cách thức nhận dữ liệu. Điểm quan trọng không nằm ở chỗ dữ liệu đến từ đâu, mà là khả năng truy cập. Lớp InputStream cung cấp một số phương thức để đọc và dùng các dòng dữ liệu để làm

115

đầu vào. Các phương thức này giúp ta tạo, đọc và xử lý các dòng đầu vào. Các phương thức được hiện trong bảng sau:

Tên phƣơng thức Mô tả

read() Đọc các byte dữ liệu từ một dòng. Nếu như không có

byte dữ liệu nào, nó phải chờ. Khi một phương thức phải chờ, các luồng đang thực hiện phải tạm dừng cho

đến khi có dữ liệu.

read (byte []) Trả về số byte đọc được hay „-1‟ nếu như đã đọc đến

cuối dòng. Nó gây ra ngoại lệ IOException nếu có lỗi xảy ra.

read (byte [], int, int) Nó cũng đọc vào một mảng byte. Nó trả về số byte thực

sự đọc được cho đến khi kết thúc dòng. Nó gây ra ngoại

lệ IOException nếu lỗi xảy ra.

available() Phương pháp này trả về số lượng byte có thể đọc được

mà không phải chờ. Nó trả về số byte hiện tại có trong

dòng. Nó không phải là phương thức tin cậy để thực

hiện tiến trình xử lý đầu vào.

close() Phương thức này đóng dòng. Nó dùng để giải phóng

mọi tài nguyên dòng đã sử dụng. Luôn luôn đóng dòng

để chắc chắn rằng dòng xử lý được kết thúc. Nó gây ra

ngoại lệ IOException nếu lỗi xảy ra.

mark() Đánh dấu vị trí hiện tại của dòng.

markSupported() Trả về giá trị boolean chỉ ra rằng dòng có hỗ trợ các

khả năng mark và reset hay không. Nó trả về True nếu

dòng hỗ trợ ngược lại trả về False.

reset() Phương thức này định vị lại dòng theo vị trí được đánh

lần cuối cùng. Nó gây ra ngoại lệ IOException nếu lỗi xảy ra.

skip()

Phương thức này bỏ qua „n‟ byte dòng vào. ‟-n‟ chỉ định số byte được bỏ qua. Nó gây ra ngoại lệ IOException nếu lỗi xảy ra. Phương thức này sử dụng

để di chuyển tới vị trí đặc biệt bên trong dòng vào.

116

Bảng 3.6. Các phương thức của lớp InputStream

3.3.2. Lớp OutputStream

Lớp OutputStream cũng là lớp trừu tượng. Nó định nghĩa cách ghi các kết xuất đến dòng. Nó cung cấp một tập các phương thức trợ giúp tạo ra, ghi và xử lý kết xuất

các dòng. Các phương thức bao gồm:

Tên phƣơng thức Mô tả

write(int) Phương thức này ghi một byte

write(byte[])

Phương thức này phong tỏa cho đến khi một byte được ghi. dòng phải chờ cho đến khi tác vụ ghi hoàn tất. Nó

gây ra ngoại lệ IOException nếu lỗi xảy ra.

write(byte[],int,int) Phương thức này ghi mảng các byte. Lớp OutputStream

định nghĩa ba dạng khác nhau của phương thức để có thể ghi một byte riêng lẻ, một mảng các byte, hay một

đoạn của một mảng byte.

flush() Phương thức này xóa sạch dòng.

Đệm dữ liệu được ghi ra dòng. Nó kích hoạt

IOException nếu lỗi xảy ra.

close() Phương thức đóng dòng.

Nó được dùng để giải phóng mọi tài nguyên gắn với

dòng. Nó kích hoạt IOException nếu lỗi xảy ra.

Bảng 3.7. Các phương thức lớp OutputStream

3.3.3. Vào ra mảng byte

Các lớp „ByteArrayInputStream‟ và „ByteArrayOutputStream‟ sử dụng các bộ

đệm. Không cần thiết phải dùng chúng cùng với nhau.

 Lớp ByteArrayInputStream

Lớp này tạo dòng đầu vào từ bộ đệm, đó là mảng các byte. Lớp này không hỗ trợ các phương thức mới. Ngược lại nó định nghĩa đè các phương thức của lớp InputStream như „read() „, „skip()‟, „available()‟ và „reset()‟.

 Lớp ByteArrayOutputStream

Lớp này tạo ra dòng ra trên một mảng các byte. Nó cũng cung cấp các khả năng

cho phép mảng ra tăng trưởng nhằm mục đích tăng kích thước. Lớp này cũng cung cấp thêm các phương thức „toByteArrray()‟ và „toString()‟. Chúng được dùng để chuyển

117

đổi dòng thành một mảng byte hay chuỗi.

Lớp ByteArrayOutputStream cũng cung cấp hai phương thức thiết lập. Một có

một đối số số nguyên dùng để ấn định mảng byte ra theo một kích cỡ ban đầu và thứ hai không có đối số nào, nó thiết lập bộ ra xuất với kích thước mặc định. Lớp này cung

cấp vài phương thức bổ sung, không được khai báo trong OutputStream:

 reset()

Thiết lập lại vùng đệm ra nhằm cho phép ghi lại từ đầu vùng đệm.

 size()

Trả về số byte hiện tại đã được ghi tới vùng đệm.

 writeto()

Ghi nội dung của vùng đệm ra dòng ra đã chỉ định. Để thực hiệnsử dụng một

đối tượng của lớp OutputStream làm đối số.

Ví dụ3.25:Sử dụng lớp „ByteArrayInputStream‟ và „ByteArrayOutputStream‟ để

import java.lang.System;

import java.io.*;

public class ByteExam

{

public static void main(String args[]) throws IOException

{

ByteArrayOutputStream os =new ByteArrayOutputStream();

String s ="Welcome to Byte Array Input Outputclasses";

for(int i=0; i

os. write (s.charAt(i) ) ;

System.out.println("Output Stream is:" + os);

System.out.println("Size of output stream is:"+ os.size());

ByteArrayInputStream in;

in = new ByteArrayInputStream(os.toByteArray());

int ib = in.available();

System.out.println("Input Stream has :" + ib + "available bytes");

byte ibuf[] = new byte[ib];

int byrd = in.read(ibuf,0,ib);

System.out.println("Number of Bytes read are :" + byrd);

118

nhập và xuất:

System.out.println("They are: " + new String(ibuf));

}

}

Kết quả thực hiện chương trình:

Hình 3.4. Kết quả chạy ví dụ 3.25

3.3.4. Vào ra tập tin

Java hỗ trợ các tác vụ nhập và xuất tập tin với sự trợ giúp các lớp sau đây:

 File  FileDescriptor  FileInputStream  FileOutputStream

Java cũng hỗ trợ truy cập nhập và xuất trực tiếp hay ngẫu nhiên bằng các lớp

„File‟,‟FileDescriptior‟, và „RandomAccessFile‟.

 Lớp File

Lớp này được sử dụng để truy cập các đối tượng tập tin và thư mục. Các

tập tin đặt tên theo qui ước đặt tên tập tin của hệ điều hành. Các qui ước này

được mô tả bằng các hằng số của lớp File. Lớp này cung cấp các phương thức thiết lập các tập tin và các thư mục. Các thiết lập chấp nhận các đường dẫn tập tin tuyệt đối lẫn tương đối của các tập tin và thư mục. Tất cả các thao tác thư mục và tập tin được thực hiện thông qua các phương thức của lớp File.

Các phương thức:

 Cho phép bạn tạo, xoá, đổi tên các file.  Cung cấp khả năng truy cập bằng đường dẫn tập tin.  Xác định đối tượng là tập tin hay thư mục.  Kiểm tra quyền truy cập đọc và ghi.

119

Giống như các phương thức truy cập, các phương thức thư mục cũng cho phép tạo, xoá, đặt tên lại và liệt kê các thư mục. Các phương thức này cho phép

truy nhập cây thư mục, cung cấp khả năng truy cập thư mục cha và các thư mục

anh em.

 Lớp FileDescriptor

Lớp này cung cấp khả năng truy cập các mô tả tập tin mà hệ điều hành sử

dụng khi tập tin và thư mục đang được truy cập. Lớp này không cung cấp các

phương thức cho phép xem chi tiết thông tin do hệ điều hành sử dụng. Nó chỉ cung

cấp một phương thức duy nhất là „valid()‟, giúp xác định một đối tượng mô tả tập tin hiện có hợp lệ hay không.  Lớp FileInputStream

Lớp này cho phép đọc vào từ một tập tin dưới dạng một dòng. Các đối tượng của lớp này được tạo ra nhờ đường dẫn tới file, đối tượng File, hoặc đối tượng

FileDescriptor làm một đối số. Lớp này định nghĩa chồng các phương thức của lớp

InputStream. Nó cũng cung cấp thêm các phương thức „finalize()‟ và „getFD()„.

Phương thức „finalize()„ được dùng để đóng dòng khi nó được bộ gom rác

Java nhặt. Phương thức „getFD()‟ trả về đối tượng FileDescriptor chứa thông tin về

sự kết nối thực sự tới file mà „FileInputStream‟ đang sử dụng.  Lớp FileOutputStream

Lớp này cho phép xuất ra một tập tin theo dòng. Các đối tượng của lớp này

cũng tạo ra sử dụng đường dẫn của tập tin, FileDesciptor làm tham số thiết lập.

Lớp này định nghĩa chồng phương thức của lớp OutputStream và cung cấp thêm

các phương thức „finalize()‟ và getFD().

import java.io.FileOutputStream;

import java.io.FileInputStream;

import java.io.File;

import java.io.IOException;

public class fileioexam

{

public static void main(String args[ ]) throws IOException

{

//tạo file abc.txt

FileOutputStream os = new FileOutputStream("abc.txt");

String s = "Welcome to File Input Output Stream " ;

for(int i = 0; i< s.length( ); ++i)

120

Ví dụ 3.26: Chương trình xuất ra một tập tin theo dòng

os. write(s.charAt(i));

os.close();

// mở file abc.txt để đọc

FileInputStream is = new FileInputStream("abc.txt");

int ibyts = is.available( );

System.out.println("Input Stream has " + ibyts + " available bytes");

byte ibuf[ ] = new byte[ibyts];

int byrd = is.read(ibuf, 0, ibyts);

System.out.println("Number of Bytes read are: " + byrd);

System.out.println("They are: " + new String(ibuf));

is.close();

File fl = new File("abc.txt");

fl.delete();

}

}

Kết quả thực hiện chương trình:

Hình 3.5. Kết quả chạy ví dụ 3.26

3.3.5. Nhập xuất lọc

Một „Filter‟ là một kiểu dòng đã thay đổi cách xử lý dòng hiện có. Các lớp, các dòng nhập xuất lọc của java sẽ giúp ta lọc vào/ra theo một số cách. Về cơ bản, các bộ

121

lọc này dùng để thích nghi các dòng theo các nhu cầu của chương trình cụ thể.

Bộ lọc nằm giữa một dòng nhập và một dòng xuất. Nó thực hiện xử lý một quá

trình nào đó trên các byte được truyền từ đầu vào đến đầu ra. Các bộ lọc có thể ghép với nhau khi đó đầu ra của bộ lọc này trở thành đầu vào của bộ lọc kia.

 Lớp FilterInputStream

Đây là lớp trừu tượng. Nó là cha của tất cả các lớp dòng nhập lọc. Lớp này

cung cấp khả năng tạo ra một dòng từ dòng khác. Một dòng có thể được đọc và đưa

kết quả cho một dòng khác. Biến „in‟ được sử dụng để làm điều này. Biến này được dùng để duy trì một đối tượng tách biệt của lớp InputStream. Lớp

FilterInputStream được thiết kế sao cho có khả năng kết chuỗi nhiều bộ lọc. Để

thực hiện điều này chúng ta dùng vài tầng lồng nhau. Mỗi lớp sẽ truy cập đầu ra

của lớp trước đó với sự trợ giúp của biến „in‟.  Lớp FilterOutputStream

Lớp này là một dạng bổ trợ cho lớp FilterInputStream. Nó là lớp cha của tất cả

các lớp dòng xuất lọc. Lớp này tương tự như lớp FilterInputStream ở chỗ duy trì đối

tượng của lớp OutputStream làm một biến „out‟. Dữ liệu ghi vào lớp này có thể sửa

đổi theo nhu cầu để thực hiện tác vụ lọc và sau đó được chuyển tới đối tượng

OutputStream.

3.3.6. Vào ra có sử dụng bộ đệm

Vùng đệm là kho lưu trữ dữ liệu. Chúng ta có thể lấy dữ liệu từ vùng đệm thay vì

quay trở lại nguồn ban đầu của dữ liệu.

Java sử dụng cơ chế nhập/xuất có lập vùng đệm để tạm thời lập cache dữ liệu

vào/ra của một dòng. Nó giúp chương trình đọc/ghi lượng dữ liệu nhỏ không ảnh

hưởng lớn đến hiệu năng chung của hệ thống.

Trong khi thực hiện vào có vùng đệm, một số lượng byte lớn được đọc tại một

thời điểm, và lưu trữ trong một vùng đệm nhập. Khi chương trình đọc dòng nhập thì

thay vì ra dòng vào để đọc nó đọc từ vùng đệm nhập.

Tiến trình lập vùng đệm ra cũng thực hiện tương tự. khi dữ liệu được một chương trình ghi ra dòng ra, dữ liệu ra được lưu trữ trong một vùng đệm ra. Dữ liệu được lưu trữ đến khi vùng đệm đầy hoặc các dòng ra thực hiện xả trống (flush). Cuối cùng liệu ra trong vùng đệm được chuyển đến dòng ra.

Các bộ lọc hoạt động trên vùng đệm. Vùng đệm được đặt giữa chương trình và

dòng ra của vùng đệm.

 Lớp BufferedInputStream

122

Lớp này tự động tạo ra và duy trì vùng đệm để hỗ trợ thao tác vào. Nhờ đó chương trình có thể đọc dữ liệu từ dòng từng byte một mà không ảnh hưởng đến tốc độ

thực hiện của hệ thống. Bởi lớp „BufferedInputStream‟ là một bộ lọc, nên có thể áp

dụng nó cho một số đối tượng nhất định của lớp InputStream và cũng có thể phối hợp với các tập tin đầu vào khác.

Lớp này sử dụng vài biến để thực hiện các cơ chế lập vùng đệm đầu vào. Các

biến này được khai báo là protected và do đó chương trình không thể truy cập trực

tiếp. Lớp này định nghĩa haiphương thức thiết lập. Một cho phép chỉ định kích cỡ của vùng đệm nhập trong khi đó phương thức thiết lập kia thì không. Nhưng cả hai phương

thức thiết lập đều tiếp nhận đối tượng của lớp InputStream làm đối số. Lớp này định

nghĩa chồng các phương thức truy cập mà InputStream cung cấp và không đưa thêm

bất kì phương thức mới nào.

 Lớp BufferedOutputStream

Lớp này cũng định nghĩa hai phương thức thiết lập, một cho phép chỉ định kích cỡ của vùng đệm xuất, một sử dụng kích cỡ vùng đệm ngầm định. Lớp này định nghĩa

chồng tất cả các phương thức của OutputStream và không đưa thêm bất kì phương

thức mới nào.

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.FileInputStream;

import java.io.SequenceInputStream;

import java.io.IOException;

public class BuffExam

{

public static void main(String args[ ]) throws IOException

{

// defining sequence input stream

SequenceInputStream Seq3;

FileInputStream Fis1 ;

Fis1 = new FileInputStream("byteexam.java");

FileInputStream Fis2;

Fis2= new FileInputStream("fileioexam.java");

Seq3 = new SequenceInputStream(Fis1, Fis2);

// create buffered input and output streams

123

Ví dụ3.27: Dưới đây mô tả cách dùng các luồng nhập/xuất có lập vùng đệm:

BufferedInputStream inst;

inst= new BufferedInputStream(Seq3);

BufferedOutputStream oust;

oust= new BufferedOutputStream(System.out);

inst.skip(1000);

boolean eof = false;

int bytcnt = 0;

while(!eof)

{

int num = inst.read();

if(num==-1)

{

eof =true;

}

else

{

oust.write((char) num);

++bytcnt;

}

}

String bytrd=String.valueOf(bytcnt);

bytrd += " bytes were read";

oust.write(bytrd.getBytes(), 0, bytrd.length());

// close all streams.

inst.close();

oust.close();

Fis1.close();

Fis2.close();

}

}

124

Kết quả thực hiện của chương trình trên:

Hình 3.6. Kết quả chạy ví dụ 3.27

3.3.7. Lớp Reader và Writer

Đây là các lớp trừu tượng, lớp cha của tất cả các lớp đọc và ghi các dòng ký tự

Unicode. Java 1.1 đã giới thiệu các lớp này.

 Lớp Reader

Lớp này hỗ trợ các phương thức:  read( )  reset( )  skip( )  mark( )  markSupported( )  close( )

Lớp này cũng hỗ trợ phương thức „ready()‟. Phương thức này trả về giá trị

kiểu boolean, xem việc đọc có tiếp tục được hay không.  Lớp Writer

Lớp này hỗ trợ các phương thức:  write( )  flush( )  close( )

3.3.8. Nhập xuất chuỗi

Các lớp „CharArrayReader‟ và „CharArrayWriter‟ cũng tương tự như các lớp ByteArrayInputStream và ByteArrayOutputStream ở chỗ chúng hỗ trợ nhập/xuất từ

các vùng đệm. Các lớp CharArrayReader và CharArrayWriter hỗ trợ nhập/ xuất ký tự

125

8 bit.

CharArrayReader bổ sung thêm phương pháp nào, nó chỉ dùng các phương thức

mà lớp Reader cung cấp. Lớp CharArrayWriter bổ sung thêm các phương thức sau đây ngoài các phương thức của lớp Writer.

 reset( )

Thiết lập lại vùng đệm

 size( )

trả về kích cỡ hiện hành của vùng đệm

 toCharArray( )

Trả về bản sao mảng ký tự của vùng đệm xuất

 toString( )

Chuyển đổi vùng đệm xuất thành một đối tượng String

 writeto( )

Ghi vùng đệm ra một luồng xuất khác.

Lớp StringReader trợ giúp đọc ký tự từ một chuỗi. Nó không bổ sung phương

thức nào ngoài các phương thức của lớp Reader.

Lớp StringWriter trợ giúp ghi ký tự ra đối tượng StringBuffer. Lớp này bổ sung

hai phương thức có tên là „getBuffer( )‟ và „toString()‟ . Phương thức „getBuffer( )‟

trả về đối tượng StringBuffer tương ứng với vùng đệm xuất, trong khi đó phương thức

toString( ) trả về một chuỗi chứa bản sao của vùng đệm xuất.

import java.io.CharArrayReader;

import java.io.CharArrayWriter;

import java.io.IOException;

public class Testl

{

public static void main(String args[ ]) throws IOException

{

CharArrayWriter ost = new CharArrayWriter( );

String s = "Welcome to Character Array Program";

for(int i= 0; i

ost.write(s.charAt(i));

System.out.println("Output Stream is: " + ost);

System.out.println("Size is: " + ost.size( ));

126

Ví dụ 3.28: Dưới đây thực hiện các tác vụ nhập/xuất mảng ký tự:

CharArrayReader inst;

inst = new CharArrayReader(ost.toCharArray( ));

int a= 0;

StringBuffer sbI = new StringBuffer(" ");

while((a = inst.read( )) != -1)

sbI.append((char) a);

s = sbI.toString( );

System.out.println(s.length() + "characters were read");

System.out.println("They are:" + s);

}

}

Hình 3.8 Kết quả thực hiện chương trình:

Hình 3.7. Kết quả chạy ví dụ 3.28

import java.lang.System;

import java.io.StringReader;

import java.io.StringWriter;

import java.io.IOException;

import java.io. * ;

public class StrioExam

{

public static void main(String args[ ]) throws IOException

{

StringWriter ost = new StringWriter( );

127

Ví dụ 3.29 Mô tả tiến trình nhập/xuất chuỗi.

String s = "Welcome to String Input Output Program";

for(int i= 0; i

ost.write(s.charAt(i)) ;

System.out.println("Output Stream is: " + ost);

StringReader inst;

inst = new StringReader(ost.toString( ));

int a= 0;

StringBuffer sb1 = new StringBuffer(" ");

while((a = inst.read())!=-1)

sb1.append((char) a);

s = sb1.toString( );

System.out.println("No of characters read: " +s.length( ));

System.out.println("They are: " + s);

}

}

Kết quả thực hiện chương trình:

Hình 3.8. Kết quả chạy ví dụ 3.29

3.3.9. Lớp PrinterWriter

Lớp „PrintStream‟ thực hiện việc kết xuất dữ liệu. Lớp này có các phương thức

bổ sung, trợ giúp cho việc in ấn dữ liệu cơ bản.

Lớp PrinterWriter‟ là một phiên bản hướng ký tự của lớp PrintStream. Nó thực tế

128

cải thiện lớp PrintStream bằng cách dùng dấu tách dòng phụ thuộc hệ điều hành để in các dòng thay vì ký tự „\n‟. Lớp này cũng cấp hỗ trợ ký tự Unicode tốt hơn so với PrintStream. Phương thức „checkError()‟ được sử dụng kiểm tra kết xuất và xoá sạch các lỗi. Phương thức setError() được sử dụng để thiết lập lỗi điều kiện. Lớp

PrinterWriter hỗ trợ in ấn các kiểu dữ liệu nguyên thủy, mảng ký tự, chuỗi và đối

tượng.

3.3.10. Giao diện DataInput

Giao diện DataInput được sử dụng để đọc các byte từ dòng nhị phân và xây dựng

lại các kiểu dữ liệu dạng nguyên thủy của Java.

DataInput cũng cho phép chúng ta chuyển đổi dữ liệu từ định dạng sửa đổi UTF- 8 tới dạng chuỗi. Chuẩn UTF cho định dạng chuyển đổi Unicode. Nó là kiểu định dạng

đặt biệt mã hoá các ký tự Unicode 16 bit . UTF cho rằng trong hầu hết các trường hợp,

byte cao trong 2 byte Unicode sẽ là 0. Giao diện DataInput được định nghĩa một số

các phương thức, các phương thức bao gồm việc đọc các kiểu dữ liệu nguyên thủy trong java.

Bảng 3.9 Tóm lượt vài phương thức. Tất cả các phương thức đều có khả năng tạo

ra ngoại lệ IOException trong trường hợp lỗi:

Tên phƣơng thức Mô tả

boolean readBoolean( ) Đọc một byte nhập, và trả về True nếu byte đó

khác 0, và False nếu byte đó bằng 0.

byte readByte( ) Đọc một byte

char readChar( ) Đọc và trả về một giá trị ký tự

short redShort( ) Đọc 2 byte và trả về giá trị kiểu short

long readLong( ) Đọc 8 byte và trả về giá trị kiểu long.

float readFloat( ) đọc 4 byte và trả về giá trị kiểu float

int readInt( ) Đọc 4 byte và trả về giá trị kiểu int

double readDouble( ) Đọc 8 byte và trả về giá trị kiểu double

String readUTF( ) Đọc một chuỗi

String readLine( ) Đọc một dòng văn bản

Bảng 3.8. Các phương thức của giao diện DataInput

3.3.11. Giao diện DataOutput

Giao diện DataOutput được sử dụng để tái tạo lại các kiểu dữ liệu nguyên thủy

trong java thành dãy byte. Nó ghi các byte này lên trên dòng nhị phân.

Giao diện DataOutput cũng cho phép chúng ta chuyển đổi một chuỗi vào trong

129

Java theo định dạng UTF-8 và ghi nó vào dòng.

Giao diện DataOutput định nghĩa một số phương thức được tóm tắt trong bảng

3.9. Tất cả các phương thức đều có khả năng gây ra ngoại lệ IOException trong trường hợp lỗi.

Tên phƣơng thức Mô tả

void writeBoolean(boolean b) Ghi một giá trị Boolean vào dòng

void writeByte(int value) Ghi giá trị 8 bit thấp

void writeChar(int value) Ghi 2 byte giá trị kiểu ký tự vào dòng

void writeShort(int value) Ghi 2 byte, biểu diễn lại giá trị kiểu short

void writeLong(long value) Ghi 8 byte, biểu diễn lại giá trị kiểu long

void writeFloat(float value) Ghi 4 byte, biểu diễn lại giá trị kiểu float

void writeInt(int value) ghi 4 byte

void writeDouble(double value) Ghi 8 byte, biểu diễn lại giá trị kiểu double

void writeUTF(String value) Ghi một chuỗi UTF tới dòng.

Bảng 3.9. Các phương thức của giao diện DataOutput

3.3.12. Lớp RandomAccessFile

Lớp RandomAccessFile cung cấp khả năng thực hiện vào/ra theo một vị trí cụ

thể bên trong một tập tin. Trong lớp này, dữ liệu có thể đọc hoặc ghi ở vị trí ngẫu

nhiên thay vì liên tục. Vì vậy lớp này có tên là RandomAccess. Phương thức „seek( )‟

hỗ trợ truy cập ngẫu nhiên. Vì thế, biến trỏ tương ứng với tệp tin hiện hành có thể ấn

định theo vị trí bất kỳ trong tập tin.

Lớp RandomAccessFile thực hiện cả hai việc nhập và xuất. Do vây, có thể thực

hiện I/O bằng các kiểu dữ liệu nguyên thủy. Lớp này cũng hỗ trợ các quyền cơ bản về

đọc hoặc ghi tập tin, điều này cho phép đọc tập tin theo chế độ chỉ đọc hoặc đọc-ghi.

Tham số „r‟ hoặc „rw‟ được gán cho lớp RandomAccessFile chỉ định truy cập „chỉ đọc‟ và „đọc-ghi‟. Lớp này giới thiệu vài phương thức mới khác với phương thức đã thừa kế từ các lớp DataInput và DataOutput.

Các phương thức mới thêm vào bao gồm:

 seek( )

Thiết lập con trỏ tập tin tới vị trí cụ thể bên trong tập tin.

 getFilePointer( )

Trả về vị trí hiện hành của con trỏ tập tin.

130

 length( )

Trả về chiều dài của tập tin tính theo byte.

Chương trình dưới đây minh hoạ cách dùng lớp RandomAccessFile. Nó ghi một giá trị boolean, một int, một char, một double tới một file có tên „abc.txt‟. Nó sử dụng

phương pháp seek( ) để tìm vị trí định vị bên trong tập tin (bắt đầu từ 1). Sau đó nó đọc

giá trị số nguyên, ký tự và double từ tập tin và hiển thị chúng ra màn hình.

import java.lang.System;

import java.io.RandomAccessFile;

import java.io.IOException;

public class RndExam

{

public static void main (String args[ ]) throws IOException

{

RandomAccessFile rf;

rf = new RandomAccessFile(“abc.txt”, “rw”);

rf.writeBoolean(true);

rf.writeInt(67868);

rf.writeChars(“J”);

rf.writeDouble(678.68);

//making use of seek() method to move to a specific file location rf.seek(1);

System.out.println(rf.readInt());

System.out.println(rf.readChar());

System.out.println(rf.readDouble());

rf.seek(0);

System.out.println(rf.readBoolean));

rf.close();

}

}

Ví dụ3.30:Minh hoạ cách dùng lớp RandomAccessFile

131

Hình 3.10Kết quả thực hiện chương trình:

Hình 3.9. Kết quả chạy ví dụ 3.30

3.4. Gói java.awt.print

Đây là gói mới mà bắt đầu từ Java 1.2 cung cấp. Nó thay thế khả năng in của

JDK 1.1. Nó bao gồm dãy các giao diện:

 Pageable  Printable  PrinterGraphics

Giao diện „Pageable‟ định nghĩa các phương thức được sử dụng cho đối tượng

mô tả các trang sẽ được in. Nó cũng chỉ ra số lượng trang sẽ được in cũng như sẽ được

in trang hiện hành hay một dãy các trang.

Giao diện „Printable‟ chỉ định phương thức print()được dùng để in một trạng trên

một đối tượng Graphics.

Giao diện „PrinterGraphics„ cung cấp khả năng truy cập đối tượng „PrinterJob‟.

Nó cung cấp các lớp sau đây:

 Paper  Book  PageFormat  Printerjob

Lớp „Page‟ định nghĩa các đặc tính vật lý của trang giấy in. Ngoài ra nó cũng

cung cấp khổ giấy và vùng vẽ.

Lớp „Book‟ là một lớp con của đối tượng duy trì một danh sách các trang được in. Lớp này cũng cung cấp các phương thức để thêm trang và quản lý trang. Nó cài đặt

giao diện Pageable.

Lớp „PageFormat‟ định nghĩa lề của trang như „Top‟, „Bottom‟,‟Left‟ và „Right‟. Nó cũng chỉ định kích cỡ và hướng in như „Portrait‟ (khổ dọc) hoặc „Landscape‟ (khổ

ngang).

Lớp „PrinterJob‟ là một lớp con của đối lượng khởi tạo, quản lý, và điều khiển

132

yêu cầu máy in. Lớp này cũng chỉ định các tính chất in.

Dưới đây là ngoại lệ và lỗi mà gói java.awt.print kích hoạt:

 PrinterException  PrinterIOException  PrinterAbortException

„PrinterException„ thừa kế java.lang.Exception nhằm cung cấp một lớp cơ sở để

in các ngoại lệ liên quan.

„PrinterIOException‟ thừa „PrinterException‟ chỉ rõ lỗi trong I/O.

„PrinterAbortException‟ là lớp con của lớp PrinterException nêu rõ việc in đã

được hủy bỏ.

Câu hỏi và bài tập chƣơng 3

1.Viết chương trình nhận một dòng văn bản từ người dùng và hiển thị đoạn văn bản đó

lên màn hình.

2.Viết chương trình sao chép nội dụng từ một tập tin tới một tập tin khác.

3.Viết chương trình truy cập ngẫu nhiên tập tin, có giao diện như sau:

Các bản ghi nên được lưu ở tập tin „.dat‟, vì vậy người dùng truy cập chúng

sau này.

4.Viết chương trình đếm số dòng của một file văn bản.

6.Viết chương trình tìm xâu dài nhất trong một file văn bản.

7.Viết chương trình ghép một file văn bản thứ hai vào file văn bản thứ nhất, trong đó

5. Viết chương trình đọc in từng kí tự của file văn bản ra màn hình, mỗi màn hình 20 dòng.

133

tất cả chữ cái của file văn bản thứ nhất phải đổi thành chữ in hoa.

8.Viết chương trình in nội dung file ra màn hình và cho biết tổng số chữ cái, tổng số

chữ số đã xuất hiện trong file.

9.Cho 2 file số thực (đã được sắp tăng dần). In ra màn hình dãy số xếp tăng dần của cả

2 file. (Cần tạo cả 2 file dữ liệu này bằng Editor của C++).

10.Viết hàm nhập 10 số thực từ bàn phím vào file INPUT.DAT. Viết hàm đọc các số

thực từ file trên và in tổng bình phương của chúng ra màn hình.

11.Viết hàm nhập 10 số nguyên từ bàn phím vào file văn bản tên INPUT.DAT. Viết

hàm đọc các số nguyên từ file trên và ghi những số chẵn vào file EVEN.DAT còn các

số lẻ vào file ODD.DAT.

12.Nhập bằng chương trình 2 ma trận số nguyên vào 2 file văn bản. Hãy tạo file văn bản thứ 3 chứa nội dung của ma trận tích của 2 ma trận trên.

13.Tổ chức quản lý file sinh viên (Họ tên, ngày sinh, giới tính, điểm) với các chức

năng : Nhập, xem, xóa, sửa, tính điểm trung chung.

14.Thông tin về một nhân viên trong cơ quan bao gồm : họ và tên, nghề nghiệp, số

điện thoại, địa chỉ nhà riêng. Viết hàm nhập từ bàn phím thông tin của 7 nhân viên và

ghi vào file INPUT.DAT. Viết hàm tìm trong file INPUT.DAT và in ra thông tin của 1

nhân viên theo số điện thoại được nhập từ bàn phím.

15.Cho một lớp shapeInt là một lớp interface và một lớp HinhCN là một cài đặt từ lớp

ShapeInt gồm các thuộc tính và phương thức như hình sau:

a. Tạo lớp ShapeInt là một lớp Interface

b. Tạo lớp HinhCN là cài đặt của ShapeInt và cài đặt các thuộc tính, phương thức

cho lớp như sau:

- ChieuCao/ChieuRong là 2 thuộc tính là chiều rộng và chiều cao của hình

134

- Các phương thức setter/getter

- Phương thức Contructor

- TinhDienTich() dùng để tính diện tích cho hình chữ nhật

- TinhChuVi() dùng để tính chu vi cho hình chữ nhật

- HienThi() dùng để hiện thị ra chiều rộng, chiều cao, diện tích, chu vi của hình

chữ nhật.

- Phương thức main dùng để tạo ra một đối tượng HinhCN và cho phép nhập

135

vào một chiều rộng, chiều cao và hiển thị các kết quả ra màn hình.

CHƢƠNG 4: LẬP TRÌNH ĐỒ HOẠ AWT

4.1. Giới thiệu về AWT

Các ứng dụng phần mềm hiện nay rất thân thiện vì được trình bày nhiều màn

hình giao diện đồ họa đẹp. Các ngôn ngữ lập trình hiện nay cung cấp các đối tượng đồ

họa, chúng có thể được điều khiển bởi người lập trình, hay bởi người sử dụng. Một

trong số những kết quả quan trọng nhất chính là các ngôn ngữ hiện nay được dựa trên Giao diện người dùng đồ họa (Graphical User Interface - GUI). Trong chương này, ta

sẽ thảo luận về Java hỗ trợ tính năng GUI cùng các sự thi hành của chúng.

GUI cung cấp chức năng nhập liệu theo cách thân thiện với người dùng. GUI đa

dạng từ ứng dụng đến ứng dụng và có thể chứa nhiều điều khiển như hộp văn bản, nhãn, hộp danh sách hay các điều khiển khác. Các ngôn ngữ lập trình khác nhau cung

cấp nhiều cách khác nhau để tạo GUI. Các ngôn ngữ như VB hay VC++ có thể cung

cấp chức năng kéo và thả trong khi đó phần mềm giống như C++ yêu cầu người lập

trình phải viết toàn bộ mã để xây dựng GUI.

Một phần tử (element) GUI được thiết lập bằng cách sử dụng thủ tục sau:

 Tạo đối tượng  Xác định sự xuất hiện ban đầu của đối tượng  Chỉ ra nó nằm ở đâu  Thêm phần tử vào giao diện trên màn hình

Một thành phần (component) GUI là một đối tượng trực quan. Người dùng tương

tác với đối tượng này thông qua con trỏ chuột hay bàn phím. Các thành phần như là

button, label … có thể được nhìn thấy trên màn hình. Bất kỳ cái gì chung cho tất cả

các thành phần GUI đều được tìm thấy trong lớp Component. Để tạo các đối tượng

GUI chúng ta cần nhập gói java.awt.

AWT là viết tắt của Abstract Windowing Toolkit. AWT là một bộ các lớp trong

Java cho phép chúng ta tạo GUI và chấp nhận các nhập liệu của người dùng thông qua

bàn phím và chuột. AWT cung cấp các thành phần khác nhau để tạo GUI hiệu quả và

lôi cuốn người sử dụng. Các thành phần này có thể là:

 Vật chứa (Container)  Thành phần (Component)  Trình quản lý cách trình bày (Layout manager)  Đồ họa (Graphic) và các tính năng vẽ (draw)  Phông chữ (Font)  Sự kiện (Event)

Gói AWT chứa các lớp, giao diện và các gói khác. Hình sau đây mô tả một phần

136

nhỏ của hệ thống phân cấp lớp AWT.

Hình 4.1. Hệ thống cây phân cấp lớp AWT

4.2. Container (vật chứa)

4.2.1. Giới thiệu

Container là vùng mà bạn có thể đặt các thành phần giao diện của bạn vào đó. Bất cứ vật gì mà kế thừa từ lớp Container sẽ là vật chứa. Applet là một vật chứa,

Applet được dẫn xuất từ Panel, lớp Panel lại được dẫn xuất từ lớp Container.

Một vật chứa có thể chứa nhiều phần tử, các phần tử này có thể được vẽ hay

được tô màu tuỳ thích. Bạn hãy xem vật chứa như một cửa sổ. Như khung (frame),

panel, latch, hook, và các thành phần có kích thước nhỏ hơn khác.

Gói java.awt chứa một lớp gọi là Container. Lớp này trực tiếp hay gián tiếp phải

sinh ra hai vật chứa được sử dụng phổ biến nhất là Frame và Panel.

Frame và Panel là các vật chứa thường được sử dụng. Frame là cửa sổ độc lập

nhưng ngược lại Panel là vùng nằm trong cửa sổ khác. Panel không có các đường biên,

chúng được trình bày trong một cửa sổ do trình duyệt hay appletviewer cung cấp.

Appletviewer là một công cụ được JDK hỗ trợ để xem các Applet. Frame là lớp con

của Window. Chúng được trình bày trong một cửa sổ độc lập, cửa sổ này có chứa các

đường biên xung quanh.

4.2.2. Frame

Frame không phụ thuộc vào Applet và trình duyệt. Frame có thể hoạt động như

một vật chứa hay như một thành phần (component). Bạn có thể sử dụng một trong những constructor sau để tạo một frame:

 Frame(): Tạo một frame nhưng không hiển thị (invisible)  Frame(String title): Tạo một frame không hiển thị, có tiêu đề.

import java.awt.*;

class FrameDemo extends Frame

{

public FrameDemo(String title)

137

Ví dụ 4.1: Minh hoạ cách tạo một Frame.

{

super(title);

}

public static void main(String args[])

{

FrameDemo f=new FrameDemo(“I have been Frameed!!!”);

f.setSize(300,200);

f.setVisible(true);

}

}

Lớp được định nghĩa FrameDemo là một lớp con của lớp Frame. Lớp

FrameDemo này có một phương thức khởi tạo, trong phương thức khởi tạo này ta cho

gọi phương thức super(). Nó sẽ gọi phương thức khởi tạo của lớp cha (trong trường

hợp này là Frame). Mục đích của super() là gọi phương thức khởi tạo của lớp cha. Nó

sẽ tạo một đối tượng của lớp con, lớp con này sẽ tạo Frame. Tuy nhiên, Frame vẫn

không nhìn thấy được và không có kích thước. Để làm được điều này, ta sử dụng hai

phương thức nằm trong phương thức main: setSize() và setVisible().

Kết chạy của chương trình giống như hình 4.2

Hình 4.2. Kết quả chạy ví dụ 4.1

4.2.3. Panel

Panel được sử dụng để nhóm một số các thành phần lại với nhau. Cách đơn giản

nhất để tạo một panel là sử dụng phương thức khởi tạo của nó, hàm Panel().

import java.awt.*;

138

Ví dụ 4.2:Chỉ ra cách tạo một panel:

class PanelTest extends Panel

{

public static void main(String args[])

{

Paneltest p=new Paneltest();

Frame f=new Frame(“Testing a Panel”);

f.add(p);

f.setSize(300,200);

f.setVisible(true);

}

public Paneltest()

{

}

}

Panel không thể được nhìn thấy trực tiếp. Do đó, chúng ta cần thêm panel đến

một frame. Vì vậy ta cần tạo một frame mới và thêm Panel mới được tạo này vào đó.

Tuy nhiên, frame sẽ không nhìn thấy được, và không có kích thước. Chúng ta sử dụng

hai phương thức trong phương thức main – setSize() và setVisible() để thiết lập kích

thước và hiển thị frame.

Kết xuất của chương trình:

Hình 4.3. Kết quả chạy ví dụ 4.2

4.2.4. Dialog

Lớp „Dialog‟ tương tự như lớp Frame, nghĩa là Dialog là lớp con của lớp

139

Window. Đối tượng Dialog được tạo như sau:

Frame myframe=new Frame(“My frame”); // calling frame

String title = “Title”;

boolean modal = true; // whether modal or not

Dialog dlg=new Dialog(myframe, title, modal);

Tham số „modal‟ chỉ ra rằng dialog sẽ ngăn chặn bất kỳ tương tác nào xảy đến

với các cửa sổ được mở khác, trong khi dialog đang được hiển thị trên màn hình. Kiểu hộp thoại này ngăn chặn người dùng tương tác với các cửa sổ khác (của cùng ứng

dụng) trên màn hình, cho tới khi dialog được đóng lại.

4.3. Thành phần (Component)

4.3.1. Giới thiệu

Một component có thể được đặt trên giao diện người dùng, có thể được thay đổi

kích thước hay làm cho nhìn thấy, ẩn. Ví dụ được dùng phổ biến nhất là Textfield,

Label, Checkbox, Textarea v.v… Và các thành phần cao cấp khác như Scrollbar,

Scrollpane và Dialog. Tuy nhiên chúng không được sử dụng thường xuyên.

Hình 4.4. Các lớp thành phần

Bây giờ chúng ta hãy xét một số thành phần thường được sử dụng.

4.3.2. Nhãn

Lớp này được sử dụng để trình bày một String. Nó không thể được sửa đổi. Đây

là một chuỗi chỉ đọc. Sử dụng một trong những constructor sau đây để tạo một label:

 Label()

140

Tạo một Label trống.

 Label(String labeltext)

Tạo một Label với nội dung được cho.

 Label(String labeltext, int alignment)

Tạo một Label với một chế độ canh lề (alignment) , canh lề có thể là

Label.LEFT, Label.RIGHT hay Label.CENTER.

Các phương thức được sử dụng phổ biến của label được trình bày ở bảng bên

dưới:

Phương thức Chức năng

setFont(Font f) Thay đổi phông chữ của Label

setText(String s) Thiết lập nhãn cho Label

getText() Lấy nội dung hiện tại của nhãn

Bảng 4.1. Các phương thức của Label

import java.awt.*;

class LabelTest extends Frame

{

Label label1=new Label(“This is just a label”);

public LabelTest(String title)

{

super(title);

add(label1);

}

}

public static void main(String args[])

{

LabelTest f=new LabelTest(“Label”);

f.setSize(300,200);

f.show();

}

}

Ví dụ 4.3:Về cách sử dụng của Label:

141

label1=new Label(“This is just a label”);

Label sẽ hiển thị chỉ khi nó được thêm vào Container. Ở đây, Frame là Container

mà thành phần Label được thêm vào. Việc này được thực hiện bằng cách sử dụng phương thức add().

Khi chạy chương trình được kết quả hình 4.5

Hình 4.5. Kết quả chạy ví dụ 4.3

4.3.3. Ô văn bản

Một Textfield là một vùng chỉ chứa một dòng văn bản, trong đó văn bản có thể

được hiển thị hay được nhập vào bởi người dùng. Trong Java, một trong những

constructor sau có thể được sử dụng để tạo một Textfield:

 TextField(): Tạo một textfield mới.  TextField(int columns): Tạo một textfield mới với số cột được cho trước.  TextField(String s): Tạo một textfield mới với chuỗi văn bản được cho trước.  TextField(String s, int columns): Tạo một textfield mới với nội dung và số

cột được cho trước.

Các phương thức thường được sử dụng của đối tượng TextField được tóm tắt

trong bảng sau:

Phƣơng thức Chức năng

setEchoChar(char) Đặt các kí tự được hiện ra thay thế ký tự nhập vào.

setText(String s) Gán nội dung cho TextField.

getText() Lấy nội dung của TextField.

setEditable(boolean) Xác định TextField có soạn thảo được hay không. Nó

chỉ được soạn thảo khi giá trị tham số truyền vào là True.

isEditable() Xác định xem trường có đang trong mode soạn thảo hay

142

không. Giá trị trả về kiểu Boolean.

Bảng 4.2. Các phương thức của TextField

import java.awt.*;

class TextFieldTest extends Frame

{

TextField tf1=new TextField(30);

public TextFieldTest(String title)

{

super(title);

setLayout(new FlowLayout());

add(tf1);

}

public static void main(String args[])

{

TextFieldTest f=new TextFieldTest(“TextField”);

f.setSize(300,200);

f.show();

}

}

Ví dụ 4.4: Cách sử dụng của TextField:

Trong ví dụ nàycó sử dụng phương thức setLayout() để thay đổi cách trình bày

của các thành phần trên vật chứa. Layout manager có chức năng sắp xếp các thành

phần trong một vật chứa.

Kết của chương trình được chỉ ra ở hình bên dưới:

143

Hình 4.6. Kết quả chạy ví dụ 4.4

4.3.4. Vùng văn bản

Một Textarea được sử dụng khi văn bản nhập vào có trên hai hay nhiều dòng. Textarea có một scrollbar. TextArea là một trường văn bản có thể được soạn thảo với

nhiều dòng.

Để tạo một Textarea, làm theo các bước sau:

1) Tạo một đối tượng. 2) Chỉ ra số dòng, số cột đối tượng này cần có. 3) Bố trí phần tử này trên màn hình.

Trong Java, bạn có thể sử dụng các constructor sau để tạo TextArea:

 TextArea(): Tạo một TextArea mới.  TextArea(int rows, int cols): Tạo một TextArea mới với số lượng cột và

dòng được cho trước.

 TextArea(String text): Tạo một TextArea mới với nội dung được cho trước.  TextArea(String text, int rows, int cols): Tạo một TextArea mới với dung,

số dòng và số cột được cho trước.

Các phương thức thường được sử dụng nhiều nhất của TextArea:

Phƣơng thức Chức năng

setText(String) Gán nội dung cho TextArea.

getText() Trả về nội dung của TextArea.

setEdiable(boolean) Xác định xem TextAreacó thể được soạn thảo hay

không. TextArea có thể được soạn thảo khi giá trị

này là True.

isEditable() Xác định xem TextArea có đang trong chế độ soạn

thảo được không. Trả về giá trị là kiểu Boolean.

insertText(String, int) Chèn chuỗi được vào vị trí được cho trước.

replaceText(String, int, int) Thay thế văn bản nằm giữa vị trí int, int cho trước.

Bảng 4.3. Các phương thức của TextArea

import java.awt.*;

class TextAreaTest extends Frame

{

Label lbl=new Label(“Details”);

144

Ví dụ 4.5: Cách sử dụng của TextArea:

TextArea ta1=new TextArea();

public TextAreaTest(String title)

{

super(title);

setLayout(new FlowLayout());

add(lbl);

add(ta1);

}

public static void main(String args[])

{

TextAreaTest t=new TextAreaTest(“TextArea”);

t.setSize(300,200);

t.show();

}

}

Kết quả của chương trình được chỉ ra ở hình bên dưới:

Hình 4.7. Kết quả chạy ví dụ 4.5

4.3.5. Nút

Nút ấn hay còn gọi là nút lệnh là một phần không thể thiếu của bất kỳ GUI nào.

Sử dụng button là cách dễ nhất để nhận các tác động của người dùng.

Để tạo một button, bạn làm theo các bước sau:

1) Tạo phần tử Button với một nhãn chỉ ra mục đích của Button. 2) Bố trí phần tử này trên màn hình. 3) Hiển thị phần tử trên màn hình.

Sử dụng một trong hai constructor sau để tạo các button trong Java:

145

 Button()  Button(String text)

Sử dụng setLabel() và getLabel() để thiết lập và lấy giá trị nhãn của button.

import java.awt.*;

class ButtonTest extends Frame

{

Button b1 = new Button(“red”);

Button b2 = new Button(“Green”);

Button b3 = new Button(“Blue”);

public ButtonTest(String title)

{

super(title);

setLayout(new FlowLayout());

add(b1);

add(b2);

add(b3);

}

public static void main(String args[])

{

ButtonTest t= new ButtonTest(“Button”);

t.setSize(300,200);

t.show();

}

}

Ví dụ 4.6:Đơn giản sau đây sẽ tạo ra 3 button

146

Kết quả của chương trình được

Hình 4.8. Kết quả chạy ví dụ 4.6

4.3.6. Checkbox và Radio Button

Checkbox được sử dụng khi người dùng tiến hành chọn một hay nhiều tùy chọn.

Người dùng phải click trên các Checkbox để chọn hay bỏ chọn chúng. Một Radio button cũng tương tự như một Checkbox. Nó được sử dụng như một Option button để

xác định các chọn lựa. Bạn chỉ có thể chọn một Option trong nhóm các nút

Radiobutton, ngược lại bạn có thể chọn nhiều hơn một Checkbox tại một thời điểm.

Làm theo các bước sau để tạo các Checkbox hay Radiobutton:

1) Tạo phần tử. 2) Xác định trạng thái khởi đầu của phần tử (chọn hay không chọn). 3) Bố trí các phần tử trên màn hình. 4) Hiển thị các phần tử trên màn hình.

Thành phần Checkbox có thể sử dụng một lớp phụ được gọi là CheckboxGroup

để tạo ra các Radiobutton.

Sử dụng các constructor sau để tạo các Checkbox trong Java:

 Checkbox(): Tạo một Checkbox trống.  Checkbox(String text): Tạo một Checkbox với nhãn được cho.

Để tạo các Radio button, đầu tiên chúng ta tạo đối tượng CheckboxGroup như

sau:

CheckboxGroup cg=new CheckboxGroup();

Sau đó chúng ta tạo các đối tượng, như sau:

Checkbox male=new Checkbox(“male”, cg, true);

Checkbox female=new Checkbox(“female”, cg, false);

Chúng ta sử dụng các phương thức setState() và getState() để thiết lập và nhận về

147

trạng thái của Checkbox.

import java.awt.*;

class CheckBoxTest extends Frame

{

Label l1=new Label(“CheckBoxes”);

Checkbox b1=new Checkbox(“red”,true);

Checkbox b2=new Checkbox(“Green”,false);

Checkbox b3=new Checkbox(“Blue”,false);

Label l2=new Label(“Radiobuttons”);

CheckboxGroup cb=new CheckboxGroup();

Checkbox b4=new Checkbox(“small”,cb,true);

Checkbox b5=new Checkbox(“medium”,cb,false);

Checkbox b6=new Checkbox(“large”,cb,false);

public CheckBoxTest(String title)

{

super(title);

setLayout(new GridLayout(8,1));

add(l1);

add(b1);

add(b2);

add(b3);

add(l2);

add(b4);

add(b5);

add(b6);

}

public static void main(String args[])

{

CheckBoxTest t=new CheckBoxTest(“Checkbox and radiobutton”);

t.setSize(300,200);

148

Ví dụ 4.7:Minh họa cách sử dụng của các Checkbox và các Radiobutton:

t.show();

}

}

Đầu tiên chúng ta tạo một đối tượng Frame, đối tượng này hoạt động như một

Container sẽ chứa thành phần Checkbox mà ta đã tạo. Sau đó ta tạo 6 Checkbox, 02

Checkbox được đánh dấu chọn. Để làm được điều này, ta đưa giá trị true như một

tham số cho hàm Contructor Checkbox, ngoài ra còn có một tham số String là nhãn của Checkbox. Để hiển thị các điều khiển này theo dạng lưới, ta phải thiết lập cách

trình bày về dạng GridLayout có 8 dòng và 1 cột. Cuối cùng, ta tạo một biểu hiện cho

lớp Checkboxtest và thiết lập kích thước cho Frame. Để hiển thị nó, ta cho gọi phương

thức show().

Kết quả chạy chương trình ở hình bên dưới:

Hình 4.9. Kết quả chạy ví dụ 4.7

4.3.7. Danh sách lựa chọn

Thỉnh thoảng, rất cần thiết để trình bày một danh sách các chọn lựa đến người

dùng trên một GUI. Người dùng có thể click vào một hay nhiều mục từ danh sách.

Một danh sách chọn lựa được tạo bằng cách sử dụng một số các chuỗi (String) hay các giá trị văn bản.

Để tạo các danh sách chọn lựa, hãy làm theo các bước được cho sau đây:

1) Tạo danh sách các phần tử. 2) Thêm các mục (có kiểu là String) vào danh sách, mỗi lần chỉ thêm được một

mục.

149

3) Bố trí danh sách trên màn hình. 4) Hiển thị danh sách trên màn hình.

Java hỗ trợ lớp Choice cho phép chúng ta tạo các danh sách chứa nhiều mục. Khi

danh sách vừa được tạo ra, nó sẽ rỗng.

Choice colors=new Choice();

Mỗi thời điểm chỉ thêm được một item bằng cách sử dụng phương thức addItem

như được chỉ ra bên dưới:

colors.addItem(“Red”);

colors.addItem(“Green”);

import java.awt.*;

class ChoiceTest extends Frame

{

Label l1=new Label(“What is your favorite color”);

Choice colors=new Choice();

public ChoiceTest(String title)

{

super(title);

setLayout(new FlowLayout());

add(l1);

colors.addItem(“White”);

colors.addItem(“Red”);

colors.addItem(“Orange”);

colors.addItem(“Green”);

colors.addItem(“Yellow”);

colors.addItem(“Blue”);

colors.addItem(“Black”);

add(colors);

}

public static void main(String args[])

{

ChoiceTest t=new ChoiceTest(“Choice list”);

150

Ví dụ 4.8:Minh họa cách tạo một danh sách chọn lựa:

t.setSize(300,200);

t.show();

}

}

Kết quả chạy chương trình ta được:

Hình 4.10. Kết quả chạy ví dụ 4.8

4.4. Quản lý cách trình bày

Layout manager điều khiển cách trình bày vật lý của các phần tử GUI như là

button, textbox, option button v.v… Một layout manager tự động bố trí các thành phần

này trong Container.

Các kiểu trình bày khác nhau:

 Flow layout  Border layout  Card layout  Grid layout  GridBag Layout

Tất cả các thành phần mà chúng ta vừa tạo sử dụng layout manager mặc định. Cho ví dụ, „FlowLayout‟ là cách trình bày mặc định của một Applet. Layout manager này sẽ tự động sắp xếp các thành phần. Tất cả các thành phần được đặt trong một Container, và được sắp xếp nhờ layout manager tương ứng. Layout manager được thiết lập bằng phương thức „setLayout()‟.

Bây giờ chúng ta sẽ tìm hiểu chi tiết các cách trình bày và cách bố trí các thành

151

phần của ta vào những vị trí mong muốn.

4.4.1. FlowLayout manager

„FlowLayout‟ là layout manager mặc định cho Applet và Panel. Các thành phần được sắp xếp từ góc trái trên đến góc phải dưới của màn hình. Khi một số thành phần

được tạo, chúng được sắp xếp theo hàng, từ trái sang phải. Các constructor của

FlowLayout:

FlowLayout mylayout = new FlowLayout() // constructor

//constructor with alignment specified

FlowLayout exLayout=new FlowLayout(FlowLayout.RIGHT);

setLayout(exLayout); //setting the layout to Flowlayout

Các điều khiển có thể được canh về bên trái, bên phải hay ở giữa. Để canh các

điều khiển về bên phải, bạn sử dụng cú pháp sau:

setLayout(new FlowLayout(FlowLayout.RIGHT));

import java.awt.*;

class FlTest extends Frame

{

Button b1=new Button(“Center Aligned Button 1”);

Button b2=new Button(“Center Aligned Button 2”);

Button b3=new Button(“Center Aligned Button 3”);

public FlTest(String title)

{

super(title);

setLayout(new FlowLayout(FlowLayout.CENTER));

add(b1);

add(b2);

add(b3);

}

public static void main(String args[])

{

FlTest t=new FlTest(“Flow Layout”);

t.setSize(300,200);

152

Ví dụ 4.9:Minh họa về FlowLayout manager.

t.show();

}

}

Kết chạy chương trình được:

Hình 4.11. Kết quả chạy ví dụ 4.9

4.4.2. BorderLayout Manager

„BorderLayout‟ là layout manager mặc định cho „Window‟, „Frame‟ và „Dialog‟.

Layout này sắp xếp tối đa 5 thành phần trong một Container. Những thành phần này

có thể được đặt ở các hướng „North‟, „South‟, „East‟, „West‟ và „Center‟ của Container.

 NORTH – Đặt ở đỉnh của Container.  EAST – Đặt phía bên phải của Container.  SOUTH – Đặt ở phía dưới của Container.  WEST – Đặt phía bên trái của Container.  CENTER – Đặt ở giữa của Container.

Để thêm một thành phần vào vùng „North‟, bạn sử dụng cú pháp sau:

Button b1=new Button(“North Button”); // khai báo thành phần

setLayout(new BorderLayout()); // thiết lập layout

add(b1,BorderLayout.NORTH); // thêm thành phần vào layout

Các thành phần vẫn giữ nguyên vị trí tương đối của chúng kể cả khi Container bị thay đổi kích thước. Các thành phần được đặt trong vùng „North‟, „South‟ được dàn

nằm ngang trong khi đó các thành phần đặt trong vùng „East‟ và „West‟ lại được dàn thẳng đứng. Các thành phần được đặt trong vùng „Center‟ sẽ được dàn đều vào những khu vực nằm giữa của Container.

153

add(b2,BorderLayout.CENTER); // thêm thành phần vào vùng „Center‟

Khi tất cả các thành phần được đặt vào các vùng tương ứng, lúc đó Frame sẽ

giống như sau:

Hình 4.12. BorderLayout

BorderLayout có thể chứa nhiều hơn 5 thành phần. Để thực hiện điều này, chúng ta có thể sử dụng các Panel với các layout khác nhau để chứa các thành phần, và sau

đó đặt các panel này vào trong BorderLayout.

4.4.3. Card Layout Manager

CardLayout có thể lưu trữ một ngăn xếp (stack) các giao diện. Mỗi giao diện

giống như một bảng (card). Bảng thường là đối tượng Panel. Một thành phần độc lập

như button sẽ điều khiển cách trình bày các bảng ở lớp trên cùng.

Đầu tiên, chúng ta bố trí tập hợp các thành phần được yêu cầu trên các panel

tương ứng. Mỗi panel sẽ được bố trí vào các layout khác nhau. Ví dụ:

panelTwo.setLayout(new GridLayout(2,1));

Panel chính sẽ chứa những panel này. Chúng ta thiết lập layout của panel chính

là Cardlayout như sau:

CardLayout card=new CardLayout();

panelMain.setLayout(card);

Bước kế tiếp là thêm các panel khác vào panel chính:

panelMain.add(“Red Panel”, panelOne);

panelMain.add(“Blue Panel”, panelTwo);

Phương thức „add()‟ sử dụng hai tham số. Tham số đầu tiên là một String làm

nhãn của panel và tham số thứ hai là tên đối tượng Panel.

import java.awt.*;

154

Ví dụ 4.10:Minh họa CardLayout:

import java.applet.*;

/**/

public class CardLayoutDemo extends Applet

{

Button Back, next;

Label lbl1,lbl2,lbl3,lbl4;

TextField other1;

Panel p1,first,second,third,fourth;

CardLayout c1;

public void init()

{

back=new Button(“Back”);

next=new Button(“Next”);

add(back);

add(next);

c1=new CardLayout();

p1=new Panel();

p1.setLayout(c1);// Set panel layout to CardLayout

lbl1=new Label(“First”);

lbl2=new Label(“Second”);

lbl3=new Label(“Third”);

lbl4=new Label(“Fourth”);

//First panel

first=new Panel();

first.add(lbl1);

//Second panel

second=new Panel();

second.add(lbl2);

//Third panel

third=new Panel();

155

third.add(lbl3);

//Fourth panel

fourth=new Panel();

fourth.add(lbl4);

//Add panels to the card deck panel

p1.add(“1”,first);

p1.add(“2”,second);

p1.add(“3”,third);

p1.add(“4”,fourth);

add(p1);

}

}

Kết quả chạy chương trình được như sau:

Hình 4.13. Kết quả chạy ví dụ 4.10

Trong hình bên trên, các panel được thêm vào panel chính như là các thẻ riêng biệt. Vì thế chỉ có thẻ đầu tiên mới được thấy trên màn hình. Nhưng người dùng có thể

điều hướng sang các panel khác sử dụng các phương thức của CardLayout.

4.4.4. GridLayout Manager

„GridLayout‟ trợ giúp việc chia Container vào trong ô lưới. Các thành phần được đặt trong các ô giao của dòng và cột. Mỗi lưới nên chứa ít nhất một thành phần. Một lưới được sử dụng khi tất cả các thành phần có cùng kích thước.

GridLayout được tạo như sau:

GridLayout g1=new GridLayout(4,3);

156

4 là số dòng và 3 là số cột.

import java.awt.*;

class GlTest extends Frame

{

Button btn[];

String str[]={“1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”};

public GlTest(String title)

{

super(title);

setLayout(new GridLayout(3,3));

btn=new Button[str.length];

for (int i=0; i

{

btn[i]=new Button(str[i]);

add(btn[i]);

}

}

public static void main(String args[])

{

GlTest t=new GlTest(“Grid Layout”);

t.setSize(300,200);

t.show();

}

}

Ví dụ 4.11: Minh họa cách trình bày lưới:

157

Kết xuất chương trình như sau:

Hình 4.14. Kết quả chạy ví dụ 4.10

4.4.5. GridBagLayout Manager

„GridBagLayout‟ là cách trình bày hiệu quả và phức tạp hơn bất cứ cách trình

bày nào khác. Layout này đặt các thành phần vào vị trí chính xác. Với layout này, các thành phần không cần có cùng kích thước. Nó tương tự như GridLayout manager, khi

các thành phần được sắp xếp trong lưới theo dòng và cột. Tuy nhiên, thứ tự đặt các

thành phần không theo nguyên tắc từ trái sang phải và từ trên xuống dưới.

GridBagLayout gb=new GridBagLayout()

ContainerName.setLayout(gb);

Để sử dụng layout này, bạn cần cung cấp thông tin về kích thước và vị trí của

mỗi thành phần. Lớp „GridBagLayoutConstraints‟ chứa tất cả các thông tin mà lớp

GridLayout cần để bố trí và định kích thước mỗi thành phần. Bảng sau liệt kê danh

sách các biến thành viên của lớp GridBagConstraints:

Mục đích Các biến

thành viên

weightx, Chỉ ra sự phân phối của khoảng trống trong GridBagLayout. Giá

weighty trị mặc định cho các biến này là 0.

gridwidth, gridheight Chỉ ra số lượng các ô (cell) chiều ngang và chiều dọc trong vùng hiển thị của một thành phần.

ipadx, ipady Chỉ ra lượng làm thay đổi chiều cao và chiều rộng tối thiểu của thành phần. Nó sẽ thêm 2*ipadx vào chiều rộng tối thiểu và 2*ipady vào chiều cao tối thiểu của thành phần. Giá trị

mặc định cho cả hai là 0.

anchor

158

Chỉ ra cách sắp xếp các thành phần trong cell. Mặc định sẽ đặt vào giữa cell. Các thành viên dữ liệu tĩnh (static) sau đây có thể

được sử dụng:

GridBagConstraints.NORTH

GridBagConstraints.EAST

GridBagConstraints.WEST

GridBagConstraints.SOUTH

GridBagConstraints.NORTHEAST

GridBagConstraints.SOUTHEAST

gridx, gridy Chỉ ra vị trí cell sẽ đặt thành phần. Khi thiết lập giá trị của gridx

là „GridbagConstraints.RELATIVE‟ thì thành phần được thêm sẽ

nằm ở vị trí bên phải của thành phần cuối cùng.

fill Chỉ ra cách mà một thành phần được bố trí vào cell thế nào nếu

như cell lớn hơn thành phần. Mặc định là kích thước thành phần

không thay đổi.

Bảng 4.4. Các biến thành viên của lớp GridBagConstraints

Bảng sau đây cung cấp một danh sách các biến dữ liệu tĩnh là các giá trị cho biến fill:

Giá trị Mô tả

GridBagConstraints.NONE Mặc định, không làm thay đổi kích thước

của thành phần.

GridBagConstraints.HORIZONTAL Tăng chiều rộng của thành phần theo

chiều ngang (HORIZONTAL) để làm

cho thành phần khớp với chiều ngang.

GridBagConstraints.VERTICAL Tăng chiều cao của thành phần theo chiều

đứng (VERTICAL) để làm cho thành

phần khớp với chiều dọc.

GridBagConstraints.BOTH

Tăng chiều rộng, chiều cao của thành phần theo cả chiều ngang và chiều dọc.

insets

Xác định khoảng cách top, Bottom, left và right giữa các thành phần. Mặc định là

0.

Bảng 4.5. Các biến thành viên dữ liệu tĩnh của biến fill

Sử dụng phương thức „setConstraints()‟ để thiết lập các hằng số cho mỗi thành

159

phần. gblay.setConstraints(lb1, gbc);

„gblay‟ là đối tượng của lớp GridBagLayout, lbl là thành phần „Label‟ và „gbc‟ là

đối tượng của lớp GridBagConstraints.

import java.awt.*;

class GblTest extends Frame

{

TextArea ta;

TextField tf;

Button b1,b2;

CheckboxGroup cbg;

Checkbox cb1,cb2,cb3,cb4;

GridBagLayout gb;

GridBagConstraints gbc;

public GblTest(String title)

{

super(title);

gb=new GridBagLayout();

setLayout(gb);

gbc=new GridBagConstraints();

ta=new TextArea(“Textarea”,5,10);

tf=new TextField(“enter your name”);

b1=new Button(“TextArea”);

b2=new Button(“TextField”);

cbg=new CheckboxGroup();

cb1=new Checkbox(“Bold”, cbg,false);

cb2=new Checkbox(“Italic”, cbg,false);

cb3=new Checkbox(“Plain”, cbg,false);

cb4=new Checkbox(“Bold/Italic”, cbg,true);

gbc.fill=GridBagConstraints.BOTH;

addComponent(ta,0,0,4,1);

160

Ví dụ 4.12: Minh họa một ví dụ của GridBagLayout và GridBagConstraints.

gbc.fill=GridBagConstraints.HORIZONTAL;

addComponent(b1,0,1,1,1);

gbc.fill=GridBagConstraints.HORIZONTAL;

addComponent(b2,0,2,1,1);

gbc.fill=GridBagConstraints.HORIZONTAL;

addComponent(cb1,2,1,1,1);

gbc.fill=GridBagConstraints.HORIZONTAL;

addComponent(cb2,2,2,1,1);

gbc.fill=GridBagConstraints.HORIZONTAL;

addComponent(cb3,3,1,1,1);

gbc.fill=GridBagConstraints.HORIZONTAL;

addComponent(cb4,3,2,1,1);

gbc.fill=GridBagConstraints.HORIZONTAL;

addComponent(tf,4,0,1,3);

}

public void addComponent(Component c, int row, int col, int nrow, int ncol)

{

gbc.gridx=col;

gbc.gridy=row;

gbc.gridwidth=ncol;

gbc.gridheight=ncol;

gb.setConstraints(c,gbc);

add(c);

}

public static void main(String args[])

{

Gbltest t=new Gbltest(“GridBag Layout”);

t.setSize(300,200);

t.show();

}

161

}

Khi một container bị thay đổi kích thước và khi khoảng trắng phụ tồn tại, các

thành phần có chiều rộng lớn hơn sẽ chiếm giữ nhiều khoảng trống hơn là các thành phần có giá trị về chiều rộng nhỏ hơn.

Kết quả chạy chương trình như sau:

Hình 4.15. Kết quả chạy ví dụ 4.12

Giải thích đoạn mã trên:

gbc.fill=GridBagConstraints.BOTH;

Thành viên fill của lớp GridBagConstraints chỉ ra thành phần có thể được mở

rộng theo hướng nằm ngang và thẳng đứng. Cú pháp sau mô tả thành phần chỉ được

mở rộng theo hướng nằm ngang:

gbc.fill=GridBagConstraints.HORIZNTAL;

Cú pháp sau sẽ thêm vào thành phần TextArea với số dòng và số cột cần chiếm:

addComponent(ta,0,2,4,1);

0 – Khởi đầu từ dòng thứ 0

2 – Khởi đầu từ dòng thứ 2

4 – ta chiếm giữ 4 dòng

1 – ta chiếm 1 cột

Sử dụng cú pháp sau để bố trí các thành phần vào trong dòng và cột nào đó:

gbc.gridx=col;

gbc.gridy=row;

Ở đây (gridx,gridy) là cột và dòng nơi mà thành phần có thể được đặt vào.

Sử dụng cú pháp sau để chỉ ra số lượng các cột và dòng mà các thành phần có thể

162

chiếm giữ:

gbc.gridwitdh=ncol;

gbc.gridheight=nrow;

Ở đây, gridwidth xác định số lượng các cột mà một thành phần chiếm giữ và

gridheight xác định số lượng các dòng mà một thành phần chiếm giữ.

Khi một container bị thay đổi kích thước và khi khoảng trắng phụ tồn tại, các

thành phần có chiều rộng lớn hơn sẽ chiếm giữ nhiều khoảng trống hơn là các thành phần có giá trị về chiều rộng nhỏ hơn.

4.5. Xử lý các sự kiện

Các hệ thống GUI xử lý các tương tác người dùng với sự trợ giúp của mô hình

hướng sự kiện (event-driven). Tương tác của người dùng có thể là di chuyển chuột, nhấn phím, nhả phím v.v…Tất cả các thao tác này thiết lập một sự kiện của một loại

nào đó.

Việc xử lý những sự kiện này phụ thuộc vào ứng dụng. Abstract Windowing

Toolkit (AWT) xử lý một vài sự kiện. Môi trường mà các ứng dụng này được thi hành

ví dụ như trình duyệt cũng có thể xử lý các sự kiện khác. Người lập trình cũng cần

phải xử lý những sự kiện nhất định và cần phải viết hàm xử lý các sự kiện đó.

Ứng dụng cần đăng ký một hàm xử lý sự kiện với một đối tượng. Hàm xử lý sự

kiện này sẽ được gọi bất cứ khi nào sự kiện tương ứng phát sinh. JDK1.2 làm việc theo

mô hình xử lý sự kiện này.

Trong quy trình này, ứng dụng cho phép bạn đăng ký các phương thức (handler),

hay gọi là listener với các đối tượng. Những handler này tự động được gọi khi một sự

kiện thích hợp phát sinh.

Một Event Listener lắng nghe một sự kiện nào đó mà một đối tượng đã thiết lập.

Mỗi event listener cung cấp các phương thức xử lý những sự kiện này. Lớp thi hành

listener cần phải định nghĩa những phương thức này. Để sử dụng mô hình này, bạn

làm theo các bước sau:

 Cài đặt giao diện listener thích hợp. Cấu trúc như sau:

public class MyApp extends Frame implements ActionListener

 Xác định tất cả các thành phần tạo ra sự kiện. Các thành phần có thể là các

button, label, menu item, hay window.

Cho ví dụ, để đăng ký một thành phần với listener, ta có thể sử dụng:

163

exitbtn.addActionListener(This);

 Xác định tất cả các sự kiện được xử lý. Các sự kiện có thể là một „ActionEvent‟ nếu một button được click hay một „mouseEvent‟ nếu như chuột được kéo đi.

 Thi hành các phương thức của listener và viết hàm xử lý sự kiện tương ứng

với các phương thức.

Bảng sau đây chỉ ra các sự kiện khác nhau và mô tả về chúng:

Lớp sự kiện Mô tả

ActionEvent Phát sinh khi một button được nhấn, một item trong danh

sách chọn lựa được nhấn đúp (double-click) hay một menu được chọn.

AdjustmentEvent Phát sinh khi một thanh scrollbar được sử dụng.

ComponentEvent Phát sinh khi một thành phần được thay đổi kích thước,

được di chuyển, bị ẩn hay làm cho hoạt động được.

FocusEvent Phát sinh khi một thành phần mất hay nhận focus từ bàn

phím.

ItemEvent Phát sinh khi một mục menu được chọn hay bỏ chọn; hay

khi một Checkbox hay một item trong danh sách được

click.

WindowEvent Phát sinh khi một cửa sổ được kích hoạt, được đóng, được

mở hay thoát.

TextEvent Phát sinh khi giá trị trong thành phần textfield hay

textarea bị thay đổi.

MouseEvent Phát sinh khi chuột di chuyển, được click, được kéo hay

thả ra.

KeyEvent Phát sinh khi bàn phím ấn, nhả.

Bảng 4.6. các sự kiện của AWT

Các giao diện cần được cài đặt để xử lý một trong số những sự kiện này là:

164

 ActionListener  AdjustmentListener  ComponentListener  FocusListener  ItemListener

 WindowListener  TextListener  MouseListener  MouseMotionListener  KeyListener

Các giao diện định nghĩa một số phương thức để xử lý mỗi sự kiện. Những

phương thức này sẽ được nạp chồng trong lớp mà cài đặt những giao diện này.

Chương trình sau đây sử dụng một ActionListener để xử lý các sự kiện liên quan

với một button. ActionEvent có hai phương thức:

 getSource(): Để trả về nguồn của sự kiện.  toString(): Để trả về chuỗi tương đương với sự kiện.

Ví dụ 4.13:Trình bày cách tính gấp đôi của một số được nhập vào. Chương trình

này được thực hiện bằng cách kết hợp các phương thức của lớp, nghĩa là các phương thức xử lý sự kiện và giao diện. Việc click trên một button sẽ làm khởi động

ActionEvent và gọi phương thức actionPerformed(). Nó sẽ kiểm tra button được click

import java.awt.*;

import java.awt.event.*;

class EvtTest extends Frame implements ActionListener

{

Label lab=new Label(“Enter a number”);

TextField tf1=new TextField(5);

TextField tf2=new TextField(5);

Button btnResult=new Button(“Double is”);

Button ext=new Button(“exit”);

public EvtTest(String title)

{

super(title);

setLayout(new FlowLayout());

btnResult.addActionListener(this);

ext.addActionListener(this);

add(lab);

165

với sự trợ giúp của hàm getSource và trả về kết quả thích hợp.

add(tf1);

add(btnResult);

add(tf2);

add(ext);

}

public void actionPerformed(ActionEvent ae)

{

if (ae.getSource()==btnResult)

{

int num=Integer.parseInt(tf1.getText())*2;

tf2.setText(String.valueOf(num));

}

if (ae.getSource()==ext)

{

System.exit(0);

}

}

public static void main(String args[])

{

EvtTest t=new EvtTest(“Event handling”);

t.setSize(300,200);

t.show();

}

}

166

Kết xuất chạy chương trình được như sau:

Hình 4.16. Kết quả chạy ví dụ 4.13

Hình 4.17 sau đây chỉ ra một phần của cây phân cấp các lớp của gói event.

Hình 4.17. Gói Event

167

Hình sau chỉ ra thứ tự phân cấp các giao diện của các event listener.

Hình 4.18. Event Listener

Hình sau là danh sách các listener được sử dụng cho các thành phần chỉ ra.

Hình 4.19. Action Listener

168

Hình 4.20. Item Listener

Hình 4.21. Window Listener

Các listener cho lớp Component được chỉ ra ở hình 4.22:

Hình 4.22. Các Component

4.6. Thực đơn

Ngôn ngữ Java có một tập hợp các lớp đối tượng để tạo các menu. Có hai loại

menu – pull down và pop-up. Menu làm cho ứng dụng ta xây dựng dễ sử dụng hơn. Ta

chỉ có đặt duy nhất một thanh menubar trong một frame. Menubar là một thanh nằm

ngang được đặt tại đỉnh của frame. Nó liệt kê các mục chọn khác nhau hay còn gọi là menu. Một menu độc lập có thể chứa các mục chọn con, các mục con này được gọi là

Menu Item. Java cung cấp các Checkbox MenuItem, chúng có thể được bật hay mở,

phụ thuộc vào trạng thái.

Ví dụ 4.14: Minh họa cách sử dụng của menubar, menu, menuItem, và

import java.awt.*;

import java.awt.event.*;

class MyFrame extends Frame implements ActionListener, MouseListener

{

MenuItem exitItem;

PopupMenu optionsMenu;

Frame frame;

169

CheckboxMenuItem.

public MyFrame()

{

setTitle("Menu Example");

setSize(300,200);

MenuBar mbar=new MenuBar();

setMenuBar(mbar);

Menu fileMenu=new Menu("File");

mbar.add(fileMenu);

fileMenu.addActionListener(this);

MenuItem newItem=new MenuItem("New");

fileMenu.add(newItem);

MenuItem openItem=new MenuItem("Open");

fileMenu.add(openItem);

fileMenu.addSeparator();

MenuItem saveItem=new MenuItem("Save");

fileMenu.add(saveItem);

MenuItem saveAsItem=new MenuItem("Save As");

fileMenu.add(saveAsItem);

fileMenu.addSeparator();

exitItem=new MenuItem("Exit");

fileMenu.add(exitItem);

saveAsItem.addActionListener(this);

Menu editMenu=new Menu("Edit");

mbar.add(editMenu);

editMenu.addActionListener(this);

MenuItem cutItem=new MenuItem("Cut");

editMenu.add(cutItem);

MenuItem copyItem=new MenuItem("Copy");

editMenu.add(copyItem);

MenuItem pasteItem=new MenuItem("Paste");

170

editMenu.add(pasteItem);

editMenu.addSeparator();

Menu helpMenu=new Menu("Help");

mbar.add(helpMenu);

helpMenu.addActionListener(this);

MenuItem contentItem=new MenuItem("Content");

helpMenu.add(contentItem);

MenuItem indexItem=new MenuItem("Index");

helpMenu.add(indexItem);

Menu findMenu=new Menu("Find");

helpMenu.add(findMenu);

addMouseListener(this);

MenuItem nameItem=new MenuItem("Search by Name");

findMenu.add(nameItem);

MenuItem cacheItem=new MenuItem("Search from cache");

findMenu.add(cacheItem);

optionsMenu=new PopupMenu("Options");

editMenu.add(optionsMenu);

optionsMenu.addActionListener(this);

MenuItem readItem=new MenuItem("Read Only");

optionsMenu.add(readItem);

optionsMenu.addSeparator();

Menu formatMenu=new Menu("Format text");

optionsMenu.add(formatMenu);

this.add(optionsMenu);

formatMenu.addActionListener(this);

CheckboxMenuItem insertItem=new CheckboxMenuItem("Insert",true);

formatMenu.add(insertItem);

CheckboxMenuItem overtypeItem=new CheckboxMenuItem("Overtype",false);

formatMenu.add(overtypeItem);

171

}

public void actionPerformed(ActionEvent ae)

{

if (ae.getActionCommand().equals("Exit"))

{

System.exit(0);

}

}

public void mouseEntered(MouseEvent m){}

public void mouseExited(MouseEvent m){}

public void mouseClicked(MouseEvent m)

{

optionsMenu.show(this,m.getX(),m.getY());

}

public void mouseReleased(MouseEvent m){}

public void mousePressed(MouseEvent m){}

public static void main(String[] args)

{

MyFrame frame=new MyFrame();

frame.show();

}

}

Khi thực thi chương trình trên, một màn hình với các trình đơn File, Edit và Help

được hiển thị. Khi click vào mục File, sẽ thấy kết xuất sau đây:

172

Hình 4.23. Kết quả chạy ví dụ 4.14

Một menu có thể chứa các menu con. Khi bạn click vào trình đơn Help, 3 mục

con có tên là Content, Index và Find sẽ xuất hiện. Trong trình đơn Find, có 2 mục con là Search by name và Search from Cache. Mặt khác một pop-up menu sẽ hiện ra nếu

bạn nhấn chuột phải trên màn hình:

Hình 4.24. Pop-up menu

Các mục chọn được trình bày trên pop-up menu là Read-Only và Format text.

Mục „Format text‟ có 2 mục con là Insert và Overtype. Những mục chọn con này

thuộc kiểu CheckboxMenuItem. Khi bạn click vào mục chọn, nó sẽ được đánh dấu và

bạn có thể thấy dấu chọn tương ứng trên mục được chọn đó. Ngôn ngữ Java cung cấp

các lớp khác nhau. Những lớp này được sử dụng để tạo thanh Menubar, Menu,

MenuItem và CheckboxMenuItem trong chương trình.

Câu hỏi và bài tập chƣơng 4

1.Viết chương trình Java để nhập thông tin chi tiết về người sử dụng như hình sau:

173

2.Sửa bài tập 1 để có giao diện như sau:

3.Viết giao diện chương trình trò chơi puzzle như sau:

4.Sửa bài 3 để khi người sử dụng click vào nút cùng hàng hoặc cùng cột với nút không

có số thì đổi chỗ nút vừa click và nút không số cho nhau. Khi người sử dụng ấn nút

không cùng hàng hay không cùng cột thì hiển thông báo "Không hợp lệ" còn khi người sử dụng đã sắp xếp các nút theo thứ tự từ 1 đến 15 thì hiện ra thông báo chúc mừng.

174

5.Viết chương trình có menu pop-up như hình dưới đây:

CHƢƠNG 5: JAVA APPLET VÀ SWING

5.1. Java Applet

5.1.1. Giới thiệu về Applet

Applet là một chương trình Java có thể chạy trong trình duyệt web. Tất cả các

Applet đều là các lớp con của lớp „Applet‟.

Lớp Applet thuộc package „java.applet‟. Lớp Applet bao gồm nhiều phương thức để điều khiển quá trình thực thi của Applet. Để tạo Applet, bạn cần import hai gói

sau:

 java.applet  java.awt

5.1.2. Cấu trúc của một Applet

Một Applet định nghĩa cấu trúc của nó từ 4 sự kiện xảy ra trong suốt quá trình

thực thi. Đối với mỗi sự kiện, một phương thức được gọi một cách tự động. Các phương thức này được minh hoạ trong bảng 5.1

Điều quan trọng là không phải lúc nào Applet cũng bắt đầu từ ban đầu. Mà nó bắt

đầu từ vị trí tiếp theo của quá trình thực thi trước đó.

Ngoài những phương thức cơ bản này, còn có những phương thức „paint()‟ và „rẻ

paint()‟. Phương thức paint() dùng để hiển thị một đường thẳng (line), text, hoặc một

hình ảnh trên nền Applet. Đối số của phương thức này là đối tượng của lớp Graphics.

Lớp này thuộc gói java.awt. Câu lệnh sau được dùng để import lớp Graphics:

import java.awt.Graphics;

Phƣơng Chức năng

thức

init()

Được gọi trong quá trình khởi tạo applet. Trong quá trình khởi tạo, nó sẽ tạo đối tượng để cung cấp cho applet. Phương thức này được dùng để tải các hình ảnh đồ hoạ, khởi tạo các biến và tạo các đối tượng.

start()

Được gọi khi một applet bắt đầu thực thi. Một khi quá trình khởi tạo hoàn tất, thì applet được khởi động. Phương thức này được dùng để khởi động lại applet sau khi nó đã ngừng trước đó

stop()

Được gọi khi ngừng thực thi một applet. Một applet bị ngừng trước khi nó bị hủy.

destroy() Được dùng để hủy một applet. Khi một applet bị hủy, thì bộ nhớ, thời

175

gian thực thi của vi xử lý, không gian đĩa được trả về cho hệ thống.

Bảng 5.1. Các phương thức của một applet

Phương thức „repaint()‟ được dùng khi cửa sổ cần cập nhật lại. Phương thức này

chỉ cần một thông số. Tham số này là đối tượng của lớp Graphics.

Applet sử dụng phương thức „showStatus()‟ để hiển thị thông tin trên thanh trạng

thái. Phương thức có tham số thuộc kiểu dữ liệu String. Để lấy các thông tin của

applet, user có thể overide phương thức „getAppletInfo()‟ của lớp Applet. Phương thức

này trả về 1 đối tượng kiểu String.

Các phương thức của applet init(), start(), stop(), destroy(), và paint() được thừa

kế từ một applet. Nhưng mặc định những phương thức này không thực thi một thao tác

nào cả.

Đây là ví dụ đơn giản của applet. Câu lệnh sau tạo một lớp có tên là „Applet1‟,

lớp này sẽ kế thừa tất cả các phương thức và biến của lớp „applet‟.

public class Applet1 extends Applet

Phương thức init() và paint() thường được dùng để thực hiện một số hàm để khởi

tạo và vẽ applet. Phương thức „g.drawString()‟ chỉ ra vị trí mà đoạn văn bản được vẽ ở

đâu trên màn hình.

import java.awt.*;

import java.applet.*;

public class Applet1 extends Applet

{

int num;

public void init()

{

num = 6;

}

public void paint (Graphics g)

{

g.drawString (“Hello to Applet. Chapter ” + num, 70, 80);

showStatus (getAppletInfo());

//Hiển thị một chuỗi được trả về từ hàm getAppletInfo() trên //thanh trạng

thái

176

Ví dụ 5.1: Hiển thị một chuỗi ở dòng 70 và cột 80:

}

public String getAppletInfo() //user overrides

{

return “Created by Aptech”;

}

}

Sử dụng cú pháp sau để dịch một Applet:

javac Applet1.java

Để thực thi một applet, ta cần tạo một file HTML. File HTML này sử dụng thẻ

applet. Thẻ applet này lấy tham số đầu tiên là đường dẫn của file applet.

Thẻ applet có hai thuộc tính sau:

 Width  Height

Để truyền tham số vào applet, sử dụng param, sau đó là thuộc tính value. Sau đây

là ví dụ của thẻ applet:

Lúc này, ta có thể thực thi applet này bằng cách dùng trình xem applet. Đây là

công cụ của JDK. Để chạy file HTML trong trình xem applet, ta gõ câu lệnh sau:

appletviewer abc.html // „abc.html‟ là tên của file HTML

Một tuỳ chọn khác của applet là ta thêm thẻ applet như là một dòng chú thích

trong đoạn code. Lúc đó, applet được dịch và thực thi bằng cách sử dụng lệnh sau:

appletviewer Applet1.java

177

Sau đây là kết quả của chương trình trên:

Hình 5.1. Kết quả chạy ví dụ 5.1

5.1.3. Chu trình sống của một Applet

Chu trình sống của một Applet được mô tả ở sơ đồ dưới đây:

Destr St oy op

Creati Starti on ng

Initalizati

on Hình 5.2. Chu trình sống của một applet

Trước tiên, applet được tạo.

Bước kế tiếp là khởi tạo. Điều này xảy ra khi một applet được nạp. Quá trình này bao gồm việc tạo các đối tượng mà applet cần. Phương thức init() được định nghĩa đè để cung cấp các hành vi để khởi tạo.

Một khi applet được khởi tạo, applet sẽ được khởi động. Applet có thể khởi động

ngay cả khi nó đã được ngừng trước đó. Ví dụ nếu trình duyệt nhảy đến một liên kết nào đó ở trang khác, lúc đó applet sẽ bị ngừng, và được khởi động trở lại khi người sử

dụng quay về trang đó.

Sự khác nhau giữa quá trình khởi tạo và quá trình khởi động là một applet có thể

178

khởi động nhiều lần, nhưng quá trình khởi tạo thì chỉ xảy ra một lần.

Phương thức „start()‟ được overide để cung cấp các thao tác khởi động cho

applet.

Phương thức „stop()‟ chỉ được gọi khi user không còn ở trang đó nữa, hoặc trang

đó đã được thu nhỏ lại ở dưới thanh taskbar.

Kế tiếp là phương thức „destroy()‟. Phương thức này giúp applet dọn dẹp trước

khi nó được giải phóng khỏi vùng nhớ, hoặc trước khi trình duyệt kết thúc. Phương thức này được dùng để hủy những luồng (thread) hay quá trình đang chạy.

Phương thức „destroy()‟ khác với phương thức finalize() là phương thức

destroy() chỉ dùng cho applet, trong khi finalize() là cách tổng quát để dọn dẹp applet.

Phương thức paint() cũng là một phương thức quan trọng khác. Phương thức này cho phép ta hiển thị một cái gì đó trên màn hình. Có thể là text, đường thẳng, màu nền,

hoặc hình ảnh. Phương thức này xảy ra nhiều lần trong suốt quá trình applet tồn tại.

Phương thức này thực thi một lần sau khi applet được khởi tạo. Nó sẽ lặp đi lặp lại khi di chuyển từ cửa sổ trình duyệt sang cửa sổ khác. Nó cũng xảy ra khi cửa sổ trình

duyệt thay đổi vị trí của nó trên màn hình.

Phương thức „paint()‟ có một tham số. Tham số này là đối tượng của lớp

Graphics. Lớp Graphics thuộc lớp java.awt, chúng ta phải import trong đoạn code của

applet. Chúng ta có thể sử dụng đoạn mã sau:

import java.awt.Graphics;

5.1.4. Truyền tham số cho Applet

Trong chương trình sau, chúng ta sẽ truyền tham số cho applet. Thành phần nút

„bNext‟ có tên được truyền như là một tham số. Phương thức „init()‟ sẽ kiểm tra tham

số có tên là „mybutton‟. Sau đó, nó tạo một nút với chuỗi đó như là tên của nút. Nếu

không có tham số truyền vào, nút đó có tên mặc định là „Default‟.

/*

*/

Bây giờ chúng ta định nghĩa thẻ trong đoạn mã HTML như sau:

import java.awt.*;

179

Ví dụ 5.2: Tạo applet sử dụng truyền tham số

import java.applet.*;

/*

*/

public class Mybutton1 extends Applet

{

Button bNext;

public void init()

{

/*getParameter returns the value of the specified pareameter in the form of a String

object*/

String str = getParameter("mybutton");

//when no parameter is passed

if (str==null)

str = new String ("Default");

//when parameter is passed

bNext = new Button(str);

add (bNext);

}

}

Sau đây là kết quả của chương trình trên:

180

Hình 5.3. Kết quả chạy ví dụ 5.2

Bây giờ chúng ta sẽ sử dụng lớp Graphics để vẽ các hình chẳng hạn như: đường

thẳng, hình oval, và hình chữ nhật. Chúng ta sẽ học lớp Font trong các phần sau. Lớp này có thể dùng để hiển thị văn bản bằng bất cứ font nào.

5.1.5. Lớp Graphics

Java cung cấp gói AWT cho phép ta vẽ các hình đồ hoạ. Lớp Graphics bao gồm

tập hợp rất nhiều phương thức. Nhưng phương thức này được sử dụng để vẽ bất cứ hình nào trong các hình sau:

 Oval  Rectangle  Square  Circle  Lines  Text

Bạn có thể vẽ những hình này bằng bất cứ màu nào. Frame, Applet và Canvas là

các môi trường để hiển thị đồ hoạ.

Để vẽ bất cứ hình ảnh nào chúng ta cần phải có nền đồ hoạ (Graphical

Background). Để có được một nền đồ hoạ, chúng ta gọi phương thức „getGraphics()‟

hay bất cứ phương thức nào trong các phương thức sau đây:

 repaint()

Được gọi khi cần vẽ lại những đối tượng đã vẽ.  update(Graphics g)

Được gọi một cách tự động bởi phương thức „repaint()‟.Phương thức này sẽ xoá

những đối tượng đã vẽ, và truyền nó cho đối tượng của lớp Graphics để gọi phương

thức „paint()‟;

 paint(Graphics g)

Được gọi bởi phương thức update().

Đối tượng được truyền cho phương thức này được dùng để vẽ. Phương thức này

dùng để vẽ các hình ảnh đồ hoạ khác nhau.

Việc gọi phương thức paint() lặp đi lặp lại thông qua phương thức repaint() sẽ xoá đi các hình đã vẽ trước đó. Để vẽ các hình mới mà vẫn giữ lại những hình đã vẽ trước đó, chúng ta cần override lại phương thức update().

public void update (Graphics g)

{

paint (g);

181

}

Ở đây, phương thức update() sẽ không xoá những đối tượng đã vẽ, nhưng chỉ gọi

phương thức paint(). Để làm được điều này, nó truyền đối tương của lớp Graphics hoặc GraphicsContext cho phương thức paint(). Ở đây, đối tượng của lớp Graphics là

„g‟.

a) Vẽ các chuỗi, các ký tự và các byte

Để vẽ hoặc in một chuỗi, lớp Graphics cung cấp phương thức „drawString()‟. Cú

pháp như sau:

drawString (String str, int xCoor, int yCoor);

Ba tham số là:  Chuỗi cần vẽ.  Tọa độ X trên frame, nơi chuỗi cần được vẽ.  Tọa độ Y trên frame, nơi chuỗi cần được vẽ.

Để vẽ hoặc xuất các ký tự trên frame, lớp Graphics cung cấp phương thức

„drawChars‟. Cú pháp như sau:

drawChars (char array[], int offset, int length, int xCoor, int yCoor);

Chú thích các tham số:

 Mảng các ký tự.  Vị trí bắt đầu, nới các ký tự được vẽ.  Số các ký tự cần được vẽ.  Tọa độ X, nơi các ký tự cần được vẽ.  Tọa độ Y, nơi các ký tự cần được vẽ.

Lớp Graphics cung cấp phương thức „drawBytes()‟ để vẽ hoặc in các byte ra

frame. Cú pháp của phương thức này như sau:

drawBytes (byte array[], int offset, int length, int xCoor, int yCoor);

5 tham số của phương thức trên là:

 Mảng các byte.  Vị trí offset hay vị trí bắt đầu.  Số byte cần vẽ.  Tọa độ X.  Tọa độ Y.

Đối với một ký tự hoặc một mảng các byte, chúng ta có thể in một phần của

mảng mà thôi. Ở đây, tọa độ x và y là tọa độ tính theo dòng.

import java.awt.*;

public class DrawStrings extends Frame

{

182

Ví dụ 5.3:Minh hoạ cách vẽ chuỗi, các ký tự và các byte.

public DrawStrings()

{

super ("Draw strings, characters, bytes");

setSize (300, 300);

setVisible (true);

}

public void paint(Graphics g)

{

g.drawString ("Good Morning", 50, 50);

g.drawString ("Good Afternoon", 50, 75);

g.drawString ("Good Night", 50, 100);

char ch[] = {'a','b','c','d','e','f'};

g.drawChars(ch,2,4,50,125);

byte b[] = {100,101,102,103,104,105,106,107};

g.drawBytes(b,1,6,50,150);

}

public static void main (String args[])

{

new DrawStrings();

}

}

Chương trình trên vẽ chuỗi, ký tự từ một mảng ký tự, và vẽ các byte từ mảng các

byte. Bạn phải import gói java.awt để sử dụng các đối tượng đồ hoạ có sẵn trong gói này. Ta phải làm điều này vì lớp Graphics nằm trong gói này.

183

Sau đây là kết quả của chương trình trên:

Hình 5.4. Kết quả chạy ví dụ 5.3

b) Vẽ đƣờng thẳng (Line) và Oval

Sau đây là cú pháp của các phương thức được sử dụng để vẽ đường thẳng và hình

oval:

 drawLine (int x1, int y1, int x2, int y2);  drawOval (int xCoor, int yCoor, int width, int height);  setColor (Color c);  fillOval (int xCoor, int yCoor, int width, int height);

Phương thức „drawLine()‟ nhận các tham số sau:

 Tọa độ X, nơi bắt đầu vẽ (x1).  Tọa độ Y, nơi bắt đầu vẽ (y1).  Tọa độ X, nơi kết thúc vẽ (x2).  Tọa độ Y, nơi kết thúc vẽ (y2).

Phương thức này bắt đầu vẽ tại tọa độ „x1‟ và „y1‟, và kết thúc tại toa độ „x2‟ và

„y2‟. Để vẽ nhưng đường thẳng có màu, chúng ta thiết lập một màu nào đó. Phương

thức „setColor‟ dùng để thiết lập màu cho hình ảnh đồ hoạ. Trong chương trình này, chúng ta sử dụng câu lệnh sau để chọn màu xanh:

g.setColor (Color.blue);

Phương thức „drawOval()‟ nhận 4 thông số sau:

 Tọa độ X.  Tọa độ Y.  Chiều rộng của hình Oval.  Chiều cao của hình Oval.

Đối với hình oval rộng, thì giá trị của chiều rộng lớn hơn chiều cao, và ngược lại

đối với hình oval cao.

Phương thức „fillOval()‟ nhận 4 thông số, nhưng nó sẽ tô hình oval. Sử dụng

phương thức setColor để đặt màu tô.

g.setColor(Color.cyan);

Ở đây, hình oval sẽ được tô với màu cyan. Lớp Color cung cấp các màu khác

184

nhau mà hệ thống có hỗ trợ.

c) Vẽ hình chữ nhật (Rectangle) và hình chữ nhật bo góc (Rounded

Rectangle)

Sau đây là cú pháp của các phương thức được dùng để vẽ hình chữ nhật và hình

chữ nhật bo góc:

 drawRect (int xCoor, int yCoor, int width, int height);  fillRect (int xCoor, int yCoor, int width, int height);  drawRoundRect (int xCoor, int yCoor, int width, int height, int arcwidth, int

archeight);

 fillRoundRect (int xCoor, int yCoor, int width, int height, int arcwidth, int

archeight);

Phương thức „drawRect()‟ được dùng để vẽ hình chữ nhật đơn giản. Phương thức

này nhận 4 tham số sau:

 Tọa độ X của góc trên bên trái  Tọa độ Y của góc trên bên trái  Chiều rộng của hình chữ nhật  Chiều cao của hình chữ nhật

Phương thức này vẽ hình chữ nhật có chiều rộng và chiều cao cho trước, bắt đầu

tại tọa độ X, Y. Chúng ta có thể thiết lập màu của hình chữ nhật. Ở đây, chúng ta chọn

màu đỏ. Câu lệnh sẽ như sau:

g.setColor (Color.red);

Phương thức „drawRoundRect()‟ vẽ hình chữ nhật có các góc tròn. Phương thức

này nhận 6 tham số, trong đó 4 tham số đầu thì giống với phương thức drawRect. Hai

tham số khác là:

 arcwidth của hình chữ nhật  archeight của hình chữ nhật

Ở đây, „arcwidth‟ làm tròn góc trái và góc phải của hình chữ nhật. „archeight‟ làm tròn góc trên đỉnh và góc đáy của hình chữ nhật. Ví dụ, arcwidth = 20 có nghĩa là hình chữ nhật được làm tròn cạnh trái và cạnh phải mỗi cạnh 10 pixel. Tương tự, archeight = 40 sẽ tạo ra hình chữ nhật được làm tròn từ đỉnh đến đáy 20 pixel.

Pixel là đơn vị đo. Nó là đơn vị nhỏ nhất trong vùng vẽ.

185

Để tô hay vẽ hình chữ nhật và hình chữ nhật bo góc, chúng ta sử dụng phương thức „fillRect()‟ và „fillRoundRect()‟. Những phương thức này nhận các tham số giống với phương thức drawRect() và drawRoundRect(). Những phương thức này vẽ các

hình ảnh với một màu cho trước hoặc mới màu hiện hành. Lệnh sau dùng để vẽ hình

với màu xanh:

g.setColor(Color.green);

d) Vẽ hình chữ nhật 3D và vẽ hình cung (Arc)

Sau đây là cú pháp của các phương thức dùng để vẽ hình chữ nhật 3D và hình

cung:

 draw3Drect (int xCoord, int yCoord, int width, int height, boolean raised);  drawArc(int xCoord, int yCoord, int width, int height, int arcwidth, int

archeight);

 fillArc(int xCoord, int yCoord, int width, int height, int arcwidth, int

archeight);

Phương thức „draw3Drect()‟ nhận 5 tham số. 4 tham số đầu thì tương tự với

phương thức để vẽ hình chữ nhật. Tuy nhiên, giá trị của tham số thứ 5 quyết định là

hình chữ này có 3 chiều hay không. Tham số thứ 5 có kiểu dữ liệu là boolean. Giá trị

này True có nghĩa là hình chữ nhật là 3D.

Phương thức „drawArc()‟ nhận 6 tham số sau:

 Tọa độ x  Tọa độ y  Chiều rộng của cung được vẽ.  Chiều cao của cung được vẽ.  Góc bắt đầu.  Độ rộng của cung (góc của cung) so với góc ban đầu.

Phương thức „fillArc()‟ cũng nhận 6 tham số giống như phương thức drawArc(),

nhưng nó vẽ cung và tô cung với màu hiện thời.

e) Vẽ hình PolyLine

Chương trình sau lấy các điểm từ hai mảng để vẽ một loạt các đường thẳng.

Cú pháp của phương thức này như sau:

 drawPolyline (int xArray[], int yArray[], int totalPoints);  g.setFont (new Font(“Times Roman”, Font.BOLD, 15));

Phương thức „drawPolyline()‟ nhận 3 tham số sau:

186

 Mảng lưu trữ tọa độ x của các điểm.  Mảng lưu trữ toa độ y của các điểm.  Tổng số điểm cần vẽ.

Để vẽ các đường thẳng ta lấy các điểm từ hai mảng như sau:

(array1[0], array2[0]) (array1[1], array2[1]) (array1[2], array2[2])….

Số đường thẳng vẽ được luôn nhỏ hơn số truyền vào thông số thứ 3 của phương

thức drawPolyline(). Ví dụ như: totalPoints - 1

import java.awt.*;

class PolyLines extends Frame

{

int x1[] = {50, 75, 95, 115, 135};

int y1[] = {50, 30, 60, 75, 60};

int x2[] = {67, 82, 95, 120, 135};

int y2[] = {150, 130, 160, 155, 180};

public PolyLines()//constructor

{

super ("Poly Lines");

setSize (300, 300);

setVisible (true);

}

public void paint (Graphics g)

{

g.drawPolyline (x1, y1, 5);

g.setFont (new Font("Times Roman", Font.BOLD, 15));

g.drawString("Current Color is black", 100, 100);

g.setColor(Color.blue);

g.drawPolyline (x2, y2, 5);

g.drawString ("Current Color is blue", 100, 200);

}

public static void main (String args[])

{

new PolyLines();

187

Ví dụ 5.4:Minh hoạ các vẽ polyline.

}

}

Kết quả của chương trình như sau:

Hình 5.5. Kết quả chạy ví dụ 5.4

f) Vẽ và tô đa giác (Polygon)

Lớp Graphics cung cấp hai phương thức để vẽ đa giác. Phương thức đầu tiên

nhận một đối tượng của lớp Polygon. Phương thức 2 lấy hai mảng điểm, và tổng số

điểm cần vẽ. Chúng ta sẽ sử dụng phương thức 2 để vẽ đa giác.

Cú pháp của drawPolygon() như sau:

drawPolygon(int x[], int y[], int numPoints);

Cú pháp của fillPolygon() như sau:

fillPolygon (int x[], int y[], int numPoints);

Chương trình dưới đây lấy các điểm từ 2 mảng để vẽ đa giác. Phương thức

„drawPolygon()‟ nhận 3 tham số sau giống như phương thức drawPolyline()

 Mảng lưu trữ tọa độ x của các điểm.  Mảng lưu trữ tọa độ y của các điểm.  Tổng số điểm cần vẽ.

import java.awt.*;

class PolyFigures extends Frame

{

int x1[] = {50, 25, 40, 100, 80};

188

Ví dụ 5.5: Sử dụng lệnh vẽ ra các đa giác

int x2[] = {80, 30, 50, 150, 100, 170};

int y1[] = {50, 70, 120, 120, 80};

int y2[] = {150, 170, 200, 220, 240,190};

public PolyFigures()

{

super ("Poly figures");

setSize(300, 300);

setVisible (true);

}

public void paint (Graphics g)

{

g.drawPolygon (x1, y1, 5);

g.setColor (Color.cyan);

g.fillPolygon (x2, y2, 6);

}

public static void main (String args[])

{

new PolyFigures();

}

}

Sau đây là kết quả của chương trình trên:

189

Hình 5.6. Kết quả chạy ví dụ 5.5

5.1.6. Điều khiển màu

Trong Java, chúng ta điều khiển màu bằng cách dùng 3 màu chính là đỏ (red), xanh lá cây (green), xanh dương (blue). Java sử dụng mô hình màu RGB. Đối tượng

của lớp Color chứa 3 số nguyên cho các tham số red, green, blue. Bảng sau trình bày

giá trị có thể có của các màu đó:

Thành phần Phạmvi

Red 0-255

Green 0-255

Blue 0-255

Bảng 5.2. Phạm vi giá trị của các thành phần màu

Sử dụng các giá trị trên để tạo ra một màu tuỳ thích. Cú pháp của hàm dựng để

tạo ra một màu như sau:

color (int red, int green, int blue);

Bảng sau hiển thị các giá trị của các màu thường gặp:

Màu Red Green Blue

White 255 255 255

Light Gray 192 192 192

Gray 128 128 128

64 Dark Gray 64 64

0 Black 0 0

Pink 255 175 175

0 Orange 255 200

0 Yellow 255 255

Magenta 255 0 255

Bảng 5.3. Các giá trị RGB

Các đối tượng màu khác nhau có thể được tạo bằng những giá trị này. Những đối

này có thể được dùng để vẽ hoặc tô các đối tượng đồ hoạ. Ví dụ, để tạo màu hồng, ta dùng lệnh sau:

color c = new Color (255, 175, 175);

190

Ta có thể thiết lập màu bằng cách dùng lệnh sau:

g.setColor (c); //g là đối tượng của lớp Graphics

Sử dụng kết hợp các giá trị RGB để tạo ra một màu tuỳ ý. Để cho dễ hơn, lớp

Color cung cấp sẵn một số màu.

color.white color.black

color.orange color.gray

color.lightgray color.darkgray

color.red color.green

color.blue color.pink

color.cyan color.magenta

color.yellow

Bảng 5.4. Các màu thường gặp

Color color1 = new Color (230, 140, 60);

Color color4 = new Color (90, 210, 130);

g.setColor (color1);

int myred = color1.getRed ();

int mygreen = color1.getGreen ();

int myblue = color1.getBlue();

color1 = color1.darker();

color4 = color4.brighter();

Đoạn mã sau minh hoạ cách tạo một màu tuỳ ý:

5.1.7. Điều khiển Font

Java cung cấp lớp Font trong gói java.awt cho phép sử dụng các loại font khác

nhau. Lớp này bao gồm một số phương thức.

Để sử dụng font, chúng ta nên kiểm tra xem hệ thống có hỗ trợ hay không.

Phương thức „getAllFont()‟ trả về tất cả các font mà hệ thống hỗ trợ.

Trước tiên, khai báo một đối tượng của lớp GraphicsEnvironment như sau:

GraphicsEnvironment ge;

ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();

Đối tượng này sử dụng cú pháp sau để lấy tất cả các font có trong mảng Font:

191

Font f[] = ge.getAllFonts();

Phương thức getAllFont() được sử dụng ở đây. Phương thức getAllFonts() thuộc

lớp GraphicsEnvironment. Đây là lớp trừu tượng, do đó ta không thể khởi tạo lớp này. thức để thức getAllFont(), chúng ta sử dụng phương truy cập phương

„getLoacalGraphicsEnvironment()‟ của lớp GraphicsEnvironment.

ge = GraphicsEnvironment.getLocalGraphicsEnvironment ();

Tham chiếu đến lớp này được gán cho biến ge. Biến này gọi phương thức getAllFont(). Chúng ta sử dụng các font khác nhau để hiển thị các chuỗi khác nhau.

Phương thức getFont() trả về font mặc định dùng để hiển thị chuỗi, khi không có chọn

font nào cả.

Font defaultFont = g.getFont (); //g là đối tƣợng Graphics

g.drawString (“Default Font is ”, 30, 50);

Dialog là font mặc định của hệ thống.

Để thay đổi font mặc định của hệ thống thành font khác, chúng ta tạo đối tượng

của lớp Font. Phương thức khởi tạo của Font có 3 tham số sau:

 Tên của font. Ta có thể lấy tên thông qua phương thức getFontList().  Kiểu của font. Ví dụ: Font.BOLD, Font.PLAIN, Font.ITALIC.  Kích thước font.

Cú pháp sau minh hoạ những thông số trên:

Font f1 = new Font (“SansSerif”, Font.ITALIC, 16);

g.setFont (f1);

Ba tham số được truyền ở đây là: „SanSerif‟ – tên của font, Font.BOLD – kiểu

font, 14 là kích thước của font. Những thông số này tạo ra đối tượng f1. Chúng ta có

thể kết hợp 2 kiểu font lại với nhau. Hãy xét ví dụ sau:

Font f3 = new Font (“Monospaced”, Font.ITALIC+Font.BOLD, 20);

Ở đây kiểu font của f3 vừa đậm, vừa nghiêng.

5.1.8. Lớp FontMetrics

Lớp này xác định kích thước của các ký tự khác nhau thuộc các loại font khác nhau. Xác định kích thước bao gồm chiều cao (height), baseline, descent, và leading.

Điều này rất cần thiết vì các ký tự khi in đều chiếm một kích thước riêng. Bạn cần tính kích thước cần thiết khi in các ký tự để tránh các ký tự đè lên nhau.

192

 Height: chiều cao của font.

 Baseline (Dòng cơ sở): xác định cơ sở của các ký tự (không kể phần thấp

nhất của ký tự)

 Ascent: khoảng cách từ đường baseline đến đỉnh của ký tự.  Descent: khoảng các từ baseline đề đáy của ký tự.  Leading: khoảng cách giữa các chữ.

Ví dụ 5.6 sau minh hoạ việc sử dụng các phương thức khác nhau mà lớp FontMetrics có. Trong chương trình này, chúng ta sử dụng các phương thức khác nhau

để xem xét chi tiết các loại font khác nhau. Lớp FontMetric là lớp trừu tượng. Phương

thức getFontMetrics() có tham số là đối tượng của lớp Font, vì FontMetrics đi đôi với

một font nào đó.

FontMetrics fm = g.getFontMetrics (f1);

Lệnh này tạo đối tượng fm của lớp FontMetrics, cùng với đối tượng f1. Bây giờ,

chúng ta sử dụng fm để lấy chi tiết của font.

Các phương thức getHeight, getAscent(), getDescent(), và getLeading() trả về chi

tiết của font. Phương thức getFont() của lớp FontMetrics trả về Font mà gắn với đối

tượng lớp FontMetrics. Phương thức getName() của lớp Font trả về tên Font.

import java.awt.*;

class FontMetricsUse extends Frame

{

public FontMetricsUse()

{

super ("Detail of Fonts");

setSize (400, 300);

setVisible(true);

}

public void paint (Graphics g)

{

Font f1 = new Font ("Times Roman", Font.PLAIN, 22);

FontMetrics fm = g.getFontMetrics (f1);

String name = fm.getFont().getName();

g.drawString ("Details of Font " + name, 30, 50);

193

Ví dụ 5.6: Sử dụng các phương thức khác nhau mà lớp FontMetrics

g.drawString ("Height: " + String.valueOf (fm.getHeight()), 50, 75);

g.drawString ("Ascent: " + String.valueOf (fm.getAscent()), 50, 100);

g.drawString ("Descent: " + String.valueOf (fm.getDescent()), 50,

125);

g.drawString ("Leading: " + String.valueOf (fm.getLeading()), 50,

150);

Font f2 = new Font ("DialogInput", Font.PLAIN, 22);

fm = g.getFontMetrics (f2);

name = fm.getFont().getName();

g.drawString ("Details of Font " + name, 30, 175);

g.drawString ("Height: " + String.valueOf (fm.getHeight()), 50, 200);

g.drawString ("Ascent: " + String.valueOf (fm.getAscent()), 50, 225);

g.drawString ("Descent: " + String.valueOf (fm.getDescent()), 50,

250);

g.drawString ("Leading: " + String.valueOf (fm.getLeading()), 50,

275);

}

public static void main (String args[])

{

new FontMetricsUse ();

}

}

Kết quả của chương trình trên:

194

Hình 5.7. Kết quả chạy ví dụ 5.6

Ví dụ 5.7 minh hoạ cách lớp FontMetrics được sử dụng để in đoạn văn bản nhiều

font, nhiều dòng. Trong chương trình này, chúng ta cần in văn bản nhiều font trên nhiều dòng. Lớp FontMetrics giúp ta xác định khoảng cách cần thiết để in một dòng

văn bản cho một font nào đó. Điều này thật cần thiết, bởi vì dòng thứ 2 được in ngay

sau dòng thứ nhất.

Trước tiên chúng ta in msg1 sử dụng font Monospaced. Sau đó, chúng ta in msg2 sử dụng font DiaglogInput. Để làm được điều này, chúng ta cần tính khoảng cách cần

thiết để in msg1. Phương thức stringWidth() của lớp FontMetrics được dùng để tính ra

tổng khoảng cách cần thiết để in msg1. Khi chúng cộng thêm khoảng cách này vào

biến x, chúng ta sẽ lấy được vị trí mà chúng ta bắt đầu in đoạn văn bản kế tiếp, msg2. Phương thức setFont() được dùng để thiết lập font để in văn bản.

Kế đó, chúng ta in msg1 và msg2 trên các dòng khác nhau sử dụng chung 1 font

Monospaced. Ở đây, chúng ta cần biết khoảng cách chiều cao của font, để in dòng kế

tiếp. Phương thức getHeight() được dùng để làm điều này.

Ví dụ 5.7: Minh hoạ cách lớp FontMetrics được sử dụng để in đoạn văn bản

import java.awt.*;

class MultiFontMultiLine extends Frame

{

public MultiFontMultiLine()

{

super ("Multiline Text");

setSize (450, 200);

setVisible (true);

}

public void paint (Graphics g)

{

Font f1 = new Font ("MonoSpaced", Font.BOLD, 18);

Font f2 = new Font ("DialogInput", Font.PLAIN, 14);

int x = 20;

int y = 50;

String msg1 = "Java Language";

195

nhiều font, nhiều dòng.

String msg2 = "A new approach to programming";

FontMetrics fm = g.getFontMetrics(f1);

g.setFont(f1);

g.drawString (msg1, x, y);

x = x + fm.stringWidth(msg1);

g.setFont(f2);

g.drawString (msg2, x, y);

g.setFont(f1);

y = 100;

x = 20;

int height = fm.getHeight();

g.drawString (msg1, x, y);

y += height;

g.drawString (msg2, x, y);

}

public static void main (String args[])

{

new MultiFontMultiLine ();

}

}

Kết quả của chương trình trên:

Hình 5.8. Kết quả chạy ví dụ 5.7

5.1.9. Chọn chế độ để vẽ

196

Các đối tượng được vẽ bằng cách sử dụng mode vẽ. Khi một đối tượng mới được vẽ, nó sẽ đè lên các hình đã vẽ trước đây. Tương tự, khi các đối tượng được vẽ đi vẽ

lại nhiều lần thì chúng sẽ xoá các đối tượng đã vẽ trước đó. Chỉ hiển thị nội dung của

đối tượng mới. Để làm cho nội dung của và nội dung mới đều hiển thị trên cùng nền, lớp Graphics cung cấp phương thức setXORMode (Color c);

Ví dụ5.8: minh hoạ tiện lợi của việc sử dụng phương thức setXORMode(). Ở

đây, chúng ta sử dụng phương thức setXORMode() để tô các hình đồ hoạ khác nhau,

mà không đè lên các hình khác. Kết quả là, khi sử dụng mode XOR thì hiển nhiên là tất cả các hình đều hiển thị đầy đủ. Điều này có nghĩa là các hình mới không đè lên các

hình cũ. Thay vào đó, phần chung giữa các hình sẽ được hiển thị thành một màu khác.

Nhưng khi không sử dụng mode XOR, hình mới hoàn toàn che khuất những hình trước

import java.awt.*;

class PaintMode extends Frame

{

public PaintMode()

{

super ("Paint Mode");

setSize (300, 300);

setVisible (true);

}

public void paint (Graphics g)

{

g.setPaintMode ();

g.setColor (Color.blue);

g.fillRect (50,50,40, 30);

g.setColor (Color.pink);

g.fillOval (70, 65, 30, 60);

g.setColor (Color.cyan);

g.fillRoundRect (90, 80, 70, 50, 20, 30);

g.setColor (Color.blue);

g.fillRect (50, 150, 40, 30);

g.setXORMode (Color.yellow);

g.fillOval (70, 165, 30, 60);

197

đó.

g.setXORMode (Color.magenta);

g.fillRoundRect (90, 180, 60, 40, 50, 20);

}

public static void main (String args[])

{

new PaintMode();

}

}

Kết quả của chương trình trên:

Hình 5.9. Kết quả chạy ví dụ 5.8

5.2. Java Swing

5.2.1 Giới thiệu về Swing

Phần trên chúng ta đã xây dựng giao diện người dùng với các lớp AWT. Bây giờ chúng ta sẽ xem xét khái niệm Swing. Swing là một bộ các lớp cung cấp các thành phần mạnh và linh hoạt hơn AWT. Ngoài các thành phần quen thuộc như nút nhấn, hộp kiểm tra và nhãn, Swing còn hỗ trợ các bảng thẻ, bảng cuộn, cây và bảng. Chính các thành phần quen thuộc như các nút nhấn có nhiều khả năng hơn trong Swing. Ví dụ, một nút nhấn có thể có hình ảnh và một chuỗi văn bản tương ứng với nó. Hình ảnh này có thể được thay đổi khi trạng thái của nút nhấn thay đổi.

Không giống các thành phần AWT, các thành phần Swing không thi hành bằng đoạn mã đặc tả nền. Thay vì thế chúng được viết hoàn toàn bằng Java, vì thế là độc lập với hệ thống.

Các lớp và giao diện trong Swing quan trọng, và chương này trình bày một vài khía cạnh tổng quát. Swing là một lãnh vực mà bạn muốn tìm hiểu nhiều hơn theo cách riêng của bạn.

Các lớp thành phần Swing được sử dụng trong cuốn sách này được trình bày như

198

sau:

Lớp Diễn giải

AbstractButton Tách các phân lớp cho các nút nhấn Swing

ButtonGroup Tóm lược một bộ các nút nhấn loại trừ lẫn nhau

ButtonGroup Tóm lược một bộ các nút nhấn loại trừ lẫn nhau

ImageIcon Tóm lược một biểu tượng

JApplet Phiên bản Swing của Applet

Jbutton Lớp nút nhấn Swing

JcheckBox Lớp hộp kiểm tra Swing

JComboBox Tóm lược một combo box (sự kết hợp của danh sách

thả xuống và văn bản )

JLabel Phiên bản Swing của nhãn

JRadioButton Phiên bảng Swing của radio

JScrollPane Tóm lược một cửa sổ cuộn

JTabbedPane Tóm lược một cửa số thẻ Jtable Tóm lược bộ điều

khiển bảng cơ sở

JTextField Phiên bảng swing của văn bản

JTree Tóm lược sự điều khiển cây cơ sở

Bảng 5.5. Các lớp thành phần Swing

Các lớp quan hệ Swing được lưu trong javax.swing và các gói của nó như

javax.swing.tree. Nhiều lớp quan hệ Swing và giao diện tồn tại mà không được xem

xét trong chương này. Phần còn lại của chương trình này xem xét các thành phần

Swing khác nhau và minh họa chúng thông qua các applet.

5.2.2. Các biểu tƣợng và nhãn

Trong Swing, biểu tượng được tóm lược bởi lớp ImageIcon, vẽ một biểu tượng từ

một hình ảnh. Hai construstor của nó như sau:

 ImageIcon (String filename)  ImageIcon (URL url)

Hàm thứ nhất sử dụng hình ảnh trong tập tin là filename.

tên Hàm thứ hai sử dụng hình ảnh trong resource được nhận biết bằng url.

199

Hàm Diễn giải

Int getIconHeight() Trả về số pixel là chiều cao của biểu tượng

Int getIconWeight() Trả về số pixel là chiều rộng của biểu tượng

Int paintIcon(Component Vẽ biểu tượng tại vị trí x,y trên đồ họa g.

thông tin về thao tác vẽ được cung cấp trong comp. Graphics g, int x, comp. int y)

Bảng 5.6. Các hàm sử dụng của JLabel

Các nhãn Swing là trường hợp của lớp JLabel, thừa kế từ Jcomponent. Nó có thể hiển thị văn bản và hay một biểu tượng. Các constructor của nó như sau:

 JLabel(Icon i)

 JLabel (String s)

 JLabel (String s, int align)

Ở đây, s và i là văn bản và biểu tượng sử dụng cho nhãn. Thông số align là LEFT, RIGHT hay CENTER. Các hằng số này được định nghĩa trong giao diện SwingConstants, cùng với những cái khác sử dụng bởi các lớp Swing. Biểu tượng và văn bản tương ứng với nhãn có thể đọc và ghi bằng các hàm sau:

 Icon getIcon()  String getText()  Void setIcon(Icon i)  Void setText(String s)

Ở đây, i và s là biểu tượng và văn bản.

import javax.awt.*;

import javax.swing.*;

public class JLabelDemo extends Japplet {

public void init() {

Container contentPane= getContentPane();

ImageIcon ii=new ImageIcon("france.gif");

Jlabel jl=new Jlabel ("France",ii,

Jlabel,CENTER);

contentPane.add(ji);

}

}

Ví dụ 5.9: Tạo ra một nhãn sử dụng Swing

5.2.3. Các trƣờng văn bản

Các trường văn bản Swing được tóm lược bởi lớp JTextCompnent, thừa kế từ

200

JComponent. Nó cung cấp các chức năng chung cho các thành phần văn bản Swing.

Một lớp con của nó là JTextField, cho phép bạn chỉnh sửa một dòng của văn bản. Các

constructor của nó là:

 JTextField()  JTextField(int cols)  JTextField(String s, int cols)  JTextField(String s)

Ở đây, s là chuỗi đang được xét, và cols là số cột trong trường văn bản.

Ví dụ 5.10 minh họa cách tạo một trường văn bản. Applet bắt đầu bằng cách lấy

content pane của nó, và sau đó một sắp xếp được ấn định như layout manager của nó.

import javax.awt.*;

import javax.swing.*;

public class JTextFieldDemo extends JApplet {

JTextField jtf;

public void init() {

Container contentPane= getContentPane();

contentPane.setLayout(new FlowLayout());

jtf=new JTextField(15);

contentPane.add(jtf);

}

}

Kế tiếp một đối tượng JTextField được tạo và được gắn vào content pane.

5.2.4. Nút nhấn

Các nút nhấn Swing cung cấp các đặc tính mà không tìm thấy trong lớp Button

được định nghĩa bởi AWT. Ví dụ, bạn có thể kết hợp một biểu tượng với một nút nhấn

Swing. Nút nhấn Swing là phân lớp của lớp AbtractButton, là lớp thừa kế từ

Jcomonent. AbtractButton, gồm nhiều hàm cho phép bạn điều khiển hoạt động của các

nút nhấn, hộp kiểm tra, và nút radio. Ví dụ, bạn có thể định nghĩa các biểu tượng khác nhau để hiển thị cho các thành phần khi nó được kích hoạt, được nhấn hay được chọn. Biểu tượng khác có thể được sử dụng như một biểu tượng "di động qua", nó được hiển thị khi chuột di chuyển đến thành phần đó. Sau đây là các hàm điều khiển các hoạt

động này:

201

 void setDisabledIcon(Icon di)  void setPressadIcon(Icon pi)  void setSelectedIcon(Icon si)  void setRolloverIcon(Icon ri)

Ở đây, di, pi, si và ri là các biểu tượng được sử dụng cho các điều kiện khác

nhau.

Văn bản tương ứng với một nút nhấn có thể đọc hay ghi thông qua các hàm sau:

 String getText()  void setText(String s)

Ở đây, s là văn bản ứng với nút nhấn.

Phân lớp cụ thể của AbstractButton tạo ra các sự kiện khi chúng được nhấn. Bộ

theo dõi ghi và tháo bỏ các sự kiện của chúng thông qua các hàm sau:

void addActionListener(ActionListener al)

void removeActionListener(ActionListener al)

Ở đây, al là bộ theo dõi hoạt động.

AbstractButotn là lớp con của nút nhấn, hộp kiểm tra và nút radio. Mỗi trường

hợp sẽ được xem xét kế tiếp.

5.2.5. Lớp JButton

Lớp JButton cung cấp chức năng của một nút nhấn. JButton cho phép một biểu

tượng, một chuỗi, hay cả hai tương ứng với nút nhấn. Các constructor của nó là:

 JButton(Icon i)  JButton(String s)  JButton(String s, Icon i)

Ở đây s và i là chuỗi và biểu tượng sử dụng cho nút nhấn.

5.2.6. Hộp kiểm tra

Lớp JcheckBox cung cấp chức năng của một hộp kiểm tra, là một lớp con cụ thể

của AbstractButton. Các constructor của nó là: JCheckBox(Icon i)

 JCheckBox(Icon I, boolean state)  JCheckBox(String s)  JCheckBox(String s, boolean state)  JCheckBox(String s, Icon i)  JCheckBox(String s, Icon I, boolean state)

Ở đây, i là biểu tượng của nút nhấn. Văn bản được xác định bởi s. nếu state là

true, lúc đầu hộp kiểm tra sẽ được chọn. Ngược lại, nó sẽ không được chọn.

Trang thái của hộp kiểm tra có thể thay đổi thông qua hàm sau: void

202

setSelacted(boolean state)

Ở đây state là true nếu hộp kiểm tra sẽ được đánh dấu.

5.2.7. Radio Button

Radio button được cung cấp bởi lớp JradioButton, là sự thi hành cụ thể của

AbstractButton. Các constructor của nó là: JRadioButton(Icon i)

 JRadioButton(Icon I, boolean state)  JRadioButton(String s)  JRadioButton(String s boolean state)  JRadioButton(String s, Icon i)  JRadioButton(String s, Icon I, boolean state)

Ở đây i là biểu tượng của button. Văn bản được xác định bởi s. Nếu state là true,

lúc đầu button được chọn. Ngược lại nó không được chọn.

Nút radio được xác định như một nhóm. Chỉ một button trong nhóm có thể được

chọn vào một thời điểm. Ví dụ, nếu người dùng nhấn một nút radio trong một nhóm,

bất kỳ nút nào trong nhóm đó đã được chọn trước cũng tự động không được chọn nữa.

Lớp ButtonGroup tạo một nhóm button. Constructor mặc định của nó cần cho mục

đích này. Sau đó các thành phần được thêm vào nhóm button thông qua hàm sau:

void add(AbstractButton ab)

Ở đây ab là một tham chiếu đến button được thêm vào nhóm.

5.2.8. ComboBox

Swing cung cấp một ComboBox (một sự kết hợp của một trường văn bản và một

danh sách thả xuống) thông qua lớp JComboBox, thừa kế từ Jcomponent. Một

ComboBox bình thường hiển thị một danh sách thả xuống để cho phép người dùng

chọn một thực thể khác. Bạn có thể nhập sự lựa chọn của bạn vào trường văn bản. Hai

constructor của JComboBox là: JComboBox() , JComboBox (Vector v)

Ở đây, v là một vector khởi tạo combo box.

Các mục được thêm vào danh sách chọn thông qua hàm addItem(), cách thức như

sau:

void add Item(Object obj)

Ở đây obj là đối tượng thêm vào combo box

5.2.9. Tabbed Pane

203

Một Tabbed Pane là một thành phần được hiển thị như một nhóm các folders trong một tập tin cabinet. Mỗi folder có một tên. Khi người dùng chọn một folder, nội

dung của nó sẽ được kích hoạt. Chỉ một folder có thể được chọn tại một thời điểm.

Tabbed pane được sử dụng để xác định cấu hình các phần.

Tabbed pane được tóm lược bởi lớp JTabbedPane, thừa kế từ Jcomponent.

Chúng ta sẽ sử dụng constructor mặc định của nó. Tab được định nghĩa thông qua hàm

sau:

void addtab(String str, Component comp)

Ở đây str là tên của tab, và comp là thành phần sẽ được thêm vào tab. Một Jpanel

hay một phân lớp của nó được thêm vào.

Thủ tục chung để sử dụng một Tabbed pane trong một applet gồm các bước sau:

Tạo một đối tượng JTabbedPane

Gọi hàm addTab() để thêm một tab vào panel. (Các thông số đến hàm này định

nghĩa tựa đề của tab và thành phần của nó).

Lặp lại bước hai cho mỗi tab

Thêm tabbed pane cho content pane của applet.

5.2.10. Scroll Pane

Scroll pane là một thành hiển thị một vùng hình chữ nhật trong một thành phần

có thể nhìn thấy. Các thanh cuộn dọc và ngang có thể được cung cấp nếu cần thiết.

Scroll pane thi hành trong Swing bởi lớp JScrollPane, là lớp mở rộng Jcomponent. Các

constructor của nó là:

 JScrollPane(Component comp)  JScrollPane(int vsb, int hsb)  JScrollPane(Component comp, int vsb, int hsb)

Ở đây comp là thành phần được thêm vào scroll pane. Vsb và hsb là các hằng số

kiểu int được định nghĩa khi thanh cuộn ngang và dọc của scroll pane này hiển thị. Các trường hợp của các hằng số này được trình bày như sau:

Hằng số Diễn giải

HORIZONTAL_SCROLLBAR_ALWAYS Luôn cung cấp thanh cuộn ngang

cấp thanh cuộn

HORIZONTAL_SCROLLBAR_AS_NEEDED Cung ngang, nếu cần

VERTICAL_SCROLLBER_ALWAYS

204

Luôn cung cấp thanh cuộn dọc

HORIZONTAL_SCROLLBAR_AS_NEEDED Cung cấp thanh cuộn dọc,

nếu cần

Bảng 5.7. Các hằng số

Sau đây là các bước mà bạn sẽ sử dụng một Jscroll pane trong một applet:

1. Tạo một đối tượng JComponent. 2. Tạo một đối tượng JScrollPane. Các thông số của constructor xác định thành

phần và sự kiểm soát của thanh cuộn doc và ngang. 3. Thêm JscrollPane vào ContentPane của applet này.

5.2.11. Cây

Cây là một thành phần miêu tả một sự hiển thị có thứ tự của dữ liệu. Người dùng

có thể mở rộng hay thu nhỏ các cây con riêng lẻ trong các hiển thị này. Cây thực hiện trong Swing bởi lớp JTree là một lớp mở rộng của JComponent. Các constructor của

nó là:

 JTree(Hashtable ht)  JTree(Object obj[])  JTree(TreeNode tn)  JTree(Vector v)

Hàm thứ nhất tạo một cây trong đó mỗi thành phần của bảng băm là một nút con. Trong hàm thứ hai mỗi thành phần của mảng obj là một nút con. Trong hàm thứ ba nút

cây tn là gốc của cây. Hàm cuối cùng sử dụng các thành phần của vector v như các nút

con.

Một đối tượng JTree phát sinh các sự kiện khi một nút được mở rộng hay thu

nhỏ. Các hàm addTreeExpansionListener() và removeTreeExpansion Listener () cho

phép bộ theo dõi ghi nhận và tháo bỏ sự khai báo này. Dạng của các hàm này như sau:

 void addTreeExpansionListener(TreeExpansionListener tel)  void removeTreeExpansionListener(TreeExpansionListener tel)

Ở đây tel là đối tượng theo dõi.

Hàm getPathForLocation() sử dụng để di chuyển chuột dựa vào một vị trí đặc

biệt của cây theo một hướng đi của cây. Dạng của nó như sau:

TreePath getPathForLocation(int x, int y)

Ở đây x và y là các tọa độ mà chuột sẽ nhấp vào. Giá trị trả về là một đối tượng

205

TreePath, đối tượng này tóm lược thông tin về nút cây được chọn bởi người dùng.

Lớp TreePath tóm lược thông tin về đường đi đến một nút đặc biệt trong một cây.

Nó cung cấp vài constructor và hàm. Trong cuốn sách này, chỉ có hàm toString() được sử dụng. Nó trả về một chuỗi tương ứng với đường đi của cây.Giao diện TreeNode

trình bày các hàm lấy thông tin về một nút cây. Ví dụ, có thể lấy được một tham chiếu

đến nút cha hay bảng danh sách các nút con. Giao diện MutableTreeNode mở rộng

TreeNode. Nó trình bày các hàm mà có thể chèn và bỏ các nút con hay thay đổi nút cha.

Lớp DefaultMutableTreeNode thực hiện giao diện MutableTreeNode. Nó miêu tả

một nút của cây. Một constructor của nó như sau:

defaultMutableTreeNode(Object obj)

Ở đây obj là đối tượng chứa nút cây này. Nút cây mới không có cha hay con.

Ðể tạo một thứ tự cho các nút cây, hàm add() của DefaultMutableTreeNode có

thể được sử dụng. Dạng của nó như sau:

void add(MutableTreeNode child)

Ở đây child là một nút cây được thêm vào như con của nút hiện hành. Các sự

kiện mở rộng cây được trình bày bởi lớp TreeExpansionEvent trong

javax.swing.event. Hàm getPath() của lớp này trả về một đối tượng TreePath, đối

tượng này trình bày đường đi đến nút đã sẽ được thay đổi. Dạng của nó như sau:

TreePath getPath()

Giao diện TreeExpansionListener cung cấp hai hàm sau:

 void treeCollapsed (TreeExpansionEvenr tee)  void treeExpandeed(TreeExpansionEvenr tee)

Ở đây tee là sự kiện mở rộng cây. Hàm thứ nhất được gọi khi một cây con bị giấu

đi, và hàm thứ hai được gọi khi cây con được hiện thị.

Sau đây là các bước tạo một cây trong một applet:

1. Tạo một đối tượng JTree.

2. Tạo một đối tượng JScrollPane. Các thông số đến constructor xác định cây và

sự kiểm soát thanh cuộn dọc và ngang)

3. Thêm cây vào scroll pane.

206

4. Thêm scroll pane vào content pane của applet.

5.2.12. Bảng

Một bảng là một thành phần hiển thị các dòng và các cột của dữ liệu. Bạn có thể kéo con trỏ trên ranh giới cột để thay đổi kích thước các cột. Bạn cũng có thể kéo một

cột đến một vị trí mới. Bảng thực hiện bởi lớp JTable, là lớp mở rộng Jcomponent.

Một constructor của nó là:

JTable(Object data[][], Object colHeads[])

Ở đây data là một mảng hai chiều của thông tin sẽ được hiển thị và colHeads là

một mảng một chiều lưu trên một cột.

Sau đây là các bước tạo một bảng trong một applet:

1. Tạo một đối tượng JTable.

2. Tạo một đối tượng JScrollPane (các thông số của constructor xác định bảng và

sự kiểm soát thanh cuộn dọc và ngang)

3. Thêm bảng vào scroll pane.

4. Thêm scroll pane vào content pane vào applet

Ví dụ 5.11:sau minh hoạ cách tạo và sử dụng một bảng. Content pane của đối

tượng JApplet được hiện hành và khung layout được ấn định như layout manager của

nó. Bảng này có ba cột. Một mảng hai chiều của chuỗi được tạo cho các ô của bảng.

Bạn có thể xem xét mỗi phần tử trong mảng là một mảng của ba chuỗi. Các mảng này

import javax.swing.*;

import javax.swing.event.*;

import javax.swing.tree.*;

import javax.accessibility.*;

import java.awt.*;

import java.awt.event.*;

import java.beans.*;

import java.util.*;

import java.io.*;

import java.applet.*;

import java.net.*;

public class TreeDemo extends DemoModule {

207

được chuyển cho constructor JTable. Bảng này được thêm vào scroll pane và sau đó scroll pane được thêm vào content pane.

public static void main(String[] args) {

TreeDemo demo = new TreeDemo(null);

demo.mainImpl();

}

public TreeDemo(SwingSet2 swingset) {

// Set the title for this demo, and an icon used to represent this

super(swingset, "TreeDemo", "toolbar/JTree.gif");

getDemoPanel().add(createTree(), BorderLayout.CENTER);

}

public JScrollPane createTree() {

DefaultMutableTreeNode

top

=

new

DefaultMutableTreeNode(getString("TreeDemo.music"));

DefaultMutableTreeNode catagory = null ;

DefaultMutableTreeNode artist = null;

DefaultMutableTreeNode record = null;

// open tree data

URL = getClass().getResource("/resources/tree.txt");

try {

// convert url to buffered string

InputStream is = url.openStream();

InputStreamReader isr = new InputStreamReader(is);

BufferedReader reader = new BufferedReader(isr);

String line = reader.readLine();

while(line != null) {

// System.out.println("reading in: ->" + line + "<-");

char linetype = line.charAt(0);

switch(linetype) {

case 'C':

catagory= new DefaultMutableTreeNode(line.substring(2));

top.add(catagory);

break;

208

case 'A':

if(catagory != null) {

catagory.add(artist = new DefaultMutableTreeNode(line.substring(2)));

}

break;

case 'R':

if(artist != null) {

artist.add(record = new DefaultMutableTreeNode(line.substring(2)));

}

break;

case 'S':

if(record != null) {

record.add(new DefaultMutableTreeNode(line.substring(2)));

}

break;

default:

break;

}

line = reader.readLine();

}

} catch (IOException e) {

}

JTree tree = new JTree(top) {

public Insets getInsets() {

return new Insets(5,5,5,5);

}

};

return new JScrollPane(tree);

}

}

209

Câu hỏi và bài tập chƣơng 5

1.Cho lớp GiaiThua và lớp Java Swing GiaiThua_UI gồm các thuộc tính, phương thức và giao diện như hình sau:

a) Viết chương trình tạo lớp GiaiThua trong đó:

- Thuộc tính n là số cần tính.

- Các phương thức gồm: các setters/getters cho các thuộc tính, Các Constructor,

phương thức KiemTra() đùng để kiểm tra giá trị nhập vào n có phải là một số nguyên

dương không, TinhGT() để tính n!.

b) Tạo lớp GiaiThua_UI sử dụng các đối tượng của Java Swing theo mẫu.

c) Sử dụng lớp GiaiThua đã tạo để thực hiện các tính toán cho lớp GiaiThua_UI

sao cho khi người dùng kích chuột vào nút Tính n! thì tính n! và hiển thị kết quảra màn

hình nếu n nguyên dương, trong trường hợp còn lại thì thông báo lỗi.

2. Cho lớp PhepTinh và lớp Swing PhepTinh_UI gồm các thuộc tính, phương thức và

giao diện mô tả như hình sau:

210

a) Viết chương trình tạo lớp PhepTinh trong đó:

-Các thuộc tính ToanHang1, ToanHang2 là các toán hạng, ToanTu là các phép

toán +, -, *, /.

- Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor,

thức KiemTra() đùng để kiểm tra giá trị nhập vào có phải là một số nguyên không và

TinhToan() để tính giá trị của biểu thức.

b) Tạo lớp PhepTinh_UI sử dụng các đối tượng của Java Swing theo mẫu ở hình

trên

c) Sử dụng lớp PhepTinh đã tạo để tính toán cho lớp PhepTinh_UI sao cho khi

người dùng kích chuột vào nút “Tính toán”thì tính giá trị rồi hiển thị kết quả, nếu giá

trị các toán hạng đều là số nguyên, trong các trường hợp còn lại thông báo lỗi ra màn hình.

3. Cho lớp PhanSo và lớp Java Swing PhanSo_UI gồm các thuộc tính, phương thức và

giao diện như mô tả ở hìnhsau:

a) Viết chương trình tạo lớp PhanSo trong đó:

- Các thuộc tính TuSo, MauSo là tử số và mẫu số của phân số.

- Các phương thứcgồm: các setters/getters cho các thuộc tính, các Constructor, KiemTra() đùng để kiểm tra giá trị nhập vào có phải là một số nguyên không, RutGon() đùng để tính và trả về phân số rút gọn của phân số đã cho và TinhToan() để tính giá trị phân số.

b) Tạo lớp PhanSo_UI sử dụng các đối tượng của Java Swing theo mẫu.

211

c) Sử dụng lớp PhanSo đã tạo để tính toán cho lớp PhanSo_UI sao cho:

- Khi người dùng kích chuột vào nút “Rút gọn” thì tìm phân số rút gọn rồi hiển

thị kết quả, nếu các giá trị tử số và mẫu số nhập vào là các số nguyên, trong các trường hợp còn lại thì thông báo lỗi.

- Khi người dùng kích chuột vào nút “Tính giá trị của phân số” tính giá trị và

hiển thị kết quả ra màn hình nếu các giá trị tử số và mẫu số nhập vào là các số nguyên,

trong các trường hợp còn lại thông báo lỗi ra màn hình.

4. Cho lớp PhuongTrinhBacNhat và lớp Java Swing PhuongTrinhBacNhat_UI gồm

các thuộc tính, phương thức và giao diện như mô tả ở hình sau:

a) Viết chương trình tạo lớp PhuongTrinhBacNhat trong đó:

- Các thuộc tính a, b là các hệ số của phương trình.

- Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor,

KiemTra() đùng để kiểm tra giá trị nhập vào có phải là một số nguyên không,

toString() đùng để trả về dạng chuỗi của phương trình bậc nhất dạng ax+b=0, và

GiaiPT() để biện luận nghiệm của phương trình bậc nhất.

b) Tạo lớp PhuongTrinhBacNhat_UI sử dụng các đối tượng của Java Swing theo

mẫu.

c) Sử dụng lớp PhuongTrinhBacNhat đã tạo để thực hiện các tính toán cho lớp

PhuongTrinhBacNhat_UI gồm:

- Nút “Phương trình” dùng để trả lại dạng chuỗi của phương trình bậc nhất, nếu

212

dữ liệu nhập vào a và b là các số nguyên, trong các trường hợp còn lại thông báo lỗi ra màn hình.

- Nút “Giải phương trình” dùng để tính nghiệm của phương trình tương ứng nếu

dữ liệu nhập vào a, b là các số nguyên và a khác 0, trong các trường hợp còn lại thông báo lỗi ra màn hình.

5. Cho lớp PhuongTrinhBac2 và lớp Swing PhuongTrinhBac2_UI gồm các thuộc tính,

phương thức và giao diện như mô tả trong hình sau:

a) Viết chương trình tạo lớp PhuongTrinhBac2 trong đó

- Các thuộc tính a, b, c là các hệ số của phương trình bậc 2 ax2+bx+c=0

- Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor,

phương thức KiemTra() đùng để kiểm tra giá trị nhập vào có phải là một số nguyên

không, toString() đùng để trả về dạng chuỗi của phương trình bậc nhất dạng

ax^2+bx+c=0, GiaiPT() để giải phương trình bậc hai.

b) Tạo lớp PhuongTrinhBac2_UI sử dụng các đối tượng của Java Swing theo

mẫu.

c) Sử dụng lớp PhuongTrinhBac2 đã tạo để thực hiện các tính toán cho lớp

PhuongTrinhBac2_UI gồm:

- Nút “Phương trình” dùng để tính và trả lại dạng chuỗi của phương trình bậc 2,

nếu dữ liệu nhập vào đúng, ngược lại sẽ thông báo lỗi

- Nút “Giải phương trình” dùng để tính nghiệm của phương trình bậc 2 tương

ứng, nếu dữ liệu nhập vào đúng, ngược lại sẽ thông báo lỗi.

213

6. Cho lớp SinhVien và lớp Swing SinhVien_UI gồm các thuộc tính, phương thức và giao diện như mô tả ở hình sau:

a) Viết chương trình tạo lớp SinhVien trong đó:

- Các thuộc tính gồm: MaSV là mã sinh viên, HoTen là họ tên, NgaySinh là ngày

sinh, GioiTinh là giới tính, DiaChi là địa chỉ của sinh viên.

- Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor.

b)Tạo lớp SinhVien_UI sử dụng các đối tượng của Java AWT theo mẫu.

c) Sử dụng lớp SinhVien đã tạo để viết chương trình cho các nút của lớp

SinhVien_UI gồm:

- Nút “Thêm sinh viên” dùng để thêm một sinh viên vào danh sách, mã sinh viên

sẽ được tự động thêm vào danh sách mã sinh viên bên trái.

- Khi người dùng lựa chọn một mã sinh viên trong danh sách thông tin của sinh

viên đó được lấy ra các control bên phải để sửa đổi.

- Khi người dùng kích vào “Sửa sinh viên” thì thông tin sửa sẽ được ghi lại.

7. Cho lớp GiaoVien và lớp Swing GiaoVien_UI gồm các thuộc tính, phương thức và

214

giao diện như mô tả ở hình sau:

a) Viết chương trình tạo lớp GiaoVien trong đó:

- Các thuộc tính gồm:MaGV là mã giáo viên, HoTen là họ tên, Diachi là địa chỉ,

HeSoLuong là hệ số lương của giáo viên.

- Các phương thức gồm: các setters/getters cho các thuộc tính, các Constructor.

b)Tạo lớp giao diện GiaoVien_UI sử dụng các đối tượng của Java AWT theo

mẫu.

c)Sử dụng lớp GiaoVien đã tạo để viết chương trình cho các nút của lớp

GiaoVien_UI gồm:

- Nút “Thêm giáo viên” dùng để thêm một giáo viên vào danh sách, danh sách

giáo viên sẽ được hiển thị ở bảng phía dưới.

- Khi người dùng lựa chọn giáo viên trong danh sách thông tin của giáo viên đó

được lấy ra các control bên trên để xóa.

- Khi người dùng kích vào “Xóa” sẽ xóa đi giáo viên được lựa chọn đó.

8.Cho lớp Sach và lớp Swing Sach_UI gồm các thuộc tính, phương thức và giao diện

mô tả như hình sau:

a) Viết chương trình tạo lớp Sach trong đó:

- Các thuộc tính gồm: Ten là tên sách, Tacgia là tác giả, gia là giá, nam là năm

xuất bản cuốn sách.

- Các phương thức gồm: các setters/getters cho các thuộc tính và các Constructor.

b) Tạo lớp giao diện Sach_UI sử dụng các đối tượng của Java AWT theo mẫu.

c)Sử dụng lớp Sach đã tạo để viết chương trình cho các nút của lớp Sach_UI

215

gồm:

- Nút “Thêm mới” dùng để thêm sách vào danh sách, danh sách sẽ được hiển thị

ở bảng phía dưới.

-Khi người dùng lựa chọn sách trong danh sách thông tin của sách đó được lấy ra

các control bên trên để xóa.

- Khi người dùng kích vào “Xóa” sẽ xóa đi sách được lựa chọn.

9. Cho lớp XeMay và lớp Swing XeMay_UI gồm các thuộc tính, phương thức, giao diện như hình sau:

a) Viết chương trình tạo lớp XeMay trong đó:

- Các thuộc tính gồm: Biensolà biển số xe, TenXe là tên xe, Mau là màu của xe,

HangSX là hãng sản xuất xe.

- Các phương thức bao gồm: các setters/getters cho các thuộc tính,

cácConstructor.

b)Tạo lớp giao diện XeMay_UI sử dụng các đối tượng của Java AWT theo mẫu.

c)Sử dụng lớp XeMay đã tạo để viết chương trình cho các nút của lớp

XeMay_UI gồm:

- Nút “Thêm mới” dùng để thêm xe máy vào danh sách, danh sách sẽ được hiển

thị ở bảng phía dưới.

-Khi người dùng lựa chọn xe máy trong danh sách thông tin của xe máy đó được

lấy ra các control bên trên để sửa.

- Khi người dùng sửa thông tin kích vào “Sửa” sẽ sửa thông tin của xe được lựa

216

chọn.

10. Cho lớp HocSinh và lớp Swing HocSinh _UI gồm các thuộc tính, phương thức,

giao diện như hình sau:

a) Viết chương trình tạo lớp HocSinh trong đó:

- Các thuộc tính gồm: Mahslà Mã học sinh, Hoten là Họ tên, DiemToan là điểm

môn toán, DiemViet là điểm môn viết.

- Các phương thức bao gồm: các setters/getters cho các thuộc tính,

cácConstructor.

b)Tạo lớp giao diện HocSinh_UI sử dụng các đối tượng của Java AWT theo

mẫu.

c)Sử dụng lớp HocSinh đã tạo để viết chương trình cho các nút của lớp

HocSinh_UI gồm:

- Nút “Thêm mới” dùng để thêm học sinh vào danh sách, danh sách sẽ được hiển

thị ở bảng phía dưới.

-Khi người dùng lựa chọn học sinh trong danh sách thông tin của học sinh đó

được lấy ra các control bên trên.

- Khi người dùng kích vào “Xóa” sẽ xóa học sinh được lựa chọn khỏi danh sách.

217

11.Cho lớp KhachHang và lớp Swing KhachHang _UI gồm các thuộc tính, phương thức, giao diện như hình sau:

a) Viết chương trình tạo lớp KhachHang trong đó:

- Các thuộc tính gồm: Makhlà Mã khách hàng, Tenkh là Họ tên khách hàng,

Email là địa chỉ email, Sotaikhoan là số tài khoản của khách.

- Các phương thức bao gồm: các setters/getters cho các thuộc tính,

cácConstructor.

b)Tạo lớp giao diện KhachHang_UI sử dụng các đối tượng của Java AWT theo

mẫu.

c)Sử dụng lớp KhachHang đã tạo để viết chương trình cho các nút của lớp

KhachHang_UI gồm:

- Nút “Thêm mới” dùng để thêm khách hàng vào danh sách, danh sách tên khách

hàng được hiển thị ở bên trái.

- Khi người dùng lựa chọn tên khách hàng trong danh sách, thông tin của khách

hàng đó được lấy ra các control bên phải.

- Khi người dùng kích vào “Xóa”, sẽ xóa khách hàng được lựa chọn khỏi danh

sách.

218

12. Cho lớp TongChan và lớp Swing TongChan_UI gồm các thuộc tính, phương thức, giao diện như hình sau:

a) Viết chương trình tạo lớp TongChan trong đó:

- Thuộc tính nlà một số nguyên.

- Các phương thức bao gồm: các setters/getters cho các thuộc tính, cácConstructor, KiemTra() dùng để kiểm tra một chuỗi có phải là số nguyên dương

không, TinhTong() dùng để tính tổng s = 1/2+1/4+...+1/2*n.

b)Tạo lớp giao diện TongChan_UI sử dụng các đối tượng của Java AWT theo

mẫu.

c)Sử dụng lớp TongChan đã tạo để viết chương trình cho các nút của lớp

TongChan_UI sao cho khi người dùng kích vào nút Tính tổng 1/2+1/4 +...+1/2*n thì

sẽ tính tông sau đó hiển thị kết quả ra màn hình, nếu giá trị nhập vào là số nguyên

dương, ngược lại sẽ thông báo lỗi.

13. Sử dụng Java Awt và Java Applet viết chương trình vẽ hình mặt người theo mẫu ở

hình sau:

14. Sử dụng Java Awt và Java Applet viết chương trình vẽ chữ JAVA theo mẫu ở hình

219

sau:

15. Sử dụng Java Awt và Java Applet viết chương trình vẽ hình người theo mẫu ở hình

sau:

16. Sử dụng Java Awt và Java Applet viết chương trình vẽ hình người tuyết theo mẫu

ở hình sau:

220

17. Sử dụng Java Awt và Java Applet viết chương trình vẽ tranh bốn mùa theo mẫu ở hình sau:

18.Sử dụng Java Awt, Java Applet, và ActionListener viết chương trình sao cho mỗi lần người dùng vào nút “Ve Bong” sẽ vẽ ra màn hình quả bóng có kích thước, màu sắc và vị trí thay đổi ngẫu nhiên ví dụ mẫu ở hình sau:

19.Sử dụng Java Awt, Java Applet, và ActionListener viết chương trình sao cho mỗi

lần người dùng vào nút “Tao bong” sẽ vẽ ra màn hình 1000 quả bóng có kích thước,

221

màu sắc và vị trí thay đổi ngẫu nhiên ví dụ mẫu ở hình sau:

20. Sử dụng Java Awt và Java Applet viết chương trình vẽ biểu đồ hình tròn theo mẫu

ở hình sau:

21. Sử dụng Java Awt, Java Applet và các sự kiện chuột MouseListener viết chương trình vẽ 4 hình vuông theo mẫu sau, sao cho khi kích chuột lên hình vuông nào đó thì

nó sẽ đổi màu ngẫu nhiên.

22. Sử dụng Java Awt, Java Applet và các sự kiện chuột MouseListener viết chương trình vẽ hình mặt người theo mẫu ở hình dưới, sao cho khi kích chuộtlên Smile thì hình mặt người là smile (Nền màu vàng và miệng cười), kích chuột lên Sad thì hình sad(Nền màu nâu, miệng méo).

222

23. Sử dụng Java Awt, Java Applet vẽ ra màn hình 20 dòng chữ Java tạo thành vòng tròn có màu biến thiên dần từ đỏ sang xanh theo mẫu ở hình sau:

24. Sử dụng Java Awt, Java Applet vẽ bia bắn (là các đường tròn đồng tâm màu đỏ và

trắng) theo mẫu ở hình sau:

25. Sử dụng Java Awt, Java Applet, viết chương trình vẽ các đoạn thẳng màu xanh

trên nền trắng quanh một tâm cố định theo mẫu ở hình sau:

223

26. Sử dụng Java Awt, Java Applet, viết chương trình vẽ các hình chữ nhật màu hồng trên nền trắng theo mẫu ở hình sau:

27. Sử dụng Java Awt, Java Applet, viết chương trình vẽ bàn cờ vua các ô màu đỏ và

đen theo mẫu ở hình sau:

CHƢƠNG 6: LẬP TRÌNH ĐA LUỒNG

6.1. Giới thiệu về lập trình luồng

Luồng là một thuộc tính duy nhất của Java. Nó là đơn vị nhỏ nhất của đoạn mã có thể thi hành được để thực hiện một công việc nhất định. Ngôn ngữ Java, máy ảo Java cả hai đều là các hệ thống phân luồng

6.2. Luồng và đa luồng

Java hỗ trợ đa luồng, nó có khả năng làm việc với nhiều luồng. Một ứng dụng có thể bao hàm nhiều luồng. Mỗi luồng được gán một công việc cụ thể, chúng được thực

thi đồng thời với các luồng khác.

224

Đa luồng làm giảm tối đa thời gian nhàn rỗi của hệ thống. Điều này cho phép bạn viết các chương trình có hiệu quả cao với sự tận dụng tối đa CPU. Mỗi phần của

chương trình được gọi một luồng, mỗi luồng định nghĩa một cách thực hiện. Đây là

một trường hợp đặc biệt của đa nhiệm.

Trong đa nhiệm, nhiều chương trình chạy đồng thời, mỗi chương trình có ít nhất

một luồng trong nó. Một bộ vi xử lý thực thi tất cả các chương trình này. Qua đó

chương trình được thực thi như là đồng thời, trên thực tế bộ vi xử lý chuyển qua

chuyển lại giữa các chương trình.

6.3. Tạo và quản lý luồng

Khi các chương trình Java được thực thi, luồng chính đã đang được thực hiện.

Hai yếu tố quan trong luồng chính (main) là:

 Các luồng con sẽ được tạo ra từ nó.  Nó là luồng cuối cùng kết thúc việc thực hiện. Ngay khi luồng chính

ngừng thực thi, chương trình bị chấm dứ t.

Cho dù luồng chính được tạo ra một cách tự động với chương trình thực thi, nó

có thể được điều khiển thông qua một đối tượng luồng.

Các luồng có thể được tạo ra từ hai cách:  Khai báo lớp là lớp con của lớp Thread, và phương thức run() của lớp Thread cần được định nghĩa đè. Ví dụ 6.1: Tạo một lớp con của lớp Thread

{

public void run()

{

//thực thi

}

}

 Khai báo lớp cài đặt giao diện Runnable. Rồi thì định nghĩa phương thức

class Mydemo extends Thread

run().

class MyDemo implements Runnable

{

public void run()

{

//thực thi

225

}

}

import java.io.*;

public class Mythread extends Thread

{

public static void main(String args[])

{

Thread t = Thread.currentThread();

System.out.println("The current Thread is :" + t);

t.setName("MyJavaThread");

System.out.println("The thread is now named: " + t);

try{

for(int i = 0; i <3;i++)

{

System.out.println(i);

Thread.sleep(1500);

}

}

catch(InterruptedException e)

{

System.out.println("Main thread interupted");

}

}

}

Ví dụ 6.2:Chỉ ra sự điều khiển luồng chính như thế nào

226

Hình sau đây sẽ chỉ ra kết quả xuất ra màn hình của chương trình trên

Hình 6.1. Kết quả thực hiện ví dụ 6.2

Trong kết quả xuất ra ở trên

[main, 5 , main]

Nhóm luồng mà nó phụ thuộc vào

Quyền ưu tiên được đặt bởi JVM

Tên của luồng

Mỗi luồng trong chương trình Java được cấp một quyền ưu tiên. Máy ảo Java

không bao giờ thay đổi quyền ưu tiên của luồng. Quyền ưu tiên vẫn còn là hằng số cho đến khi luồng bị ngắt.

Mỗi luồng có một giá trị ưu tiên nằm trong khoảng của một

Thread.MIN_PRIORITY (=1), và đến Thread.MAX_PRIORITY (=10). Mỗi luồng

thuộc vào một nhóm luồng, và mỗi nhóm luồng có quyền ưu tiên của chính nó. Mỗi

luồng có một giá trị ưu tiên mặc định Thread.NORM_PRIORITY là 5. Luồng mới

thừa kế quyền ưu tiên của luồng mà tạo ra nó.

Lớp Thread có vài phương thức khởi tạo, hai trong số các phương thức khởi

dựng được đề cập đến dưới đây:

 public Thread(String threadname)

Cấu trúc một luồng với tên là “threadname”

 public Thread()

Cấu trúc một luồng với tên “Thread, gép với một số; ví dụ Thread-1, Thread-2,

v.v…

Chương trình bắt đầu thực thi luồng với việc gọi phương thức start() của lớp

227

Thread. Phương thức này sẽ gọi phương thức run(), nơi định nghĩa công việc được

thực thi. Phương thức này có thể được định nghĩa đè trong lớp con của lớp Thread,

hoặc trong đối tượng cài đặt giao diện Runnable.

6.4. Vòng đời của luồng

Hình 6.2. Vòng đời của luồng

6.5. Trạng thái của luồng và các phƣơng thức của lớp Thread

Một luồng vừa được tạo ra có trạng thái 'born'. Luồng không bắt đầu chạy ngay

lập tức sau khi nó được tạo ra. Nó đợi phương thức start() của chính nó được gọi. từ đó

nó ở trạng thái “sẵn sàng để chạy”. Luồng đi vào trạng thái “đang chay” khi hệ thống

cấp quyền sử dụng bộ vi xử lý cho nó.

Có thể sử dụng phương thức sleep() để tạm thời dừng sự thực thi của luồng.

Luồng trở thành sẵn sàng sau khi phương thức sleep() kết thúc. Luồng khi rơi vào

trạng thái 'ngủ' (sleeping) không sử dụng bộ vi xử lý. Luồng đi vào trạng thái “đợi"

(waiting) khi một luồng gọi phương thức wait().

Khi các luồng khác sử dụng xong đối tượng, gọi phương thức notify(), luồng trở

lại trạng thái “sẵn sàng" (ready). Luồng đi vào trạng thái “blocked” khi nó đang thực

thi các phép toán vào/ra (Input/output). Nó đi vào trạng thái "sẵn sàng" khi các phương

thức vào/ra nó đang đợi được hoàn thành. Luồng đi vào trạng thái “chết” (dead) sau khi phương thức run() đã được thực thi xong, hoặc khi phương thức stop() của nó được gọi.

Bên cạnh các phương thức đã được đề cập ở trên, lớp Thread còn có các

phương thức sau:

Phƣơng thức Mục đích

228

enumerate(Thread t) Sao chép tất cả các luồng hiện hành vào mảng được chỉ

định từ nhóm luồng, và các nhóm con của nó.

getName() Trả về tên của luồng

isAlive() Trả về True, nếu luồng là vẫn còn tồn tại (sống)

getPriority() Trả về mức ưu tiên của luồng

setName(String name) Đặt tên cho luồng.

join() Đợi cho đến khi luồng chết.

isDaemon() Kiểm tra nếu luồng là luồng một luồng chạy ngầm

(deamon)

setDeamon(boolean on) Đánh dấu luồng như là luồng chạy ngầm hoặc

resume() Tiếp tục thực hiện từ trạng thái treo

sleep() Dừng thực thi luồng một khoảng thời gian.

start() Gọi phương thức run() để bắt đầu một luồng.

Bảng 6.1. Các phương thức của một lớp luồng

Điều phối round-robin đề cập đến các luồng với cùng mức ưu tiên. Khi các luồng

cùng mức ưu tiên thực thi cùng một thời điểm thì phương pháp chia sẻ thời gian sử

dụng bộ vi xử lý round-robin tự động được áp dụng.

Phiên bản mới nhất của Java không hỗ trợ các phương thức Thread.suspend()

(treo), Thread.resume() (trở lại) và Thread.stop() (dừng), vì các phương thức tạo ra

nguy cơ khoá chết (deadlock).

6.6. Thời gian biểu luồng

Hầu hết các chương trình Java là đa luồng. Nhưng CPU chỉ có khả năng thực thi

một luồng tại một thời điểm. Khi có nhiều hơn một luồng có cùng mức ưu tiên thực thi

trong cùng một thời điểm thì người lập trình, hoặc máy ảo Java, hoặc hệ điều hành đảm bảo rằng CPU được chia sẻ giữa chúng. Điều này được gọi là điều khiển luồng (thời gian biểu luồng).

Không có máy ảo Java nào có cơ chế cụ thể cho việc điều khiển luồng. Một số môi trường Java hỗ trợ việc chia nhỏ thời gian. Ở đây, mỗi luồng nhận một phần nhỏ của thời gian bộ vi xử lý, được gọi là định lượng (quantum). Luồng có thể thực thi tác

229

vụ của chính nó trong suốt khoảng thời gian được cấp. Sau khoảng thời gian này, luồng phải tạm dừng thực hiện, ngay cả khi nó chưa hoàn thành. Luồng kế tiếp có cùng quyền ưu tiên sẽ sử dụng bộ vi xử lý trong khoảng thời tiếp theo. Java điều khiển việc chia nhỏ thời gian giữa tất cả các luồng có cùng mức ưu tiên.

Phương thức setPriority() có một tham số kiểu số nguyên dùng để đặt mức ưu

tiên của luồng. Đây là giá trị nằm trong khoảng 1 đến 10, mặc khác, phương thức có thể gây ra ngoại lệ IllegalArgumentException.

Phương thức yield() tam dừng luồng và tạo khả năng cho các luồng khác một cơ

hội được thực thi. Phương thức này thích hợp cho các hệ thống không chia sẻ thời gian

(non-time-sliced), nơi mà các luồng hiện thời hoàn thành việc thực hiện trước khi các luồng có quyền ưu tiên ngang nhau kế tiếp được thực thi. Ở đây, bạn sẽ gọi phương

thức yield() tại những khoảng thời gian riêng biệt để có thể tất cả các luồng có quyền

ưu tiên ngang nhau chia sẻ thời gian thực thi CPU.

Ví dụ 6.3:Thể hiện quyền ưu tiên của luồng:

Priority t1,t2,t3;

public PriorityDemo(){

t1 = new Priority();

t1.start();

t2 = new Priority();

t2.start();

t3 = new Priority();

t3.start();

}

public static void main(String args[]){

new PriorityDemo();

}

class PriorityDemo {

int sleep;

int prio = 3;

public Priority(){

sleep += 100;

prio++;

setPriority(prio);

}

230

class Priority extends Thread implements Runnable{

public void run(){

try{

Thread.sleep(sleep);

"+

getName()+" Priority =

"+

System.out.println("Name getPriority());

}

catch(InterruptedException e){

System.out.println(e.getMessage());

}

}

}

}

Kết quả hiển thị như hình 6.4

Hình 6.3. Kết quả chạy ví dụ 6.3

6.7. Luồng chạy ngầm

Một chương trình Java bị ngắt chỉ sau khi tất cả các luồng thực thi xong. Trong

Java có hai loại luồng:

 Luồng người sử dụng  Luồng chạy ngầm (deamon)

Người sử dụng tạo ra các luồng người sử dụng, trong khi các luồng deamon là

231

các luồng nền. Luồng deamon cung cấp các dịch vụ cho các luồng khác. Máy ảo Java

thực hiện tiến trình thoát, khi đó chỉ còn duy nhất luồng deamon vẫn còn sống. Máy

ảo Java có ít nhất một luồng deamon là luồng “garbage collection” (thu lượm tài nguyên - dọn rác). Luồng dọn rác thực thi chỉ khi hệ thống không có tác vụ nào. Nó là

một luồng có quyền ưu tiên thấp. Lớp luồng có hai phương thức để làm việc với luồng

deamon:

 public void setDaemon(boolean on)  public boolean isDaemon()

6.8. Đa luông với Applet

Trong khi đa luồng là rất hữu dụng trong các chương trình ứng dụng độc lập, nó

cũng được quan tâm với các ứng dụng trên Web. Đa luồng được sử dụng trên web, ví dụ trong các trò chơi đa phương tiện, các bức ảnh chuyển động, hiển thị các dòng chữ

chạy qua lại trên biểu ngữ, hiển thị đồng hồ thời gian như là một phần của trang Web

… Các chức năng này tạo cho các trang web sinh động hơn.

Chương trình Java dựa trên Applet thường sử dụng nhiều hơn một luồng. Trong

đa luồng với Applet, lớp java.applet.Applet được thừa kế để tạo ra các applet. Vì Java

không hỗ trợ đa thừa kê từ lớp, vì thế nó không thể thừa kế trực tiếp từ lớp Thread.

Tuy nhiên, có thể sử dụng một đối tượng người sử dụng đã định nghĩa, mà đối tượng

này đã được dẫn xuất từ lớp Thread. Hoặc cài đặt giao diện Runnable

import java.awt.*;

import java.applet.*;

public class Myapplet extends Applet implements Runnable {

int i;

Thread t;

/**

* Myapplet constructor comment.

*/

public void init(){

t = new Thread(this);

t.start();

}

public void paint(Graphics g){

g.drawString(" i = "+i,30,30);

232

Ví dụ 6.4:Sau đây chỉ ra điều này thực hiện như thế nào:

}

public void run(){

for(i = 1;i<=20;i++){

try{

repaint();

Thread.sleep(500);

}catch(InterruptedException e){

System.out.println(e.getMessage());

}

}

}

}

Trong chương trình này, chúng ta tạo ra một Applet tên là Myapplet và cài đặt

giao diện Runnable để cung cấp khả năng đa luồng cho applet. Sau đó chúng ta tạo ra

một thể hiện (instance) lớp Thread, với thể hiện applet hiện thời như là một tham số.

Sau đó gọi phương thức start() để thực thi luồng. Phương thức run() sẽ được gọi và

đây chính là phần công việc của luồng. Chúng ta in số từ 1 đến 20 với thời gian trễ là

500 mili giây giữa mỗi số. Phương thức sleep() được gọi để tạo thời gian trễ này. Đây

là một phương thức tĩnh được định nghĩa trong lớp Thread. Nó cho phép luồng dừng

(ngủ) trong khoản thời gian đó.

Kết quả chạy chương trình như sau:

Hình 6.4. Kết quả chạy ví dụ 6.4

6.9. Nhóm luồng

233

Một lớp nhóm luồng (ThreadGroup) quản lý một nhóm các luồng. Ví dụ một nhóm luồng trong một trình duyệt có thể quản lý tất cả các luồng của một applet. Tất cả các luồng trong máy ảo Java phụ thuộc vào các nhóm luồng mặc định. Mỗi nhóm

luồng có một nhóm luồng cha. Vì thế, các nhóm hình thành một cấu trúc dạng cây.

Nhóm luồng “hệ thống” là gốc của tất cả các nhóm luồng. Một nhóm luồng có thể là thành phần của cả các luồng, và các nhóm luồng.

Lớp ThreadGroup có hai phương thức thiết lập là:

 public ThreadGroup(String str)

Ở đây “str” là tên của nhóm luồng mới nhất được tạo ra.

 public ThreadGroup(ThreadGroup tgroup, String str)

Ở đây “tgroup” chỉ ra luồng đang chạy hiện thời như là luồng cha, “str” là tên

của nhóm luồng đang được tạo ra.

Một số các phương thức trong nhóm luồng (ThreadGroup):

 public synchronized int activeCount()

Trả về số lượng các luồng đang hoạt động trong nhóm luồng

 public sunchronized int activeGroupCount()

Trả về số lượng các nhóm đang hoạt động trong nhóm luồng

 public final String getName()

Trả về tên của nhóm luồng

 public final ThreadGroup getParent()

Trả về cha của nhóm luồng

6.10. Sự đồng bộ luồng

Trong khi đang làm việc với nhiều luồng, nhiều hơn một luồng có thể muốn thâm

nhập cùng một biến tại cùng thời điểm. Ví dụ một luồng có thể cố gắng đọc dữ liệu,

trong khi luồng khác cố gắng thay đổi dữ liệu. Trong trường hợp này dữ liệu có thể bị

sai.

Trong những trường hợp này cần cho phép một luồng hoàn thành trọn vẹn tác vụ

của nó và rồi thì mới cho phép các luồng kế tiếp thực thi. Khi hai hoặc nhiều hơn một

luồng cần thâm nhập đến một tài nguyên được chia sẻ, cần chắc chắn rằng tài nguyên đó sẽ được sử dụng chỉ bởi một luồng tại một thời điểm. Tiến trình này được gọi là “sự đồng bộ”, (synchronization) được sử dụng để giải quyết vấn đề này, Java là ngôn ngữ duy nhất hỗ trợ sự đồng bộ ở mức ngôn ngữ. Phương thức “đồng bộ” (synchronized) báo cho hệ thống đặt khóa trên tài nguyên.

234

Mấu chốt của sự đồng bộ hóa là khái niệm “monitor” (giám sát), hay còn gọi “semaphore” (cờ hiệu). Một “monitor” là một đối tượng mà được khóa độc quyền. Chỉ một luồng có thể có monitor tại mỗi thời điểm. Tất cả các luồng khác cố gắng thâm nhập vào monitor sẽ bị trì hoãn, cho đến khi luồng đầu tiên thoát khỏi monitor.

Các luồng khác được báo chờ đợi monitor. Một luồng có thể monitor một đối tượng

nhiều lần.

6.10.1. Đồng bộ mã

Tất cả các đối tượng trong Java được liên kết với các monitor của riêng nó. Để

đăng nhập vào monitor của một đối tượng, ta sử dụng từ khóa synchronized (đồng bộ)

để gọi một phương thức hiệu chỉnh (modified). Khi một luồng đang được thực thi trong phạm vi một phương thức đồng bộ (synchronized), bất kỳ luồng khác hoặc

phương thức đồng bộ khác mà cố gắng gọi nó trong cùng thể hiện sẽ phải đợi.

Ví dụ 6.5: mô tả sự làm việc của từ khóa synchronized. Ở đây lớp “Target” có

một phương thức “display()” mà phương thức này lấy một tham số kiểu số nguyên (int). Số này được hiển thị trong phạm vi các cặp ký tự “<> # số # <>”. Phương thức

“Thread.sleep(1000) tạm dừng luồng hiện tại sau khi phương thức “display()” được

gọi.

Thiết lập (khởi dựng) của lớp “Source” lấy một tham chiếu đến một đối tượng “t”

của lớp “Target”, và một biến số nguyên. Ở đây một luồng mới cũng được tạo ra.

Luồng này gọi phương thức run() của đối tượng “t”. Lớp chính “Sync” tạo thể hiện lớp

“Target” là “target và tạo ra 3 đối tượng của lớp “Source”. Cùng đối tượng “target”

được truyền cho mỗi đối tượng “Source”. Phương thức “join()” làm luồng được gọi

class Target {

synchronized void display(int num) {

System.out.print("<> "+num);

try{

Thread.sleep(1000);

}catch(InterruptedException e){

System.out.println("Interrupted");

}

System.out.println(" <>");

}

}

class Source implements Runnable{

int number;

Target target;

235

đợi cho đến khi luồng thực thi xong.

Thread t;

public Source(Target targ,int n){

target = targ;

number = n;

t = new Thread(this);

t.start();

}

public void run()

{

target.display(number);

}

}

class Sync {

public static void main(String args[]){

Target target = new Target();

int digit = 10;

Source s1 = new Source(target,digit++);

Source s2 = new Source(target,digit++);

Source s3 = new Source(target,digit++);

try{

s1.t.join();

s2.t.join();

s3.t.join();

}catch(InterruptedException e){

System.out.println("Interrupted");

}

}

}

236

Kết quả hiện thị như hình cho dưới đây:

Hình 6.5. Kết quả chạy ví dụ 6.5

Trong chương trình trên, có một “dãy số” liên tiếp được hiển thị “display()”.

Điều này có nghĩa là việc thâm nhập bị hạn chế một luồng tại mỗi thời điểm. Nếu

không có từ khóa synchronized trong định nghĩa phương thức “display()” của lớp

“Target”, tất cả luồng trên có thể cùng lúc gọi cùng phương thức, trên cùng đối

tượng. Điều kiện này được biết đến như là race -condition. Trong trường hợp này,

kết quả sẽ như hình 6.6

Hình 6.6. Kết quả hiển thị của ví dụ 6.5 không có sự đồng bộ

6.10.2. Sử dụng khối đồng bộ (Synchronized Block)

237

Tạo ra các phương thức synchronzed trong phạm vi các lớp là một phương pháp dễ và có hiệu quả của việc thực hiện sự đồng bộ. Tuy nhiên, điều này không làm việc trong tất cả các trường hợp.

Hãy xem một trường hợp nơi mà ta muốn sự đồng bộ được thực hiện với các đối

tượng của lớp mà không được thiết kế cho thực đa luồng. Tức là, lớp không sử dụng các phương thức đồng bộ. Hơn nữa, mã nguồn là không có. Vì thế từ khoá

synchronized không thể được thêm vào các phương thức thích hợp trong phạm vi lớp.

Để đồng bộ được đối tượng của lớp này, tất cả các lời gọi phương thức mà lớp

này được đặt bên trong một khối đồng bộ. Tất cả chúng sử dụng chung một câu lệnh đồng bộ được cho như sau:

synchronized(object)

{

// các câu lệnh đồng bộ

}

Ở đây, “object” là một tham chiếu đến đối tượng được đồng bộ. Dấu ngoặc móc

không cấn thiết khi chỉ một câu lệnh được đồng bộ. Một khối đồng bộ bảo đảm rằng

việc gọi đến một phương thức của đối tượng chỉ được thực hiên sau khi luồng hiện

hành đã sử dụng xong phương thức.

class Target {

void display(int num) {

System.out.print("<> "+num);

try{

Thread.sleep(1000);

}catch(InterruptedException e){

System.out.println("Interrupted");

}

System.out.println(" <>");

}

}

class Source implements Runnable{

int number;

Target target;

Thread t;

238

Ví dụ 6.6:Chỉ ra câu lệnh đồng bộ sử dụng như thế nào:

public Source(Target targ,int n){

target = targ;

number = n;

t = new Thread(this);

t.start();

}

// đồng bộ gọi phương thức display()

public void run(){

synchronized(target) {

target.display(number);

}

}

}

class Synchblock {

public static void main(String args[]){

Target target = new Target();

int digit = 10;

Source s1 = new Source(target,digit++);

Source s2 = new Source(target,digit++);

Source s3 = new Source(target,digit++);

try{

s1.t.join();

s2.t.join();

s3.t.join();

}catch(InterruptedException e){

System.out.println("Interrupted");

}

}

}

239

Ở đây, từ khóa synchronized không hiệu chỉnh phương thức “display()”. Từ khóa

này được sử dụng trong phương thức run() của lớp “Target”. Kết quả xuất ra màn hình tương tự với kết quả chỉ ra ở hình số 6.6

6.10.3. Ƣu điểm của các phƣơng thức đồng bộ

Người lập trình thường viết các chương trình đơn luồng. Tất nhiên một số trường

hợp nhất định đa luồng là không hiệu quả. Ví dụ nó không làm tăng hiệu năng của các trình biên dịch. Trình biên dịch Java Sun không chứa nhiều phương thức đồng bộ.

Các phương thức đồng bộ không thực thi tốt như là các phương thức không đồng

bộ. Các phương thức này chậm hơn từ ba đến bốn lần so với các phương thức tương

ứng không đồng bộ. Trong trường hợp chúng ta cần hiệu năng cao thì nên hạn chế sử dụng các phương thức đồng bộ.

6.11. Cơ chế đợi thông báo

Luồng chia các tác vụ thành các đơn vị cụ thể và logic. Điều này thay thế các

hình thức lập trình lặp sự kiện. Các luồng loại trừ “polling” (kiểm tra liên tục).

Một vòng lặp dùng để kiểm tra điều kiện gọi là “polling”. Khi điều kiện nhận giá

trị là True (đúng), các câu lệnh tương ứng được thực hiện. Đây là tiến trình thường

lãng phí thời gian của CPU. Ví dụ khi một luồng sinh ra một số dữ liệu và các luồng

khác đang chi phối nó luồng sinh ra phải đợi cho đến khi các luồng sử dụng nó hoàn

thành trước khi phát sinh ra dữ liệu.

Để tránh trường hợp polling, Java bao gồm một cơ chế giao tiếp giữa các tiến

trình bằng các phương thức “wait()”, “notify()” và “notifyAll()” . Các phương thức

này được thực hiện như là các phương thức final trong lớp Object, vì vậy tất cả các lớp

có thể thâm nhập chúng. Tất cả 3 phương thức này có thể được gọi chỉ từ trong phạm

vi một phương thức đồng bộ (synchronized).

Các chức năng của phương thức “wait()”, “notify()”, và “notifyAll()” là:

 Phương thức wait() làm cho luồng gọi nó từ bỏ yêu cầu monitor, và chuyển sang trạng thái “sleep” (chờ) cho đến khi luồng khác thôi monitor tài nguyên nó cần (đối tượng đang monitor gọi phương thức “notify()”).

 Phương thức notify() đánh thức, hoặc thông báo cho luồng đầu tiên mà đã

gọi phương thức wait() trên cùng đối tượng.

 Phương thức notifyAll() đánh thức, hoặc thông báo tất cả các luồng mà đã

gọi phương thức wait() trên cùng đối tượng.

 Luồng có quyền ưu tiên cao nhất là luồng chạy đầu tiên.

240

Cú pháp của 3 phương thức này như sau:

final void wait() throws IOException

final void notify()

final void notifyAll()

Các phương thức wait() và notify() cho phép chia sẻ đối tượng, làm tạm ngừng

luồng, khi đối tượng trở thành không còn giá trị cho luồng. Chúng cũng cho phép

luồng tiếp tục khi thích hợp.

Các luồng bản thân nó không bao giờ kiểm tra trạng thái của đối tượng đã chia

sẻ.

Một đối tượng mà điều khiển các luồng yêu cầu nó theo kiểu này được gọi là

monitor. Trong phạm vi Java, một monitor là bất kỳ đối tượng nào mà có mã đồng bộ. Các monitor được sử dụng cho các phương thức wait() và notify(). Cả hai phương thức

này phải được gọi trong mã đồng bộ.

Một số điểm cần nhớ trong khi sử dụng phương thức wait():

 Luồng gọi trả CPU  Luồng gọi mở khóa  Luồng gọi đi vào vùng đợi của monitor.

Các điểm chính cần nhớ về phương thức notify()

 Một luồng vùng đợi của monitor chuyển sang trạng thái sẵn sàng.  Luồng mà đã được thông báo phải yêu cầu khóa monitor trước khi nó có thể bắt đầu.

 Phương thức notify() là không chính xác, vì nó không thể chỉ ra được luồng được thông báo. Trong một trạng thái đã trộn lẫn, luồng có thể thay đổi trạng thái của monitor mà điều này làm ảnh hưởng đến luồng đã được đưa thông báo. Trong trường hợp này, các phương thức của monitor đưa ra 2 sự đề phòng: o Trạng thái của monitor sẽ được kiểm tra trong một vòng lặp “while” thay vì là câu lệnh if o Sau khi thay đổi trạng thái của monitor, phương thức notifyAll() nên được sử dụng thay vì notify().

import java.applet.*;

import java.awt.*;

import java.awt.event.*;

/* */

public class mouseApplet extends Applet implements MouseListener{

boolean click;

241

Ví dụ 6.7: Biểu thị cho việc sử dụng các phương thức notify(0 và wait():

int count;

public void init() {

super.init();

add(new clickArea(this)); //doi tuong ve duoc tao ra va them vao

add(new clickArea(this));//doi tuong ve duoc tao ra va them vao

addMouseListener(this);

}

public void mouseClicked(MouseEvent e) {

}

public void mouseEntered(MouseEvent e) {

}

public void mouseExited(MouseEvent e) {

}

public void mousePressed(MouseEvent e) {

synchronized (this) {

click = true;

notify();

}

count++; //dem viec click

Thread.currentThread().yield();

click = false;

}

public void mouseReleased(MouseEvent e) {

}

} //kết thúc Applet

class clickArea extends java.awt.Canvas implements Runnable{

mouseApplet myapp;

clickArea(mouseApplet mapp){

this.myapp = mapp;

setSize(40,40);

242

new Thread(this).start();

}

public void paint(Graphics g){

g.drawString(new Integer(myapp.count).toString(),15,20);

}

public void run(){

while(true){

synchronized (myapp) {

while(!myapp.click){

try{

myapp.wait();

}catch(InterruptedException ie){

}

}

}

repaint(250);

}

}//end run

}

Không cần các phương thức wait() và notify(), canvas không thể biết khi nào cập

nhập hiển thị. Kết quả xuất ra ngoài của chương trình được đưa ra như sau:

243

Hình 6.7. Kết quả sau mỗi lần kích chuột

6.12. Khoá chết (Deadlock)

Một “deadlock” xảy ra khi hai luồng có một phụ thuộc vòng trên một cặp đối tượng đồng bộ; ví dụ, khi một luồng thâm nhập vào monitor trên đối tượng “ObjA”,

và một luồng khác thâm nhập vào monitor trên đối tượng “ObjB”. Nếu luồng trong

“ObjA” cố gắng gọi phương thức đồng bộ trên “ObjB” khoá chết xảy ra.

Khó để tìm ra khóa chết bởi những nguyên nhân sau:

 Nó hiểm khi xảy ra, khi hai luồng chia nhỏ thời gian thực thi cùng lúc  Nó liên quan đến nhiều hơn hai luồng và hai đối tượng đồng bộ

Nếu một chương trình đa luồng bị treo thường xuyên, ngay lập tức kiểm tra lại

điều kiện gây ra khoá chết.

Ví dụ 6.8:sau tạo ra điều kiện khoá chết. Lớp chính bắt đầu 2 luồng. Mỗi luồng

gọi phương thức đồng bộ run(). Khi luồng “t1” thức dậy, nó gọi phương thức

“synchIt()” của đối tượng deadlock “dlk2”. Khi luồng “t1” monitor “dlk2”, luồng “t1”

bắt đầu đợi monitor. Khi luồng “t2” thức, nó cố gắng gọi phương thức “synchIt()” của

đối tượng Deadlock “dlk1”. Bây giờ, “t2” cũng phải đợi, bởi vì đây là trường hợp

tương tự với luồng “t1”. Từ đó, cả hai luồng đang đợi lẫn nhau, cả hai sẽ không bao

public class Deadlock implements Runnable{

public static void main(String args[]){

Deadlock dlk1= new Deadlock();

Deadlock dlk2 = new Deadlock();

Thread t1 = new Thread(dlk1);

Thread t2 = new Thread(dlk2);

dlk1.grabIt = dlk2;

dlk2.grabIt = dlk1;

t1.start();

t2.start();

System.out.println("Started");

try{

t1.join();

t2.join();

}catch(InterruptedException e){

System.out.println("error occured");

244

giờ thức được. Đây là điều kiện khoá chết.

}

System.exit(0);

}

Deadlock grabIt;

public synchronized void run() {

try{

Thread.sleep(1500);

}catch(InterruptedException e){

System.out.println("error occured");

}

grabIt.syncIt();

}

public synchronized void syncIt() {

try{

Thread.sleep(1500);

System.out.println("Sync");

}catch(InterruptedException e){

System.out.println("error occured");

}

System.out.println("In the syncIt() method");

}

}

245

Kết quả của chương trình này được hiển thị như sau:

Hình 6.8. Kết quả thực hiện ví dụ 6.8

6.13. Thu dọn rác

Thu dọn “rác” (Garbage collection) cải tạo hoặc làm trống bộ nhớ đã cấp cho các

đối tượng mà các đối tượng này không sử dụng trong thời gian dài. Trong ngôn ngữ lập trình hướng đối tượng khác như C++, lập trình viên phải tự giải phóng. Thất bại

trong việc giải phóng bộ nhớ có thể gây ra một số hậu quả. Java tự động tiến hành thu

dọn rác để cung cấp giải pháp cho vấn đề này. Một đối tượng trở nên thích hợp cho sự

dọn rác nếu không có tham chiếu đến nó, hoặc nếu nó được gán bằng null.

Trình thực thi dọn rác là một luông chạy ngầm (deamon) co mức ưu tiên thấp.

Ta có thể gọi phương thức gc() của thể nghiệm để dọn rác. Tuy nhiên, bạn không thể

dự đoán hoặc bảo đảm rằng sự dọn rác sẽ thực thi ngay sau đó.

Sử dụng câu lệnh sau để tắt đi sự dọn rác trong ứng dụng:

Java –noasyncgc ….

Nếu chúng ta tắt đi sự dọn rác, chương trình hầu như chắc chắn rằng bị treo do

thiếu bộ nhớ.

Phƣơng thức finalize()

Java cung cấp một phương pháp để làm sạch rác trước khi một luồng, chương

trình kết thúc. Điều này tương tự như phương thức Destructor của C++

Phương thức finalize(), nếu có, sẽ được thực thi trên mỗi đối tượng, trước khi

sự dọn rác thực hiện.

Câu lệnh của phương thức finalize() như sau:

protected void finalize() throws Throwable

Tham chiếu không phải là rác; chỉ các đối tượng mới gọi là rác

Ví du:

246

Object a = new Object();

Object b = a;

a = null;

Ở đây, nó sẽ sai khi nói rằng “b” là một đối tượng. Nó chỉ là một tham chiếu.

Hơn nữa, trong đoạn mã trích trên mặc dù “a‟ được đặt là rỗng, nó không thể được

dọn, bởi vì nó vẫn còn có một tham chiếu b đến nó. Vì thế “a” vẫn còn với truy cập

được, thật vậy, nó vẫn còn có phạm vi sử dụng trong phạm vi chương trình. Ở đây, nó sẽ không được dọn.

Tuy nhiên, trong ví dụ cho dưới đây, giả định rằng không có tham chiếu đến “a”

tồn tại, đối tượng “a” trở nên thích hợp cho việc dọn rác.

Object a = new Object();

a = null;

Một ví dụ khác:

Object m = new Object();

Object m = null;

Đối tượng được tạo ra ban đầu trở thành đối tượng cần dọn cho bộ dọn rác

Object m = new Object();

m = new Object();

Bây giờ, ban đầu sẽ được dọn, và một đối tượng mới tham chiếu bởi “m” đang

tồn tại.

Bạn có thể chạy phương thức gc() (garbage collection), nhưng không bảo đảm

rằng nó sẽ chạy.

class GCDemo

{

public static void main(String args[])

{

int i;

long a; ,

247

Ví dụ 6.9: điển hình cho gc().

Runtime r=Runtime.getRuntime();

long valuesD =new long[200];

System.out.println("Amount of free memory is" + r.freeMemory());

r.gc();

System.out.println("Amount of free memory after garbage collection is "

+ r.freeMemory());

for (a=10000,i=0;i<200;a++,i++)

{

values[i] =new long(a);

}

System.out.println("Amount of free memory after creating the array

" + r.freeMemory());

for (i=0;i<200;i++)

{

values[i] =null;

}

System.out.println("Arnount of free memory after garbage collection is

" + r.freeMemory());

}

}

Chúng ta khai một mảng gồm 200 phần tử, trong đó kiểu dữ liệu là kiểu long.

Trước khi mảng được tạo ra, chúng ta phải xem lượng bộ nhớ trống, và hiển thị nó.

Rồi thì chúng ta gọi phương thức gc() của đối tượng Runtime hiện thời. Điều này

không chắc sẽ thực thi dọn rác ngay. Rồi chúng ta tạo ra mảng, và gán giá trị cho các phần tử của mảng. Điều này sẽ giảm bớt số lượng bộ nhớ trống. Để làm các phần tử mảng trở thành đối tượng cho bộ thu nhặt rác ta gán chúng bằng null. Cuối cùng, chúng ta sử dụng phương thức gc() để gọi bộ dọn rác lần nữa.

248

Kết quả xuất ra màn hình của chương trình trên như sau:

249

Hình 6. 9. Kết quả chạy ví dụ 6.9

Câu hỏi và bài tập chƣơng 6 1. Viết một chương trình mà hiển thị một sự đếm lùi từng giây cho đến không, như hình sau:

Ban đầu, số 300 sẽ được hiển thị. Giá trị sẽ được giảm dần cho đến 1 đến khi đạt

giá trị 0. Giá trị sẽ được trả lại 300 một lần nữa giảm đến trở thành 0.

2. Viết một chương trình mà hiển thị như hình dưới đây:

Tạo 3 luồng và một luồng chính trong “main”. Thực thi mỗi luồng như một chương trình. Khi chương trình kết thúc, các câu lệnh thoát cho mỗi luồng sẽ được hiển thị. Sử dụng kỹ thuật xử lý ngoại lệ.

250

3. Sử dụng Java Awt, Java Applet, và Thread viết chương trình vẽ một đoạn thẳng màu đỏ trên nền đen chuyển động quy trung quanh một tâm cố định theo mẫu ở hình sau:

4. Sử dụng Java Awt, Java Applet, Thread vẽ ra màn hình dòng chữ “WellCome” màu

magenta, chạy ngẫu nhiên trên màn hình khi chạm các cạnh sẽ bật trở lại theo mẫu ở

hình sau:

5. Sử dụng Java Awt, Java Applet, Thread vẽ ra màn hình dòng chữ “Java Applet”

màu xanh, chạy từ trái sang phải theo mẫu ở hình sau:

251

6. Sử dụng Java Awt, Java Applet, Thread vẽ ra màn hình dòng chữ “Khoa Công Nghệ Thông Tin” chạy từ phải sang trái mẫu ở hình sau:

7. Sử dụng Java Awt, Java Applet và Thread viết chương trình tạo hình quả bóng

chuyển động ngẫu nhiên trên màn hình, khi chạm vào các cạnh sẽ bật trở lại theo mẫu

ở hình sau:

8. Viết một chương trình cho thấy rằng những Thread có độ ưu tiên cao được thực thi,

những thread có độ ưu tiên thấp sẽ bị dừng.

9. Viết một chương trình ví dụ cho một thread có độ ưu tiên cao sử dụng Sleep để cho

những thread có độ ưu tiên thập sẽ chuyển sang chế độ run.

10. Viết một chương trình mà hiển thị một sự đếm lùi từng giây cho đến không, như

hình sau:

252

11. Viết một chương trình hiển thị như hình dưới đây:

Tạo 3 luồng và một luồng chính trong “main”. Thực thi mỗi luồng như một

chương trình. Khi chương trình kết thúc, các câu lệnh thoát cho mỗi luồng sẽ được

253

hiển thị. Sử dụng kỹ thuật xử lý ngoại lệ.

TÀI LIỆU THAM KHẢO

[1]. Trần Tiến Dũng. Giáo Trình Lý Thuyết Và Bài Tập Java. NXB Lao động xã hội.

2005.

[2]. Hoàng Đức Hải. Nguyễn Phương Lan. Java Tập 1. NXB Lao động xã hội. 2004.

[3]. Hoàng Đức Hải. Nguyễn Phương Lan. Java Tập 2. NXB Lao động xã hội. 2004.

[4]. Hồ Trọng Long. Nguyễn Duy Hoàng Mỹ. Nhập Môn Lập Trình Java. NXB Thống

Kê. 2005

[5]. Đoàn Thiện Ngân. Cấu trúc dữ liệu với Java. Nhà xuất bản Lao động – Xã

hội2005

[6]. Bruce Eckel, Thinking in Java, Prentice-Hall mid-June, 2006.

[7]. Horstmann and GaryCornell.Core JavaVolume II--Advanced Features (9th

Edition) (Core Series). Publisher2013.

[8]. Michael Ernest. Java SE 7 Programming Essentials, Publisher2013

[9]. http://www.programmingtutorials.com/java.aspx

[10]. http://www.oracle.com/technetwork/java/javase/documentation/index.html

254

[11]. http://www.tutorialspoint.com/java/java_encapsulation.htm