Data Type
Duy Văn | 05/03/2025
Trong Java có các loại biến khác nhau, chúng được phân ra làm hai loại, loại nguyên thuỷ và loại không nguyên thuỷ:
- Nguyên thuỷ: bao gồm
byte,short,int,long,float,double,char,boolean - Không nguyên thuỷ: bao gồm
String,Array,Class
Primitive Data Type
Nguyên thuỷ hiểu nôm na là không thể chia nhỏ hơn, nó là các kiểu dữ liệu cơ bản nhất trong Java, ví dụ như một String thì chứa nhiều char nên String không phải nguyên thuỷ, còn char thì chỉ chứa một ký tự nên nó là nguyên thuỷ
| Data Type | Description |
| byte | lưu số nguyên từ -128 đến 127 |
| short | lưu số nguyên từ -32768 đến 32767 |
| int | lưu số nguyên từ -2^31 đến 2^31-1 |
| long | lưu số nguyên từ -2^63 đến 2^63-1 |
| float | lưu số thực, đủ tốt để lưu số có 6-7 chữ số thập phân |
| double | lưu số thực, đủ tốt để lưu số có 15 chữ số thập phân |
| boolean | lưu giá trị true hoặc false |
| char | lưu một ký tự ASCII |
Numbers
Số của kiểu data nguyên thuỷ được chia vào làm số nguyên và số thực
Integer Types
Bytes
Byte là kiểu dữ liệu nguyên thuỷ chứa được các giá trị số nguyên từ -128 đến 127 nó có thể dùng thay int để tiết kiệm bộ nhớ trong tình huống phải chắc chắn giá trị của biến không vượt quá giới hạn
byte a = 100;
System.out.println(a);
Nếu chúng ta thử gán cho a giá trị lớn hơn 127 thì sẽ báo lỗi error: incompatible types: possible lossy conversion from int to byte, tức là, số chúng ta đưa vào ban đầu được mặc định là int (Java mặc định literals số là int trước khi thực hiện bất kỳ dạng ép kiểu nào), sau khi sử dụng cú pháp dùng bytethì mới được chuyển thành byte, nhưng do byte không thể lưu quá 127 nên bit dư ra sẽ bị mất, dẫn đến lỗi lossy conversion
Short
Tương tự, nhưng Short có một vùng giá trị lớn hơn, từ -32768 đến 32767
short a = 10000;
System.out.println(a);
Int
Int là kiểu dữ liệu nguyên thuỷ chứa được các giá trị số nguyên từ -2^31 đến 2^31-1. Đây cũng là kiểu giữ liệu mặc định dành cho các literal dạng số nguyên, miễn là không có dấu thập phân thì ban đầu được coi là int
int a = 1000000;
System.out.println(a);
Long
Long là kiểu dữ liệu nguyên thuỷ chứa được các giá trị số nguyên từ -2^63 đến 2^63-1. Vì giá trị ban đầu được Java ghi nhận khi truyền literal dạng số nguyên là int, khi gán các giá trị lớn hơn mức của int thì nên đặt L hoặc l ở cuối giá trị để Java hiểu rằng đó là long
long a = 1000000000L;
System.out.println(a);
Có một khái niệm là Widening vs Narrowing Conversions, tức là ép kiểu từ kiểu dữ liệu nhỏ hơn sang lớn hơn sẽ được thực hiện tự động khi cần thiết, nhưng từ lớn hơn sang nhỏ hơn (narrowing) thì cần thực hiện các khâu xử lý cũng như phải cast chay để đảm bảo không mất dữ liệu
Nếu khâu tính toán bị vượt mức giới hạn của dữ liệu, Java sẽ không báo về lỗi mà chỉ đơn giản là chạy ngược về phía giới hạn bên kia. Ví dụ, nếu chúng ta cộng 1 cho giá trị lớn nhất của int thì kết quả sẽ là giá trị nhỏ nhất của int
Floating Points
Các số thực sẽ chứa dấu thập phân, và tốt hơn hết là khi khai báo và gán giá trị cho các biến thuộc kiểu dữ liệu này nên có chữ cái “f” (cho float) hoặc “d” (cho double)
Float
Float là kiểu dữ liệu nguyên thuỷ chứa được các giá trị số thực, đủ tốt để lưu số có 6-7 chữ số thập phân
float a = 3.14f;
System.out.println(a);
Double
Double là kiểu dữ liệu nguyên thuỷ chứa được các giá trị số thực, đủ tốt để lưu số có 15 chữ số thập phân
double b = 3.1415926535897938d;
System.out.println(b);
Ngoài ra chúng ta có thể dùng “e” hay “E” để biểu diễn số mũ, ví dụ 1.2e3 tương đương với 1.2 * 10^3
Boolean
Boolean về cơ bản là một kiểu dữ liệu chỉ chứa giá trị true hoặc false, nó thường được sử dụng trong các biểu thức điều kiện
boolean a = true;
System.out.println(a);
Char
Char là kiểu dữ liệu nguyên thuỷ chứa một ký tự ASCII, nó được bao bọc bởi cặp dấu nháy đơn '
char a = 'a';
System.out.println(a);
Ngoài ra chúng ta có thể gán giá trị số tương ứng với ký tự ASCII, ví dụ char a = 65; sẽ tương đương với ký tự A
char a = 65;
System.out.println(a); // A
Mặc dù String mình sẽ viết ở bài sau nhưng cũng đáng nói đến vì String là kiểu dữ liệu dùng rất nhiều và được tích hợp cực mạnh trong Java, object string có các method để biến đổi, và String thì được cấu thành từ nhiều char, nhưng cũng có thể có những String chỉ có một ký tự
Non Primitive Data Type
Non-primitive còn có tên gọi khác là Reference Type, nó không chứa object có giá trị trực tiếp mà chỉ chứa địa chỉ của các object đó, nó bao gồm các kiểu dữ liệu như String, Array, Class
- Primitive trong Java được định nghĩa từ đầu và xây dựng sẵn trong Java, còn Non-primitive thì được xây dựng từ các kiểu dữ liệu nguyên thuỷ và có thể được tạo ra từ các class khác, chỉ riêng String được xây dựng sẵn, còn Array và Class cần phải được tạo ra bởi lập trình viên
- Non-rpimitive có thể được dùng để gọi ra method để thực hiện các tác vụ, còn Primitive thì không thể
- Primitive được đánh dấu tên là viết thường toàn bộ, như
int,float,boolean, còn Non-primitive thì viết hoa chữ cái đầu, nhưString,Array,Class - Primitive luôn có giá trị, còn Non-primitive thì chứa địa chỉ của object nên có thể null
Casting
Như mình đã đề cập ở trên thì có hai khái niệm là Widening Casting và Narrowing Casting
- Widening casting sẽ được thực hiện tự động, do bản chất các data type dạng số có giới hạn lớn hơn đã bao gồm các data type có giới hạn nhỏ hơn ở trong, một số ở type byte cũng là thuộc short và int rồi. Thứ tự chuyển đổi :
byte->short->char->int->long->float->double - Narrowing casting sẽ phải thực hiện bằng tay, do bản chất data khi đi từ các vùng giới hạn lớn về nhỏ sẽ đi từ mức khối lượng nhị phân cao xuống thấp, ví dụ ở int chúng ta có 32bit, short là 16bit, vậy thì khi chuyển từ int về short, nếu giá trị các bit có nghĩa vượt quá 16bit, sẽ gây mất dữ liệu:
double->float->long->int->char->short->byte
Có hai vấn đề mà ta cần làm rõ:
- Cú pháp để cast
- Tại sao
charlại chui vào giữa kia?
Cú pháp để cast
Widening casting sẽ được thực hiện tự động, ví dụ
int myInt = 9;
double myDouble = myInt * 2; // tự động chuyển myInt thành double
System.out.println(myDouble); // 18.0
Trong tình huống này thì ban đầu myInt là int, sau đó do một biến double tham gia vào phép toán nên int được tự đưa về double để thực hiện phép nhân
Nhưng ngược lại thì sao?
double myDouble = 9.78;
int myInt = myDouble*2; // error: possbile lossy conversion from double to int
System.out.println(myInt);
Để thực hiện được chuyển đổi ngược, chúng ta cần thực hiện Narrowing casting, cú pháp của nó là:
double myDouble = 9.78;
int myInt = (int) myDouble * 2;
System.out.println(myInt); // 18
Ở đây chúng ta có thể quan sát là 9.78 sau khi chuyển về int sẽ chỉ còn phần nguyên, hành vi này về cơ bản là giống phía Python
Nhưng tại sao char lại chui vào giữa kia?
Vì char là kiểu dữ liệu chứa một ký tự ASCII, nó có giá trị từ 0 đến 65535, nên nó có thể chứa được các giá trị của byte, short, int, long, float, double, nhưng ngược lại thì không, nếu chúng ta chuyển từ char sang int thì giá trị của char sẽ được chuyển sang mã ASCII tương ứng
char myChar = 'A';
int myInt = myChar;
System.out.println(myInt); // 65
Ngược lại, cần phải thực hiện Narrowing casting
int myInt = 65;
char myChar = (char) myInt;
System.out.println(myChar); // A