Biến (Variables) và kiểu dữ liệu.
Các đối tượng được lưu trữ trạng thái trong các
thuộc tính. Tuy nhiên trong ngôn ngữ lập trình Java, chúng được gọi là các biến.
Trong mục này sẽ trình bày các mối quan hệ, vai trò và những quy ước khi sử dụng
tên biến, các kiểu dữ liệu cơ bản (Dữ liệu nguyên thủy, kiểu chuỗi, mảng), giá
trị mặc định…
Mục lục seri các bài viết hướng dẫn tự học lập trình Java:
Trong chương 1, chúng ta đã biết, một đối tượng được lưu trữ bởi các thuộc tính. Biến cũng giống như vậy, trước hết nó là một đối tượng. Ví dụ:
int cadence = 0; int speed = 0; int gear = 1; |
Trong phần định nghĩa một đối tượng, chúng ta đã được giới
thiệu các trường (thuộc tính). Tuy nhiên hẳn sẽ còn một vài câu hỏi lien
quan như: Các vai trò và các quy ước với các tên thuộc tính là gì?
Bên trong từ khóa int
hoặc các kiều dữ liệu khác hàm chứa điều gì? Các trường dữ liệu đó có được khởi
gán khi chúng ta định nghĩa biến. Các biến được khởi gán giá trị mặc định nếu
chúng không được khởi tạo rõ ràng. Chúng ta sẽ tìm hiểu các câu trả lời trong
chương này, tuy nhiên trước đó chúng ta sẽ tìm hiểu một vài khái niệm công nghệ
trước khi đi tiếp.
Trong ngôn ngữ lập trình Java, các khái niệm thuộc tính
hoặc biến đều được sử dụng, nhưng với những lập trình viên mới thì hai
khái niệm này nó như là giống nhau.
Trong ngôn ngữ lập trình Java định nghĩa các loại biến sau:
Instance
Variables: Biến (Không phải biến tĩnh) khái niệm của loại biến này
được định nghĩa là không phải biến tĩnh, loại biến này khi định nghĩa thì không
cần sử dụng từ khóa static. Biến không tĩnh được biết đến chính là Instance
Variables bởi vì các giá trị của biến là duy nhất cho mỗi thể hiện
(instance) của một lớp (mỗi đối tượng của một lớp khác nhau là khác nhau) ví dụ
thuộc tính currentSpeed
của một chiếc xe
đạp sẽ khác với thuộc tính currentSpeed
của một chiếc xe khác. Đôi khi cũng sẽ gây nhầm lẫn đối với các lập trình viên
mới.
Class
Variables (Static Fields): Biến tĩnh: Một biến bất kỳ được của loại
này được định nghĩa với từ khóa static nói lên rằng trình biên dịch sẽ
chỉ lấy một bản copy của biến này để dùng cho tất cả các trường hợp sử dụng biến
bất kể biến đó được dùng bao nhiêu lần. Ví dụ: định nghĩa một trường là các
bánh răng của một loại xe đạp được định nghĩa bởi tử khóa static
thì kể từ đó, con số này là cố định. Như đoạn code:
static int numGears = 6; |
Đoạn code này sẽ tạo ra một trường tĩnh. Thêm nữa, từ khóa final
có thể được thêm vào để biểu thị số bánh răng này là không bao giờ thay đổi
trong suốt chương trình.
Local
Varialbes Tương tự như một đối tượng được lưu trữ trạng thái
trong các thuộc tính (trường), một phương thức sẽ thường lưu trữ tạm thời trong
các biến cục bộ (Local Variables). Cú pháp khai báo biến cục bộ tương tự như
khai báo các biến khác. Ví dụ:
int count = 0;
Ở đây không có từ
khóa đặc biệt để khai báo biến cục bộ, và nó chỉ được sử dụng trong phần mà biến
đó được khai báo (thường là trong các phương thức). Biến cục bộ không được truy
cập từ ngoài lớp hoặc phương thức nó khai báo.
Parameters:
Biến tham số. Chúng ta xem xét ví dụ về biến tham chiếu, cả trong lớp Bicycle
và trong hàm main cho ứng dụng “Hello world”, hàm main được viết như
sau:
Ở đây biến args là một biến tham số. Điều quan
trọng cần nhớ đó là biến tham số luôn luôn là biến và không phải là thuộc
tính.
Có thể nói rằng, vấn đề chính được đưa ra bàn ở đây là các thuộc
tính và các biến. Nếu chúng ta nói thuộc tính chung chung (bao gồm biến cục
bộ và biến tham số) thì chúng ta có thể nói là thuộc tính (trường). Nhưng nếu
bàn tính áp dụng thì có thể nói tất cả đều là biến. Trong trường hợp này phân
loại ra sẽ có các mục (trường tĩnh, biến cục bộ, v.v..) là phù hợp nhất. Nhưng
thỉnh thoảng, ta xem nó như là thành viên. Một kiểu thuộc tính, các phương thức
các kiểu lồng nhau có thể được gọi chung là các thành viên.
Hiểu một cách đơn giản: Biến là vùng nhớ được đặt tên
có kích thước bằng với kích thước của kiểu dữ liệu.
Định danh:
Tất cả các ngôn ngữ lập trình đều có đặt ra các quy tắc và
quy ước cho việc đặt tên. Ngôn ngữ lập trình Java cũng không ngoại lệ. Các quy
tắc và quy ước cho việc đặt tên biến có thể tóm lại như sau:
Tên biến phân biệt chữ hoa và chữ thường. Một biến có thể được
tạo thành từ các chữ cái unicode, dấu đô la ‘$’ và dấu gạch dưới ‘_’ tuy nhiên
không được bắt đầu bằng dấu đô la ‘$’ hoặc gạch dưới ‘_’ và không giới hạn độ
dài. Thêm nữa, Java đã quy định dấu đô la không được sử dụng trong tên biến. Bạn
có thể tìm được một vài tình huống sử dụng tên tự động sẽ bao gồm dấu đô la,
nhưng tên biến thì nên tránh. Tương tự quy ước đã tồn tại ký tự gạch dưới được
bắt đầu tên biến, nhưng khuyên không nên sử dụng. Khoảng trắng trong tên biến
là không được phép.
Dãy ký tự, số, ký tự đô la hoặc ký tự gạch dưới. Quy ước (phổ
dụng) áp dụng. Khi chọn tên biến, nên sử dụng cả từ, tránh các từ viết tắt khó
hiểu. Hãy tạo nên các dòng code dễ đọc và dễ hiểu. Trong nhiều trường hợp chúng
ta có thể dử dụng các từ văn bản mô tả để đặt tên, các thuộc tính cadence
, speed
, gear
chẳng hạn, đặt tên biến khó hiểu là c,s,g cho các thuôc tính kia. Có thể chọn từ
mang tính gợi ý.
Nếu tên được chọn bao gồm một từ, có thể sử dụng toàn bộ
đánh vần của từ đó, nếu có từ 2 từ trở lên, hãy viết hoa chữ cái đầu tiên của
các từ tiếp theo. Ví dụ: gearRatio, currentGear, nếu biến chứa
giá trị là các hằng số thì có thể viết hoa toàn bộ biến đó. Ví dụ
final int NUM_GEARS = 6
Kiểu dữ liệu nguyên thủy.
Trong ngôn ngữ lập trình Java có kiểu biến tĩnh, những biến
mà phải định nghĩa trước khi sử dụng. Liên quan đến kiểu biến, tên biến. Ví dụ:
int gear = 1;
Trong ví dụ trên, tên biến là ‘gear’ có kiểu dữ liệu là kiểu số, khởi gán
giá trị ban đầu bằng “1”. Quyết định kiểu dữ liệu cho một biến và giá trị của
nó bao gồm các phép toán thực thi trên đó. Ngoài kiều int, ngôn ngữ lập trình
Java hỗ trợ 7 kiểu dữ liệu nguyên thủy khác. Mỗi kiều dữ liệu này được định
nghĩa trước bởi ngôn ngữ là từ khóa đặt. Các giá trị nguyên thủy không chia sẻ
được với nhau. Tám kiểu dữ liệu nguyên thủy được hỗ trợ trong Java là: byte, short, int, long, float, double, boolean.
Kiểu byte: Kiểu dữ liệu byte là kiểu dữ
liệu có dấu và có kích thước 8-bit. Giá trị nhỏ nhất từ -128 và giá trị
lớn nhất là 127. Kiểu dữ liệu này là tiện ích tiết kiệm bộ nhớ trong các
mảng lớn. Kiểu này có thể được sử dụng thay thế cho kiểu int.
Kiểu short: Kiểu dữ liệu short là kiểu
dữ liệu có dấu và có kích thước 16-bit. Giá trị nhỏ nhất của kiểu này là
-32 768 và giá trị lớn nhất mà nó có thể biểu diễn được là 32767. Cũng giống với
kiểu byte, bạn cũng có thể dùng kiểu này để tiết kiệm bộ nhớ cho các mảng
lớn, là một giải pháp quan trọng trong việc tiết kiệm bộ nhớ.
Kiểu int: Mặc định, kiểu dữ liệu int là kiểu dữ
liệu có dấu và có kích thước 32-bit, giá trị nhỏ nhất mà nó biểu diễn được là -231 và giá trị lớn nhất mà nó biểu diễn được là 231-1. Từ Java SE 8 trở đi bạn
có thể sử dụng kiểu dữ liệu này để biểu diễn kiểu dữ liệu số nguyên không dấu.
Tức là giá trị nhỏ nhất là 0 và giá trị lớn nhất mà nó biểu diễn được là 232-1. Sử dụng lớp Interger
là lớp sử dụng kiểu dữ liệu int, là số nguyên không dấu. Lớp số nguyên
này có được nhiểu thông tin hơn. Các phương thức tĩnh như compareUnsigned
, divideUnsigned
vân vân đã được thêm lớp
số nguyên Interger hỗ trợ các phép toán cho các số nguyên không dấu.
Kiểu long: Kiểu long là kiểu dữ liệu có kích thước
64-bit. Kiểu long có dấu, biểu diễn được các số từ -263 đến 263-1. Từ Java SE 8 trở đi, bạn
có thể sử dụng kiểu dữ liệu này cho số nguyên không dấu 64-bit, có thể biểu diễn
được các số từ 0 đến 264-1. Sử dụng kiểu dữ liệu này
khi bạn cần phạm vi giá trị lớn hơn so với phạm vi giá trị của kiểu
int. Lớp Long, bao gồm các phương thức như compareUnsigned
, divideUnsigned
vân vân để hỗ trợ các
phép toán cho số nguyên không dấu long.
Kiểu double: Kiểu dữ liệu double biểu diễn
chính xác sau dấu phẩy được754 số. Chúng ta không bàn đến phạm vi giá trị của
nó ở đây. Nhưng trong Java, khi sử dụng các số thập phân, thì mặc định kiểu dữ
liệu này được chọn. Các lớp có sử dụng kiểu dữ liệu này có thể kế đến các lớp đặc
biệt như: Floating-Point Types, Formats, và Values.
Kiểu boolean: Kiểu dữ liệu boolean là kiểu dữ
liệu chỉ bao gồm 2 giá trị true và false. Sử dụng kiểu dữ liệu này để làm cờ
đơn giản như điều kiện true/flase. Kiểu dữ liệu này có kích thước 1-bit.
Kiểu char: Kiểu dữ liệu char là kiểu dữ liệu không dấu
Unicode 16-bit. Phạm vi biểu diễn của kiểu dữ liệu này là từ '\u0000'(hoặc
0) đến '\uffff'
(hoặc 65,535).
Ngoài tám kiểu dữ liệu nguyên thủy kể trên, ngôn ngữ lập
trình Java cũng đặc biệt cung cấp một kiểu dữ liệu kiểu chuỗi nằm trong lớp java.lang.String.
Chuỗi ký tự không bao gồm các dấu phẩy động, đối tượng của lớp String, ví dụ:
String s = "this is a string" |
Các đối tượng của lớp String là không thay đổi, có nghĩa là
một khi được tạo ra, giá trị của nó không thể thay đổi. Lớp String không thuộc
kiểu dữ liệu nguyên thủy nhưng nó được đặc biệt nhắc tới vì nó hỗ trợ cho ngôn
ngữ lập trình một cách tương tự dữ liệu nguyên thủy.
Các giá trị mặc định của kiểu dữ liệu nguyên thủy.
Không cần khởi gán các giá trị khi định nghĩa các biến. Các
biến (fields) được định nghĩa mà không khởi tạo sẽ nhận các giá trị mặc định
khi biên dịch. Các giá trị này ở trong bảng giá trị mặc định. Có thể nói mặc định là
không (zero) hoặc null, phụ thuộc vào từng kiểu dữ liệu. Dựa vào các giá trị mặc
định, tuy nhiên nó xác định phong cách lập trình không tốt. Tóm lại các giá trị
mặc định cho các biến phụ thuộc vào các kiểu dữ liệu như trong bảng giá trị mặc định.
Các biến cục bộ địa phương (Local variables) thì khác, trình
biên dịch không bao giờ gán giá trị mặc định cho một biến chưa được khởi tạo
giá trị ban đầu. Nếu bạn không gán giá trị khởi tạo cho các giá trị ban đầu cho
các biến cục bộ thì cần phải gán giá chị cho chúng trước khi sử dụng. Nếu không
kết quả sẽ là quá trình biên dịch chương trình sẽ bị lỗi.
Literals
Bạn có thể nhận thấy rằng từ khóa new không được sử dụng
để khởi tạo các giá trị cho biến có kiểu dữ liệu nguyên thủy. Kiểu dữ liệu
nguyên thủy là một kiểu dữ liệu đặc biệt được xây dưng cho ngôn ngữ lập trình.
Chúng không phải là đối tượng được tạo ra từ các lớp (class). Một literal là một
mã nguồn được cố định giá trị, các literal trực tiếp biểu diễn trong mã nguồn của
bạn mà không cần tính toán. Giống như đoạn mã nguồn phía dưới đây:
boolean result = true;
char capitalC = 'C';
byte b = 100;
short s = 10000;
int i = 100000;
Literal dạng số (Integer Literals)
Một literal dạng số có kiểu dữ liệu là long nếu nó có kết
thúc là L hoặc l, trong trường hợp còn lại thì nó có kiểu dữ liệu là int. Được
khuyến khích sử dụng chữ viết hoa L, bởi vì chữ viết thường l đôi khi được nhầm
lẫn với số 1.
Toàn bộ các giá trị kiểu byte, short, int và long có thể được
tạo ra từ các literal kiểu int. Các giá trị của kiểu dữ liệu long vượt ra ngoài
phạm vi giá trị kiểu int có thể được tạo ra bởi các literal kiểu long. Các
literal dạng số có thể được thể hiện qua qua hệ thống số sau:
·
Số thập phân: Dựa trên 10 chữ số từ 0 đến 9, đây
là số được sử dụng hàng ngày.
·
Số hexa. 16 số bao gồm các số từ 0 đến 9 và các
ký tự A đến F.
·
Số nhị phân: 2 số. Bao gồm các số 0 và 1.
Mục đích lập trình nói chung, hệ thống số thập phân là hệ thống duy nhất bạn thường sử dụng. Tuy nhiên nếu bạn cần sử dụng các hệ thống số khác thì cần thêm vào tiền tố 0x để xác định hệ hexa và 0b để các định hệ nhị phân.
Các Literal dạng số thực (Floating-Point Literals)
Số thực literal có kiểu dữ liệu là float nếu kết thúc số là
chữ F hoặc f, tương tự số literal có kiểu dữ liệu là double nếu kết thúc số có thêm
D hoặc d.
Các kiểu dấu chấm động (float và double) cũng thể hiện sử dụng
chữ E hoặc e (thể hiện số khoa học), F hoặc f (literal số thực 32-bit), và D hoặc
d (literal số thực 64-bit), đó là những điều mặc định.
double d1 = 123.4;
// tương tự giá trị d1, nhưng trong thể hiện số khoa học.
double d2 = 1.234e2;
float f1 = 123.4f;
Các Literal dạng chuỗi và ký tự.
Các Literals kiểu char và kiểu String có thể
bao gồm bất kỳ các mã Unicode (UTF-16) nào. Nếu như hệ thống và bộ soạn thảo của
bạn cho phép, bạn có thể sử dụng các ký tự trực tiếp trong code của bạn hoặc không.
Ngôn ngữ lập trình Java cũng hỗ trợ một vài ký tự đăc biệt
cho char và String \b
(backspace), \t
(tab), \n
(line feed), \f
(form feed), \r
(carriage return), \"
(double quote), \'
(single quote), and \\
(backslash).
Cũng có một ký tự literal đặc biệt là null, có thể bạn cần sử
dụng giá trị này cho các kiểu tham chiếu. null có thể được gán cho bất kỳ
biến nào ngoại trừ các biến có kiểu dữ liệu nguyên thủy. Bạn có thể làm việc với
giá trị null bằng cách kiểm tra giá trị của biến. Do vậy null thường
được sử dụng trong lập trình để kiểm tra một vài đối tượng không xác định.
Cuối cùng, các giá trj đặc biệt của các literal được gọi là một
lớp literal được sắp xếp bằng cách lấy tên một lớp và thêm “.class” vào. Ví dụ String.class,
điều này thể hiện đối tượng thể hiện cho chính kiểu đó.
Sử dụng các ký tự gạch dưới cho các Literal dạng số.
Từ Java SE 7 trở đi, bất kỳ một số nào có các ký tự gạch dưới,
có thể xuất hiện bất cứ đâu giữa các số trong Literal dạng số. Tính năng này
cho phép bạn phân tách các nhóm chữ số trong dãy literal dạng số và nâng cao hiệu
quả đọc code của bạn.
Ví dụ, nếu code cảu bạn bao gồm các số với rất nhiều số thập
phân, bạn có thể sử dụng các dấu gạch dưới để phân tách các số thập phân thành các
nhóm 3 chứ xố, tương tự bạn đánh dấu câu như dấu phẩy, dấu cách hoặc khoảng trắng.
Ví dụ sau sẽ thể hiện cách này và sử dụng các dấu gạch dưới
cho Literal dạng số.
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 3.14_15F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;
Bạn có thể thay thế các dấu gạch dưới giữa các số, nhưng bạn
không thể thay thế dấu gạch dưới đó bằng các khoảng trắng:
-
Ở vị trí bắt đầu và vị trí kết thúc của số.
-
Liền kề dấu chấm của số thập phân trong Literal
dạng số thực.
-
Ngay trước chữ F hoặc L.
Ví dụ sau thể hiện các vị trí mà dấu gạch dưới hợp lệ và không
hợp lệ trong các Literal dạng số:
// Invalid: không thể đặt dấu gạch dưới
// liền kề dầu chấm của số thập phân
float pi1 = 3_.1415F;
// Invalid: không thể đặt dấu gạch dưới
// liền kề dấu chấm của số thập phân
float pi2 = 3._1415F;
// Invalid: không thể đặt dấu gạch dưới
// ngay trước ký tự L thể hiện loại số.
long socialSecurityNumber1 = 999_99_9999_L;
// OK (literal thập phân)
int x1 = 5_2;
// Invalid: Không thể đặt dấu gạch dưới
// ở vị trí kết thúc Literal
int x2 = 52_;
// OK (Literal thập phân)
int x3 = 5_______2;
// Invalid: không thể đặt dấu gạch dưới
// giữa ký các ký tự 0x thể hiện số hexa
int x4 = 0_x52;
// Invalid: không thể đặt dấu gạch dưới
// tại vị trí bắt đầu số.
int x5 = 0x_52;
//OK (hexadecimal literal)
int x6 = 0x5_2;
//Invalid: không thể đặt dấu gạch dưới
//tại vị trí kết thúc
int x7 = 0x52_;