VII. Ngôn ngữ Java
1. Cú pháp của Java:
Ngôn ngữ Java hoàn toàn tơng tự nh C++. Trong phần này chúng ta sẽ đi sâu xem xét cú pháp và các từ khoá (keyword) của Java. Các thành phần của ngôn ngữ này bao gồm:
• Lời chú thích (comment) : Những dòng chú thích của lập trình viên nhằm làm chơng trình dễ hiểu hơn.
• Các phát biểu (statement) : Mỗi phát biểu là một "dòng" trong chơng trình.
• Các khối mã lệnh (code block) : Tập hợp các phát biểu đợc gộp với nhau thành một đơn vị nhất định.
• Cấu trúc tập tin (file structure) : Các thành phần của một tập tin nguồn Java, thứ tự của chúng trong đó.
• Từ khoá (keyword) : Các từ đợc định nghĩa trớc trong ngôn ngữ Java (không đợc dùng các từ này làm từ định nghĩa).
• Từ định danh (identifier) : Tên bạn đặt cho các lớp, biến hay hàm. Từ định danh có một số giới hạn về ký tự đứng đầu.
• Cách viết (literal) : Các hằng số đợc viết một cách khác nhau tuỳ theo kiểu dữ liệu (láy ví dụ, để phân biệt ngời ta viết "123" là xâu ký tự, còn 123 là số nguyên).
• Biểu thức (expression) : Kết hợp của một hay nhiều các phép tính nhằm tính ra một giá trị nào đó.
• Các tác tử (operator) : Các tác tử thực hiện lệnh cộng, trừ, nhân, chia.. và các toán tử logic.
Các thành phần ngôn ngữ Java sẽ lần lợt đợc trình bày khái quát trong các phần sau:
1.1. Chú thích (comment):
Có hai cách thêm các dòng chú thích vào trong tập tin nguồn Java: a. Dùng cặp /* và */ để bao một hay nhiều dòng chú thích:
/* Đây là chú thích
Nó sẽ bị bỏ qua khi dịch chơng trình */
b. Dùng hai ký tự // để tạo ra một đoạn chú thích kể từ vị trí đó đến cuối dòng:
1.2. Phát biểu (statement):
Phát biểu là một "dòng" riêng biệt trong mã Java. Chú ý rằng không có sự t- ơng đơng giữa "dòng" trong mã lệnh với dòng trong tập tin nguồn. Lấy ví dụ:
a = b + c + d + e + f + g; // Đây là một phát biểu
và
a = b + c // Đây cũng là một phát biểu
+ d + e + f + g;
Khoảng cách giữa các thành phần của một phát biểu có thể dài tuỳ ý, bao gồm các ký tự trống (whitespace) : Dấu trống (space), ký tự tab, ký tự hết dòng và ký tự về đầu dòng (cr và lf).
Lu ý: Trên UNIX, kết thúc dòng là ký tự hết dòng (carriage return - mã ASCII 13). Với Windows, các dòng văn bản lại kết thúc bằng ký tự hết dòng và ký tự về đầu dòng (linefeed - mã ASCII 10). Bộ biên dịch Java coi tất cả các ký tự này là ký tự trống, do đó tơng thích trên cả UNIX và Windows.
1.3. Khối mã lệnh:
Các phát biểu có thể đứng gộp với nhau thành một nhóm, điều này giúp một phát biểu đơn nào đó có thể dễ dàng điều khiển việc thực hiện của một nhóm lệnh khác. Khối mã lệnh Java đợc giới hạn trong dấu "{" và "}". Chúng ta có thể thấy một khối mã lệnh để nhóm các phát biểu thuộc về một lớp:
class Flight {
int altitude;
int heading;
int speed;
void turnFlight(int angle){
heading = (heading + angle) % 360;
if (heading < 0) heading = heading + 360; }
} // End of class Flight
Nh thấy trên ví dụ, một khối mã lệnh có thể đặt lồng trong một khối mã lệnh khác.
1.4. Cấu trúc tập tin nguồn Java:
Một tập tin nguồn chỉ chứa 3 kiểu phát biểu ngoài các khối mã lệnh:
• Phát biểu package : Phát biểu này định nghĩa một gói mà các lớp trong tập tin này nằm trong đó.
• Phát biểu import : Phát biểu này cho phép quy chiếu đến một hay nhiều lớp đã có (nh các lớp trong API) thông qua tên lớp.
• Phát biểu class : Định nghĩa lớp mà bạn muốn tạo.
Các phát biểu import và package đều là tuỳ chọn, nhng nếu có mặt các phát biểu này, chúng ta phải xếp đúng theo trình tự ở trên (tức là package, import, rồi cuối cùng mới đến class). Ví dụ:
package com.sybex.examples; import java.awt.Panel; import java.awt.Color;
class ColorPanel extends Panel{
// Định nghĩa của lớp ColorPanel đợc đặt ở đây ...
}
Đoạn mã trên định nghĩa một lớp mới tên là ColorPanel thuộc về gói có tên là com.sybex.examples. Những chơng trình khác khi dùng lớp này sẽ quy chiếu đến nó với tên com.sybex.examples.ColorPanel.
Phát biểu import đơn giản hoá việc quy chiếu đến một lớp trong một gói đã có sẵn. Gói java.awt là gói Abstract Windowing Toolkit của Java, đây là một phần cơ bản của Core API. Phát biểu import đầu tiên trong ví dụ trên cho phép chúng ta gọi đến java.awt.Panel một cách đơn giản là Panel. Tơng tự, trong phát biểu import thứ hai cho phép bạn chỉ đến lớp Color trong gói java.awt bằng tên của nó mà thôi.
Phát biểu cuối cùng là phát biểu class dùng để định nghĩa lớp. Chú ý rằng đây là một phát biểu kép, nghĩa là nó bao gồm một khối các phát biểu. Sau định nghĩa lớp này, chúng ta có thể có một vài phát biểu lớp phụ khác.
1.5. Từ khoá:
Một từ khoá là từ có ý nghĩa đặc biệt đối với bộ biên dịch Java, ví dụ nh từ để định nghĩa kiểu dữ liệu.. Các từ khoá đợc liệt kê trên bảng sau:
Bảng II.01 : Các từ khoá của Java
abstract boolean break byte
case catch char class
const* continue default do double else extends final finally float for goto* if implements import instanceof int interface long native new null package private protected public return short
static super switch synchronized this throw throws transient* try void volatile while
* là các từ khoá cha đợc sử dụng trong phiên bản hiện tại của Java (dành cho tơng lai).
1.6. Từ định danh (identifier):
Một từ định danh là tên đặt cho một biến, lớp, hoặc hàm. Bạn có thể chọn bất cứ thứ gì làm từ định danh, miễn là nó bắt đầu bằng một chữ cái (a..z và A..Z) và không trùng với từ khoá nào. Cần chú ý rằng Java là ngôn ngữ phân biệt hoa - thờng (case sensitive).
Chúng ta có một số quy định về cách đặt tên định danh. Tuy nhiên quy định này là không bắt buộc, nó chỉ nhằm mục đích làm chơng trình trở nên dễ đọc, dễ hiểu. Bảng sau đây đa ra một số quy định đặt tên định danh:
Bảng II.02 : Quy định đặt tên định danh
Kiểu định
danh Quy định Ví dụ
Tên class Viết hoa chữ cái đầu mỗi từ trong định danh Flight, CommercialFlight Tên hàm Viết hoa chữ cái đầu mỗi từ trong định danh, trừ
từ đầu tiên
printFlight, turnFlight Tên biến Viết hoa chữ cái đầu mỗi từ trong định danh, trừ
từ đầu tiên
altitude, flightNumber Tên hằng Viết hoa chữ cái đầu mỗi từ trong định danh,
giữa các từ là dấu gạch nối "_" MAX_INBOUND_FLIGHT
1.7. Quy tắc viết các giá trị (literal):
Cách viết các kiểu dữ liệu chuẩn của Java đợc liệt kê đầy đủ trong bảng 03.
Bảng II.03 : Khuôn dạng biểu diễn dữ liệu của Java
Kiểu dữ liệu Cách viết
byte, short int Các con số thập phân (không đợc bắt đầu bằng 0). 0x theo sau là con số thập lục (ví dụ, 0xFF12). 0 theo sau là con số biểu diễn cơ số 8 (ví dụ, 0763).
long Cũng giống nh kiểu int, nhng theo sau là chữ cái l hoặc L (ví dụ, 1234L, 0x192FL)
float Các số với dấu chấm thập phân hoặc số mũ, theo sau là chữ cái f hay F (ví dụ, 122.02f, 1.203E+5F, .1234F)
double Cũng giống nh float, nhng không có đuôi f hay F, mà thay vào đó là d hoặc D (tuỳ chọn). Lấy ví dụ : 12.202, 1.234D, 1234E-4
boolean true hoặc false
char Một ký tự ASCII đặt trong hai dấu nháy đơn (ví dụ 'a' hay 'B'). Nếu nh bộ soạn thảo đang sử dụng chèn đợc ký tự kiểu Unicode, chúng ta cũng có thể đa mã này vào.
Các ký tự đặc biệt đợc định nghĩa trớc bằng chuỗi thoát (escape sequence) nh '\t', '\012' (xem thêm bảng 04).
String Một chuỗi các ký tự đặt trong cặp dấu nháy kép (ví dụ "Công Huân")
Bảng II.04 : Chuỗi thoát ký tự
Chuỗi thoát đặc biệt \b \t \n \f \r \" \' \\ backspace tab ký tự về đầu dòng ký tự hết dòng ký tự xuống dòng Dấu nháy kép Dấu nháy đơn Dấu \
Chuỗi thoát cơ số 8 \DDD Ký tự ASCII đợc biểu diễn thông qua 3 số cơ số 8 DDD (0..7), ví dụ \071 là ký tự ASCII có mã thập phân là 71.
(0..9,A..F), ví dụ \u00FA.
1.8. Biểu thức và các tác tử:
Biểu thức là tập hợp của các biến, từ khoá hay biểu tợng tơng ứng với một giá trị nào đó. Giá trị này có thể là số, xâu ký tự, hay một lớp hoặc kiểu dữ liệu khác. Biểu thức có thể coi nh bất cứ thứ gì viết ở phía bên phải của phát biểu gán:
Một biểu thức đơn giản nh sau:
a // Một biến
15 // Một giá trị số
“Hello” // Một xâu ký tự
Ví dụ sau đây gán xâu ký tự “Hello” vào một biến s:
s = “Hello”;
Cũng giống nh C, một phát biểu gán của Java cũng trả về giá trị đợc gán ở phía bên phải, lấy ví dụ: Phát biểu sau đây gán cho b bằng 15, đồng thời trả về giá trị 15 đặt vào biến a:
a = b = 15;
a. Hàm và biến thành phần của một đối tợng:
Một kiểu biểu thức khác là gọi hàm thành phần (member function). Một hàm có thể tính toán và trả về giá trị, do đó chúng có thể đợc đặt phía bên phải một biểu thức gán:
a = incomingFlight.getHeading();
Cấu trúc chung để gọi hàm hoặc biến thành phần của một đối tợng nh sau:
đối_tợng.biến_thành_phần hoặc
đối_tợng.hàm_thành_phần(các tham số)
Trong trờng hợp các hàm hay biến thành phần là tĩnh (static), chúng ta có thể gọi thông qua tên lớp:
lớp.biến_thành_phần hoặc
lớp.hàm_thành_phần(các tham số)
b. Cấp phát bộ nhớ cho đối tợng:
Cấp phát bộ nhớ đối tợng với Java chỉ là một cách gọi đặc biệt đến hàm thành phần. Để tạo một đối tợng trên bộ nhớ, chúng ta chỉ việc dùng từ khoá new khi gọi hàm dựng (constructor) của lớp đó:
Flight f; // Khai báo biến f là kiểu Flight
f = new Flight(); // Cấp phát và khởi tạo đối tợng
hoặc viết ngắn gọn hơn:
Flight f = new Flight();
Chú ý rằng nếu một lớp đợc tạo ra không có hàm dựng, Java sẽ tạo ra một hàm dựng mặc định không có tham số. Tuy nhiên, nếu nh đã có hàm dựng với tham số, chúng ta không thể dùng hàm dựng mặc định này nữa.
c. Các từ khoá this và super:
Hai từ khoá đặc biệt này cũng đợc dùng trong các biểu thức. Từ khoá this tơng ứng với tên đối tợng hiện thời, còn từ khoá super để chỉ lớp cha của đối tợng hiện thời. Lấy ví dụ, chúng ta có thể dùng từ khoá this để yêu cầu đối tợng bất kỳ in thông tin về chính bản thân nó:
Đầu tiên, phải đa hàm sau vào khai báo trong lớp:
public void print(){
System.out.println(this); //In ra chính nó }
Khi dùng đến, chúng ta chỉ cần gọi hàm thành phần này:
Flight f = new Flight(); f.setHeading(140);
f.print();
Nếu một lớp con đợc dẫn xuất từ lớp cha thông qua từ khoá extends, chúng ta có thể dùng từ khoá super để chỉ đến các hành vi hay biến của lớp cha đó. Ví dụ:
// Khai báo lớp cha class Parent{ String name; void print (){ System.out.print("Parent " + name); } }
// Khai báo lớp con
class Child extends Parent{ String name;
String childName(){ return name; }
String parentName(){
// Trả về tên của lớp cha return super.name;
}
void print(){
System.out.print("Child " + name + "is child of"); supper.print(); // gọi đến hành vi print() lớp cha }
}
d. Biều thức với toán tử:
Một loại biểu thức khác liên quan đến tổ hợp các biến, hằng số, hàm và toán tử (operation). Ví dụ một biểu thức đơn giản sử dụng toán tử * (nhân):
a = b * c;
Khi có nhiều hơn một toán tử trong một biểu thức, Java sẽ dựa vào mức độ u tiên của từng loại toán tử để thực hiện các phép tính.
* Toán tử số học:
Các toán tử số học của Java đợc mô tả trong bảng 05. Trong bảng này cũng có đề cập đến mức độ u tiên của nó:
Bảng II.05 : Toán tử số học của Java
Toán tử Mục đích Độ u tiên Vị trí
++, -- Tự động tăng biến lên 1, tự động giảm biến đi 1
1 (cao nhất) Bên phải +, - Cộng và trừ đơn nguyên 2 Bên phải
* Nhân 4 Bên trái
/ Chia 4 Bên trái
% Lấy phần d (chia modulo) 4 Bên trái
+, - Cộng, trừ 5 Bên trái
* Biến logic và các toán tử quan hệ:
Toán tử quan hệ so sánh hai đối tợng bất kỳ để xem chúng bằng nhau, hoặc một đối tợng lớn hơn hay nhỏ hơn đối tợng kia. Toán tử dùng để kiểm tra bằng nhau là == (khi đó nếu bằng, nó trả về true, ngợc lại là false). Ví dụ:
a = (2 == 4); // a = false bởi 2 không bằng 4
Bảng II.06 : Toán tử quan hệ của Java
Toán tử Mục đích Độ u tiên Vị trí
>, <, >=, <= Kiểm tra lớn hơn, nhỏ hơn, lớn hơn hoặc bằng
và nhỏ hơn hoặc bằng 7 Bên trái == Kiểm tra bằng nhau 8 Bên trái != Kiểm tra không bằng 8 Bên trái ?: Toán tử điều kiện, trả về một trong hai toán
hạng tuỳ thuộc vào điều kiện của toán hạng thứ ba.
4 Bên trái
Riêng về toán tử điều kiện, chúng ta có thể dùng toán tử này thay cho các lệnh if : if (a > c) { max = a; } else { max = c; }
tơng đơng với:
max = (a > c) ? a : c;
Bên cạnh các toán tử quan hệ, Java còn có các toán tử logic, làm việc với các toán hạng logic để trả về giá trị logic. Đây là các toán tử: AND, OR, NOT và XOR.
Riêng với các toán tử AND và OR, đôi khi chỉ cần tính giá trị logic của một toán hạng đầu là quyết định kết quả (ví dụ toán hạng đầu trong phép toán OR là true thì dù toán hạng sau có là gì thì kết quả vẫn là true). Chính vì lý do này, Java đa ra hai loại phép toán: nếu dùng AND kiểu & và OR kiểu |, Java sẽ tính kỹ cả hai toán hạng, trong khi nếu dùng AND kiểu && và OR kiểu ||, Java chỉ cần xét một bên nếu nó đã đủ đa ra kết luận. Ví dụ:
boolean b;
// Do dùng OR kiểu |, Java sẽ tính toán cả hai vế rồi // mới so sánh. Vì vậy, tất cả các phép nhân trong // biểu thức sẽ đợc thực hiện
b = (100>(5*6)) | (100 > (8*8)); // b sẽ bằng true
// Do dùng OR kiểu ||, Java sẽ tính toán vế đầu tiên trớc // vế đầu đã là true, do đó có thể kết luận ngay là
// toàn bộ biểu thức bằng true. Vế sau không đợc tính đến
b = (100>(7*9)) || (100>(4*5)); // b sẽ bằng true
Bảng II.07 : Toán tử logic của Java
Toán tử Mục đích Độ u tiên Vị trí
! Phủ định 2 Bên phải
& Phép toán AND logic 9 Bên trái ^ Phép toán hoặc loại trừ 10 Bên trái | Phép toán OR logic 11 Bên trái && Phép toán AND điều kiện 12 Bên trái || Phép toán OR điều kiện 13 Bên trái
* Các toán tử xử lý bit:
Các kiểu số nguyên (byte, short, int và long) đều đợc biểu diễn trong bộ nhớ máy tính nh một chuỗi các bit. Giống nh số thập phân, chuỗi bit này cũng có bit giá trị cao nhất nằm ở bên trái. Các số nguyên của Java giống hệ C, sử dụng bit đầu tiên làm bit dấu:
Bảng II.08: Các toán tử xử lý bit
Toán tử Mục đích Độ u tiên Vị trí
~ Phủ định (lấy số bù) 2 Bên phải <<, >> Phép toán dịch trái, dịch phải (có xét đến dấu) 6 Bên trái
>>> Dịch phải không xét đến bit dấu 6 Bên trái & Phép toán AND theo bit 9 Bên trái ^ Phép toán XOR theo bit 10 Bên trái | Phép toán OR theo bit 11 Bên trái
Hình II.01: Dữ liệu kiểu số nguyên của Java. Khi bit dấu bằng 1, đó là số âm
7 bit 15 bit 31 bit 63 bit 1 bit dấu short integer long
Chú ý: Các phép toán xử lý bit chỉ làm việc với toán hạng là int hay long. Nếu chúng ta