lập trình hướng Đối tượng trong java ( vietnamese )

114
AptechVietnam 1/114 Chương 1: Giới thiệu vJava Mục tiêu bài học Kết thúc bài học này, học viên có thể: Tìm hiểu các thực thể trong thế giới thực như là các đối tượng Định nghĩa một đối tượng phần mềm Tìm hiểu và giải thích cấu trúc của một lớp So sánh các lớp và các đối tượng Tìm hiểu tầm quan trọng của Java Tìm hiểu bộ JDK và các công cụ của nó Tìm hiểu về máy ảo Java - Java Virtual Machine (JVM) 1.1 Giới thiệu về lớp và đối tượng Các đối tượng (object) và các lớp (class) nền tảng để xây dựng Java. Lớp là một khuôn mẫu, bao gồm một tập các đối tượng có tính chất tượng tự nhau. Lớp mô tả tất cả các thuộc tính của một tập các đối tượng cụ thể. 1.1.1 Các thực thể trong thế giới thực như là các đối tượng Đối tượng là khái niệm dùng để mô tả các thực thể trong thế giới thực. dụ các đối tượng có thể nhainf thấy quanh ta như: Chó - Dogs, Ô tô - Cars, bông hoa - Flowers, ngôi nhà Houses, … Bất kỳ một thực thể hữu hình hoặc có thể sờ mó được nào đó, đều có thể xem như như một đối tượng trong thế giới thực. Mỗi đối tượng có các thuộc tính hoặc tính chất nhằm mô tả nó là cái gì. Ví dụ, các thuộc tính của đối tượng Dog là: Giống Màu lông Tuổi Ngoài ra, một đối tượng có thể thực hiện các hành động. Chẳng hạn, các hành động của Dog có thể thực hiện là: Sủa Ăn

Upload: dong-luong

Post on 22-Jan-2017

1.123 views

Category:

Education


0 download

TRANSCRIPT

Page 1: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 1/114

Chương 1: Giới thiệu về Java

Mục tiêu bài học

Kết thúc bài học này, học viên có thể:

Tìm hiểu các thực thể trong thế giới thực như là các đối tượng

Định nghĩa một đối tượng phần mềm

Tìm hiểu và giải thích cấu trúc của một lớp

So sánh các lớp và các đối tượng

Tìm hiểu tầm quan trọng của Java

Tìm hiểu bộ JDK và các công cụ của nó

Tìm hiểu về máy ảo Java - Java Virtual Machine (JVM)

1.1 Giới thiệu về lớp và đối tượng

Các đối tượng (object) và các lớp (class) là nền tảng để xây dựng Java. Lớp là một khuôn mẫu, bao gồm một tập các đối tượng có tính chất tượng tự nhau. Lớp mô tả tất cả các thuộc tính của một tập các đối tượng cụ thể.

1.1.1 Các thực thể trong thế giới thực như là các đối tượng

Đối tượng là khái niệm dùng để mô tả các thực thể trong thế giới thực. Ví dụ các đối tượng có thể nhainf thấy quanh ta như: Chó - Dogs, Ô tô - Cars, bông hoa - Flowers, ngôi nhà – Houses, … Bất kỳ một thực thể hữu hình hoặc có thể sờ mó được nào đó, đều có thể xem như như một đối tượng trong thế giới thực.

Mỗi đối tượng có các thuộc tính hoặc tính chất nhằm mô tả nó là cái gì. Ví dụ, các thuộc tính của đối tượng Dog là:

Giống

Màu lông

Tuổi

Ngoài ra, một đối tượng có thể thực hiện các hành động. Chẳng hạn, các hành động của Dog có thể thực hiện là:

Sủa

Ăn

Page 2: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

2/114 AptechVietnam

Chạy

Như vậy, hai yếu tố quan trọng nhất của đối tượng trong thế giới thực là: thuộc tính (Attribute) và hành động (Action). Nhờ đó, đối tượng có thể được định nghĩa như một thực thể tồn tại, có các thuộc tính và hành động hoàn toàn xác định. Tương tự như vậy, trong phạm vi của công nghệ phần mềm, các đối tượng cũng được mô tả dựa theo trạng thái và hành vi của nó. Trạng thái của một đối tượng phần mềm được định nghĩa bởi các thuộc tính của nó. Các hoạt động của một đối tượng phần mềm được định nghĩa như là các hành vi của nó. Hãy xem xét một đối tượng trong thế giới thực, chẳng hạn Dog, một con chó. Nếu đối tượng này được ánh xạ đến một đối tượng phần mềm, trạng thái của nó có thể là màu lông, giống và tuổi. Hành vi của đối tượng Dog có thể là sủa, ăn, chạy, … Vì vậy, một đối tượng phần mềm là một thực thể bao gồm các trạng thái (thuộc tính) và hành vi (hành động). Nó lưu giữ các trạng thái của nó trong các trường, được gọi là các biến trong ngôn ngữ lập trình. Còn các hành vi được xem như là các phương thức, giống như các hàm trong ngôn ngữ lập trình. Một phương thức là một chuỗi các câu lệnh được đặt tên nhằm thực hiện các tác vụ cụ thể nào đó. Tham khảo hình 1.1, đây là ví dụ về đối tượng „Cashier‟ (Thủ quỹ)

Hình 1.1: Đối tượng „Cashier‟ và đối tượng „Customer‟

Các thuộc tính của đối tượng „Cashier‟ là:

Tên

Chức vụ

Tuổi

Một đối tượng thực thi một số hành động. Các hành động của đối tượng ‟Cashier‟ là:

Page 3: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 1 Giới thiệu về Java

3/114

Thu tiền từ khách hàng

In hoá đơn

Sự thuận lợi khi sử dụng đối tượng là:

Nó giúp chúng ta hiểu hơn về thế giới thực

Nó ánh xạ các thuộc tính và các hành động của các đối tượng trong thế

giới thực thành trạng thái và hành vi của các đối tượng phần mềm.

1.1.2 Class (Lớp)

Như đề cập ở trên, Dog là một ví dụ về một đối tượng trong thế giới thực. Tuy nhiên, trong thế giới thực có nhiều loại động vật khác như mèo, cừu, cọp, …Vì vậy, Dog chỉ là một thể hiện của lớp động vật. Các thuộc tính và hành động chung của một số đối tượng được nhóm lại thành một đơn vị. Đơn vị này có thể được dùng để tạo ra một đại diện của loại tương ứng, gọi là lớp. Mỗi một lớp mô tả một tập các đối tượng riêng lẻ. Một lớp là một khuôn mẫu hoặc bản thiết kế định nghĩa đặc điểm chính của trạng thái và hành vi cho tất cả các đối tượng thuộc về lớp. Tất cả các thể hiện của lớp, được gọi là đối tượng, sẽ có chung các trạng thái và hành vi. Tuy nhiên, các thuộc tính và hành động có thể được gán chỉ sau khi một đối tượng được tạo ra. Khi đối tượng được tạo ra ta mới có một đại diện thật sự của thực thể. Vì vậy, lớp là một mô hình khái niệm của thực thể. Nó mô tả các thuộc tính và hành động chung chứ không riêng cho một đối tượng cụ thể nào. Ví dụ, xem xét một lớp có tên Animal (Động vật) như trong bảng 1.1. Lớp chứa các thuộc tính (trạng thái) và hành động (hành vi) của động vật.

Class Animal

Type

Animal Name

Color

Motion

Breathing

Eating

Bảng 1.1: Mô tả lớp Animal

Lớp này có trạng thái „Type‟, „Animal Name‟ và „Color‟. Trong đó, Type - Mô tả loài động vật.

Animal name - Tên loại động vật, chẳng hạn cừu, cọp, ếch, …

Page 4: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

4/114 AptechVietnam

Color - Mô tả màu da/ lông của loài đó

„Motion‟ (Di chuyển), „Breathing‟ (Thở) và „Eating‟ (Ăn) là các hành vi của lớp Animal.

1.1.3 So sánh giữa lớp (class) và đối tượng (object)

Có sự khác biệt giữa một lớp và một đối tượng. Một lớp mô tả một thực thể, trong khi một đối tượng là một thực thể thật sự. Đối tượng là vật thật, trong khi lớp là một mô hình khái niệm định nghĩa tất cả các trạng thái và hành vi cần thiết của một đối tượng. Một lớp là một nguyên mẫu (prototype) của một đối tượng. Nó định rõ các hành động và các tính chất cần thiết cho một loại đối tượng cụ thể. Bảng 1.2 là ví dụ về lớp và đối tượng.

Class (Lớp)

Object (Đối tượng)

Animal Cat

Dog

Car Honda City

Benz

Bảng 1.2: Ví dụ về lớp và đối tượng

Lớp không thay đổi, trong khi dữ liệu chứa trong một đối tượng có thể thay đổi. Các thuộc tính của lớp được thiết lập trong suốt thời gian chay chương trình, không thể thêm bớt thuộc tính trong lúc chương trình đang chạy. Khi một đối tượng được tạo từ một lớp, nó sẽ là một thành phần của lớp đó cho đến khi bị huỷ bỏ. Sự tồn tại của đối tượng có hạn chế, đối tượng sẽ được tạo ra và hủy bỏ theo đúng trình tự. Mỗi đối tượng được gọi là một thể hiện của một lớp. Trong ví dụ bên dưới, Employee là một lớp, còn các đối tượng là: Cashier, System Administrator, Stock Manager, Salesman, Purchase Manager.

Page 5: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 1 Giới thiệu về Java

5/114

Hình 1.2: Các đối tượng và lớp.

1.2 Giới thiệu về Java

Vào năm 1995, hãng Sun Microsystem giới thiệu một ngôn ngữ lập trình mới là Java. Cho đến giờ, từ “Java” chỉ có nghĩa là tên một hòn đảo ở Indonesia hoặc một kiểu cách pha café nào đó.

1.2.1 Sự cần thiết của Java

Java là một ngôn ngữ dành cho các nhà lập trình chuyên nghiệp. Java được xây dựng trên cơ sở ngôn ngữ lập trình C và C++. Nó kế thừa cú pháp của C và các nét đặc trưng của C++. Một nhóm kỹ sư của hãng Sun Microsystem cần thiết kế một ngôn ngữ cho các thiết bị điện tử như tivi, máy giặt, …Vì vậy, mục đích cơ bản là phát triển ngôn ngữ để tạo ra phần mềm có thể nhúng trong các thiết bị điện tử. Trong ngôn ngữ C và C++ trình biên dịch phụ thuộc vào loại CPU riêng biệt. Các trình biên dịch rất đắt và mất nhiều thời gian để tạo ra. Do đó, không thể có trình biên dịch cho mọi loại CPU. Và yêu cầu là cần có cách dễ dàng và hiệu quả để tạo ra các trình biên dịch. Ngoài ra, phần mềm phải nhỏ, nhanh, hiệu quả và độc lập nền tảng, nghĩa là mã chương trình có thể thực hiện trên nhiều CPU khác nhau dưới các môi trường khác nhau. Trong nỗ lực tạo ra một ngôn ngữ độc lập nền tảng và khả chuyển, James Gosling và nhóm cộng sự đi tiên phong

Page 6: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

6/114 AptechVietnam

trong vấn đề này. Kết quả là sự ra đời của ngôn ngữ Java. Đầu tiên nó được gọi là “Oak”, sau đó được đổi tên thành Java. Ngày nay, Java đã có những bước phát triển hết sức thành công và được chấp nhận bởi hàng triệu lập trình viên trên toàn thế giới. Java là một ngôn ngữ lập trình phổ dụng, được dùng để viết các chương trình có thể chạy được trên Internet. Nét đặc trưng chính của Java là ngôn ngữ hướng đối tượng và độc lập nền tảng. Độc lập nền tảng có nghĩa là chương trình có thể chạy trên nhiều nền tảng khác nhau như: Microsoft Windows, Apple Macintosh, Linux, …Java không những được dùng cho các ứng dụng độc lập, các chương trình trên mạng, mà còn được dùng trong các thiết bị tiêu dùng như: điện thoại, các thiết bị cầm tay, …

1.2.2 Các đặc trưng của ngôn ngữ Java

Hướng đối tượng (Object-oriented)

Java là một ngôn ngữ hướng đối tượng. Trong Java không có các hằng, biến hoặc hàm độc lập. Nghĩa là tất cả đều là một phần của đối tượng. Các hằng, biến và hàm được truy nhập thông qua các lớp và các đối tượng.

Các ngôn ngữ hướng đối tượng lai ghép khác như C++, có đặc điểm của ngôn ngữ cấu trúc cộng thêm sự mở rộng đối tượng. Ví dụ, C++ là một ngôn ngữ hướng đối tượng, nhưng lập trình theo hướng cấu trúc, phương thức main() nằm ngoài bất kỳ lớp và đối tượng nào. Java thì không cho phép cách khai báo này. Trong Java, phương thức main() chỉ được khai báo bên trong một lớp.

Độc lập nền tảng (Platform independent)

Java là một nền tảng để phát triển ứng dụng. Nó có thể được dùng như một ngôn ngữ. Nền tảng ở đây là sự kết hợp giữa phần cứng và phần mềm hệ thống mà hầu hết các phần mềm đều chạy được. Ví dụ, bộ xử lý Intel chạy trên nền Windows XP là một nền tảng.

Độc lập nền tảng nói đến khả năng của chương trình di chuyển từ một nền tảng này sang một nền tảng khác không có bất kỳ một khó khăn nào. Độc lập nền tảng trong Java ở mức mã nguồn và mã bytecode (mã trung gian).

Java là một ngôn ngữ định kiểu mạnh mẽ, nghĩa là phải cần khai báo kiểu dữ liệu cho mọi biến. Kiểu dữ liệu trong Java là đồng nhất trong tất cả các nền tảng phát triển. Java có các lớp thư viện cơ sở. Điều này cho phép các lập trình viên có thể di chuyển mã từ nền tảng này đến một nền tảng khác mà không cần viết lại mã.

Page 7: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 1 Giới thiệu về Java

7/114

Nói tóm lại, độc lập nền tảng ở mức mã nguồn cho phép di chuyển mã nguồn từ một hệ thống này sang một hệ thống khác, biên dịch và thực hiện êm xuôi trên mọi hệ thống.

Sử dụng mã bytecode, Java đã giải quyết vấn đề độc lập nền tảng. Không giống như trình biên dịch của C, trình biên dịch Java đưa ra một định dạng đặc biệt, đó là bytecode, giống nhau trên mọi nền tảng.

Các chương trình Java được biên dịch thành bytecode vẫn cần một trình thông dịch để thực thi nó trên bất kỳ một nền tảng đã định sẵn nào. Công việc của trình thông dịch là đọc mã bytecode và chuyển nó thành ngôn ngữ máy.

Để chạy Java trên một máy tính hoặc một hệ điều hành mới, chỉ cần trình thông dịch và một vài gói thư viện cần thiết. Bởi vì bytecode là độc lập nền tảng.

Mạnh mẽ

Java là ngôn ngữ định kiểu mạnh mẽ. Nó được thiết kế để viết các phần mềm có độ tin cậy cao và mạnh. Tuy nhiên, nó yêu cầu phải khai báo các phương thức một cách rõ ràng. Java kiểm tra lỗi cú pháp tại thời điểm biên dịch, và tại thời điểm thông dịch. Vì vậy, nó chắc chắn loại bỏ một số lỗi trong lập trình.

Java không có con trỏ và phép tính con trỏ. Nó kiểm tra tất cả truy nhập đến mảng và chuỗi tại thời điểm chạy chương trình. Ngoài ra nó còn kiểm tra sự chuyển kiểu của đối tượng từ kiểu này đến kiểu khác tại thời điểm chạy chương trình.

Trong các môi trường lập trình truyền thống, lập trình viên phải tự cấp phát bộ nhớ. Kết thúc chương trình, lập trình viên phải giải phóng vùng bộ nhớ này. Các sự cố sẽ nảy sinh khi lập trình viên quên giải phóng bộ nhớ. Trong Java, lập trình viên không cần quan tâm đến việc giải phóng bộ nhớ. Nó được thực hiện tự động bởi bộ thu gom rác (garbage collection).

An toàn (Secure)

Virus máy tính 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ét virus các tập tin trước khi tải về và 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ý việc 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ì thế, Java không chỉ là ngôn ngữ lập

Page 8: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

8/114 AptechVietnam

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. Ở mức đầu tiên, dữ liệu và các phương thức được truy nhập thông qua các interface (giao tiếp) do lớp cung cấp. Java không hỗ trợ các phép toán con trỏ. Do đó, nó không cho phép truy nhập trực tiếp đến các vùng nhớ và cung cấp cơ chế thu gom rác giúp thu hồi bộ nhớ không sử dụng. Các đặc trưng này giúp Java an toàn tối đa và có khả năng khả chuyển cao. Trong mức 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. Mức thứ ba, được đảm bảo bởi trình thông dịch. Chúng kiểm tra xem bytecode phải đảm bảo các qui tắc an toàn trước khi thực thi. Mức 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.

Phân tán (Distributed)

Java có thể được dùng để phát triển các ứng dụng trên nhiều nền tảng, hệ điều hành và nhiều giao tiếp người dùng đồ hoạ. Java được thiết kế để hỗ trợ các ứng dụng trên mạng. Do đó, Java được dùng như là một công cụ phát triển trên môi trường Internet.

Đa luồng (Multithreaded)

Đa luồng là yếu tố cần thiết cho một ngôn ngữ như Java. Một ứng dụng thực hiện một tác vụ trong khi đang chờ người dùng nhập liệu. Trong một ứng dụng đồ hoạ trên mạng như một trình duyệt web, thường có rất nhiều tác vụ xảy ra tại một thời điểm. Java cung cấp hỗ trợ cho xử lý gọi là “multithreading” để thực hiện nhiều tác vụ đồng thời. Java cung cấp cơ chế đồng bộ nhiều xử lý. Cơ chế này cũng hỗ trợ cho luồng (thread) giúp các ứng dụng tương tác trên mạng chạy êm xuôi.

Động (Dynamic)

Java được thiết kế như một ngôn ngữ động để đáp ứng cho những môi trường mở. Mã nguồn Java được lưu trữ trong tập tin có dạng .java. Trình biên dịch sẽ biên dịch chúng thành bytecode chứa trong tập tin .class. Mỗi tập tin .java thường được biên dịch thành một tập tin .class.

Đầu tiên trình biên dịch kiểm tra đường dẫn trên thư mục hiện hành và các thư mục khác được xác định trong biến môi trường CLASSPATH. Điều này cần thiết giúp xác định các lớp khác được tham chiếu trong tập tin nguồn. Ví dụ, nếu tập tin được biên dịch phụ thuộc vào các tập tin không được biên dịch khác, trình biên dịch sẽ cố tìm và biên dịch chúng. Trình biên dịch có thể điều khiển sự phụ thuộc vòng tròn cũng như các phương thức được sử dụng trước khi được khai báo. Nó cũng xác định tập tin nguồn có thay đổi so với lần biên dịch cuối cùng hay không. Vì vậy, trình biên dịch khá thông minh.

Page 9: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 1 Giới thiệu về Java

9/114

Kiến trúc trung lập (Architecture-neutral)

Công nghệ Java được thiết kế để hỗ trợ các ứng dụng sẽ được triển khai trong các môi trường không đồng nhất trên mạng. Trong các môi trường như vậy, các ứng dụng phải có khả năng thực thi tốt trên nhiều kiến trúc phần cứng.

Các chương trình Java được biên dịch thành một định dạng bytecode không phụ thuộc kiến trúc có thể thực thi trên nhiều nền tảng phần cứng và phần mềm. Vì vậy, vấn đề phân phối nhị phân và phiên bản được giải quyết bởi bản chất thông dịch của công nghệ Java.

Các chương trình Java chạy trên bất kỳ hệ thống nào có hỗ trợ máy ảo Java. Điều này không những hữu ích trên mạng mà còn có ích trên các hệ thống phân tán.

Khả chuyển (Portable)

Khả chuyển là hệ quả của kiến trúc trung lập. Công nghệ Java có được khả năng khả chuyển là nhờ vào định nghĩa chặt chẽ của ngôn ngữ. Nó chỉ rõ kích thước của các kiểu dữ liệu cơ bản để loại trừ sự phụ thuộc cài đặt và hành vi của các toán tử số học. Hệ thống Java bản thân là khả chuyển. Trình biên dịch Java được viết bằng Java, trong khi hệ thống thực thi Java được viết bằng ANSI C với ranh giới khả chuyển rõ ràng.

Hiệu suất cao (high performance)

Hiệu suất luôn được cân nhắc. So với các ngôn ngữ kịch bản thông dịch mức cao, Java có hiệu suất cao nhất. Bộ thu gom rác tự động thực hiện như luồng ngầm bên dưới, có độ ưu tiên thấp nhất, đảm bảo khả năng khả chuyển cao nhất. Sun khẳng định khả năng thực thi của bytecode chuyển thành mã máy tốt gần như C hoặc C++.

1.3 Giới thiệu JDK

Sun Microsystems cung cấp ngôn ngữ Java trong sản phẩm gọi là Java Development Kit (JDK). JDK bao gồm các công cụ phát triển, môi trường thực thi (Java Runtime Environment hay JRE), các thư viện, các chương trình demo và mã nguồn. Một số công cụ nằm trong JDK dùng phát triển các ứng dụng Java được mô tả dưới đây:

Trình biên dịch Java, „Javac‟

Trình biên dịch dịch mã nguồn Java thành tập tin class. Trình biên dịch javac được dùng để biên dịch các tập tin mã nguồn Java thành bytecode. Mã nguồn Java có thể được tạo ra bằng bất kỳ trình soạn thảo văn bản nào, chẳng hạn Notepad

Cú pháp:

Javac [option] source

Page 10: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

10/114 AptechVietnam

Trong đó,

source - 1 hoặc nhiều tên tập tin có phần mở rộng là .java

option - Các tuỳ chọn dòng lệnh

Ví dụ,

Javac FirstProgram.java

Câu lệnh này sẽ tạo ra một tập tin có tên „FirstProgram.class‟. Tập tin class này sẽ chạy trên máy ảo Java (JVM). Một vài tuỳ chọn của câu lệnh javac được mô tả trong bảng 1.3

Tùy chọn (Option)

Mô tả

-classpath

Xác định rõ vị trí chứa các lớp được tham chiếu trong mã nguồn (được lưu trong biến môi trường CLASSPATH)

-d Xác định thư mục đích chứa các tập tin .class

-g In tất cả các thông tin gỡ lỗi thay cho số dòng và tên tập tin mặc định.

-verbose Hiển thị các thông tin đầu ra mỗi lần lớp được nạp và tập tin nguồn được biên dịch.

-version Hiển thị thông tin về phiên bản

-sourcepath Cho biết vị trí lớp

-help Hiển thị các tuỳ chọn chuẩn

Bảng 1.3: Các tuỳ chọn của trình biên dịch Javac

Xem xét ví dụ sau sử dụng tuỳ chọn –d:

javac –d c:\FirstProgram.java

Câu lệnh này sẽ biên dịch và lưu tập tin „FirstProgram.class‟ trong ổ đĩa C.

Trình thông dịch Java, „Java‟

Trình thông dịch Java được sử dụng để thực thi mã bytecode. Nó lấy đối số là tên tập tin .class để thực thi hoặc tên tập tin lưu trữ Java gọi là jar.

Cú pháp:

Page 11: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 1 Giới thiệu về Java

11/114

java [option] classname [arguments]

Trong đó,

option - Các tuỳ chọn dòng lệnh

classname - Tên tập tin class được dùng

arguments - Đối số được truyền vào cho hàm main

Ví dụ, Java FirstProgram

Một vài tuỳ chọn của câu lệnh Java được mô tả trong bảng 1.4

Tùy chọn Mô tả

-classpath Xác định rõ vị trí chứa các lớp cần (được lưu trong biến môi trường CLASSPATH)

-v hoặc

–verbose

Hiển thị các thông tin đầu ra mỗi lần lớp được nạp và tập tin nguồn được biên dịch.

-version Hiển thị thông tin về phiên bản và thoát

-jar Sử dụng tên tập tin JAR thay cho tập tin class

-help Hiển thị thông tin trợ giúp về lệnh Java và thoát

-X Hiển thị thông tin và các tùy chọn phi chuẩn và thoát

Bảng 1.4: Các tuỳ chọn của trình thông dịch Java

1.4 Máy ảo Java (Java Virtual Machine - JVM)

Máy ảo Java là trái tim của ngôn ngữ lập trình Java. Môi trường Java bao gồm năm phần tử sau:

Ngôn ngữ

Ðịnh nghĩa Bytecode

Các thư viện lớp Java/Sun

Máy ảo Java (JVM)

Cấu trúc của tập tin .class

Page 12: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

12/114 AptechVietnam

Đặc tính khả chuyển của tập tin .class cho phép các chương trình Java viết một lần chạy mọi nơi (“Write once, run anywhere”). Đặc tính này có được nhờ sự cài đặt máy ảo Java (JVM).

1.4.1 Máy ảo Java là gì?

Lập trình viên cần thiết phải biết máy ảo Java là gì và cách làm việc của nó. Điều này sẽ giúp ta tận dụng được điểm mạnh của JVM và tránh những điểm yếu. Máy ảo là một khái niệm phần mềm dựa trên ý tưởng của máy tính tưởng tượng. 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. Máy ảo có thể được xem như một hệ điều hành thu nhỏ. 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 không phụ thuộc vào phần cứng riêng biệt. Trình thông dịch là một ứng dụng hiểu các lệnh của máy ảo và chuyển các lệnh đó thành các lệnh thực hiện trên phần cứng bên dưới. Máy ảo, về cơ bản, tạo ra một hệ thống thực thi giúp thực hiện các mã lệnh bằng cách:

Nạp các tập tin .class

Các trình “Class Loaders” là một trong các thành phần cơ bản của kiến trúc máy ảo Java. Nó cho phép máy ảo Java nạp các lớp mà không cần biết bất cứ thứ gì về ngữ nghĩa hệ thống tập tin và cho phép các ứng dụng nạp các lớp Java như các module mở rộng.

Quản lý bộ nhớ

Máy ảo Java quản lý bộ nhớ theo các cách sau:

Khi một máy ảo Java được triệu gọi để chạy một ứng dụng, nó yêu cầu hệ điều hành cấp đủ bộ nhớ cho bản thân JVM chạy và bộ nhớ trống cho các ứng dụng tạo các đối tượng mới.

Khi một đối tượng mới được tạo, JVM cấp phát bộ nhớ cho đối tượng đó từ vùng nhớ trống.

Khi vùng nhớ trống giảm xuống sau khi tạo một số đối tượng, JVM yêu cầu hệ điều hành cấp thêm.

Khi một đối tượng không còn được dùng nó sẽ bị huỷ bỏ. Bộ nhớ bị chiếm dụng sẽ được giải phóng và trả lại vùng bộ nhớ trống.

Khi vùng nhớ trống bị chiếm giữ, và không thể xin cấp thêm vùng nhớ từ hệ điều hành thì JVM tạm dừng ứng dụng và đưa ra thông báo lỗi “Out of memory error”.

Thực hiện thu gom “rác” (Garbage Collection)

Quá trình thu gom rác là giải phóng bộ nhớ đang sử dụng của một đối tượng ngay khi nó kết thúc, nghĩa là đối tượng đó không được truy nhập sau một thời

Page 13: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 1 Giới thiệu về Java

13/114

gian dài. Quá trình này xảy ra tự động và an toàn đối với các đối tượng không còn cần tham chiếu đến nữa.

Khi máy ảo Java thực thi mã, một thanh ghi cục bộ gọi là „program counter‟ được dùng. Thanh ghi này trỏ đến các lệnh đang được thực thi hiện tại. Nếu cần thiết, các lệnh này sửa nội dung thanh ghi này để thay đổi luồng thực thi. Mặt khác, luồng được thực hiện tuần tự nên nó trỏ từ lệnh này đến lệnh khác.

Một khái niệm phổ biến khác được dùng trong Java là trình biên dịch JIT – Just-In-Time. Các trình duyệt web như Netscape Navigator, Internet Explorer bao gồm trình biên dịch JIT nhằm tăng tốc độ thực thi mã Java. Mục đích chính của JIT là chuyển đổi mã bytecode sang mã máy ứng với từng bộ vi xử lý cụ thể. Các lệnh này được lưu trữ và dùng bất cứ lúc nào, được gọi như là một phương thức xác định. Hình 1.3 hiển thị mối quan hệ giữa trình biên dịch Java và JIT.

Hình 1.3: Mối quan hệ giữa trình biên dịch Java và JIT

Mã chương trình được biên dịch và chạy thông qua một máy ảo thay cho vi xử lý của máy tính. Bằng cách sử dụng cách tiếp cận này, mã nguồn Java có thể chạy trên bất kỳ nền tảng nào, nó được biên dịch và chạy thông qua máy ảo Java.

Máy ảo Java không biết gì về ngôn ngữ lập trình Java, nó chỉ nhận ra một định dạng nhị phân xác định của một file gọi là file class. Các chương trình Java được biên dịch thành mã bytecode có thể tuyền trên mạng và thực thi bởi máy ảo Java. Mỗi file class chứa định nghĩa của một lớp hoặc một giao tiếp. File bytecode có phần tên mở rộng là .class

Page 14: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

14/114 AptechVietnam

Tóm Tắt Bài Học

Một đối tượng là một thể hiện của một lớp.

Một lớp định nghĩa một thực thể trên giới hạn của các tính chất và hành động phổ biến.

Một lớp định nghĩa một thực thể, trong khi đó một đối tượng là một thực thể thực sự.

Java là một ngôn ngữ thông dịch và biên dịch.

Các tính năng của Java:

o Hướng đối tượng

o Độc lập nền tảng

o Mạnh mẽ

o An toàn

o Phân tán

o Đa luồng

o Động

o Kiến trúc trung lập

o Khả chuyển

o Hiệu suất cao

Sun Microsystems giới thiệu ngôn ngữ Java như là một sản phẩm dưới tên gọi Java Development Kit (JDK)

Bên trong máy ảo, Java tạo ra một hệ thống thời gian thực giúp thực thi mã lệnh như sau:

o Nạp các files .class

o Quản lý bộ nhớ

o Thực thi việc thu gom rác

Page 15: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 15/114

t

Chương 2: Biến và toán tử

Mục tiêu bài học

Kết thúc bài học này, học viên có thể:

Cú pháp khai báo biến

Định nghĩa các kiểu dữ liệu cơ bản

Tìm hiểu và giải thích cấu trúc của một lớp

Mô tả các kiểu định dạng

Mô tả các ký tự đặc biệt (escape sequences)

Xác định các toán tử khác nhau

2.1 Biến

Biến là đơn vị lưu trữ cơ bản trong chương trình Java. Biến là các thành phần xác định dữ liệu và được dùng để tham chiếu đến các giá trị xác định được tạo ra trong chương trình. Đặt tên các biến có ý nghĩa giúp ta dễ dàng đọc hiểu mã nguồn hơn.

2.1.1 Khai báo biến

Tất cả các biến phải được khai báo trước khi sử dụng. Dạng khai báo biến cơ bản là:

datatype variablename;

Trong đó, datatype - Kiểu dữ liệu hợp lệ variablename - Tên biến hợp lệ Các biến có thể khai báo và khởi tạo theo nhiều cách khác nhau, được minh hoạ trong đoạn mã 1.

Đoạn mã 1:

Line0: int x,y,z; //Khai báo 3 biến kiểu int x,y

//và z

Page 16: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

16/114 AptechVietnam

Line1: int a=5,b,c=10; //Khai báo 3 biến kiểu int

//, khởi tạo a và c

Line2: byte num=20; // Khai báo 1 biến num có kiểu

//byte và khởi tạo giá trị là 20

Line3: char c=‟c‟; //Khai báo c là biến có kiểu

//char, khởi tạo giá trị là c

Line4: int num1=num2=10; //Giá trị 10 được lưu trữ

//trong num1 và num2

Line0 và Line1 là các ví dụ về khai báo danh sách các biến được cách nhau bởi dấu phẩy và Line4 là ví dụ về gán một giá trị cho một hoặc nhiều biến lúc khai báo biến.

Đoạn mã 2 là một ví dụ về gán các giá trị hệ bát phân (8) và hệ thập lục phân (16) cho các biến.

Đoạn mã 2:

Line1: int decVal = 10;//giá trị 10 trong hệ

// thập phân (hệ 10)

Line2: int octVal = 012;//giá trị 10 trong hệ

// bát phân (hệ 8)

Line3: int hexVal = 0xa;//giá trị 10 trong hệ

// thập lục phân (hệ 16)

Đoạn mã 2 hiển thị cú pháp đúng để chứa các giá trị trong hệ bát phân và hệ thập lục phân. Line2 biểu diễn một định dạng trong hệ bát phân với tiếp đầu ngữ 0. Hệ thống số bác phân có 8 ký tự số từ 0 đến 7 để mô tả các số.

Line3 biểu diễn định dạng trong hệ thập lục phân với tiếp đầu ngữ 0x. Hệ thống số thập lục phân có 16 ký tự số để mô tả các số, từ 0 đến 9 và từ ký tự A đến F. Từ A đến F tương ứng từ 10 đến 15. Ví dụ, giá trị 26 trong hệ 16 là:

int hexVal = 0x1a;

Hệ thống thập phân được mô tả trong Line1, là cách dùng thông dụng trong lập trình.

Page 17: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 2 Biến và toán tử

17/114

2.1.2 Khai báo hằng (literals)

Hằng là một giá trị không đổi và được mô tả trực tiếp trong đoạn mã mà không cần đến tính toán. Ví dụ,

Đoạn mã 3:

Line1: int val = 50;

Line2: float num = 35.7F;

Line3: char x = „x‟;

Một hằng được sử dụng ở bất kỳ nơi nào cho phép kiểu giá trị của nó. Tuy nhiên, có nhiều kiểu hằng khác nhau. Một vài trong số chúng là:

Hằng nguyên (Integer literals)

Hằng nguyên được dùng để biểu diễn một giá trị nguyên (int), trong Java là một giá trị số nguyên 32 bit. Trong một chương trình, số nguyên là kiểu thường được sử dụng nhất. Bất kỳ một giá trị số nguyên nào đều là một hằng số nguyên.

Các số nguyên có thể biểu diễn như:

Các giá trị thập phân được biểu diễn trong hệ cơ số 10

Các giá trị bát phân được biểu diễn trong hệ cơ số 8

Các giá trị thập lục phân được biểu diễn trong hệ cơ số 16

Mỗi một loại trên có hằng tương ứng. Một hằng số nguyên có thể được gán đến một kiểu số nguyên khác như byte hoặc long. Khi một giá trị hằng được gán đến một biến kiểu byte hoặc short, sẽ không có lỗi nếu giá trị hằng nằm trong phạm vi của kiểu đích. Các số nguyên có thể được biểu diễn với một ký tự hoa („L‟)hoặc thường („l‟) ở cuối, để biểu diễn một số nguyên dài 64 bit.

Hằng dấu chấm động (floating-point literals)

Các hằng số thực biểu diễn các giá trị thập phân với phần lẻ. Các hằng số thực có các thành phần sau:

Thành phần số, ví dụ 0, 1, 2, …, 9

Dấu thập phân, ví dụ 4.90, 3.141

Số mũ được biểu diễn bởi E hoặc e đi theo sau một số thập phân có thể dương hoặc âm. Ví dụ: e+208, 7.436E6, 23763E-05, …

Kiểu hậu tố D, d, F hoặc f

Page 18: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

18/114 AptechVietnam

Trong Java, các hằng số thực, ngầm định, có độ chính xác double. Một hằng float được biểu diễn bởi F hoặc f, theo sau là giá trị, và một hằng số thực double được biểu diễn bởi D hoặc d.

Hằng Boolean (boolean literals)

Các hằng boolean đơn giản và chỉ có hai giá trị logic là true và false. Các giá trị này không thể chuyển đổi đến bất kỳ hệ thống số nào. Trong Java, true không bằng 1 và false không bằng 0. Nó chỉ có thể gán đến các biến được khi báo là boolean hoặc dùng trong các biểu thức với các toán tử boolean.

Hằng ký tự (character literals)

Các hằng ký tự được đặt trong cặp dấu ngoặc đơn. Tất cả các ký tự ASCII hợp lệ có thể đặt trực tiếp trong cặp dấu ngoặc đơn, như „g‟, „$‟ và „z‟.

Hằng Null (null literals)

Khi một đối tượng được tạo, lượng bộ nhớ xác định được cấp phát cho đối tượng. Địa chỉ bắt đầu của bộ nhớ được lưu trong đối tượng, đó là, một biến tham chiếu. Tuy nhiên đôi khi, biến tham chiếu không tham chiếu đến đối tượng. Trong hợp này, biến tham chiếu được gán một hằng null như ví dụ bên dưới:

obj = null ;

Hằng chuỗi (String literals)

Hằng chuỗi chứa chuỗi các ký tự trong cập dấu ngoặc kép. Các ký tự có thể là ký tự thường, có thể đọc được trên màn hình, hoặc ký tự điều khiển, không nhìn thấy được trên màn hình. Các dấu vạch chéo ngược, dấu nháy kép và các ký tự điều khiển khác có thể được biểu diễn bằng dấu chéo ngược (\) đi kèm với mã điều khiển, các ký tự này còn gọi là escape sequences. Ví dụ sau là một xâu thường, không có ký tự điều khiển:

“Welcome to Java”

2.2 Kiểu dữ liệu (Data type)

Page 19: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 2 Biến và toán tử

19/114

Các ứng dụng xử lý dữ liệu đầu vào và xuất dữ liệu kết quả. Đầu vào, đầu ra, và kết quả của các quá trình tính toán đều liên quan đến dữ liệu. Trong môi trường tính toán, dữ liệu được phân lớp theo các tiêu chí khác nhau phụ thuộc vào bản chất của nó. Ở mỗi tiêu chí, dữ liệu có một tính chất xác định và có một kiểu thể hiện riêng biệt.

Java cung cấp một vài kiểu dữ liệu mà chúng được hỗ trợ trên tất cả các nền tảng. Ví dụ, dữ liệu loại int (integer) của Java được thể hiện bằng 4 bytes trong bộ nhớ của tất cả các loại máy bất luận ở đâu chạy chương trình Java. Bởi vậy các chương trình Java không cần phải thay đổi khi chạy trên các nền khác nhau.

Trong Java kiểu dữ liệu được chia thành hai loại:

Các kiểu dữ liệu nguyên thủy (primitive)

Các kiểu dữ liệu tham chiếu (reference)

2.2.1 Các kiểu dữ liệu nguyên thuỷ (Primitive Data Type)

Java hỗ trợ tám kiểu dữ liệu nguyên thuỷ được chia ra thành 4 nhóm:

Kiểu số nguyên (Integer)

Trong nhóm này có các kiểu dữ liệu: byte, short, int và long và, hầu hết, biểu diễn các giá trị số nguyên có dấu.

Kiểu dấu chấm động (Floating-point)

Trong nhóm này có các kiểu dữ liệu: float và double và biểu diễn các số có phần thập phân.

Kiểu ký tự (Character)

Nhóm này có kiểu dữ liệu char, biểu diễn các ký hiệu trong tập hợp các ký tự như ký tự chữ cái và ký tự số.

Kiểu Boolean

Kiểu dữ liệu boolean thuộc nhóm này và biểu diễn cho hai giá trị true (Đúng) hoặc false (Sai).

Page 20: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

20/114 AptechVietnam

Kiểu dữ liệu số nguyên gồm 4 loại:

Kiểu byte

Dùng để lưu trữ lượng dữ liệu nhỏ. Đây là kiểu dữ liệu có độ dài 8 bit, phạm vi lưu trữ từ -128 đến 127. Nó thường được dùng khi làm việc với luồng dữ liệu từ mạng hoặc từ file. Kiểu dữ liệu này thường dùng khi làm việc với dữ liệu nhị phân chuẩn và có thể không tương thích với các kiểu dữ liệu khác của Java. Từ khoá byte được dùng trong khai báo một biến kiểu này. Ví dụ:

byte val;

Kiểu short

Đây là kiểu dữ liệu ít dùng nhất. Có độ dài 16 bit và phạm vi lưu trữ từ -32.768 đến 32.767. Kiểu dữ liệu này thường dùng cho các ứng dụng trên các máy tính 16 bit. Kiểu dữ liệu này cũng được dùng để lưu trữ dữ liệu số nhỏ. Từ khoá short dùng để khai báo biến kiểu này.

Kiểu int

Đây là kiểu dữ liệu thường dùng nhất. Nó có độ dài 32 bit, với phạm vi lưu trữ từ -2.147.483.648 đến 2.147.483.647. Kiểu int là kiểu linh hoạt và hiệu quả nhất. Ví dụ: Tổng số lương phải trả cho tất cả nhân viên trong công ty.

Kiểu long

long là kiểu dữ liệu 64 bit có dấu, với phạm vi lưu trữ từ

-9.223.372.036.854.775.808 đến +9.223.372.036.854.775.807. Nó được dùng khi kiểu int không đủ để lưu một số lớn hơn phạm vi của nó. Ví dụ, dân số của một nước.

2.2.2 Các kiểu dữ liệu tham chiếu

Trong Java, các đối tượng và các mảng là các biến tham chiếu. Khi một đối tượng hoặc một mảng được tạo, một vùng nhớ nhất định được gán cho nó và địa chỉ của vùng nhớ này được lưu trữ trong biến tham chiếu. Nói cách khác, kiểu dữ liệu tham chiếu là địa chỉ của một đối tượng hoặc một mảng được tạo ra trong bộ nhớ.

Page 21: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 2 Biến và toán tử

21/114

2.3 Định dạng nhập và xuất

Bất cứ khi nào kết quả hiển thị trên màn hình, thì cần phải được định dạng. Java cung cấp các ký tự điều khiển định dạng (escape sequence) để thực hiện định dạng các dữ liệu nhập và xuất

2.3.1 Định dạng xuất

Có thể định dạng theo 2 cách:

print() và println()

format()

Các phương thức này hoạt động tương tự nhau. Phương thức format()dùng lớp Java.util.Formatter để thực hiện nhiều định dạng phức tạp hơn.

Các phương thức print() và println()

Các phương thức này chuyển dữ liệu dùng phương thức toString thích hợp và trả lại một giá trị chuỗi. Hãy xem xét đoạn mã 4.

Đoạn mã 4:

int num1 = 5;

int num2 = 10;

int sum = num1 + num2;

System.out.print(“The sum of”);

System.out.print(num1);

System.out.print(“ and ”);

System.out.print(num2);

System.out.print(“ is “);

System.out.print(sum);

System.out.println(“.”);

Page 22: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

22/114 AptechVietnam

int num3 = 2;

sum = num1 + num2 + num3;

System.out.println(“The sum of ” + num1 + “, ” + num2 + “

and ” + num3 + “ is ” + sum + “.”);

Kết quả của đoạn mã trên là:

The sum of 5 and 10 is 15.

The sum of 5, 10, and 2 is 17.

Biến sum được định dạng hai lần: lần đầu trong lệnh print; lần thứ hai bằng mã chuyển được tự động tạo ra bởi trình biên dịch Java và sử dụng phương thức toString. Trong trường hợp này, bất kỳ giá trị nào cũng có thể được định dạng.

Phương thức format()

Phương thức này định dạng nhiều đối số dựa trên một chuỗi định dạng. Chuỗi định dạng chứa cả phần định dạng lẫn hằng chuỗi bình thường nhưng nó không kết hợp với bất kỳ một đối số nào. Các chuỗi định dạng hỗ trợ nhiều đặc tính. Thành phần định dạng bắt đầu bằng dấu %. Ví dụ,

System.out.println(“%d”, + sum);

Câu lệnh này hiển thị giá trị nguyên của biến sum.

Cú pháp của một chuỗi định dạng như sau:

Cú pháp:

%[arg_index$][flags][width][.precision]conversion character

Trong đó,

arg_index – Là một số nguyên theo sau là $. Số nguyên cho biết đối số sẽ được in tại vị trí được đề cập.

flags – Có nhiều biến cờ khác nhau trong Java như bảng 2.1

Flag (Cờ) Mô tả

“-“ Đối số được canh thẳng theo bên trái

“+” Chèn một dấu + hoặc - với đối số này

Page 23: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 2 Biến và toán tử

23/114

“0” Dẫn đầu đối số này với các số 0

“,” Dùng dấu ngăn cách nhóm

“(“ Số âm được bao quanh bằng dấu ngoặc đơn

Bảng 2.1: Kiểu của các flag

width - Xác định số tối thiểu các ký tự sẽ được in

precision – xác định số ký tự số sẽ được in sau dấu thập phân. Dùng với các số dấu chấm động.

conversion character - Kiểu của đối số được định dạng. Ví dụ, b là boolean, c là char, d là số nguyên, ….

Các giá trị đặt trong cặp dấu “[]” là tùy chọn. Chỉ các thành phần yêu cầu xác định định dạng là dấu % và ký tự chuyển đổi.

Đoạn mã 5 minh họa cách sử dụng phương thức format()

int val = 5;

double sq = 2.236068;

System.out.format("The square root of %d is %f.%n",val,sq);

Kết quả của đoạn mã trên như sau:

The square root of 5 is 2.236068.

2.3.2 Ký tự điều khiển (escape sequences)

Ký tự điều khiển được dùng để biểu diễn các ký tự đặc biệt. Các ký tự điều khiển có thể dùng cho hằng ký tự hoặc hằng chuỗi.

Bảng 2.2 giới thiệu một vài ký tự đặc biệt trong Java

Page 24: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

24/114 AptechVietnam

Ký tự đặc biệt Giá trị ký tự

\f Cuộn trang giấy vào máy in (Form feed)

\r Xuống hàng (Carriage return)

\xxx Ký tự tương ứng với giá trị bát phân xxx, xxx nằm trong khoảng 000 đến 0377.

\uxxxx Ký tự unicode với mã hoá xxxx, trong đó xxxx là một số thập lục phân gồm 4 ký số.

Bảng 2.2: Ký tự điều khiển (Escape sequence)

Để biểu diễn một ký tự unicode , ký tự điều khiển unicode \u có thể được dùng trong chương trình Java. Một ký tự unicode có thể được biểu diễn bằng một chuỗi thập lục phân hoặc bát phân. Đoạn mã 6 minh hoạ vấn đề này.

Đoạn mã 6:

// In „Hello‟ sử dụng chuỗi thập lục phân

System.out.println("\u0048\u0065\u006C\u006C\u006F" +

"!\n");

//In „Blake‟ sử dụng chuỗi bát phân cho ký tự „a‟

System.out.println("Bl\141ke\"2007\"");

Chú ý: Các ký tự điều khiển thập lục phân bắt đầu với \u, theo sau là 4 ký số hệ thập lục phân. Các ký tự điều khiển bát phân gồm 3 ký số sau dấu \. Ví dụ:

\xyy

Trong đó, x có thể bất kỳ ký số nào từ 0 đến 3, y từ 0 đến 7.

Kết quả đoạn mã 6 là:

Hello!

Blake”2007”

Hai kiểu ký tự điều khiển có thể có nghĩa khác nhau bởi vì ký tự đặc biệt \u được xử lý trước các ký tự đặc biệt khác.

Page 25: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 2 Biến và toán tử

25/114

2.4 Các toán tử

Các toán tử kết hợp với các biến đơn hoặc các biểu thức thành các biểu thức mới phức tạp hơn mà nó trả lại các giá trị.

2.4.1 Loại toán tử

Java cung cấp các loại toán tử khác nhau như sau:

Toán tử số học

Các toán tử số học được dùng trong các biểu thức toán học. Các toán hạng của các toán tử số học phải ở dạng số. Các toán hạng kiểu boolean không sử dụng được, song các toán hạng ký tự cho phép sử dụng với loại toán tử này.

Toán tử quan hệ

Các toán tử quan hệ kiểm tra mối quan hệ giữa hai toán hạng. Kết quả của một biểu thức có dùng các toán tử quan hệ là những giá trị boolean, true (đúng) hoặc false (sai).

Toán tử logic

Các toán tử logic làm việc với các toán hạng boolean.

Toán tử gán

Toán tử gán là một dấu bằng, =, dùng để gán giá trị cho một biến. Có thể gán giá trị cho một hoặc nhiều biến kế tiếp nhau. Nghĩa là, có thể sử dụng một dãy các phép gán.

Toán tử thao tác bit (bitwise)

Một toán tử bitwise cho phép ta thao tác trên từng bit riêng biệt trong các kiểu dữ liệu nguyên thuỷ. Toán tử bitwise dựa trên cơ sở đại số boolean. Nó thực hiện

Page 26: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

26/114 AptechVietnam

phép tính trên hai bit có vị trí tương ứng trên hai toán hạng để tạo ra một kết quả mới.

Khi các toán hạng là các số, toán tử „&‟ thực thi hàm AND giữa hai bit, và toán tử „|‟ thực thi hàm OR giữa hai bit. Bảng 2.3 mô tả vấn đề này.

Condition1 (Điều kiện 1)

Condition2 (Điều kiện 2)

Condition1 & Condition2

Condition1 | Condition2

True True True True

True False False True

False False False False

False False False False

Bảng 2.3: AND và OR

Toán tử dịch bit sang phải không dấu

Mỗi lần một dịch bit xảy ra, toán tử >>> tự động điền bit có thứ tự ưu tiên cao với nội dung trước đó của nó nhưng vẫn giữ dấu của giá trị. Tuy nhiên, thỉnh thoảng dịch chuyển này không thích hợp với những giá trị không phải là số. Trường hợp này thường xảy ra khi làm việc với dữ liệu là đồ họa và các giá trị dựa trên pixel. Trong tình huống đó, một giá trị 0 được dịch chuyển đến bit có thứ tự ưu tiên cao không quan tâm tới giá trị ban đầu của nó. Điều này gọi là dịch chuyển bit không dấu. Vì vậy, một toán tử dịch bit sang phải không dấu, >>>, luôn luôn dịch chuyển 0 đến các bit có thứ tự ưu tiên cao hơn.

Đoạn mã 7:

int val = -3;

val = val >>> 20;

Trong đó, val được gán bằng -3. Giá trị này sau đó được dịch chuyển sang phải 20 bits, điền 0 vào 20 bits đó và lờ đi bit dấu. Giá trị sau khi dịch bit là 4095.

Toán tử instanceof

Page 27: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 2 Biến và toán tử

27/114

Toán tử này chỉ được dùng cho các biến tham chiếu đối tượng để kiểm tra một đối tượng có thuộc một kiểu xác định hay không. Dạng tổng quát của toán tử instanceof là:

object instanceof type

Ở đây, đối tượng là thể hiện của một lớp, và có kiểu là kiểu của lớp. Nếu đối tượng thuộc về một kiểu xác định hoặc có thể ép kiểu thành kiểu xác định, thì toán tử instanceof trả lại giá trị true, ngược lại thì false.

Đoạn mã 8 minh hoạ cách dùng toán tử này.

Đoạn mã 8:

Scanner input = new Scanner(System.in);

Boolean val = input instanceof Scanner;

System.out.println(val);

Giá trị được in ra trong đoạn mã trên là true

Toán điều kiện

Toán tử điều kiện là một loại toán tử đặc biệt vì nó gồm ba thành phần cấu thành biểu thức điều kiện. Toán tử này có thể thay thế câu lệnh if-then-else.

Cú pháp :

expression1 ? expression2 : expression3;

Trong đó,

expression1: là biểu thức logic bất kỳ trả về giá trị true hoặc false.

expression2: là biểu thức sẽ được ước lượng khi giá trị của expression 1 là true.

expression3: là biểu thức sẽ được ước lượng khi giá trị của expression 1 là false

Các toán tử điều kiện có thể lồng nhau và được minh họa trong đoạn mã 9:

Đoạn mã 9:

int num1 = 5;

Page 28: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

28/114 AptechVietnam

int num2 = 10;

char op = '*';

System.out.println( (op == '-') ? (num1 - num2) : (op ==

'+') ? (num1 + num2) : (op == '*') ? (num1 * num2) :

"Invalid operator");

Trong đoạn mã 9, điều kiện cuối cùng là true nên kết quả là 50.

2.4.2 Thứ tự ưu tiên của các toán tử và sự kết hợp

Khi một biểu thức chứa nhiều toán tử, chúng sẽ được thực hiện theo một thứ tự xác định bởi thứ tự ưu tiên của nó. Thứ tự ưu tiên của các toán tử toán học tuân theo các nguyên tắc số học. Nếu không chắc chắn, ta dùng dấu ngoặc đơn để thay đổi thứ tự ưu tiên.

Xét biểu thức 5+3*2, nó được tính 5+(3*2) bằng 11, chứ không tính (5+3)*2 bằng 16. Toán tử (*) có độ ưu tiên cao hơn toán tử (+), do vậy phép nhân được thực hiện trước.

Xét biểu thức 8-3-2, nó được tính (8-3)-5 bằng 3, chứ không tính 8-(3-2) bằng 7. Vì vậy , khi biểu thức có nhiều toán tử - thì độ ưu tiên (sự kết hợp) được thực hiện từ trái sang phải.

Hai toán tử + và – có cùng độ ưu tiên. Nên cũng được thực hiện từ trái sang phải. Xét biểu thức:

x + 3 – y +5

(x + 3) – y +5

((x + 3) – y) +5

((x + 3) – y +5)

Các toán tử =, += và -= có cùng độ ưu tiên nhưng được thực hiện từ phải sang trái trong cùng biểu thức. Xét biểu thức:

x = y += z -= 4

x = y += (z -= 4)

x = (y += (z -= 4))

Page 29: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 2 Biến và toán tử

29/114

(x = y += (z -= 4))

Dùng dấu ngoặc đơn để thay đổi thứ tự ưu tiên của các toán tử. Nếu không dùng nó thì độ ưu tiên và sự kết hợp sẽ được dùng để xác định thứ tự các toán tử sẽ thực thi. Khoảng trắng không có tác dụng.

Thứ tự ưu tiên được thực hiện như sau:

Trong một biểu thức các toán tử được thực hiện theo thứ tự ưu tiên từ cao xuống thấp (tightest binding)

Nếu có nhiều hơn 1 toán tử ưu tiên như nhau trong biểu thức thì thực hiện từ trái sang phải

Hãy đặt các biểu thức ưu tiên nhất trong dấu ngoặc tròn để chúng được thực hiện trước nhất.

Tiếp tục với biểu thức con thứ hai và các biểu thức tiếp theo

Tiếp đến các toán tử có độ ưu tiên cao nhất còn lại theo cùng qui trình trên.

Rất dễ bị sai nếu quên đi thứ tự ưu tiên. Hãy xem biểu thức sau:

x = a+b/5.0-c**d+1*e

là tương đương với

x = ((a+(b/5.0))-(c**d))+ (1*e)

Tuân theo qui trình sau để đóng ngoặc các biểu thức

Toán tử ưu tiên cao nhất là **. Nghĩa là c**d được thực hiện trước tiên, ta đóng nó trong ngoặc tròn.

Dấu / và dấu * là toán tử ưu tiên cao nhất thứ hai, sẽ được thực hiện kế tiếp, do vậy đặt b/5.0 và 1*e vào ngoặc tròn.

Dấu + và – có độ ưu tiên cao nhất kế tiếp. Do chúng có cùng độ ưu tiên với nhau nên thực hiện từ trái sang phải.

Cuối cùng phép gán được thực hiện.

2.4.3 Ép kiểu

Ép kiểu hay chuyển đổi kiểu hàm ý nói đến việc thay đổi kiểu dữ liệu của một biến sang kiểu khác. Thực hiện điều này ta thu được một số tinh năng ưu việt

Page 30: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

30/114 AptechVietnam

của thang bậc các dữ liệu. Chẳng hạn, các giá trị giới hạn của một tập các số nguyên integer có thể chứa trong một khuôn dạng cô đọng hơn. Có thể chuyển đổi kiểu sang dạng khác phù hợp với việc tính toán hơn vì không thể tính toán với kiểu dữ liệu trước đó, chẳng hạn như là chia các số với độ chính xác cao hơn. Trong ngôn ngữ lập trình hướng đối tượng, chuyển đổi kiểu cho phép chương trình xem đối tượng của một kiểu như là kiểu tiền bối của chúng để làm đơn giản việc tương tác với chúng. Có 2 loại chuyển kiểu: mặc định và tường minh. Thuật ngữ chuyển kiếu mặc định có nghĩa là nâng kiểu. Hầu hết các dạng chuyển kiểu tường minh được hiểu là ép kiểu. Chuyển kiểu tường minh cũng có thể thu được từ các thường trình chuyển kiểu được viết riêng như là nạp chồng hàm dựng của đối tượng.

Chuyển kiểu ngầm định

Chuyển kiểu ngầm định, cũng còn gọi là nâng kiểu (coercion), là tự động chuyển kiểu bởi trình biên dịch. Một số ngôn ngữ cho phép, thậm chí đòi hỏi bộ biên dịch phải hỗ trợ chuyển kiểu. Trong một biểu thức phức hợp, chương trình sẽ chạy đúng nếu dữ liệu của kiểu dưới có thể chuyển lên kiểu trên. Đoạn lệnh 10 mô tả chuyển kiểu mặc định. short a=2000;

int b;

b=a;

Ở đây giá trị ‟a‟ được nâng cấp từ short lên int và không cần toán tử ép kiểu nào cả. Điều này được hiểu là chuyển kiểu thông thường. Chuyển kiểu thông thường tác động trên các kiểu dữ liệu cơ bản, cho phép chuyển đổi các dữ liệu số (short lên int, int lên float, float lên double...) và một số chuyển đổi con trỏ. Một số chuyển đổi có thể gây ra mất độ chính xác, lúc ấy trình biên dịch có thể cảnh báo. Điều này có thể không cần đến chuyển kiểu tường minh.

Chuyển kiểu tường minh

Nhiều loại chuyển kiểu, đặc biệt là hạ thấp kiểu gây mất dữ liệu, đòi hỏi chuyển kiểu tường minh. Đoạn lệnh 11 mô tả chuyển kiểu tường minh short a=2000;

int b;

b= (int)a; // cú pháp ép kiểu

Page 31: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 2 Biến và toán tử

31/114

b= int (a); // cú pháp kiểu hàm

Có nhiều loại ép kiểu tường minh, đó là:

Có kiểm tra Trước khi chuyển kiểu được thực hiện, một kiểm tra trong lúc chạy chương trình được thực hiện để xem kiểu đích đến có chứa được giá trị nguồn hay không. Nếu không, một lỗi bị sinh ra.

Không kiểm tra Không có kiểm tra nào được thực hiện. Nếu kiểu đích không chứa được giá trị gốc, kết quả không được ghi nhận.

Mẫu bit Dữ liệu không được xem xét gì tất, các bít thô được sao chép nguyên bản.

Page 32: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

32/114 AptechVietnam

Tóm tắt bài học

Biến là một tên được dùng để tham chiếu đến các giá trị chỉ định trong chương trình.

Hằng là giá trị không đổi được viết thẳng trong mã lệnh không đòi hỏi sự tính toán nào cả.

Java có các loại dữ liệu có sẵn, gọi là dữ liệu nguyên thủy.

Định dạng dữ liệu để xuất ra người ta dùng lớp Formatter hoặc sử dụng các phương thức format() và printf() của lớp PrintStream.

Một số các ký tự đặc biệt được biểu diễn kết hợp với dấu \

Java cung cấp nhiều loại toán tử, đó là

Toán tử số học

Toán tử bit

Toán tử quan hệ

Toán tử logic

Toán tử điều kiện

Toán tử gán

Page 33: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 33/114

Chương 3: Lệnh rẽ nhánh và vòng lặp

Mục đích

Cuối chương này, học viên có thể nắm được:

Các cấu trúc rẽ nhánh if

Cấu trúc switch-case

Cấu trúc vòng lặp while, do while, for và vòng lặp lồng nhau

Câu lệnh break, continue

3.1 Câu lệnh rẽ nhánh

Câu lệnh rẽ nhánh hay câu lệnh điều khiển được sử dụng để điều khiển luồng

thực thi trong ngôn ngữ lập trình. Java hỗ trợ hai loại lệnh rẽ nhánh đó là: cấu

trúc if và cấu trúc switch. Các câu lệnh này cho phép chương trình thực thi các

đoạn mã lệnh khác nhau dựa trên kết quả của biểu thức điều kiện hoặc giá trị

của biến.

3.1.1 Lệnh if lồng nhau

Câu lệnh if-else kiểm tra kết quả của biểu thức hoặc biến điều kiện rồi dựa trên

kết quả trả về để thực hiện các lệnh tương ứng. Câu lệnh if có thể được sử dụng

lồng trong một cấu trúc if khác, loại if này được gọi là nested-if. Như vậy, nested-

if là cấu trúc if được lồng trong cấu trúc if hoặc else khác.

Các điểm quan trọng cần lưu ý khi sử dụng lệnh if lồng nhau:

Câu lệnh else luôn luôn được kết hợp lệnh if gần nhất.

Nó nằm trong cùng khối với else và không kết hợp với else nào cả.

Page 34: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

34/114 AptechVietnam

Cú pháp:

if (Điều_kiện)

{

if (Điều_kiện)

{

//câu lệnh/khối lệnh này được thực hiện nếu điều kiện đúng

}

else

{

//câu lệnh/khối lệnh này được thực hiện nếu điều kiện sai

}

}

else

{

//câu lệnh/khối lệnh này được thực hiện nếu điều kiện sai

}

Đoạn mã dưới đây minh họa cách sử dụng cấu trúc nested-if

Đoạn mã 1:

Scanner input = new Scanner(System.in);

System.out.println(“Nhập một số: ”);

num = input.nextInt();

//Kiểm tra xem num có chia hết cho 3 không?

//câu lệnh if ngoài

if(num % 3 == 0)

{

//kiểm tra xem num có chia hết cho 5 không?

if(num % 5 == 0)

{

//in ra thông báo num chia hết cho 3 và 5

System.out.println(“The number is divisible by both 3 and 5.”);

}

else

{

//Số chia hết cho 3 nhưng không chia hết cho 5

System.out.println(“The number is divisible by 3 but not by 5.”);

}

}

else

{

//Số không chia hết cho 3 và 5

System.out.println(“The number is not divisible by 3 and 5.”);

}

Page 35: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 3 Rẽ nhánh và vòng lặp

35/114

Giải thích đoạn mã lệnh

Đoạn mã trên khái báo một biến num và chứa số nguyên nhập vào từ người dùng. Chương trình sử dụng cấu trúc if lồng nhau, đầu tiên chương trình kiểm tra số nguyên người dùng nhập vào (biến num) có chia hết cho 3 và 5 hay chỉ chia hết cho 3 và in ra các thông báo tương ứng. Ở đây, câu lệnh else cuối cùng được kết hợp với if (num % 3 ==0). Câu lệnh else bên trong được kết hợp với if (num % 5 == 0) bởi vì nó được kết hợp với if gần nhất trong cùng khối lệnh.

3.1.2 Lệnh Switch-case lồng nhau

Cấu trúc switch-case có thể được sử dụng thay thế cho cấu trúc if-else-if khi có nhiều if-else lồng nhau. Sử dụng switch-case khiến khả năng thực thi của chương trình tốt hơn. Tương tự như if-else, switch-case có thể sử dụng lồng trong một switch-case khác. Do switch-case định nghĩa các khối lệnh riêng, nên không có sự xung đột giữa các hằng giá trị case của lệnh switch-case trong và lệnh switch-case ngoài. Đoạn mã dưới đây minh họa cách sử dụng các lệnh switch-case: Đoạn mã 2: ......

switch (day)

{

case 0:

switch(target)

{

case 1:

System.out.println(“Target is 1 to 7.”);

break;

}

break;

case 1:

System.out.println(“Sunday”);

break;

case 2:

System.out.println(“Monday”);

break;

case 3:

System.out.println(“Tuesday”);

break;

case 4:

System.out.println(“Wednesday”);

break;

......

}

Page 36: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

36/114 AptechVietnam

Trong đoạn mã trên, khối lệnh case 1 của switch-case trong không xung đột/nhầm lẫn với khối lệnh case 1 của switch-case ngoài. Biến day được sử dụng để so sánh với danh sách các khối case của switch-case ngoài, nếu giá trị của biến day bằng 0 thì biến target mới được so sánh với danh sách các khối case của switch-case bên trong. Có ba điểm quan trọng cần lưu ý khi sử dụng cấu trúc switch-case;

Cấu trúc switch khác với cấu trúc if. switch chỉ có thể kiểm tra bằng, trong

khi đó if có thể kiểm tra bất kỳ giá trị của biểu thức logic nào.

Không cho phép 2 giá trị case giống nhau trong cùng một switch-case.

Nếu switch-case lồng nhau, giá trị của các hằng case của switch-case

trong và ngoài có thể giống nhau.

Cấu trúc lệnh switch-case hiệu quả hơn sử dụng nhiều câu lệnh if-else

lồng nhau.

3.2 Vòng lặp

Vòng lặp được sử dụng rất phổ biến trong lập trình, nó được sử dụng để thực

hiện các câu lệnh lặp lại nhiều lần. Các loại vòng lặp được hỗ trợ trong Java

gồm:

- Vòng lặp while

- Vòng lặp do while

- Vòng lặp for

Vòng lặp thực hiện một lệnh (hoặc khối lệnh) chừng nào thõa mãn một điều kiện

hoặc thõa mãn số lần lặp xác định.

3.2.1 Vòng lặp while

Vòng lặp while dùng để thực hiện một lệnh hoặc khối lệnh chừng nào điều kiện

còn true (đúng). Tại một vị trí nào đó trong vòng lặp làm cho biểu thức điều kiện

thành false (sai), nếu không vòng lặp sẽ thực hiện vô tận.

Page 37: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 3 Rẽ nhánh và vòng lặp

37/114

Cú pháp:

...

while(điều_kiện)

{

Các câu lệnh;

...

}

...

Thân của vòng lặp là rỗng nếu nó không chứa câu lệnh nào cả, về mặt cú pháp,

nó vẫn đúng trong Java. Đoạn mã dưới đây minh họa cách sử dụng thân vòng

lặp rỗng:

Đoạn mã 3:

......

int num1 = 1;

int num2 = 30;

while (++num1 < --num2);

System.out.println(“The midpoint is: ” + num1);

......

Trong đoạn mã trên, giá trị của biến num1 được tăng 1 và giá trị của biến num2

được giảm 1, sau đó đem so sánh. Vòng lặp được thực hiện chừng nào giá trị

của num1 lớn hoặc bằng num2. Do vậy, biến num1 có giá trị giữa num1 và

num2, kết quả in ra:

The midpoint is: 16

Page 38: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

38/114 AptechVietnam

Biểu thức điều kiện của vòng lặp có thể phức tạp, biến điều khiển có thể được

thay đổi giá trị trong thân vòng lặp. Tuy nhiên biểu thức điều kiện phải false (sai

hoặc không thõa) nếu không vòng lặp sẽ thực hiện vô tận. Đoạn mã dưới đây

minh họa vòng lặp vô tận:

Đoạn mã 4:

......

int count = 0;

while (count < 100)

{

System.out.println(“This goes on forever, HELP!!!”);

count = count + 10; //Tăng count lên 10 đơn vị

System.out.println(“Count = ” + count);

count = count - 10; //giảm count 10 đơn vị

System.out.println(“Count = ” + count);

}

......

Trong đoạn mã trên, giá trị của count luôn bằng 0 và nhỏ hơn 100. Vì vậy, biểu

thức điều kiện luôn luôn true (đúng), vòng lặp không bao giờ kết thúc. Một lệnh

break có thể được dùng để ngắt vòng lặp trong chương trình trên. Nếu có dòng

này ở cuối vòng lặp, vòng lặp chỉ thực hiện 1 lần và kết thúc, kết quả hiển thị:

This goes on forever, HELP!!!

Count = 10

Count = 0

Tuy nhiên, đây chỉ là minh họa cách sử dụng, trong thực tế chúng ta không dùng

như vậy.

Page 39: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 3 Rẽ nhánh và vòng lặp

39/114

Điểm quan trọng cần lưu ý khi sử dụng vòng lặp while là, các biến điều khiển

(biến được sử dụng trong biểu thức điều kiện) phải được khai báo trước khi sử

dụng. Xem ví dụ dưới đây là không hợp lệ

while (int val = 10) { } //biểu thức điều khiển không hợp

lệ

3.2.2 Vòng lặp do-while

Vòng lặp do-while thực hiện các câu lệnh chừng nào điều kiện còn true (đúng).

Khác với vòng lặp while, vòng lặp do-while thực hiện các câu lệnh ít nhất một

lần sau đó biểu thức điều kiện mới được kiểm tra. Nếu điều kiện đúng, vòng lặp

tiếp tục thực hiện, nếu điều kiện sai, vòng lặp kết thúc. Đoạn mã dưới đây minh

họa cách sử dụng vòng lặp do-while

Đoạn mã 5:

......

int number = 5;

int guest;

Scanner input = new Scanner(System.in);

System.out.println(“Guess a number between 1 to 10”);

do

{

System.out.println(“Enter your guess: ”);

guess = input.nextInt();

if (guess > number)

{

System.out.println(“Value too high”);

}

if (guess < number)

Page 40: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

40/114 AptechVietnam

{

System.out.println(“Value too low”);

}

}while (guess != number);

System.out.println(“The answer is %d.”, number);

......

Trong đoạn mã trên, 2 biến number và guess được khai báo. Biến number được

gán giá trị 5, biến guess được nhập vào từ người dùng. Nếu giá trị người dùng

nhập vào bằng giá trị của biến number thì dòng lệnh println() in ra dòng “The

answer is 5.” còn nếu không thì vòng lặp tiếp tục thực hiện.

3.2.3 Vòng lặp for

Vòng lặp for được sử dụng khi người dùng biết trước số bước lặp. Khai báo của

vòng lặp for gồm 3 phần:

Khai báo và khởi tạo giá trị của các biến

Biểu thức logic

Biểu thức điều khiển lặp

Ba phần khai báo này phân cách nhau bằng dấu chấm phẩy (;). Khi vòng lặp bắt

đầu, phần khai báo của vòng lặp được thực hiện. Thông thường, đây là biểu

thức khởi gán giá trị ban đầu và đóng vai trò là biến đếm điều khiển vòng lặp.

Biểu thức khởi tạo chỉ thực hiện duy nhất một lần. Tiếp đến, biểu thức logic được

thực hiện để kiểm tra biến điều khiển với giá trị của số lần lặp. Nếu biểu thức

logic có giá trị đúng thì vòng lặp tiếp tục thực hiện, ngược lại vòng lặp kết thúc.

Sau cùng, biểu thức điều khiển lặp được thực hiện, thông thường biểu thức lặp

dùng để tăng hoặc giảm giá trị của biến điều khiển.

Page 41: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 3 Rẽ nhánh và vòng lặp

41/114

Trong phần khai báo và biểu thức lặp, chúng ta có thể sử dụng nhiều biến phân

cách nhau bởi dấu phẩy và thứ tự thực hiện từ trái qua phải. Thứ tự thực hiện

rất quan trọng nếu như giá trị của biểu thức thứ 2 phụ thuộc vào kết quả thực

hiện. Đoạn mã dưới đây minh họa cách sử dụng vòng lặp for với phân cách

nhiều biến khai báo:

Đoạn mã 6:

......

int i,j;

int max = 10;

System.out.println(“The sum of two variables for a table of

10 is: ”);

for(i=0,j=max;i<=max;i++,j--)

{

System.out.println(“\n%d + %d = %d”,i,j,i+j);

}

......

Trong đoạn mã trên, 3 biến i, j, max được khai báo. Biến max được gán giá trị

bằng 10. Trong phần khởi tạo, biến i được gán bằng 0 và biến j được gán bằng

max = 10. Ở đây chúng ta thấy 2 biến được khởi tạo giá trị phân cách bởi dấu

phẩy. Biểu thức điều kiện; i<=max đảm bảo rằng vòng lặp thực hiện chừng nào i

còn nhỏ hơn hoặc bằng max=10.Vòng lặp sẽ kết thúc khi biểu thức điều kiện trả

về false (sai) có nghĩa lúc đó i = 11. Cuối cùng, biểu thức lặp cũng bao gồm 2

phép tính i++, j--. Sau mỗi bước lặp, i được tăng 1 đơn vị và j giảm 1 đơn vị.

Tổng của 2 biến này luôn luôn bằng max và bằng 10 được in ra. Kết quả của

đoạn mã trên như dưới đây:

The sum of two variables for a table of 10 is:

0 + 10 = 10

Page 42: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

42/114 AptechVietnam

1 + 9 = 10

2 + 8 = 10

3 + 7 = 10

4 + 6 = 10

5 + 5 = 10

6 + 4 = 10

7 + 3 = 10

8 + 2 = 10

9 + 1 = 10

10 + 0 = 10

Như thảo luận trong phần trước, có 3 phần trong khai báo vòng lặp for, tuy nhiên

bất kỳ một hoặc tất cả các phần trong khai báo for có thể để trống. Đoạn mã

dưới đây minh họa cách sử dụng vòng lặp for không sử dụng phần khai báo:

Đoạn mã 7:

......

//Khởi tạo giá trị của biến num

int num = 1;

for(; num != 40;num++)

{

System.out.println(“Enter a number: ”);

num = input.nextInt();

}

......

Trong đoạn mã trên, biến num được người dùng nhập vào cho đến khi có giá trị

bằng 40. Vòng lặp không có phần khởi tạo. Thay vào đó, num được khởi tạo ở

ngoài phần khai báo vòng lặp. Biến num được tăng lên 1. Vòng lặp sẽ kết thúc

Page 43: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 3 Rẽ nhánh và vòng lặp

43/114

khi num bằng 40. Nếu cả 3 phần để trống, vòng lặp sẽ lặp vô tận. Đoạn mã dưới

đây minh họa cách sử dụng đó:

Đoạn mã 8:

......

for( ; ; )

{

System.out.println(“This will go on and on”);

}

......

Trong đoạn mã trên, dòng chữ “This will go on and on” được in cho đến khi

vòng lặp kết thúc. Câu lệnh break có thể dùng để kết thúc vòng lặp này. Đoạn

mã trên dẫn đến vòng lặp vô hạn, vòng lặp vô hạn khiến chương trình chạy

không giới hạn trong khoảng thời gian dài và sử dụng hết tài nguyên của hệ

thống, điều này dẫn đến treo hệ thống. Trong thực tế, chúng ta nên tránh sử

dụng loại vòng lặp kiểu này trong chương trình của mình.

3.2.4 Vòng lặp lồng nhau

Trong Java, chúng ta có thể sử dụng vòng lặp lồng nhau, đó là vòng lặp được

đặt trong một vòng lặp khác. Đoạn mã dưới đây minh họa cách sử dụng vòng lặp

lồng nhau:

Đoạn mã 9:

......

int num = 0;

//Lặp từ 0 cho đến 5 (lặp 6 lần)

System.out.println(“The factorials of the first five

Page 44: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

44/114 AptechVietnam

numbers are: ”);

do

{

int i = 1;

int factorial = 1;

System.out.println(“Factorial of “ + num + “ :”);

if(num==0)

{

System.out.println(factorial);

}else

{

while(num>=i)

{

factorial = factorial * i;

i++;

}

System.out.println(factorial);

}

num++;

}while(num < 6);

......

Trong đoạn mã trên, chương trình in ra giai thừa của các số từ 0 – 5. Câu lệnh

if-else được dùng để tính giai thừa của biến num. Lệnh được đặt trong vòng lặp

do-while. Kết quả hiển thị như sau:

The factorials of the first five numbers are:

Factorial of 0 : 1

Factorial of 1 : 1

Factorial of 2 : 2

Factorial of 3 : 6

Page 45: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 3 Rẽ nhánh và vòng lặp

45/114

Factorial of 4 : 24

Factorial of 5 : 120

3.3 Câu lệnh nhảy

Đôi khi số bước lặp của vòng lặp không được biết trước. Điều kiện để kết thúc vòng lặp được xác định sau, và ngay trong thân vòng lặp. Trong một số tình huống khác, một số câu lệnh trong thân vòng lặp được bỏ qua dựa trên điều kiện nào đó. Đáp ứng những yêu cầu đó, Java cung cấp hai lệnh nhảy đó là break và continue dùng để thay đổi luồng điều khiển của chương trình dựa trên các điều kiện. Do vậy, các câu lệnh nhảy này được sử dụng để chuyển điều khiển đến phần khác của chương trình. 3.3.1 Lệnh break Lệnh break thoát khỏi vòng lặp trong cùng và thực hiện câu lệnh tiếp theo sau khối lệnh của vòng lặp ngoài. Câu lệnh break là câu lệnh độc lập, do vậy cần phải kết thúc lệnh break bằng dấu chấm phẩy. Lệnh break không chỉ được sử dụng trong vòng lặp for mà còn được sử dụng trong cả while và do-while. Các câu lệnh sau break không được thực hiện. Ví dụ dưới đây minh họa cách sử dụng break trong vòng lặp while. Đoạn mã 10: ......

int i = 1;

int sum = 0;

while (i < 100)

{

sum = sum + i;

System.out.println(“Sum: “ + sum);

if(i==10)

break;

i++;

}

......

Page 46: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

46/114 AptechVietnam

Trong đoạn mã trên, vòng lặp while được thiết kế để chạy từ 1 đến 99 và tính tổng của các số từ 1 đến 99. Nhưng câu lệnh break được sử dụng để kết thúc vòng lặp khi i bằng 10. Do vậy kết quả được hiển thị như sau: Sum: 1

Sum: 3

Sum: 6

Sum: 10

Sum: 15

Sum: 21

Sum: 28

Sum: 36

Sum: 45

Sum: 55

3.3.2 Lệnh continue

Câu lệnh continue chuyển điều khiển sang bước lặp kế tiếp và bỏ qua các câu

lệnh sau continue. Có nghĩa là khi gặp câu lệnh continue, các lệnh sau continue

trong thân vòng lặp sẽ không được thực hiện mà điều khiển sẽ được chuyển

sang bước lặp kế tiếp. Câu lệnh continue thường được sử dụng với một biểu

thức điều kiện trong thân vòng lặp như lệnh break. Lệnh continue sử dụng

được trong 3 loại vòng lặp: while, do-while và vòng lặp for. Với vòng lặp while,

do-while, lệnh continue chuyển điều khiển đến biểu thức điều khiển của vòng

lặp. Trong khi đó, với vòng lặp for, điều khiển nhảy đến phần biểu thức lặp (phần

thứ 3 trong khai báo vòng lặp) sau đó thực hiện tiếp biểu thức điều khiển. Đoạn

mã dưới đây minh họa cách sử dụng continue trong vòng lặp do-while:

Đoạn mã 11:

......

char letter = „A‟;

do

{

if(letter ==‟J‟ || letter == „L‟)

{

Page 47: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 3 Rẽ nhánh và vòng lặp

47/114

letter++;

continue;

}

System.out.println(“Letter: ” + letter);

letter++;

}while(letter !=‟N‟)

...

Trong đoạn mã trên, các ký tự từ A đến M được in ra màn hình bằng vòng lặp

do-while. Lệnh continue bỏ qua 2 ký tự in ra là J và L.

3.4 Nhãn

Các câu lệnh trong Java có thể đánh nhãn. Nhãn thường được hay dùng với

vòng lặp for, while kết hợp với break hoặc continue. Nhãn được nhận diện bởi

tên định danh theo sau là dấu 2 chấm và đặt trước lệnh đánh nhãn.

Lệnh đánh nhãn được sử dụng trong trường hợp các vòng lặp lồng nhau. Nó

dùng để chỉ định vòng lặp lồng trong thực hiện bước lặp kế tiếp hay ngắt vòng

lặp. Đoạn mã dưới đây minh họa cách sử dụng nhãn

Đoạn mã 12:

int i;

outer:

for(i=0;i<5;i++)

{

if(i==2)

{

System.out.println("Hello");

break outer;

}

Page 48: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

48/114 AptechVietnam

System.out.println("This is the outer loop");

}

System.out.println("Good bye");

......

Trong đoạn mã trên, vòng lặp thực hiện 5 lần. Hai lần lặp đầu tiên chương trình

in ra dòng chữ “This is the outer loop”. Bước lặp thứ 3 giá trị của i = 2, do vậy

sẽ in ra dòng chữ “Hello”. Tiếp đến, lệnh break được thực hiện và chuyển điều

khiển đến nhãn outer: . Vòng lặp kết thúc và dòng lệnh cuối được in ra. Kết quả

hiển thị như sau:

This is the outer loop

This is the outer loop

Hello

Good bye

Đoạn mã dưới đây minh họa cách sử dụng lệnh continue

Đoạn mã 13:

......

outer:

for(i=0;i<5;i++)

{

if(j=0;j<5;j++)

{

System.out.println("Hello");

continue outer;

}

System.out.println("This is the outer loop");

}

Page 49: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 3 Rẽ nhánh và vòng lặp

49/114

System.out.println("Good bye");

......

Trong đoạn mã trên, dòng chữ “Hello” được in ra 5 lần. Sau khi câu lệnh

continue được thực hiện, luồng điều khiển của chương trình được chuyển sang

bước lặp kế tiếp của vòng lặp có đánh nhãn. Cuối cùng, điều kiện của vòng lặp

ngoài (vòng lặp có đánh nhãn) trả về false, vòng lặp kết thúc và chương trình in

ra dòng chữ “Good bye”. Kết quả thực hiện như sau:

Hello

Hello

Hello

Hello

Hello

Good bye

Page 50: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

50/114 AptechVietnam

Tóm tắt bài học

Luồng điều khiển chương trình được điều khiển bởi các cấu trúc lệnh rẽ

nhánh

Các loại vòng lặp hỗ trợ trong Java gồm: while, do-while, for. Vòng lặp sử

dụng để thực hiện các câu lệnh một cách lặp lại

Lệnh break và continue là các câu lệnh nhảy trong Java

Lệnh break kết thúc vòng lặp lồng trong và thực hiện lệnh kế tiếp trong

khối lệnh của vòng lặp ngoài.

Lệnh continue bỏ qua các câu lệnh nằm sau nó và thực hiện bước lặp tiếp

theo

Lệnh nhãn thường được sử dụng với các vòng lặp như for, while.

Page 51: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 51/114

Chương 4: Giới thiệu về lớp

Mục tiêu bài học

Kết thúc bài học này, học viên có thể nắm được những vấn đề sau:

Tạo mảng đối tượng

Khái niệm đệ qui trong Java

Mô tả và sử dụng kiểu enum

Thu gom rác (Garbage collection) trong Java và cơ chế hoạt động

4.1 Mảng các tham chiếu đối tượng

Trong Java, có thể khai báo mảng chứa các phần có bất kỳ kiểu dữ liệu nào, kể

cả kiểu tham chiếu đến đối tượng. Khi một mảng được tạo ra, tham chiếu đến

một lớp, ta nói đó là một mảng các đối tượng.

Các đối tượng được khởi tạo chỉ khi hàm dựng của đối tượng được gọi khi thực

thi chương trình. Sau khi đối tượng được khởi tạo, nó có thể truy nhập thông qua

biến đối tượng.

Nếu kiểu của mảng khai báo là một lớp thì các tham chiếu đến các đối tượng

của kiểu lớp được khai báo được lưu trong mảng. Do vậy, các thành phần của

mảng các đối tượng chỉ là các tham chiếu đến các đối tượng chứ không phải là

các thể hiện thực sự của các đối tượng.

4.2 Tạo mảng đối tượng

Page 52: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

52/114 AptechVietnam

Tạo mảng đối tượng thực hiện theo 3 bước như sau:

1. Khai báo biến có thể tham chiếu đến mảng đối tượng có kiểu thích hợp.

2. Tạo đối tượng mảng

3. Gán các phần tử mảng với các thể hiện của kiểu tương ứng.

Ví dụ dưới đây khai bảo mảng có tên emp_Array chứa 20 đối tượng của lớp

Employee;

Đoạn mã 1:

Employee [] emp_Array = new Employee[20];

Khởi tạo mảng bằng vòng lặp for

Đoạn mã 2:

for (int m=0;m<emp_Array.length;m++)

{

//khởi tạo mảng đối tượng

emp_Array[m] = new Employee();

}

Giá trị ngầm định của mỗi phần tử mảng đối tượng là null trừ khi các đối tượng

thực sự được tạo và tham chiếu của chúng được gán cho mỗi phần tử mảng.

Đoạn mã dưới đây minh họa cách khởi tạo các đối tượng

Đoạn mã 3:

Page 53: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 4 Giới Thiệu về Lớp

53/114

emp_Array[0] = new Employee(“Derek”,121);

emp_Array[1] = new Employee(“Clark”,55);

Hoặc khởi tạo bằng cách khác như sau:

Đoạn mã 4:

Employee [2] emp_Array = {new Employee(“Derek”,121),new

Employee(“Clark”,55)};

4.3 Đệ qui trong Java

Trong lập trình, đệ qui là cách thức gọi lại chính nó (gọi lại chính hàm đó). Trong

thực tế, rất nhiều bài toán được biểu diễn và giải quyết bằng thuật toán đệ qui. Ví

dụ trong Java, tính giai thừa dùng đệ qui được biểu diễn như sau:

Đoạn mã 5:

public static int calculateFactorial(int n)

{

if(n==0)

return 1;

else

return n * calculateFactorial(n–1);

//lời gọi đệ qui

}

Đệ qui là phương thức gọi lại chính nó. Ở ví dụ trên, phương thức

calculateFactorial() gọi lại chính nó. Một điều lưu ý là làm sao để đảm bảo rằng

đến lúc nào đó thì lời gọi đệ qui kết thúc còn không chương trình sẽ chạy vô tận

và sử dụng hết tài nguyên của máy tính dẫn đến treo máy. Với minh họa ở trên

Page 54: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

54/114 AptechVietnam

chúng ta thấy đệ qui sẽ kết thúc khi n =0. Tình huống kết thúc đệ qui còn được

gọi là tình huống nền (base case) của đệ qui.

Tất cả các phương thức dùng thuật giải đệ qui có những đặc tính như sau:

Số lần mà phương thức gọi lại chính nó còn được gọi là độ sâu của đệ qui

Mỗi khi phương thức gọi chính nó, máy tính sẽ lưu các biến trong ngăn

xếp (stack). Stack là vùng nhớ hạn chế, do vậy với thuật giải đệ qui có độ

sâu lớn sẽ dẫn đến treo máy. Trường hợp như vậy được gọi là Stack

Overflow (tràn stack).

Phương thức đệ qui có một điều kiện kết thúc. Trong ví dụ trên, phương

thức calculateFactorial() sẽ kết thúc khi n = 0. Nếu điều kiện này không có

thì phương thức sẽ gọi lại chính nó vô hạn. Trường hợp này được gọi là

đệ qui không điểm dừng.

Tất cả các phương thức đệ qui đều có 2 giai đoạn. Giai đoạn thứ nhất

được gọi là Winding, xảy ra khi phương thức gọi lại chính nó và đẩy giá trị

vào stack. Giai đoạn thứ 2 được gọi là Unwinding, xảy ra khi phương thức

lấy giá trị từ stack ra.

Ví dụ dưới đây dùng thuật giải đệ qui để tính lũy thừa hai:

Ví dụ 1:

package test;

import Java.util.Scanner;

public class PowerOfTwo

{

public static void main(String [] args)

Page 55: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 4 Giới Thiệu về Lớp

55/114

{

Scanner input = new Scanner(System.in);

int power, result;

System.out.println(“Calculate the power of two”);

System.out.println(“Enter an integer power: ”);

Power = input.nextInt();

Result = powerOfTwo(power);

System.out.println(“Two to power ” + power + “ is “ +

result);

return;

}

public static int powerOfTwo(int exponent)

{

if(exponent ==0)

return 1;

else

return (2 * powerOfTwo(exponent - 1));

}

}

4.4 Kiểu dữ liệu liệt kê (enum)

Giả sử bạn muốn biểu diễn các mùa trong năm trong chương trình, bạn có thể sử dụng các con số 0, 1, 2, 3 tương ứng với mùa xuân, mùa hè, mùa thu và mùa đông. Dùng theo cách này hệ thống hoạt động được nhưng không trực quan và không rõ ràng. Đây không phải là giải pháp tốt để xây dựng những tình huống như vậy. Chẳng hạn người ta có thể nhập sai các con số khác chứ không phải các con số 0, 1, 2, 3 như mong muốn và không có cách gì để khống chế việc nhập sai. Java hỗ trợ kiểu dữ liệu enum để giải quyết tốt những vấn đề trên. Với kiểu dữ liệu enum, giá trị của nó là các tên tượng trưng. 4.5 Tạo kiểu liệt kê Kiểu enum có thể có thứ tự hoặc không có thứ tự. Kiểu enum không thứ tự là kiểu không có thứ tự logic. Ví dụ: kiểu dữ liệu boolean là một dạng của kiểu

Page 56: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

56/114 AptechVietnam

không thứ tự vì không có lập luận nào xác định true trước hay sau false. Với kiểu enum có thứ tự, các giá trị có thể sắp xếp theo một trật tự logic. 4.5.1 Mục đích của enum

Kiểu enum được giới thiệu nhằm giúp liệt kê hiệu quả dãy các giá trị, ví dụ như

biểu diễn các ngày trong tuần. Ưu điểm của kiểu enum:

Khắc phục được việc ánh xạ giữa số và chuỗi một cách tẻ nhạt. Kiểu

enum được sử dụng bằng các chuỗi thay cho các con số.

Dùng số nguyên dùng để để thay cho kiểu liệt kê không an toàn. Chẳng

hạn, biến DaysOfWeek là biến nguyên để liệt kê ngày trong tuần, chúng ta

có thể gán giá trị bằng 9, như vậy là không hợp lệ. Rõ ràng, dùng số

nguyên thay cho kiểu liệt kê là không an toàn. Để giải quyết các hhược

điểm này người ta dùng kiểu liệt kê. Với kiểu liệt kê, chúng ta chỉ có thể

gán các giá trị đã được định nghĩa trước.

Trong Java, kiểu dữ liệu enum được định nghĩa bằng tứ khóa enum. Vì các giá

trị của nó là các hằng số, nên các trường của kiểu enum khai báo bằng chữ hoa.

Ví dụ dưới đây khai báo biến Months thuộc kiểu enum:

Đoạn mã 6:

public enum Months

{

JANUARY,

FEBRUARY,

MARCH,

APRIL,

MAY,

JUNE,

Page 57: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 4 Giới Thiệu về Lớp

57/114

JULY,

AUGUST,

SEPTEMBER,

OCTOBER,

NOVEMBER,

DECEMBER

}

Kiểu enum sử dụng cú pháp khai báo như khai báo lớp, chỉ khác sử dụng từ khóa enum thay cho class và được gọi là lớp đặc biệt. Phần thân của kiểu enum có thể khai báo các phương thức cũng như các trường. Trình biên dịch sẽ bổ sung thêm các phương thức đặc biệt khi enum được tạo ra. Chúng ta có thể định nghĩa kiểu enum trong một tập tin riêng hoặc trong một tập tin định nghĩa các lớp. Nếu định nghĩa kiểu enum public thì phải định nghĩa trong một tập tin riêng biệt có tên trùng với tên enum. Ví dụ dưới đây minh họa việc định nghĩa kiểu enum để biểu diễn lược đồ màu: Đoạn mã 7: enum ColorScheme

{

Red, Blue, Green, Cyan, Magenta, Yellow, Black

}

Sau khi định nghĩa kiểu enum, chúng ta có thể khai báo biến thuộc kiểu enum như sau: ColorScheme CMYK;

Sau đó gán giá trị cho biến: CMYK = ColorScheme.Magenta;

In ra giá trị của biến ColorSchema bằng lệnh như sau:

System.out.println(“The color used is: ” + CMYK);

Page 58: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

58/114 AptechVietnam

Kết quả sẽ là: The color used is: Magenta

4.6 Garbage Collection (GC) – Thu gom rác

Khi một đối tượng (Object) được tạo ra, nó được cấp phát vùng nhớ trong bộ

nhớ. Vùng nhớ này được gọi là Heap. Các đối tượng trong Java được tạo ra

bằng toán tử new và vùng nhớ của đối tượng được cấp phát trong Heap lúc

thực thi. Khi đối tượng trên Heap không còn được tham chiếu bởi chương trình,

chúng trở nên thích hợp cho thành phần thu gom rác (Garbage Collection), và

Java Heap được gọi là Garbage Collection Heap. Thuật ngữ “Garbage

Collection” (Thu gom rác) hàm ý các đối tượng trong Java khi không còn cần

thiết bởi chương trình, gọi là rác và có thể loại bỏ. Điều đó có nghĩa là vùng nhớ

cấp phát cho các đối tượng sẽ được tái sử dụng cho đối tượng mới tiếp sau.

Java Virtual Machine sẽ xác định những đối tượng nào mà không còn dùng

nữa và sẽ thu hồi vùng nhớ bị chiếm dụng.

Bên cạnh việc giải phóng vùng nhớ của các đối tượng không được tham chiếu,

Garbage Collector (bộ thu gom rác) còn giúp ngăn sự phân mãnh vùng nhớ

heap. Sự phân mãnh heap xảy ra trong quá trình chương trình thực thi. Trong

quá trình thực thi, các đối tượng được tạo ra và hủy bỏ khi không dùng đến nữa,

do đó dẫn đến tình trạng có các block (khối) rỗng nằm giữa các block đang dùng.

Khi yêu cầu vùng nhớ để cấp phát cho các đối tượng mới cần phải nới rộng kích

thước của heap mặc dù vẫn còn đủ vùng nhớ trong heap để cấp phát. Vấn đề

này thường hay xảy ra do không còn đủ vùng nhớ liên tục trong heap để cấp

phát.

Page 59: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 4 Giới Thiệu về Lớp

59/114

Trong đặc tả JVM, chỉ vùng heap của JVM phải được thu gom rác. Nhưng không

định nghĩa cách thức bộ thu gom rác (Garbage Collector) hoạt động, mà mỗi nhà

phát triển JVM phải quyết định cách cài đặt.

4.6.1 Ưu điểm của GC (Garbage Collection)

GC làm giảm bớt gánh nặng cho lập trình viên trong việc giải phóng vùng nhớ.

Đối với lập trình viên, việc quyết định khi nào giải phóng vùng nhớ rất khó khăn

và đầy rủi ro, do vậy công việc này được giao cho JVM, điều này có nhiều ưu

điểm:

Ưu điểm thứ nhất là tăng hiệu suất cho lập trình viên và cho phép họ tập

trung hơn vào việc lập trình.

Thứ hai, GC đảm bảo tính toàn vẹn chương trình. GC là phần quan trọng

trong chiến lược bảo mật của Java. Lập trình viên Java không thể tình cờ

làm hỏng JVM do việc giải phóng sai vùng nhớ.

Nhược điểm tiềm ẩn của GC là làm tăng tài nguyên sử dụng và làm giảm hiệu

năng của chương trình. JVM luôn theo dõi những đối tượng nào đang được sử

dụng bởi chương trình và giải phóng những đối tượng không còn dùng nữa.

Hoạt động này gây tiêu tốn nhiều thời gian của CPU.

Page 60: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

60/114 AptechVietnam

Tóm tắt bài học

Mảng đối tượng là một mảng chứa tham chiếu đến các đối tượng

Đệ qui là hình thức gọi (gọi hàm) lại chính nó

Kiểu enum là một danh sách hằng số và tập hữu hạn các giá trị có thể gán

cho biến.

GC (Garbage Collection) là giải pháp quản lý vùng nhớ tự động

Page 61: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 61/114

Chương 5: Arrays – Mảng

Mục tiêu của bài

Kết thúc bài học này, học viên có thể:

Mô tả mảng trong Java

Mô tả lớp String và những phương thức của nó

Giải nghĩa lớp StringBuilder và những phương thức của nó

5.1 Giới thiệu về mảng (Arrays) Mảng lưu trữ các phần tử cùng kiểu dữ liệu vào những vùng nhớ liên tiếp nhau. Dữ liệu trong mảng có thể là kiểu dữ liệu nguyên thuỷ (primitive data type) hoặc đối tượng. Giống như biến, giá trị trong mảng có thể được gán giá trị ngay lúc tạo mảng. Nếu không có giá trị nào được gán cho mảng, thì Java sẽ gán giá trị mặc định cho tất cả các phần tử trong mảng, tùy thuộc vào kiểu dữ liệu. Ví dụ, những phần tử của mảng có kiểu dữ liệu là int, mặc định, được khởi gán là 0. Mảng có thể là mảng một chiều hay mảng nhiều chiều. Một phần tử xác định trong mảng được truy nhập bằng tên hoặc chỉ số. Phần tử đầu tiên trong mảng có chỉ số là 0, phần tử thứ 2 có chỉ số là 1, và cứ tiếp tục như vậy. Mảng là phương tiện thuận tiện để nhóm thông tin có liên quan với nhau. Mảng có thể được tạo theo ba cách như bảng 5.1

Phương pháp Mô tả Cú pháp Ví dụ

Chỉ khai báo Khai báo một mảng

datatype[] identifier

char[] ch; khai báo một mảng kiểu ký tự tên là ch

Khai báo và tạo mảng

Khai báo và cấp phát vùng nhớ cho các phần tử trong mảng dùng từ khoá new

datatype[] identifier = new datatype[size]

char[] ch = new char[10]; khai báo và khởi tạo một mảng ch để lưu 10 ký tự.

Khai báo, tạo và khởi gán giá trị cho các phần tử

Khai báo mảng, cấp phát vùng nhớ và khởi gán giá trị cho các phần tử

datatype[] identifier = {value1, value2,…valueN}

char[] ch = {„A‟,‟B‟,‟C‟,‟D‟}; khai báo một mảng ch bốn phần tử với các giá trị được khởi gán

Bảng 5.1 : Tạo mảng

Page 62: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

62/114 AptechVietnam

5.1.1 Mảng ký tự Mảng ký tự có thể được tạo như trong đoạn mã sau. Đoạn mã 1: char[] ch ={„A‟,‟B‟,‟C‟,‟D‟};

System.out.println(ch[3]);

Đoạn mã 1 tạo mảng một chiều có tên ch, với các giá trị khởi tạo A, B, C và D. Dòng thứ hai in ra giá trị của phần tử có chỉ số 3, đó là D. Trong Java, mảng nhiều chiều là một mảng có các thành phần một mảng khác, ta cũng nói đó là mảng của các mảng. Mảng nhiều chiều các ký tự có thể được tạo ra như đoạn mã 2. Đoạn mã 2 : Char [][] ch = {{„A‟,‟B‟,‟C‟,‟D‟}, {„E‟,‟F‟,‟G‟,‟H‟}};

System.out.println(ch[1][1]);

Đoạn mã 2 khởi tạo một mảng nhiều chiều với hai hàng và bốn cột. Dòng thứ 2 in ra giá trị của phần tử tại vị trí hàng 1, cột 1. Kết quả in ra là „F‟. Đoạn mã 3 mô tả mảng chuỗi 2 chiều. Đoạn mã 3: String [][] names = {{“John”, “Andrews”, “David”,

“Scott”},{“- Manager”,” – CEO”}};

System.out.println(names[0][0] + names[0][1]);

System.out.println(names[0][2] + names[1][1]);

Đoạn mã 3 sẽ được hiển thị kết quả như:

John – Manager David – CEO

Thuộc tính length có thể dùng để xác định kích thước của mảng hay số các phần tử được lưu trong mảng.

System.out.println(ch.length); Câu lệnh trên sẽ in ra kích thước của mảng

Page 63: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 5 Mảng

63/114

5.1.2 Sắp xếp mảng Luôn cần thiết để sắp xếp các phần tử trong mảng theo giá trị có thứ tự từ cao đến thấp (thứ tự giảm dẫn - descending order) hoặc ngược lại (thứ tự tăng dần – ascending order). Quá trình sắp xếp mảng cần phải hoán đổi giá trị giữa các phần tử mảng. Có nhiều cách khác nhau để sắp xếp mảng. Các cách sắp xếp giống nhau ở chỗ chúng đều so sách mỗi phần tử trong mảng với phần tử khác và hoán đổi giá trị nếu giá trị đặt sai vị trí. Quá trình sắp xếp hoán đổi thực hiện với các phần tử bắt đầu tập giá trị. Kiểu sắp xếp này so sánh giá trị của phần tử đầu tiên với mỗi phần tử tiếp theo và thực hiện các hoán đổi cần thiết. Tiếp đến phần tử thứ hai sẽ được so sánh với mỗi phần tử tiếp theo còn lại trong mảng và thực hiện các hoán đổi cần thiết. Tương tự với các phần tử còn lại trong mảng đến khi không còn hoán đổi nào xảy ra ở bước cuối cùng. Hình 5.1 mô tả khái niệm của sắp xếp hoán đổi.

65 10 57 98 105 304

Hình 5.1 : Sắp xếp hoán đổi Bảng 5.2 chỉ rõ các phần tử được sắp xếp theo thứ tự tăng dần sau mỗi bước. Mỗi bước bắt đầu từ khi lấy một phần tử và so sánh với các phần tử còn lại tiếp theo trong mảng, nếu cần thiết, thực hiện các hoán đổi giá trị của các phần tử.

Các giá trị gốc trong mảng

65 10 57 98 105 304

Sau bước thứ nhất 304 10 57 65 98 105

Sau bước thứ hai 304 105 10 57 65 98

Sau bước thứ ba 304 105 98 10 57 65

Sau bước thứ tư 304 105 98 65 10 57

Sau bước thứ năm 304 105 98 65 57 10

Bảng 5.2: Giá trị của các thành phần sau mỗi bước trong sắp xếp hoán đổi.

Page 64: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

64/114 AptechVietnam

Đầu tiên 2 phần tử (65 và 10) được so sánh và giá trị lớn hơn sẽ được dời sang bên trái. Trong trường hợp này, 10 nhỏ hơn 65; nên không hoán đổi. Tiếp theo, so sánh giá trị đầu tiên với giá trị thứ 3 (65 và 57) và cũng tương tự, không hoán đổi. Sau đó, phần tử đầu tiên và phần tử thứ 4 (65 và 98) được so sánh và hoán đổi vị trí, đưa 98 lên đầu và 65 về vị trí thứ 4. Cứ như vậy so sánh phần tử đầu tiên với tất cả các phần tử còn lại tiếp theo trong mảng, giá trị lớn nhất 304 sẽ được đặt vào phần tử đầu tiên. Kết thúc bước lặp thứ 2 giá trị lớn nhất thứ 2 (304) sẽ được đặt vào vị trí thứ hai. Quá trình tiếp tục và giá trị nhỏ nhất được đặt vào cuối danh sách khi kết thúc. Đoạn mã 4 minh họa cách sắp xếp các phần tử theo thứ tự giảm dần trong Java. Đoạn mã 4 : int [] array = {65,10,57,98,105,304};

int i,j;

int temp ;

// lấy chiều dài của mảng

int arrayLength = array.length;

// phần tử được so sánh trong vòng lặp này

for (i=0; i< (arrayLength -1);i++)

{

for (array[i]<array[j])

{

//hoán đổi vị trí

temp = array[i];

array[i] = array[j];

array[j] = temp;

}

}

}

// hiển thị các giá trị sau khi sắp xếp

for(i=0;i<array.length; i++)

{

System.out.println(array[i]+”\t”);

}

Kết quả hiển thị là : 304 105 98 65 57 10 5.1.3 Tìm kiếm trong mảng Có nhiều cách khác nhau để tìm kiếm giá trị trong một mảng.. Trong tất cả các cách tìm kiếm, tìm kiếm tuần tự là cách đơn giản và dễ cài đặt nhất. Trong tìm kiếm tuần tự, giá trị cần tìm được so sánh với các giá trị của mảng bắt đầu từ

Page 65: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 5 Mảng

65/114

phần tử đầu tiên. Vì cách tìm kiếm này thực hiện theo dạng tuyến tính nên còn được gọi là tìm kiếm tuyến tính (linear search). Tuy nhiên, nếu thực hiện tìm trên mảng không được sắp xếp thì chậm. Kỹ thuật hiểu quả nhất có thể được áp dụng để tìm các phần tử trong một mảng được sắp xếp là kỹ thuật tìm kiếm nhị phân. Kỹ thuật này nhanh hơn so với các phương thức tìm kiếm khác. Kỹ thuật này kiểm tra phần tử nằm giữa của mảng. Nếu giá trị cần tìm bằng phần tử ở giữa, thì tìm kiếm kết thúc. Nếu giá trị cần tìm nhỏ hơn phần tử ở giữa, thì thực hiện tìm nhị phân trên nữa phần đầu của mảng. Nếu lớn hơn, thì thực hiện tìm nhị phân trên nữa phần sau của mảng. Trước khi thực hiện tìm kiếm nhị phân thì mảng phải được sắp xếp. Hình 5.2 minh họa kỹ thuật tìm kiếm nhị phân trên một mảng đã được sắp xếp. Giá trị cần tìm là 11.

Hình 5.2 Kỹ thuật tìm kiếm nhị phân Đoạn mã 5 mô tả mã Java cho thực hiện tìm kiếm nhị phân Đoạn mã 5 : int [] array = {0,11,13,14,15,17,18,19,21,24,26,28,29};

int low =0;

int high = array.length -1;

int searchValue = 11;

int flag =0;

while(low<=high)

{

int mid = low + (high – low)/2;

if(searchValue == array[mid]){

flag = 1;

System.out.println(“ Element found at index “ + mid);

Break;

} else if(searchValue<array[mid]){

high = mid -1;

Page 66: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

66/114 AptechVietnam

}else if (searchValue > array[mid]){

low = mid +1;

}

}

if(flag ==0){

System.out.println(“Element not found in the array”);

}

Kết quả được hiển thị như dưới : Element found at index 1 5.2 Lớp String Khái niệm chuỗi trong Java rất khác so với C và C++. Trong Java, một hằng chuỗi là một đối tượng của lớp String. Vì thế, các thao tác với chuỗi được thực hiện thông qua các phương thức được cung cấp bởi lớp String. Khi đối tượng của lớp String được tạo ra, các ký tự tạo nên chuỗi không thể thay đổi. Nhưng có thể thay đổi chuỗi như thế nào? Mỗi lần cần thay đổi một chuỗi, một đối tượng String mới được tạo ra cùng với các thay đổi so với đối tượng cũ, chuỗi gốc vẫn giữ nguyên như cũ. Các phương thức của lớp String trong J2SE 5.0 được giới thiệu trong các phần sau: 5.2.1 codePointAt() Phương thức codePointAt() trả về mã số của kí tự tại chỉ mục xác định. Khái niệm then chốt trong Unicode là code point. Unicode code points là chỉ những số chỉ vị trí của các ký tự trong bảng mã. Ví dụ trong Unicode, code point 65 thể hiện cho ký tự in „A‟ Cú pháp: int codePointAt(int index);

Ví dụ 1:

String str = “ Aptech Global Learning Solutions”;

System.out.print(str.codePointAt(0));

Kết quả:

65 5.2.2 codePointBefore() Phương thức codePointBefore() trả về mã số unicode của kí tự (Unicode code point) trước ký tự có chỉ mục xác định.

Page 67: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 5 Mảng

67/114

Cú pháp: int codePointBefore(int index);

Ví dụ 2 :

String str = “Aptech Global Learning Solutions”;

System.out.println(str.codePointBefore(1));

Kết quả:

65 5.2.3 codePointCount() Phương thức codePointCount() trả về số lượng ký tự Unicode giữa hai chỉ mục trong chuỗi. Cú pháp: int codePointCount(int start, int end)

Ví dụ 3:

String str = “Aptech Global Learning Solutions”;

System.out.println(str.codePointCount(0,5));

Kết quả:

5 5.2.4 startsWith() Phương thức startsWith() trả về giá trị logic để kiểm tra chuỗi có bắt đầu bằng một tiền tố (prefix) xác định hay không. Cú pháp: boolean startsWith(String prefix);

Ví dụ 4 :

String str = “Aptech Global Learning Solutions”;

System.out.println(str.startsWith(“Apt”));

Kết quả:

true 5.2.5 endsWith()

Page 68: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

68/114 AptechVietnam

Phương thức endsWith() trả về giá trị logic để kiểm tra chuỗi có kết thúc bằng hậu tố (suffix) xác định hay không. Cú pháp: boolean endsWith(String suffix);

Ví dụ 5:

String str = “Aptech Global Learning Solutions”;

System.out.println(str.endsWith(“tions”));

Kết quả:

true 5.2.6 toUpperCase() Phương thức toUpperCase() chuyển các kí tự trong chuỗi thành chữ viết hoa Cú pháp: String toUpperCase();

Ví dụ 6:

String str = “Aptech Global Learning Solutions”;

System.out.println(str.toUpperCase());

Kết quả hiển thị:

APTECH GLOBAL LEARNING SOLUTIONS 5.2.7 toLowerCase() Phương thức toLowerCase() chuyển các kí tự trong chuỗi thành chữ viết thường Cú pháp: String toLowerCase();

Ví dụ 6:

String str = “Aptech Global Learning Solutions”;

System.out.println(str.toLowerCase());

Page 69: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 5 Mảng

69/114

Kết quả hiển thị:

aptech global learning solutions 5.2.8 valueOf() Phương thức valueOf() trả về chuỗi tương ứng với đối số xác định. Đối số có thể là một trong các giá trị sau: boolean, char, float, double, int, long, mảng char, hoặc object. Cú pháp: Static String valueOf(char[] data);

Static String valueOf(char[] data, int offset, int count);

Ví dụ 8:

Char [] array = {„A‟, „p‟,‟t‟,‟e‟,‟c‟,‟h‟,‟

‟,‟G‟,‟l‟,‟o‟,‟b‟,‟a‟,‟l‟};

System.out.println(String.valueOf(array));

System.out.println(String.valueOf(array,7,6);

Kết quả hiển thị:

Aptech Global Global

5.2.9 toCharArray() Phương thúc toCharArray() chuyển nội dung của chuỗi thành mảng các ký tự. Cú pháp: char [] toCharArray();

Ví dụ:

char [] array;

String str =”Aptech Global Learning Solutions”;

Array = str.toCharArray();

System.out.println(String.valueOf(array));

Kết quả hiển thị :

Aptech Global Learning Solutions 5.2.10 equalsIgnoreCase()

Page 70: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

70/114 AptechVietnam

Phương thức equalsIgnoreCase() so sánh hai chuỗi, bỏ qua kiểu viết và trả về giá trị boolean. Nếu các chuỗi giống nhau thì phương thúc trả về giá trị true, ngược lại là false. Cú pháp: boolean equalsIgnoreCase(String anotherString);

Ví dụ:

String str = “ Aptech Global Learning Solutions”;

String anotherString = “APTECH GLOBAL LEARNING SOLUTIONS”;

System.out.println(str.equalsIgnoreCase(anotherString));

Kết quả:

true 5.3 Lớp StringBuider Chuỗi trong Java, một khi đã được tạo ra, không thể nào thay đổi trực tiếp. Đặc tính này được gọi là tính bất biến của chuỗi. Để khắc phục, Java cung cấp lớp StringBuilder, thể hiện cho một dãy khả biến các ký tự. StringBuilder là lớp tương đương với chuỗi và thể hiện cho các dãy ký tự có khả năng biến đổi được. Chúng có thể có nhiều kí tự được chèn vào giữa hoặc thêm vào phần cuối của chuỗi. Những phương thức của lớp StringBuilder được mô tả dưới đây: 5.3.1 charAt() Phương thức charAt() trả về kí tự tại chỉ mục xác định Cú pháp: char charAt(int index);

Ví dụ 11:

StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

System.out.println(sb.charAt(7));

Kết quả hiển thị :

G 5.3.2 deleteCharAt()

Page 71: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 5 Mảng

71/114

Phương thức deleteCharAt() xoá kí tự tại vị trí xác định. Cú pháp: StringBuilder deleteCharAt(int index);

Ví dụ 12:

StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

System.out.println(sb.deleteCharAt(5));

Kết quả hiển thị : Aptec Global Learning Solutions 5.3.3 getChars() Phương thức getChars() sao chép số lượng xác định các ký tự của chuỗi vào một mảng. Cú pháp: void getChars(int begin, int end, char[] destArray, int

destArraybegin);

Ví dụ 13:

char [] array = new char[6];

StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

sb.getChars(0,6,array,0);

System.out.println(array);

Đoạn mã trên sao chép những kí tự của đối tượng StringBuilder, sb, bắt đầu từ ký tự có chỉ mục 0 đến chỉ mục 5. Những kí tự được sao chép được lưu vào mảng ký tự có tên array bắt đầu từ chỉ mục 0. Kết quả hiển thị :

Aptech 5.3.4 length() Phương thức length() trả về tổng số kí tự từ đối tượng StringBuilder. Cú pháp:

Page 72: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

72/114 AptechVietnam

int length();

Ví dụ:

StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

System.out.println(sb.length());

Hiển thị :

32 5.3.5 replace() Phương thức replace() thay thế những kí tự trong đối tượng StringBuilder bằng những kí tự mới. Cú pháp: StringBuilder replace(int begin, int end, String str);

Ví dụ 15 :

StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

System.out.println(sb.replace(23,32,”Services”));

Kết quả hiển thị : Aptech Global Learning Services 5.3.6 setCharAt() Phương thức setCharAt() thay thế một kí tự tại chỉ mục xác định từ đối tượng StringBuilder bằng một kí tự mới. Cú pháp: void setCharAt(int index, char ch);

Ví dụ:

StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

sb.setCharAt(7,‟g‟);// thay thế chữ hoa G thành chữ thường

g

System.out.println(sb);

Page 73: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 5 Mảng

73/114

Kết quả hiển thị : Aptech global Learning Solutions 5.3.7 setLength() Phương thức setLength() thiết lập độ dài của StringBuilder. Cú pháp: void setLength(int newLength); Nếu độ dài mới lớn hơn độ dài hiện tại, tất cả các kí tự mới được thiết lập về kí tự null („\u0000‟). Nếu chiều dài nhỏ hơn chiều dài hiện tại, thì kí tự đầu tiên của mảng cho đến kí tự cuối cùng của chiều dài mới được giữ lại, phần còn lại sau cùng được cắt bỏ. Ví dụ 17: StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

sb.setLength(35);// chiều dài cũ là length=32, chiều dài

mới length = 35;

System.out.println(sb);

sb.setLength(13); // chiều dài mới là length = 13

System.out.println(sb);

Kết quả hiển thị : Aptech Global Learning Solutions Aptech Global 5.3.8 appendCodePoint() Phương thức appendCodePoint() bổ sung thêm một Unicode code point vào đối tượng StringBuilder Cú pháp: StringBuilder appendCodePoint(int codePoint);

Ví dụ:

StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

System.out.println(sb.appendCodePoint(123)); // chèn kí tự

„{„

Kết quả hiển thị :

Page 74: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

74/114 AptechVietnam

Aptech Global Learning Solutions{ 5.3.9 capacity() Phương thức capacity() trả về dung lượng hiện có của đối tượng StringBuilder. Dung lượng là số lượng lưu trữ giành cho các ký tự được chèn mới. Cú pháp: int capacity(); Ví dụ 19: StringBuilder sb = new StringBuilder(); //dung lượng mặc

định

System.out.println(sb.capacity()); // trả về 16

sb = new StringBuilder(“Aptech Global Learning Solutions”);

System.out.println(sb.length()); // trả về 32

//dung lượng mặc định + chiều dài của đối tượng sb

System.out.println(sb.capacity());// trả về 48

Kết quả hiển thị 16 32 48 5.3.10 substring() Phương thức subString() tạo một chuỗi con từ đối tượng StringBuilder. Cú pháp: String substring(int startIndex);

String substring(int startIndex, int endIndex);

Phương thức thứ hai trả về chuỗi con từ đối tượng StringBuilder. Chuỗi con bắt đầu tại startIndex và kết thúc tại endIndex - 1 Ví dụ 20: String str;

StringBuilder sb = new StringBuilder(“Aptech Global

Learning Solutions”);

str = sb.substring(7);//trả về một chuỗi con bắt đầu ở vị

trí chỉ mục thứ 7

System.out.println(str);

Page 75: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 5 Mảng

75/114

//Trả về chuỗi con ở vị trí chỉ mục 7 và cho đến chỉ mục 21

str = sb.substring(7,21);

System.out.println(str);

Kết quả hiển thị: Global Learning Solutions Global Learning

Page 76: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

76/114 AptechVietnam

Tóm Tắt Bài Học

Mảng được dùng để lưu trữ nhiều phần tử cùng kiểu dữ liệu trong một dãy vị trí vùng nhớ liên tiếp nhau

Giá trị lưu trữ trong các phần tử mảng có thể được tìm và sắp xếp

Hằng String trong Java là một trường hợp của lớp String.

Lớp String cung cấp nhiều phương thức đa dạng để tìm kiếm và chia tách trong chuỗi

String không thể biển đổi được, có nghĩa rằng chúng là hằng và giá trị của chúng không thể thay đổi được

Lớp StringBuilder được dùng như một String có thể thay đổi nội dung

Page 77: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 77/114

Chương 6: Gói và thành phần chỉ định truy nhập

Mục Tiêu Bài Học

Kết thúc bài học này, học viên có thể:

Định nghĩa packages và liệt kê nhiều loại package

Các bước để cài đặt và sử dụng packages của người dùng định nghĩa

Nhận biết các cách truy nhập public, private và protected

Trạng thái dùng default hoặc truy nhập package

Định nghĩa và liệt kê các trường và định nghĩa phương thức

6.1 Gói (Package) Package trong Java là một nhóm các lớp (class) và các giao tiếp (interface) có liên quan với nhau được tổ chức thành một đơn vị. Một package, lần lượt, chứa các package con hoặc không. Nếu một lớp được tạo ra không chỉ ra package nào, nó thuộc về package không có tên. Điều này thường gặp trong các ứng dụng nhỏ. Tên lớp đi kèm tên các package chứa lớp ấy gọi là tên đầy đủ của lớp(Fully qualified name). Ví dụ, tools.drawing.Shapes là tên đầy đủ của lớp Shape. Package được phân thành package có sẵn hay định nghĩa trước và package do người dùng định nghĩa. Những package có sẵn là phần Java API. Package do người dùng định nghĩa được tạo bởi nhà phát triển ứng dụng. 6.2 Tạo và sử dụng package Các bước tạo một package trong Java được cụ thể như sau: Bước đầu tiên là chọn tên cho package. Tên package được viết dưới dạng chữ in thường, không bắt đầu bằng ký tự chữ số và không có dấu nối (-). Tuy nhiên, chúng có thể chứa các ký tự gạch dưới. Ngay khi tên package được chọn, một thư mục có tên ứng với tên package được tạo ra. Tất cả các tập tin nguồn chứa

Page 78: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

78/114 AptechVietnam

chương trình Java trong package được đặt trong thư mục đó. Lệnh package được thêm vào đầu trong mỗi tập tin nguồn chứa trong thư mục. Cú pháp: package <packagename>; Ví dụ, giả sử Machines.Java và Gadgets.Java là hai tập tin được tạo ra trong package có tên factory. Như ở bước 1, thư mục được tạo ra với tên factory. Dòng package factory; được thêm vào mỗi tập tin Machines.Java và Gadgets.Java. Trong hai đoạn mã 1 và đoạn mã 2 minh họa ví dụ. Đoạn mã 1: Line0 : package factory;

Line1 : public class Machines {

Line0 hiển thị mô tả package. Class Machines bây giờ nằm

trong package factory

Đoạn mã 2: Line0 : package factory;

Line1 : public class Gadgets {

Giả sử hai đoạn mã đã được hoàn thành và hai tập tin đã được lưu. Bước kế tiếp là biên dịch và thực thi ứng dụng. Ví dụ, để thực thi lớp Machines thực hiện câu lệnh sau tại dấu nhắc lệnh với thư mục hiện tại là thư mục cha chứa tập tin nguồn: Java factory.Machines Để sử dụng một lớp tại bất kỳ vị trí nào, lệnh import được dùng. Ví dụ về cách dùng lệnh import để nhập lớp Machines từ package factory: import factory.Machines; //import một class đơn Để import tất cả các lớp trong package, dùng lệnh dưới đây : import factory.*; // import tất cả các class Đoạn mã 3 minh họa cách dùng lớp Machines từ packge factory trong lớp Resources đặt trong một package khác.

Page 79: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 7 Thừa kế và giao tiếp

79/114

Đoạn mã 3: package company;

import factory.Machines;

public class Resources {

public void testMethod() {

Machines objMachines = new Machines();

}

}

Để chỉ ra lớp Resources thuộc package company, lệnh package được thêm vào dòng đầu tiên của đoạn mã. Để dùng lớp Machines trong lớp Resources, cần phải import lớp Machines vào. Lúc biên dịch lớp Resources, phải đảm bảo classpath chứa đường dẫn của thư mục chứa các package, ở đây là factory. Tất cả các lệnh import trong lớp phải được đặt sau câu lệnh package và trước khai báo lớp. Các lệnh import và package đặt trong một tập tin sẽ ảnh hưởng đến tất cả các lớp trong tập tin và không thể áp dụng cho riêng từng lớp. Trong lệnh package, tên của các package và tên của package con được phân cách bởi dấu chấm. Mỗi thành phần của tên package phải là tên một thư mục trên máy cục bộ. Ví dụ, nếu lệnh package như dưới đây: package demo.management.list.src;

thì, phải tạo cấu trúc thư mục như hình dưới : demo\management\list\src. Thông thường, tên đầy đủ cần phải sử dụng để truy nhập những thành phần tĩnh (static) của lớp trong lớp khác. Tuy nhiên, điều đó làm cho mã trở nên cồng kềnh, đặc biệt khi có nhiều lời gọi thành phần tĩnh trong cùng một câu lệnh. Java cung cấp cách giải quyết bằng các lệnh import static. Lệnh import static cho phép một chương trình import riêng lẽ hay toàn bộ những thành phần tĩnh. Ví dụ, nếu lớp Machines thuộc vào package mnc.factory có một thành phần tĩnh boltSize cần được dùng trong lớp Gadgets, thì lệnh import static có thể được sử dụng. Lúc này, boltSize có thể được dùng ở bất cứ nơi đâu bên trong lớp Gadgets mà không cần dùng tên đầy đủ của nó. Đoạn mã 4 minh họa trường hợp này. Đoạn mã 4: import static mnc.factory.Machines.boltSize;

class Gadgets {

public void assign() {

Page 80: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

80/114 AptechVietnam

boltSize = 20;

}

}

import static cũng có thể được dùng trong thư viện : import static Java.lang.Math.PI; double area = PI*radius*radius; Dù import static là đặc tính rất hữu ích, nhưng không nên sử dụng thường xuyên. Dùng quá nhiều import static làm cho chương trình trở nên khó đọc cồng kềnh khó đọc và cũng gây ra các vấn đề bảo dưỡng. 6.3 Chỉ định truy nhập (Accesss Specifiers) Chỉ định truy nhập hay thành phần xác định phạm vi truy nhập trong Java điều khiển cách truy nhập và khả năng nhìn thấy của các lớp và các thành phần của lớp. Các nguyên lý hướng đối tượng được cài đặt trong chương trình thông qua sử dụng các chỉ định truy nhập. Có bốn mức truy nhập sau :

public Chỉ định truy nhập public xác định mức truy nhập phổ biến. Chỉ định truy nhập public, khi áp dụng cho lớp Java, cho phép lớp có thể truy nhập ở mọi nơi, thậm chí bên ngoài package. Khi áp dụng cho bất kỳ thành phần nào của lớp, nó cho phép thành phần có thể truy nhập từ mọi nơi trong chương trình. Các lớp mức trên chỉ có thể là public hoặc truy nhập ngầm định và không thể là private hoặc protected.

private Chỉ định truy nhập private xác định mức truy nhập hạn chế. Khi một lớp hoặc thành phần của nó được khai báo là private, chỉ có thể truy nhập ở bên trong lớp và không thể truy nhập ở bất kỳ đâu khác. Các lớp khác không thể truy nhập vào thành phần private của lớp này.

protected Chỉ định truy nhập protected xác định mức truy nhập có bảo vệ. Khi thành phần lớp được khai báo là protected, thì chỉ có thể truy nhập bên trong lớp của nó, trong cùng package và các lớp kế thừa.

Truy nhập ngầm định hoặc package. Không có từ khóa xác định cho mức truy nhập ngầm định hoặc package. Nếu lớp hay interface không xác định các chỉ định truy nhập ở trên, thì sẽ

Page 81: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 7 Thừa kế và giao tiếp

81/114

có mức truy nhập ngầm định. Và có thể truy nhập ở những lớp khác trong cùng package. Tương tự, nếu một thành phần của lớp không khai báo chỉ định truy nhập, thành phần đó sẽ nhận truy nhập ngầm định và chỉ có thể được truy nhập trong cùng package và không thể truy nhập từ ngoài package.

Bảng 6.1 thể hiện mối quan hệ giữa các chỉ định truy nhập và những thành phần trong một chương trình Java.

Chỉ định truy nhập

Có thể truy nhập

Trường dữ liệu

Phương thức

Hàm dựng Lớp Interface

public Yes Yes Yes Yes Yes

private Yes Yes Yes No No

protected Yes Yes Yes No No

mặc định/ không mô tả

Yes Yes Yes Yes Yes

Bảng 6.1 : Chỉ định truy nhập cho những thành phần khác nhau

Như vậy, như bảng 6.1, lớp và interface mức cao nhất có thể là public hoặc mặc định cách truy nhập, trường dữ liệu, hàm dựng và phương thức có thể có một trong bốn chỉ định truy nhập. Ví dụ, một hàm dựng có thể được khai báo như đoạn mã 5.

Đoạn mã 5: public class Building {

private int floors;

Building( ) {

floors=10;

}

}

Trong đoạn mã 5, hàm dựng không định nghĩa chỉ định truy nhập (access modifier), vì thế chỉ có truy nhập trong package của nó. Bảng 6.2 thể hiện cách thức các chỉ định truy nhập tác động đến lớp, interface hoặc thành phần. Ví dụ, một thành phần protected của lớp có thể truy nhập bên trong lớp của nó, bên trong lớp con và bên trong những lớp trong cùng package

Chỉ định truy nhập

Những thành phần cỏ thể truy nhập

Lớp Package Lớp con Bên ngoài Package

public Yes Yes Yes Yes

Page 82: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

82/114 AptechVietnam

private Yes No No No

protected Yes Yes Yes No

Không khai báo

Yes Yes No No

Bảng 6.2 Chỉ định truy nhập và phạm vi truy nhập của chúng

Đoạn mã 6 sẽ minh họa cách sử dụng một số chỉ định truy nhập. Đoạn mã 6: class Building {

protected int floors = 3;

private String name;

public void getName() {

System.out.println(“Building name is ”+name);

}

public void setName() {

Scanner imput = new Scanner(System.in);

input.useDelimiter(“\n”);

name = input.next();

}

}

public class BuildingTest {

public static void main(String args[]) {

Building objBuilding = new Building();

System.out.println(“Number of floors are ”+

objBuilding.floors);

objBuilding.setName();

objBuilding.getName();

}

}

Đoạn mã 6, khi biên dịch và thực thi, sẽ hiển thị số tầng là 3 và nhắc người dùng nhập tên của tòa nhà (building) vào trường name và hiển thị tên được nhập. Như trong đoạn mã, tên được truy nhập và in ra dùng phương thức public bởi vì trường name là private và không thể truy nhập bên ngoài lớp. Quan sát ta thấy lớp BuildingTest là không được kế thừa từ lớp Building nhưng cách đơn giản là tạo đối tượng và kích hoạt các phương thức của nó. Một thành phần (lớp, interface hoặc thành viên) có thể có một trong các chỉ định truy nhập được mô tả trong bảng 6.2. Ví dụ, public và protected không thể được thiết lập đồng thời cho một thành phần của lớp. Khi thiết kế lớp, tốt nhất là tạo ra các biến thể hiện private và có các phương thức truy nhập (accessor methods)

Page 83: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 7 Thừa kế và giao tiếp

83/114

public để truy xuất và thiết lập giá trị cho các biến. Và cũng đảm bảo không có phương thức private hoặc biến private nào không được sử dụng trong lớp. Các biến cục bộ khai báo trong phương thức không khai báo chỉ định truy nhập (access specifier). Chỉ các biến mức lớp, còn gọi là biến thể hiện (instance variable), mới khai báo chỉ định truy nhập. 6.4 Bổ từ cho trường và phương thức Bổ từ cho trường và phương thức (field and method modifier)là những từ khóa được dùng để xác định các phương thức và trường cần được khai báo để điều khiển truy nhập của người dùng. Một số sử dụng kèm với từ khóa public và protected. Java cung cấp các từ khóa volatile, transient và native như là các bổ từ cho trường và phương thức (field and method modifiers).

volatile Bổ trợ volatile cho phép nội dung của biến đồng bộ cho tất cả các tuyến trình đang chạy vì vậy khi giá trị của biến thay đổi, tất cả các tuyến trình sẽ nhìn thấy sự thay đổi đó. Bổ từ volatile chỉ áp dụng cho trường. Những hàm dựng, phương thức, lớp và interface không có áp dụng bổ trợ này. Biến volatile hữu ích trong môi trường đa xử lý và không thường sử dụng trong các môi trường khác.

native Bổ trợ native chỉ được áp dụng cho các phương thức của lớp và cho biết cài đặt của phương thức nằm bên ngoài lớp. Thường cài đặt cũng nằm ngoài JVM và được tạo ra với ngôn ngữ hay công nghệ không phải Java như C hay C++. Hàm dựng, trường, lớp và interface không có bổ từ này. Những phương thức khai báo sử dụng bổ từ native được gọi là phương thức native . Tập tin mã nguồn Java thường chỉ chứa khai báo và không có phần cài đặt của phương thức native. Những phương thức native vi phạm vào tính chất độc lập nền tảng (platform) của Java và do đó không nên sử dụng trừ khi thực sự cần thiết. Đối tượng gọi phương thức native thậm chí không cần biết phương thức được khai báo trong lớp, bởi vì chúng kích hoạt phương thức bình thường giống như lời gọi các phương khác. Một vài lớp trong Java API có các phương thức native. Lớp Object, ví dụ, trong gói Java.lang, có nhiều phương thức native.

transient Bổ từ transient được dùng để khai báo các trường sẽ không lưu trữ hoặc khôi phục như một phần trạng thái của đối tượng. Trong nhiều trường hợp, tuần tự hóa (serialization) tương đối tốn kém bộ nhớ ngoài. Trong trường hợp như vậy, từ khóa transient giảm lượng dữ liệu tuần tự, nâng

Page 84: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

84/114 AptechVietnam

cao hiệu suất thực hiện, và giảm chi phí. Tuần tự hóa (Serialization) là một quá trình chuyển đổi trạng thái của đối tượng thành một luồng các byte nhị phân (binary stream). Luồng có thể được ghi ra đĩa hoặc lưu trữ trong bộ nhớ.

Bổ từ volatile và transient không thể áp dụng cho các biến cục bộ.

Java không xác định cụ thể vị trí của các chỉ định truy nhập (access specifiers) và bổ từ (modifiers) trong khai báo. Vì thế các khai báo sau là hợp lệ:

native public void testNative(); private volatile int price; transient private volatile int price;

Tuy nhiên, câu lệnh dưới đây không hợp lệ bởi vì chỉ định truy nhập hay bổ từ cho trường và phương thức phải đặt trước kiểu dữ liệu.

int private volatile price;

Page 85: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 7 Thừa kế và giao tiếp

85/114

Tóm Tắt Bài Học

Package (gói) trong Java là một nhóm các lớp (class) có quan hệ với nhau và những cái chung được sắp xếp lại với nhau như một đơn vị.

Package có thể chia làm 2 loại: package có sẵn hoặc package do người dùng định nghĩa.

Câu lệnh khai báo package được khai báo vào phần trên cùng của một class và dùng lệnh import để import một hoặc nhiều class(lớp) từ một package khác vào trong chương trình.

Cách thức truy nhập chỉ định và mô tả trong điều khiển Java cho phép truy nhập của lớp và các thành viên của lớp.

Bổ từ cho trường và phương thức là những từ khóa dùng để xác định trường và phương thức cần được khai báo cho điều khiển truy nhập của người dùng

Page 86: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )
Page 87: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 87/114

Chương 7 : Thừa kế và giao tiếp

Mục tiêu bài học:

Kết thúc bài học này, học viên có thế :

Hiểu khái niệm về sự thừa kế

Hiểu khái niệm về nạp chồng

Mô tả khái niệm về giao tiếp

7.1 Sự thừa kế

Thừa kế (inheritance) là một khái niệm rất mạnh. Thừa kế cho phép sử dụng lại mã . Xét tình huống người dùng muốn tạo một lớp mới và đã có lớp khác chứa mã có thể sử dụng lại. Trong trường hợp đó, người dùng có thể chỉ đơn giản thừa kế lớp mới từ lớp hiện có mà không cần viết hay gỡ rối mã mới.

Lớp thừa kế từ lớp khác được gọi là lớp con (sub-class) hoặc lớp dẫn xuất hoặc lớp mở rộng . Lớp mà được lớp con dẫn xuất gọi là lớp cha (superclass) hoặc lớp cơ sở (base class). Tất cả các lớp trong Java ngầm định là lớp con của lớp Object. Một lớp Java có thể được dẫn xuất tường minh từ một và chỉ một lớp cha sử dụng từ khoá “extends”. Nói cách khác, Java chỉ hỗ trợ đơn thừa kế.

Đoạn mã 1:

class Mammal {} // Động vật có vú, lớp cha

class Whale extends Mammal{} // Cá voi, lớp con

Một lớp con thừa hưởng các trường, các phương thức và thậm chí các lớp lồng từ lớp cha. Tuy nhiên, các hàm dựng (constructor) không được thừa kế vì các hàm dựng không phải là thành phần của lớp. Hàm dựng của lớp cha có thể được gọi từ lớp con .

Một lớp con thừa kế tất cả các thành phần public và protected của lớp cha bất kể gói (package) của lớp con. Nếu các lớp con cùng package với lớp cha thì nó không thừa kế các thành phần private của lớp cha. Tuy nhiên, các phương thức public và phương thức protected sử dụng để truy xuất tới các trường private cũng có thể được dùng bởi lớp con.

Một lớp lồng (nested-class) có thể truy xuất đến tất cả các trường private và các phương thức private của lớp chứa nó. Do đó, một lớp lồng public hay protected được thừa kế bởi lớp con truy xuất gián tiếp tới tất cả các thành phần private của lớp cha.

Page 88: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

88/114 AptechVietnam

7.2 Hiệp biến kiểu trả về (Covariant Return Types)

Nét mới trong J2SE 5.0 là nó cho phép một phương thức ghi đè, trả về một đối tượng có kiểu là 1 lớp con của kiểu được trả về của phương thức bị ghi đè ở lớp cha. Đây gọi là hiệp biến kiểu trả về. Ưu điểm chính của hiệp biến kiểu trả về là nó giảm ép kiểu và kiểm tra kiểu.Ví dụ, xem lớp cha có tên là Student cài đặt 2 phương thức có tên getMarks(). Một phương thức trả về 1 thể hiện của lớp java.lang.Number và lớp kia trả về lớp java.lang.Integer như trong Đoạn mã nguồn 2.

Đoạn mã 2:

class Student

{

public Number getMarks(){

return new Number();//Trả về đối tượng của lớp Number

}

public Integer getMarks(){

return new Integer();//Trả về đối tượng lớp Integer

}

}

Theo đoạn mã trên khi biên dịch lớp này sẽ nhận được kết quả lỗi như sau:

Student.Java:6: getMarks() is already defined in Student

public Integer getMarks(){

^

Lý do của lỗi này là nếu một lớp gọi phương thức getMarks() trong lớp Student trình biên dịch sẽ không biết nó nên gọi phương thức nào trong hai phương thức. Vì số nguyên (Integer) là lớp con của lớp Number, một trong hai phiên bản của phương thức có thể được gọi đúng .

Bây giờ xem lớp cha Student có một cài đặt đơn của phương thức getMarks(). Xem lớp con có tên ExchangeStudent ghi đè cùng phương thức getMarks() nhưng trả về đối tượng có kiểu Integer là kiểu con của lớp Number. Nếu đoạn mã 3 được biên dịch trong JDK phiên bản trước J2SE 5.0 sẽ xảy ra lỗi .Tuy nhiên, nó biên dịch thành công trong J2SE 5.0 JDK.

Đoạn mã 3:

class Student {

public Number getMarks(){

return new Number();//Trả về đối tượng của lớp Number

Page 89: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 7 Thừa kế và giao tiếp

89/114

}

}

class ExchangeStudent extends Student{

public Integer getMarks(){

return new Integer();//Trả về đối tượng của lớp Integer

}

}

Chú thích của ND: Ví dụ trên chỉ mang tính minh họa, không dịch được vì Number là lớp Abstract, hàm dựng của Integer không đúng. Ngoài ra trong bản chính còn thiếu việc thừa kế lớp Student.

7.2.1 Che lấp phương thức (hidden method)

Che lấp phương thức là hiện tượng được thể hiện bởi các phương thức tĩnh của một lớp. Nếu lớp con định nghĩa một phương thức tĩnh có cùng ký hiệu nhận biết (signature) như phương thức trong lớp cha, phương thức trong lớp con được gọi là che lấp phương thức trong lớp cha.

Sự khác biệt giữa che lấp và ghi đè có các hàm ý quan trọng. Phiên bản nào của phương thức che lấp được gọi, tùy thuộc vào nó được gọi từ lớp cha hay từ lớp con. Xét lớp cha Vehicle cài đặt phương thức tĩnh accelerate() và phương thức động brake(). Giả sử lớp con Truck ghi đè phương thức brake() và phương thức tĩnh accelerate(). Ở đây phương thức trong lớp Truck gọi là che lấp phương thức trong lớp Vehicle. Giờ thì phiên bản nào của phương thức che lấp sẽ được thực thi tùy thuộc vào thể hiện của lớp cha hay lớp con. Các phương thức che lấp được mô tả chi tiết trong đoạn mã 4. Đoạn mã 4:

class Vehicle{ // Xe cộ

static void accelerate(){ // Tăng tốc

System.out.println("Accelerate in Vehicle.");

}

void brake(){ // Hãm thắng

System.out.println("Brake in Vehicle.");

}

}

public class Truck extends Vehicle // Truck: xe tải

{

static void accelerate(){

Page 90: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

90/114 AptechVietnam

System.out.println("Accelerate in Truck...");

}

void brake(){

System.out.println("Brake in Truck...");

}

public static void main(String args[]){

Truck dumper = new Truck();

Vehicle trailor = dumper;

//Kiểm tra che lấp phương thức

Vehicle.accelerate();

//Phương thức tỉnh trong lớp Vehicle được gọi

//Kiểm tra sự che lấp phương thức accelerate

// trong lớp Vehicle

dumper.accelerate();//phương thức tỉnh trong Truck

trailor.accelerate();//phương thức trong Vehicle chạy

//Ghi đè đơn giản

trailor.brake(); //phương thức ghi đè trong lớp

//Truck được chạy

}

}

7.2.3 Che lấp các thuộc tính của lớp cha

Một thuộc tính (field) của lớp con che lấp bất kì thuộc tính nào của lớp cha cùng tên, thậm chí kiểu của chúng khác nhau. Bên trong lớp con, thuộc tính của lớp cha không thể được tham chiếu bằng tên thông thường mà phải dùng từ khóa super. Đới với các tham số của hàm dựng và phương thức, nếu tên của một tham số và thuộc tính giống nhau, thì tham số che lấp thuộc tính. Đối với trường hợp này, từ khoá this phải được dùng để truy nhập đến thuộc tính. Nói chung, các thuộc tính ẩn không được khuyên dùng vì nó làm cho mã khó đọc. Tuy nhiên, để mã dễ đọc hơn, lập trình viên thỉnh thoảng đặt tên giống nhau cho biến thuộc tính và tham số của phương thức. Khi đó, tham số che lấp thuộc tính. Do đó, từ khoá this được dùng để truy xuất đến thuộc tính. 7.3 Nạp chồng (Overloading) 7.3.1 Nạp chồng phương thức

Page 91: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 7 Thừa kế và giao tiếp

91/114

Một số phương thức, có cùng tên nhưng khác tham số, khi được định nghĩa trong một lớp thì được gọi là nạp chồng phương thức (method overloading). Các phương thức nạp chồng không thể khác kiểu dữ liệu trả về. Chúng có thể khác số lượng tham số, khi đó, các tham số có thể cùng kiểu dữ liệu; hoặc cùng số lượng tham số với điều kiện các tham số này phải khác kiểu dữ liệu. Các phương thức có thể được nạp chồng thậm chí khi các tham số được truyền vào là các đối tượng của lớp. Xét ví dụ về phương thức được nạp chồng có số lượng các đối tượng truyền vào khác nhau. Cho lớp có tên Laboratory (phòng thí nghiệm) định nghĩa một phương thức nạp chồng có tên là makeCompound() nhận các đối tượng Chemical (hóa chất) qua các tham số truyền vào. Đoạn mã 5 mô tả nạp chồng phương thức. Đoạn mã 5:

class Laboratory {

public Compound makeCompound(Chemical a, Chemical b){

//Some application code here

}

public Compound makeCompound(Chemical a, Chemical b,

Chemical c){

//Some application code here

}

}

Ở đây, Chemical là lớp được tạo bởi chính ứng dụng nhưng vẫn có thể được dùng giống như các lớp dựng sẵn của Java (build-in Java). Do đó, các phương thức có thể được nạp chồng bằng cách truyền vào số lượng khác nhau các tham số có cùng kiểu dữ liệu. Phương thức cũng có thể được nạp chồng bằng cách truyền vào số lượng cố định các tham số nhưng kiểu dữ liệu của tham số khác nhau. Đoạn mã 6 mô tả loại nạp chồng phương thức này. Đoạn mã 6:

class Laboratory {

public Compound makeCompound(Chemical a, Chemical b,

double quantityOfWater){

//Some application code here

}

Page 92: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

92/114 AptechVietnam

public Compound makeCompound(Chemical a, Chemical b,

Chemical c){

//Some application code here

}

}

Phương thức makeCompound được nạp chồng với ba tham số có kiểu dữ liệu khác nhau được truyền vào. 7.3.2 Nạp chồng hàm dựng Các hàm dựng (constructor) của lớp cũng có thể được nạp chồng giống phương thức. Nạp chồng hàm dựng có thể được thực hiện hoặc với cùng số lượng tham số và kiểu dữ liệu của tham số khác nhau hoặc khác số lượng tham số và kiểu dữ liệu của tham số giống nhau.

Đoạn mã 7 mô tả nạp chồng hàm dựng

Đoạn mã 7:

public class Compound {

public Compound(Element a, Element b){

//Some application code here

}

public Compound(Element a, Element b, Element c){

//Some application code here

}

}

Hàm dựng của lớp Compound được nạp chồng với số các tham số đối tượng khác nhau và cùng kiểu dữ liệu. Thực tiễn này được sử dụng khi một đối tượng mới được tạo ra sử dụng một số đối tượng hiện có.

Cách thứ hai của nạp chồng hàm dựng là truyền vào số lượng cố định các tham số có kiểu dữ liệu khác nhau như minh họa trong đoạn mã 8.

Đoạn mã 8:

public class Compound {

public Compound(Element a, Element b, double

quantityOfWater){

//Some application code here

}

public Compound(Element a, Element b, Compound c){

//Some application code here

}

}

Page 93: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 7 Thừa kế và giao tiếp

93/114

Từ khoá this thường dùng để tham chiếu tới đối tượng hiện tại. Từ khoá this có thể được sử dụng tại vị trí cho phép tham chiếu đến đối tượng của lớp hiện hành. Tuy nhiên, this cũng có thể được dùng để gọi hàm dựng khác trong cùng một lớp. Đây gọi là triệu gọi hàm dựng tường minh. Khái niệm được thảo luận sâu hơn trong đoạn mã 9.

Đoạn mã 9:

public class Man {

private int height;

private double weight;

public Man(){

this(0,0);

}

public Man(double wt){

this(0,wt);

}

public Man(int height, double weight){

this.height = height;

this.weight = weight;

}

public static void main(String[] args){

//Initializes height and weight to 0

Man objMan1 = new Man();

//Initilizes weight to 160.50 and height to zero

Man objman2 = new Man(160.50);

//Initilizes weight to 160.50 and height to 190

Man objman2 = new Man(190, 160.50);

}

}

Hàm dựng đầu tiên trong lớp trên triệu gọi tường minh hàm dựng có hai đối số bằng việc sử dụng từ khoá this và gán 0 vào hai đối số. Hàm dựng thứ hai cũng được gọi với từ khoá this và một đối số đơn weight được truyền vào. Hàm dựng thứ ba là hàm dựng chuẩn khởi gán các biến thể hiện bằng lời gọi tới các hàm dựng có tham số thông qua từ khóa this. Một quy tắc chuẩn cần phải tuân theo là nếu có lệnh gọi đến hàm dựng khác, thì lệnh đó phải được đặt đầu tiên trong hàm dựng hiện tại.

Giao tiếp (interface) là sự trừu tượng của lớp. Một interface xác định những điều một lớp phải làm, nhưng không chỉ rõ làm bằng cách nào. 7.5 Giao tiếp (interface)

7.5.1 Truy xuất các cài đặt thông qua tham chiếu giao tiếp Các giao tiếp có thể được gán các biến tham chiếu tương tự việc gán một tham chiếu đến lớp. Biến này có thể lưu một thể hiện của bất kì lớp nào cài đặt giao

Page 94: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

94/114 AptechVietnam

tiếp này. Việc triệu gọi một phương thức trên bất kì một trong các tham chiếu sẽ gọi phiên bản cài đặt phù hợp của phương thức tùy thuộc vào thể hiện thực sự của giao tiếp đang được tham chiếu tới. Kết quả, phương thức sẽ được thực thi được tìm kiếm theo cách động trong lúc thực thi và các lớp được tạo ra sau đoạn mã gợi thực hiện các phương thức của chúng. Vì phương thức được gọi được xác định tại thời điểm thực thi, nên sẽ tốn chi phí để thực thi loại mã này. Do đó, phương pháp này thực tế không được dùng với các mã hướng đến khả năng thực thi. 7.5.2 Các biến trong giao tiếp: Các giao tiếp có thể có các biến. Các biến được mô tả trong giao tiếp ở dạng ngầm định là final và static (tĩnh). Chúng không thể được thay đổi bởi các lớp cài đặt giao tiếp. Do đó, chúng phải được khởi tạo với một giá trị hằng. Nếu giao tiếp được khai báo là public thì ngầm định tất cả các phương thức và các biến được khai báo trong giao tiếp cũng là public. Khi một giao tiếp được cài đặt bởi một lớp, tất cả các biến của giao tiếp sẽ hoạt động như các biến hằng trong lớp. Đoạn mã 10 minh họa khái niệm này.

Đoạn mã 10: public interface Aircraft{

//biến trong interface – cần được khởi tạo

public int passengerCapacity = 400;

void fly();

}

public class FighterJet implements Aircraft {

//biến của interface là final và static. Do vậy không thể

// thay đổi trong lớp

passengerCapacity = 2; //sẽ sinh lỗi

void fly(){

//Biến Static không thể truy nhập từ vùng non-static

System.out.println(passengerCapacity);

//sẽ sinh ra lỗi

}

public static void main(String[] args) Ơ

System.out.println(passengerCapacity); //In ra 400

}

}

7.5.3 Giao tiếp có thể được mở rộng Trong Java, một lớp đơn giản có thể có thể mở rộng thành một lớp khác, đến lượt nó cũng theo cách đó mở rộng thành một lớp thứ ba và cứ như vậy. Khái niệm này cũng được gọi là thừa kế và cũng có thể sử dụng cho giao tiếp. Một giao tiếp là một lớp trừu tượng. Do đó một giao tiếp có thể mở rộng từ giao tiếp khác giống như một lớp mở rộng từ lớp Java khác. Thực ra, ngôn ngữ Java thực

Page 95: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 7 Thừa kế và giao tiếp

95/114

hiện đa thừa kế chỉ với giao tiếp. Có nghĩa là một giao tiếp riêng lẻ có thể thừa kế nhiều hơn một giao tiếp. Điều này được giải thích trong đoạn mã 11. Đoạn mã 11: interface Game extends Player, Animation

{

//Variables and method declarations of both Player and

Animation interfaces available here

}

Ở đây giao tiếp Game thừa kế giao tiếp Player và Animation. Do đó có thể nói rằng giao tiếp Game thừa kế các phương thức của cả hai giao tiếp Player và Animation. 7.5.4 Quan hệ LÀ MỘT / IS-A: Đây là khái niệm dựa trên sự thừa kế lớp hay sự cài đặt (implementation) giao tiếp. Một quan hệ IS-A thể hiện thứ bậc lớp trong trường hợp thừa kế lớp. Ví dụ nếu một lớp Ferrari mở rộng lớp Car, thì câu “Ferrari LÀ MỘT Car “ là đúng. Nếu lớp Car bản thân nó mở rộng từ lớp Verhical khác thì mối quan hệ Ferrari IS-A Verhical cũng đúng. Một quan hệ IS-A có thể được mô tả bằng mũi tên trong hình vẽ mô tả thứ bậc của lớp.

Hình 7.1: Mối quan hệ IS-A

Quan hệ IS-A cũng có thể được sử dụng trong trường hợp cài đặt giao tiếp. Một quan hệ IS-A trong trường hợp của giao tiếp được thể hiện trong Java với từ khoá implements. Một lớp Java có thể cài đặt nhiều giao tiếp. Đó gọi là đa-thừa-kế-giao-tiếp trong Java. Nó được sử dụng để buộc một lớp con phải tuân theo quy tắc do người dùng định nghĩa cho các phương thức ghi đè.

A Car IS-A Vehicle

A Ferrari IS-A Car

A Ferrari IS-A Vehicle

Ferrari

Vehicle

Car

Page 96: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

96/114 AptechVietnam

Tóm tắt chương

Khái niệm về dẫn xuất một lớp mới từ lớp tồn tại được biết như là thừa kế.

Tất cả các lớp là lớp con ngầm định của lớp đối tượng. Java chỉ hỗ trợ đơn thừa kế có nghĩa là một lớp Java chỉ có thể được dẫn xuất từ một lớp cha.

Giao tiếp cũng được mở rộng từ các giao tiếp khác giống như các lớp. Từ khoá extends được dùng để dẫn xuất các giao tiếp mới từ các giao tiếp cha khác.

Java hỗ trợ đa thừa kế trong các giao tiếp

Ghi đè phương thức là một khái niệm mà trong đó 2 phương thức có cùng tên, số lượng và kiểu tham số và cùng kiểu trả về .

Nạp chồng phương thức là một khái niệm mà trong đó 2 phương thức có thể có cùng tên, kiểu trả về nhưng khác tham số.

Các hàm dựng được nạp chồng tương tự các phương thức .

Từ khoá this dùng để gọi các hàm dựng từ bên trong 1 hàm dựng khác.

Các tham chiếu giao tiếp có thể dùng để lưu trữ thể hiện của bất kì lớp nào cài đặt giao tiếp này.

Các giao tiếp có thể chứa biến mà các biến ngầm định là final và static trong lớp cài đặt giao tiếp đó.

Page 97: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 97/114

Chương 8: Mở rộng lớp

Mục tiêu bài học:

Kết thúc bài học này, học viên có thể :

Định nghĩa phạm vi của biến

Hiểu sự khác nhau giữa lớp và biến thể hiện

Mô tả phương thức tĩnh

Chỉ rõ các ưu khuyết của phương thức tĩnh

Chỉ rõ các ưu khuyết của biến thể hiện

8.1 Giới thiệu về biến: Có hai loại biến trong Java. Loại thứ nhất là biến nguyên thuỷ (Primitive variable). Một biến nguyên thuỷ dùng để lưu giá trị có kiểu dữ kiệu nguyên thuỷ. Loại thứ hai là biến tham chiếu (Reference variable) dùng để lưu tham chiếu đến đối tượng. 8.1.1 Biến nguyên thuỷ

Biến thể hiện (Instance variable) Các biến nguyên thuỷ có thể thuộc về hai loại tùy thuộc vào vị trí chúng được khai báo. Một biến thể hiện là một biến được khai báo bên trong một lớp nhưng ngoài phương thức. Các biến thể hiện là các trường của lớp và chỉ được khởi tạo khi lớp được tạo thể hiện. Khi tạo ra đối tượng, nó có riêng bản sao mỗi trường của lớp tùy thuộc vào kiểu từ khóa được dùng với biến ấy. Các biến hoạt động cho đến khi lớp còn hoạt động. Nói cách khác, các biến có thể được truy xuất cho đến khi một tham chiếu đến lớp hoặc đối tượng tồn tại.

Biến cục bộ (Local variable)

Biến được khai báo bên trong phương thức được gọi là biến cục bộ. Các biến được tạo ra ngay khi phương thức được triệu gọi và được giải phóng khi phương thức kết thúc. Biến cục bộ chỉ có thể truy xuất ở bên trong phương thức chúng đang được khai báo. Chúng không thể truy xuất từ bất cứ phương thức khác hay bất cứ đâu bên trong lớp.

Page 98: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

98/114 AptechVietnam

Khái niệm về biến thể hiện và biến cục bộ được giải thích trong Đoạn mã 1

Đoạn mã 1: public class Vehicle

{

private int horsepower; //Biến thể hiện, thuộc tính

public void getEngineType(){

int numberOfCylinders; //Biến cục bộ

horsepower = 1000; //Có thể truy nhập

}

public void getVehicleType(){

//Không hể truy nhập biến này từ phương thức này

numberOfCylinders = 8;

}

}

8.1.2 Biến tham chiếu Biến tham chiếu (Reference variable) dùng để chứa các tham chiếu đến các đối tượng. Chúng được khai báo với một kiểu xác định không bao giờ bị thay đổi sang kiểu khác. Tuy nhiên chúng có thể được dùng để tham chiếu đến bất kì đối tượng của kiểu được khai báo hoặc kiểu con của kiểu được khai báo. Chúng có thể được khai báo như các biến thể hiện, biến cục bộ hoặc biến tĩnh . Biến thể hiện (Instance Variable)

Một biến tham chiếu khi được khai báo như một biến thể hiện có thể truy xuất thông qua lớp. Các biến được khởi tạo mặc định khi lớp tạo ra thể hiện của nó. Phạm vi của biến thể hiện là trong lớp khai báo nó và biến tồn tại cho đến khi còn tham chiếu đến lớp Biến cục bộ (local variable)

Biến cục bộ không thể được đánh dấu trừu tượng (abstract) hay tĩnh (static) nhưng chúng có thể được đánh dấu là final. Biến cục bộ không có giá trị mặc định như biến thể hiện. Do đó, như một quy tắc, chúng phải được khởi tạo trước khi sử dụng trong phương thức. Một biến cục bộ không thể tham chiếu từ bất kì mã nào bên ngoài phương thức nơi nó được khai báo. Có thể đặt tên biến cục bộ trùng với tên biến thể hiện. Kỹ thuật này được gọi là tạo bóng. Phương pháp này thực tế không được khuyến khích nhưng nếu người phát triển ứng dụng muốn lấy cùng tên cho biến cục bộ và biến thể hiện để mã dễ đọc hơn thì nên dùng từ khoá this.

Page 99: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 8 Mở rộng lớp

99/114

Đoạn mã 2: public class PixelPoint

{

Pixel pix; //Biến tham chiếu thể hiện

public void showPixel(){

Pixel newPixel; //Biến tham chiếu cục bộ

}

public vodi setPixel(Pixel pix){

//Khởi tạo biến tham chiếu thể hiện dùng từ khóa this

this.pix = pix;

}

}

8.1.3 Từ khoá static: các biến và các phương thức Một biến static được gọi là biến lớp bởi vì một bản sao đơn lẻ của biến tồn tại cho tất cả thể hiện của lớp. Nói cách khác, tất cả các đối tượng của lớp chia sẻ cùng một bản sao của biến static. Điều này cũng đúng với biến tham chiếu. Phương thức có thể được đánh dấu static. Tuy nhiên, một phương thức static có thể che lấp nhưng không được ghi đè. Các phương thức static có thể được truy xuất trực tiếp dùng tên lớp hơn là tham chiếu đối tượng. Một phương thức static chỉ có thể truy xuất các biến cục bộ, các thuộc tính static và các tham số phương thức. Bộ khởi tạo tĩnh (static initializer)

Một lớp có thể chứa mã trong khối static mà không thuộc về bất kì phương thức bình thường nào. Như các khối static được khai báo trong đoạn mã 3. Các khối static được thực thi chỉ một lần khi lớp được nạp. Nếu nhiều hơn một khối static tồn tại bên trong lớp thì chúng sẽ được thực thi theo thứ tự xuất hiện trong mã. Trong Đoạn mã 3, khi lớp Vehical đầu tiên được nạp, khối static initializer được thực thi và biến price được khởi tạo với giá trị 50.50

Đoạn mã 3: public class Vehicle

{

static double price; //Instance Variable

static{

price = 50.50; //Static initializer block

}

}

Page 100: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

100/114 AptechVietnam

8.2 Biến lớp và biến thể hiện:

Biến thể hiện (instance variable)

Biến lớp (class variable)

Được khởi tạo chỉ khi lớp được tạo thể hiện

Là các biến tĩnh và được gán giá trị mặc định thậm chí trước khi lớp được tạo thể hiện.

Chỉ có thể được truy nhập thông qua tham chiếu đối tượng.

Có thể được truy nhập thông qua tham chiếu đối tượng cũng như tên lớp. Vì chúng được gọi từ các biến lớp, nên không nên sử dụng các tham chiếu đối tượng để truy nhập đến các biến lớp hay biến tĩnh.

Mỗi thể hiện của lớp có bản sao riêng của biến.

Tất cả các thể hiện của lớp chia sẻ cùng một bản sao của biến tĩnh

Bảng 8.1 Khác nhau giữa biến lớp và biến thể hiện 8.3.1 Các ưu điểm của phương thức static

1. Phương thức static có thể được triệu gọi bằng sử dụng trực tiếp tên lớp.

2. Phương thức static dùng để cài đặt các ứng xử (hành vi) bị ảnh hưởng bởi trạng thái của bất cứ thể hiện nào.

3. Phương thức static có thể được định nghĩa lại trong các thể hiện

4. Phương thức static có thể truy nhập thông qua thể hiện của lớp cùng với toán tử chấm (.). Tuy nhiên đây là cú pháp khác thường vì trên thực tế trình biên dịch thay thế tham chiếu thể hiện bằng tên lớp trước khi gọi phương thức.

8.3.2 Các nhược điểm của phương thức tĩnh :

1. Phương thức static có thể được gọi bằng cách dùng một tham chiếu đối tượng không khởi tạo. Trình biên dịch chỉ kiểm tra kiểu của đối tượng và phương thức đang được gọi có phải là phương thức static hay không. Do đó, một phương thức static có thể được truy nhập bởi một biến tham chiếu đối tượng không được khởi tạo.

2. Một phương thức static không được ghi đè, mặc dù nó có thể che lấp

3. Các thuộc tính non-static của lớp không thể truy xuất từ trong phương thức static.

4. Các phương thức non-static của lớp hoặc thể hiện không thể được truy xuất trong phương thức static.

5. Vì phương thức static có thể được gọi không sử dụng thể hiện của lớp chứa nó, sử dụng từ khoá this bị cấm.

8.4 Thuận lợi và bất lợi của các phương thức thể hiện :

Page 101: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 8 Mở rộng lớp

101/114

8.4.1 Thuận lợi của phương thức thể hiện:

1. Các phương thức thể hiện (instance method) có thể được ghi đè và nạp chồng .

2. Các phương thức thể hiện ghi đè có thể có kiểu trả về khác là kiểu mới và là lớp con của kiểu trả về được khai báo của phương thức được ghi đè.

3. Chúng có thể chỉ được truy nhập bằng thể hiện của lớp dùng toán tử chấm „.‟

8.4.2 Bất lợi của phương thức thể hiện:

1. Các biến static không thể được truy xuất từ phương thức thể hiện

2. Các phương thức có quyền truy nhập private trong một lớp thì không được thừa kế và do đó không thể được ghi đè.

3. Phương thức thể hiện trong Java chỉ có thể trả về một tham số đơn. Tham số này có thể là một kiểu nguyên thuỷ hoặc là một đối tượng tham chiếu.

Page 102: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

102/114 AptechVietnam

Tóm tắt bài học

Các biến nguyên thuỷ được dùng để lưu trữ các giá trị kiểu dữ liệu nguyên thuỷ và các biến tham chiếu dùng để lưu trữ các tham chiếu đến các đối tượng.

Các biến thể hiện nguyên thuỷ có thể truy nhập thông qua lớp mà chúng mới được khai báo.

Các biến cục bộ nguyên thuỷ được khai báo bên trong các phương thức và phạm vi của chúng bị giới hạn trong phương thức đó.

Các biến tham chiếu dùng để lưu trữ các tham chiếu đến các đối tượng. Phạm vi của biến thể hiện và biến cục bộ tương tự như các biến nguyên thuỷ .

Một biến static được gọi một biến lớp bởi vì một bản sao đơn của biến đang tồn tại trong tất cả các thể hiện của lớp.

Các khối khởi tạo static đều không có trong thân của bất kì phương thức nào và dùng để khởi tạo lớp .

Page 103: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

AptechVietnam 103/114

Chương 9: Ngoại lệ

Mục tiêu bài học

Kết thúc bài học này, học viên có thể:

Mô tả về các kiểu ngoại lệ Mô tả về công dụng của từ khóa throw và throws Tạo ngoại lệ do người dùng định nghĩa Cài đặt các assertion

Ngoại lệ là một khả năng điều quản lỗi trong ngôn ngữ lập trình Java. Nhà phát triển có thể xác định các hành vi của một ứng dụng bằng cách sử dụng ngoại lệ. Ví dụ điều kiện điển hình có thể ngay gây ra ngoại lệ là triệu gọi phương thức với các đối số không được chấp nhận, lỗi mạng, hoặc cố mở một tập tin không tồn tại. 9.2. Các kiểu ngoại lệ Có hai loại ngoại lệ chính trong ngôn ngữ lập trình Java:

Ngoại lệ có kiểm soát (Checked Exception) Ngoại lệ có kiểm soát được tạo ra trong các tình huống thực hiện bình thường của một chương trình. Một số ví dụ về ngoại lệ có kiểm soát là – yêu cầu một tập tin không có, người dùng nhập liệu sai, và mạng hỏng. Những ngoại lệ này được xử lý để tránh các lỗi biên dịch. Nếu một ngoại lệ xảy ra trong quá trình thực hiện phương thức, phương thức có thể điều quản ngoại lệ hoặc chuyển ngoại lệ cho phương thức đang gọi để chỉ rõ vấn đề xảy ra. Phương thức đang gọi lại có thể điều quản ngoại lệ hoặc chuyển cho phương thức đang gọi nó. Quá trình này có thể tiếp tục đến khi ngoại lệ đạt đến đỉnh của luồng (thread) và luồng bị kết thúc thực hiện. Kỹ thuật này gọi là Call-stack. Ưu điểm chính của kỹ thuật là nhà phát triển có thể đặt các đoạn mã xử lý lỗi ở bất cứ vị trí nào họ muốn.

Ngoại lệ không kiểm soát (Unchecked Exception) Ngoại lệ không kiểm soát được tạo ra trong các tình huống được xem là không thể phục hồi đối với chương trình. Ví dụ thông thường về các tình huống là hành động truy nhập một phần tử nằm ngoài mảng. Một ứng dụng không được yêu cầu kiểm soát các loại ngoại lệ kiểu này. Ngoại lệ thực thi (runtime exception) cũng là ví dụ của ngoại lệ không kiểm soát. Thường thì chúng phát sinh do các lỗi logic (logical bugs). Ngoại lệ không kiểm soát phát sinh do các vấn đề môi

Page 104: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

104/114 AptechVietnam

trường hoặc các lỗi không thể được phục hồi và được gọi là Error (lỗi). Sử dụng hết bộ nhớ được cấp phát của chương trình là ví dụ về lỗi (error). 9.2. Điều quản ngoại lệ trong Java Như đã thảo luận ở trước, một ứng dụng chỉ được yêu cầu điều quản các ngoại lệ kiểm soát (checked exception). Ngoại lệ trong Java có thể được điều quản bằng các sử dụng các khối try-catch, hoặc có thể được chuyển đến phương thức đang gọi sử dụng từ khóa throw hoặc throws. 9.2.1. Từ khóa throw và throws Từ khóa throws khai báo rằng một phương thức có thể ném ra ngoại lệ trong quá trình thực hiện của nó. Từ khóa throw được sử dụng để ném ra ngoại lệ sau khi thực hiện một số kiểm tra tính hợp lệ trong chương trình. Khi câu lệnh throw thực hiện, luồng thực hiện bị thay đổi và các câu lệnh tiếp sau không được thực hiện. Sau đó, Ngoại lệ tạo bởi lệnh throw được truyền cho phương thức đang gọi trước đó trong stack (Ngăn xếp). Đôi khi một phương thức có thể ném ra nhiều hơn một ngoại lệ. Danh sách các ngoại lệ được phân cách bằng dấu phẩy (,) được ném ra bởi phương thức được đặt ở khai báo phương thức. Kỹ thuật “call-stack” (gọi ngăn xếp) phải được tuân theo trong khai báo các phương thức có lời gọi các phương thức khai tạo ra ngoại lệ. Đoạn mã 1 minh họa cách dùng lệnh throws. Đoạn mã 1: public class ExceptionDemo

{

public void checkException(){

try{

//Statements

check();

}catch(Exception e){

//statements

}

}

void check() throws NullPointerException{

if (flag < 0){

throw new NullPointerException();

}

}

}

9.3. Ngoại lệ do người dùng định nghĩa (User-defined exception)

Page 105: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 9 Ngoại lệ

105/114

Ngoại lệ do người dùng định nghĩa được dẫn xuất từ lớp Exception. Ngôn ngữ lập trình Java cho phép các nhà phát triển chỉ định định nghĩa và tạo các ngoại lệ có kiểm soát (checked exception) 9.3.1. Tạo ngoại lệ do người dùng định nghĩa (User-defined exception)

Lớp Exception là lớp cơ sở của tất cả các ngoại lệ do người dùng định nghĩa. Các ngoại lệ do người dùng định nghĩa cung cấp các giải pháp thiết thực có thể được sử dụng trong chương trình. Lớp Throwable là lớp cha của tất cả các ngoại lệ và lỗi trong ngôn ngữ Java. Tất cả các phương thức của lớp Throwable được kế thừa bởi lớp Exception, do đó, lớp Exception là lớp con của lớp Throwable. Vì vậy, các lớp ngoại lệ do người dùng định nghĩa có thể sử dụng tất cả các phương thức của lớp Throwable.

9.3.2. Khi nào ném ra các ngoại lệ? Điều quan trọng đối với nhà phát triển cần biết khi nào dùng các ngoại lệ. Có thể có một khuynh hướng sử dụng quá nhiều lớp Exception vì nó tiện dụng. Điểm lưu ý là mỗi khi một ngoại lệ được ném ra làm cản trở sự thực hiện của chương trình. Chỉ có một hướng dẫn cần tuân theo để quyết định khi nào ném ra một ngoại lệ là nếu một phương thức gặp một điều kiện bất thường mà nó không thể điều quản, nó nên ném ra ngoại lệ. Điều này tạo ra một câu hỏi khác về điều kiện nào có thể được phân loại như là “điều kiện bất thường” (abnormal condition) Do vậy, điều kiện bất thường có thể bất cứ điều kiện nào tạo ra kết quả không như mong đợi so với chức năng bình thường của một phương thức. Các ngoại lệ có thể được dùng để kiểm tra có các tham số truyền vào cho phương thức public hoặc hàm dựng public là hợp lệ hay không. 9.3.3. Xâu chuỗi ngoại lệ Xâu chuỗi ngoại lệ (exception chaining) hay bao đóng ngoại lệ, là một kỹ thuật lập trình hướng đối tượng điều quản ngoại lệ bằng việc “ném lại” re-throw” một ngoại lệ đã đón bắt sau khi bao đóng (wrapping) nó với một ngoại lệ mới. Ngoại lệ gốc được lưu như thuộc tính (như nguyên nhân) của ngoại lệ mới. Ý tưởng là một phương thức ném các ngoại lệ được định nghĩa ở cùng mức như của chính phương thức mà không loại bỏ các thông tin từ các mức thấp hơn. Đoạn mã thường dùng trong Java để đón bắt ngoại lệ và ném ra ngoại lệ khác. try{

}catch(YourException e){

throw new MyException();

}

Page 106: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

106/114 AptechVietnam

Nếu thông tin từ ngoại lệ gốc bị mất, việc dò lỗi trở nên không thể. Vì thế, trong khi bao đóng các ngoại lệ, một bộ truy xuất thường được cung cấp để trích xuất thông tin. Điều này cho phép các nhà phát triển hình thành các xâu chuỗi ngoại lệ gồm các ngoại lệ được bao đóng. Ưu điểm của sử dụng khả năng xâu chuỗi ngoại lệ là:

Sự kiện một ngoại lệ được tạo ra từ các mức thấp hơn có thể được ghi lại không quan tâm ngoại lệ đó là gì?

Vì các API phổ biến được sử dụng để ghi lại sự kiện một ngoại lệ được

tạo ra từ các mức thấp hơn, sẽ khuyến khích các lập trình viên duy trì hồ sơ về xâu chuỗi ngoại lệ (exception chain)

Hồ sơ (record) một ngoại lệ cụ thể gây ra ngoại lệ khác có thể được lưu

và tham chiếu đến sau đó. Để giữ hồ sơ của xâu chuỗi ngoại lệ, hai phương thức của lớp Throwable được sử dụng. Phương thức getCause() và initCause(Throwable) và hai hàm dựng Throwable(Throwable) và Throwable(String, Throwable) được dùng để ghi xâu chuỗi của các ngoại lệ. Tuy nhiên, thậm chí không sử dụng các hàm dựng cũng có thể bao đóng các lớp bọc bằng phương thức initCause(). Sửa đổi cài đặt của phương thức Throwable.printStackTrace để hiển thị các lần vết ngược với toàn bộ xâu chuỗi gây ra ngoại lệ. Phương thức mới getStackTrace() cung cấp truy nhập do lập trình để theo dõi thông tin lần vết được cung cấp bởi printStackTrace. 9.4 Giả định (Assertion) Assertion là lệnh Java cho phép nhà phát triển kiểm tra các giả định được tạo ra bởi ứng dụng. Lệnh assert kiểm tra biểu thức boolean trong khi thực thi. Biểu thức Có hai dạng lệnh assert: assert booleanExpression;

assert booleanExpression : expression;

Dạng đầu tiên chỉ đánh giá biểu thức boolean và trả lại kết quả true hay false. Dạng thứ hai giống như dạng một. Ngoài ra, nếu biểu thức boolean ước lượng là false, biểu thức thứ hai được trả lại thông điệp chi tiết cho AssertionError. Biểu thức thứ hai có thể thuộc về bất kỳ kiểu nào, ngoại trừ void. AssertionError là một Error, không là Exception. Ưu điểm của sử dụng lệnh assert là:

Page 107: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 9 Ngoại lệ

107/114

Lệnh assert không cần đặt trong khối try-catch.

AssertionError được tạo ra không để được đón bắt và điều quản bởi ứng dụng. Vì vậy, không đoạn mã phức tạp nào cần trong ứng dụng. Ngoài lệnh thực sự, không cần thêm mã lệnh nào. Các assertion có thể được dùng để:

Đón bắt các lỗi logic

Kiểm tra kết quả của một thao tác

Kiểm tra các điều kiện lỗi đã được điều quản 1.4.1 Assertion trong mã Assertion có thể sử dụng trong bất cứ vị trí nào sau đây:

Các định lượng bất biến trong (internal invariants).

Các định lượng bất biến luồng điều khiển (Control-flow invariants)

Các định lượng bất biến lớp, tiền điều kiện (precondition) và hậu điều kiện (postcondition)

Chi tiết được giải thích như sau: Các định lượng bất biến trong Các giả thiết trong chương trình biểu thị bằng các chú thích. Ví dụ, trong một lệnh if, chú thích giải thích cho mệnh đề else như trong đoạn mã 2. Đoạn mã 2: if (sum > 600) {

} else if (sum > 600 && basic <=1000){

} else{

} //The sum should be less than 600

Thay vì sử dụng chú thích, assertion có thể được dùng tại cùng vị trí. Bây giờ, lệnh if có thể được viết lại trong đoạn mã 3.

Page 108: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

108/114 AptechVietnam

Đoạn mã 3: if (sum > 600) {

} else if (sum > 600 && basic <=1000){

} else{

assert sum < 600 : "sum is less than 600";

}

Tại vị trí khác, assertion có thể được sử dụng, là lệnh switch không có mệnh đề default. Khi nhà phát triển mong muốn một trong các mệnh đề case được thực hiện thì mệnh đề default có thể được loại bỏ. Ví dụ, đoạn mã 4, một trong các lệnh switch-case sẽ được thực hiện. Đoạn mã 4: switch (num) {

case 1:

...;

break;

case 2:

...;

break;

case 3:

...;

break;

}

Chỉ rõ một giả định để nhằm đảm bảo biến num sẽ có một trong chỉ ba giá trị. Mệnh đề assert có thể được thêm vào trong mệnh đề default để kiểm tra giả định, như minh họa trong đoạn mã 5. Đoạn mã 5: default:

assert false : num;

Nếu biến num nhận một giá trị khác và assertion được cho phép, assert sẽ hỏng và AssertionError sẽ được ném ra. Có một lựa chọn khác chấp nhận được trong đoạn mã 6 Đoạn mã 6:

Page 109: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 9 Ngoại lệ

109/114

default:

throw new AssertionError(num);

Lựa chọn trên làm tăng tính bảo vệ ngay cả assertion không được cho phép, nhưng sự bảo vệ mở rộng thêm không tăng thêm chi phí. Lệnh throw sẽ không thực hiện trừ khi chương trình hỏng. Hơn nữa, lựa chọn trên là hợp lệ trong mọi trường hợp khi lệnh assert không được phép. Nếu phương thức bao trả lại giá trị, mỗi case trong lệnh switch chứa một lệnh return. Nếu không lệnh return nào đặt sau lệnh switch, thì sẽ gây ra lỗi cú pháp thêm vào mệnh đề default với assertion. Phương thức không trả lại giá trị nào nếu không có case nào thõa mãn và assertion bị vô hiệu. Các định lượng bất biến luồng điều khiển Một assertion có thể đặt tại vị trí điều khiển không đạt tới. Lệnh assertion cũng có thể được sử dụng như trong đoạn mã 7. assert false;

void assertdemo(){

for (...){

if (...){

return;

}

}

//Execution should never reach this point!!!

}

Đoạn mã trên có thể được viết lại như trong đoạn mã 8 Đoạn mã 8: void assertdemo(){

for (...){

if (...){

return;

}

}

assert false;

}

9.4.2 Cho và không cho phép assertion Các tùy chọn dòng lệnh của lệnh Java cho phép hoặc không cho phép assertion ở mức lớp. Dòng lệnh chuyển đổi –enableassertion hay viết tắt là –ea, cho

Page 110: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

110/114 AptechVietnam

phép assertion. Ngầm định, assertion bị vô hiệu lúc thực thi. Hai tùy chọn dòng lệnh cho phép nhà phát triển lựa chọn cho phép hoặc không cho phép assertion. Để cho phép assertion sử dụng –enableassertion hay –ea. Để không cho phép assertion sử dụng –disableassertion hay –da. Chi tiết về các tùy chọn như sau:

-ea Cho phép hoặc không cho phép assertion trong tất cả các lớp ngoại trừ các lớp hệ thống.

-ea:<tên_package > Cho phép hoặc không cho phép assertion trong package có tên và các package con.

-ea:… Cho phép hoặc không cho phép assertion trong package không chỉ tên trong thư mục làm việc hiện tại.

-ea:<tên_lớp> Cho phép hoặc không cho phép assertion trong lớp có tên.

Kết quả sau khi thực thi lớp Excep bằng lệnh Java Excep, với môi trường thực thi ngầm định không cho phép assertion: Excep.m1 ( 1 ) : OK Excep.m1 ( -1) : OK Với các assertion bị vô hiệu, không lời gọi phương thức m1(int) nào gây ra kiểm tra assertion. Như mô tả ở trước, để cho phép assertion cần sử dụng các tùy chọn dòng lệnh. Bất cứ lệnh nào sau đây, thể hiện trong đoạn mã 9, cho phép kiểm tra assertion trong lớp Excep Java –ea Excep

Java –ea:Excep Excep

Java –ea:... Excep

Kết quả sau khi thực hiện các lệnh: Excep.m1( 1 ) : OK

Excep.m1( -1 ) : Exception in thread “main”

Java.lang.AssertionError

at Excep.m1(Excep.Java:6)

at Excep.main(Excep.Java:17)

Gọi phương thức m1(int) với tham số 1 không gây ra assertion. Tuy nhiên, -1 vi phạm giả định rằng tham số phải làm một số nguyên dương. Hệ thống thực thi Java thông báo giả định thất bại thông qua thể hiện của lớp, java.lang.AssertionError.

Page 111: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 9 Ngoại lệ

111/114

Để nắm rõ hàm dựng AssertionError nào được dùng, xem xét cách thức các assertion được xử lý khi cho phép Ước lượng expression1

Nếu true o Không có hành động nào

Nếu false o Và nếu expression2 tồn tại

Ước lượng expression2 và sử dụng kết quả dưới dạng tham số đơn của hàm dựng AssertionError.

o Ngược lại

Sử dụng hàm dựng ngầm định AssertionError.

Vì lệnh assert trong lớp Excep sử dụng dạng biểu thức đơn, sự vi phạm đã xảy ra khi truyền -1 vào phương thức m1(int) thúc đẩy hàm dựng AssertionError được sử dụng. Hàm dựng ngầm định thực tế sử dụng hàm dựng ngầm định của java.lang.Throwable để in thông điệp ngoại lệ bao gồm mô tả lần vết dạng văn bản. Kết quả lỗi assertion khi thực hiện lớp Excep thiếu. Như đã thấy AssertionError xảy ra trong phương thức m1 tại dòng 6, nhưng kết quả không mô tả sai điều gì. May mắn là dạng lệnh assert có hai biểu thức cung cấp khả năng này. Như giải thích ở trên, trong dạng hai biểu thức, khi biểu thức expression1 được ước lượng là false, assertion sẽ truyền kết quả của biểu thức expression2 vào cho tham số của hàm dựng AssertionError. Biểu thức expression2 thực tế mang thông điệp dạng chuỗi. Nghĩa là các hàm dựng một tham số của lớp AssertionError phải chuyển kết quả của biểu thức expression2 sang chuỗi (String). Lớp Bar, được mô tả trong đoạn mã 10, sử dụng dạng hai biểu thức cho một assertion đơn trong phương thức m1(int). Đoạn mã 10: public class Bar {

public void m1(int value){

assert 0 <= value: "Value must be non-negative :

value= " + value;

System.out.println("OK");

}

public static void main(String[] args) {

Page 112: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

112/114 AptechVietnam

Bar bar = new Bar();

System.out.println("bar.m1( 1 ): ");

bar.m1(1);

System.out.println("bar.m1( -1 ): ");

bar.m1(-1);

}

}

Kết quả thực hiện Bar có cho phép assertion như sau: bar.m1( 1 ): OK bar.m1( -1 ): Exception in thread “main” Java.lang.AssertionError: Value must be non-negative: value= -1 at Bar.m1(Bar.Java:6) at Bar.main(Bar.Java:17) Kết quả cho thấy sự chuyển đổi kết quả của biểu thức expression thành chuỗi (String) và nối vào cuối thông điệp ngoại lệ, trước thông tin theo vết. Thông điệp chi tiết tất nhiên là cải thiện công dụng của thông điệp ngoại lệ. Vì tạo các thông điệp lỗi hợp lý không khó, các nhà phát triển ủng hộ dạng hai biểu thức của lệnh assert. Lớp Java.lang.Throwable cho phép định dạng rõ ràng thông tin lần vết. Lớp FooBar, liệt kê trong đoạn mã 11, sử dụng các khả năng mới này để định dạng thông điệp ngoại lệ được tạo ra bởi lỗi assertion. Đoạn mã 11: public class Bar {

public void m1(int value){

assert 0 <= value: "Value must be non-negative : value= " +

value;

System.out.println("OK");

}

public static void printAssertionError(AssertionError ae){

StackTraceElement[] stackTraceElements=ae.getStackTrace();

StackTraceElement stackTraceElement=stackTraceElements[0];

System.err.println("AssertionError");

System.err.println("Class="+stackTraceElement.getClassName());

System.err.println("method="+stackTraceElement.getMethodName());

System.err.println("message="+stackTraceElement.getMessage());

}

public static void main(String[] args) {

Page 113: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Chương 9 Ngoại lệ

113/114

try{

Bar bar = new Bar();

System.out.println("bar.m1( 1 ): ");

bar.m1(1);

System.out.println("bar.m1( -1 ): ");

bar.m1(-1);

}catch(AssertionError ae){

printAssertionError(ae);

}

}

}

Kết quả thực thi FooBar có cho phép assertion hiển thị thông báo AssertionError rõ ràng: fooBar.m1( 1 ): OK

fooBar.m1( 1 ): AssertionError

class = FooBar

method= m1

message= Value must be non-negative: value= -1

9.4.4 So sánh Assertion Khó khăn hay xảy ra đối với các nhà phát triển mới là quyết định khi nào sử dụng assertion và khi nào dùng ngoại lệ. Cả hai đều đón bắt các sự cố trong chương trình, nhưng mục đích sử dụng thì rất khác nhau. Sự khác nhau giữa ngoại lệ, về sử dụng, so với assertion như sau:

Một ngoại lệ cho người dùng của chương trình biết điều gì sai.

Một assertion mô tả một số giả định về chương trình

Khi một assertion thất bại, nó chỉ ra lỗi trong logic lập trình

Các ngoại lệ được tạo ra để giải quyết các vấn đề có thể xảy ra trong tiến trình thực hiện của chương trình.

Assertion được viết nhằm chỉ ra các giả định tạo ra bởi chương trình.

Page 114: Lập Trình Hướng Đối Tượng trong Java ( Vietnamese )

Learn Java by Example

114/114 AptechVietnam

Tóm Tắt Bài Học

Các ngoại lệ có kiểm soát thể hiện các tình huống không được kiểm soát bởi chương trình.

Các ngoại lệ không kiểm soát thể hiện khiếm khuyết trong chương trình

Từ khóa throw cho biết ngoại lệ xảy ra.

Các ngoại lệ cũng có thể được định nghĩa bởi người dùng và có thể được sử dụng trong chương trình.

Assertion có thể được đặt tại bất kỳ vị trí nào điều khiển không đạt tới.