Hai nhóm: kiểu nguyên thủy và kiểu tham chiếu
Mọi giá trị trong Java đều có một kiểu dữ liệu (data type), và kiểu đó thuộc một trong hai nhóm. Kiểu nguyên thủy (primitive type) chứa trực tiếp một giá trị thô: một con số, một ký tự, hoặc đúng/sai. Kiểu tham chiếu (reference type) chứa một tham chiếu (con trỏ) tới một đối tượng nằm ở chỗ khác trong bộ nhớ: String, mảng, và mọi lớp do bạn hoặc thư viện định nghĩa.
Sự phân biệt này quan trọng vì kiểu nguyên thủy nhẹ và được sao chép theo giá trị, còn biến tham chiếu trỏ tới các đối tượng dùng chung. Hãy bắt đầu với tám kiểu nguyên thủy: chúng là nền móng cho mọi thứ khác.
Tám kiểu nguyên thủy
Java có đúng tám kiểu nguyên thủy có sẵn. Sáu kiểu là số, một kiểu là ký tự, một kiểu là boolean:
Vài điều cần để ý ngay: dấu gạch dưới trong 2_000_000 chỉ là dấu phân cách cho dễ đọc và trình biên dịch bỏ qua, hậu tố L và f là bắt buộc (xem thêm bên dưới), còn char dùng nháy đơn trong khi String dùng nháy kép.
Chọn kiểu số
Bạn gần như không bao giờ cần byte hay short: chúng chỉ tiết kiệm bộ nhớ trong những mảng rất lớn. Lựa chọn thực sự là giữa int/long cho số nguyên và float/double cho số thập phân.
| Kiểu | Kích thước | Dùng khi |
|---|---|---|
int | 32 bit | Mặc định cho số nguyên (tới khoảng 2,1 tỷ) |
long | 64 bit | Giá trị vượt khoảng 2 tỷ: dấu thời gian mili giây, kích thước tệp |
double | 64 bit | Mặc định cho số thập phân, độ chính xác tốt |
float | 32 bit | Chỉ khi bộ nhớ eo hẹp và chấp nhận giảm độ chính xác |
Cái bẫy ở đây là tràn số (overflow). Một int vượt quá phạm vi sẽ lặng lẽ quay vòng: không báo lỗi, chỉ ra kết quả sai:
maxInt + 1 bị tràn thành -2147483648 vì cả hai toán hạng đều là int và kết quả vẫn là int. Ép kiểu một vế sang long trước sẽ tính toán bằng 64 bit. Khi giá trị có thể lớn, nhất là số mili giây tính từ năm 1970, hãy dùng long.
Literal và hậu tố
Một literal số viết trơn có kiểu mặc định, và điều này hay khiến người học vấp. 100 là int; 3.14 là double. Để viết literal kiểu long hoặc float, bạn phải thêm hậu tố:
Bỏ L khỏi 8_000_000_000L thì code không biên dịch được, vì literal đã vượt phạm vi int trước khi tới được biến long. Bỏ f khỏi 0.5f thì Java báo rằng không thể gán double cho float mà không ép kiểu. Hậu tố không phân biệt hoa thường, nhưng hãy dùng L viết hoa: chữ l thường trông y hệt chữ số 1.
char thực chất là một con số
Một char lưu một đơn vị mã Unicode 16 bit, nghĩa là nó cũng là một số nguyên không dấu nhỏ. Bạn có thể tính toán với nó và chuyển qua lại bằng ép kiểu:
Cách này đôi khi tiện để dịch chữ cái, nhưng đừng dựa vào nó để xử lý văn bản: việc đó là của String. Lưu ý char dùng nháy đơn ('A'); nháy kép ("A") tạo ra một String một ký tự, là một kiểu hoàn toàn khác.
Giá trị mặc định (và cái bẫy của biến cục bộ)
Thuộc tính và phần tử mảng mà bạn không khởi tạo sẽ nhận một giá trị mặc định hợp lý. Nhưng điều này chỉ áp dụng cho thuộc tính và ô mảng: biến cục bộ không có giá trị mặc định, và dùng biến trước khi gán giá trị là lỗi biên dịch, không phải bất ngờ lúc chạy.
Nếu bạn viết int x; System.out.println(x); bên trong main, trình biên dịch sẽ từ chối với thông báo "variable x might not have been initialized." Sự nghiêm ngặt đó là có chủ đích: nó ngăn bạn đọc phải dữ liệu rác. Các giá trị mặc định cần nhớ: kiểu số là 0, boolean là false, char là '\u0000', và mọi kiểu tham chiếu là null.
Tiếp theo: chuỗi
String là kiểu tham chiếu bạn sẽ dùng nhiều nhất: văn bản, ghép giá trị, dữ liệu người dùng nhập. Nó hoạt động khác với các kiểu nguyên thủy ở đây: nó là đối tượng, nó bất biến, và so sánh chuỗi có những cạm bẫy riêng. Trang tiếp theo sẽ nói về nó.
Câu hỏi thường gặp
Java có bao nhiêu kiểu dữ liệu?
Java có tám kiểu nguyên thủy: byte, short, int, long, float, double, char và boolean. Mọi thứ còn lại, gồm String, mảng và mọi đối tượng, đều là kiểu tham chiếu. Vì vậy số kiểu nguyên thủy có sẵn cố định là tám, còn kiểu tham chiếu thì không giới hạn vì bạn (và các thư viện) có thể định nghĩa lớp mới.
int và long trong Java khác nhau thế nào?
Cả hai đều chứa số nguyên, nhưng int dài 32 bit (phạm vi khoảng ±2,1 tỷ) còn long dài 64 bit (phạm vi khoảng ±9,2 tỷ tỷ). Mặc định hãy dùng int; chuyển sang long khi giá trị có thể vượt hai tỷ, như dấu thời gian tính bằng mili giây, kích thước tệp, bộ đếm lớn. Literal kiểu long cần hậu tố L, ví dụ long big = 10000000000L;.
Giá trị mặc định của int trong Java là gì?
Một thuộc tính kiểu int mặc định là 0, nhưng chỉ thuộc tính và phần tử mảng mới có giá trị mặc định. long mặc định là 0L, double là 0.0, boolean là false, char là '\u0000', và mọi kiểu tham chiếu là null. Biến cục bộ hoàn toàn không có giá trị mặc định: bạn phải gán giá trị trước khi dùng, nếu không code sẽ không biên dịch được.