- [Java Core] B1: Tại sao nên học Java, syntax cơ bản
- [Java Core] B2: Class và Object (Lớp và đối tượng)
- [Java Core] B3: Cách Java quản lý dữ liệu
- [Java Core] B4: Tính chất đóng gói, kế thừa và đa hình trong Java
- [Java Core] B5: Tính chất trừu tượng, Interface và Abstract Class trong Java
- [Java Core] B6: Design Pattern Iterator. Iterable và Collection trong Java
- [Java Core] B7: Exception trong Java
Mục lục:
- Cách Java lưu trữ dữ liệu (Kiểu dữ liệu nguyên thuỷ, Object, Wrapper class, Auto-boxing, Auto-unboxing)
- Constructor, Super, This
- Pass by value
- Garbage Collector
I. Cách Java lưu trữ dữ liệu (Kiểu dữ liệu nguyên thuỷ, Object, Wrapper class, Auto-boxing, Auto-unboxing)
1. Kiểu dữ liệu nguyên thuỷ (Primitive data type)
- Trong Java, có 8 kiểu dữ liệu nguyên thuỷ (Primitive data type):
byte
,short
,int
,long
,float
,double
,boolean
,char
. - Kiểu dữ liệu nguyên thuỷ là các kiểu dữ liệu mà không phải là đối tượng, không có các phương thức, thuộc tính, … như các đối tượng.
- Tức là, bạn không thấy các phương thức
toString()
,equals()
,hashCode()
, … trong các kiểu dữ liệu nguyên thuỷ.
inta=1;
// a không có hàm hay biến như các object// a.toString() không tồn tại// a.equals() không tồn tại// a.(gì đó) không tồn tại
- Ví dụ, khi khai báo 1 biến
int a = 1;
, thì Java sẽ cấp phát 1 vùng nhớ có kích thước 4 byte để lưu giá trị1
vào biếna
. Thực chất trong bộ nhớ nó được lưu theo binary là00000000 00000000 00000000 00000001
. (1 byte = 8 bit) - Do có các 32 bit, nên int có thể lưu giá trị từ -2^31 đến 2^31 – 1. (1 bit để lưu dấu)
- Tương tự, các kiểu dữ liệu khác cũng được lưu theo binary, và có kích thước khác nhau.
2. Object
2.1 Object là gì?
- Trong Java, mọi thứ đều là đối tượng (Object). Vậy nên các kiểu dữ liệu nguyên thuỷ cũng là đối tượng.
- Hiện tại các em chưa học về kế thừa, nhưng cứ tạm hiểu, khi ta nói về 1 tập đối tượng, ví dụ: Xe Ô Tô, hay class Car, có các thuộc tính, phương thức gì đó. Xe máy, hay class Motorbike, cũng có các thuộc tính, phương thức gì đó. Vậy thì Xe Ô Tô và Xe Máy đều là đối tượng, và chúng có các thuộc tính, phương thức giống nhau.
- Vậy ta gọi tập đối tượng có cả xe máy và xe ô tô, không chứa các phương thức đặc thù như
chạy
,dừng
, … là gì? Đó là tập đối tượngXe
, hay classVehicle
. Vậy nên,Xe Ô Tô
vàXe Máy
đều làXe
, hayVehicle
. Tập hợpVehicle
có thể nằm trong tập hợp to hơn làPhương Tiện Giao Thông
, hay classTransportation
. - Trong Java cũng như vậy, mọi class các em tạo ra mặc định đều thuộc 1 tập hợp chung là
Object
. Và class này luôn cung cấp cho mọi object tạo ra từ các class khác các phương thức, thuộc tính nhưtoString()
,equals()
,hashCode()
, … như đã nói ở trên.
2.2 Object được lưu thế nào trong Java
- Hiểu đơn giản, giá trị cách lưu object thực sự rất phức tạp, điều đó đều do Java quản lý, và ta không cần quan tâm. Ta chỉ cần biết, khi tạo ra 1 object, ta chỉ lưu 1 địa chỉ tới object đó. Và từ địa chỉ này, ta có thể ra lệnh từ xa để lấy các thuộc tính, phương thức của object đó.
- Vậy nên, khi ta gán 1 object cho 1 biến, thực chất ta đang gán 1 địa chỉ tới object đó cho biến đó.
Studentobj=newStudent();
// obj là 1 địa chỉ tới object Student
- Vậy nên ta không thể so sánh 2 object với nhau bằng toán tử
==
, mà phải dùng phương thứcequals()
.
Studentobj1=newStudent();
Studentobj2=newStudent();
// obj1 != obj2// obj1.equals(obj2) == false
2.3 Wrapper class
- Wrapper class là các class được tạo ra để bao bọc các kiểu dữ liệu nguyên thuỷ. Ví dụ:
Integer
,Float
,Double
,Boolean
,Character
, … - Wrapper class cung cấp các phương thức để thao tác với các kiểu dữ liệu nguyên thuỷ. Ví dụ:
Integer.parseInt()
,Integer.toString()
,Integer.valueOf()
, …
Primitive data type | Wrapper class |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
- Các kiểu dữ liệu lưu dạng object này tuân theo cách Java lưu trữ object, tức là không lưu trực tiếp object, mà chỉ lưu địa chỉ tới object đó.
Ưu điểm | Nhược điểm |
---|---|
Có thể sử dụng các phương thức của object | Tốn bộ nhớ hơn |
- Ví dụ, ở Java ta có thể chuyển String thành số, từ các số hay từ 1 object bất kì sang String rất nhanh bằng toString(), ngoài ra khi nó là Object, ta có thể sử dụng các kĩ thuật OOP như kế thừa, đa hình, … (sẽ nói ở các buổi sau).
inta=1;
// a không có hàm hay biến như các object// a.toString() không tồn tạiIntegerb=1;
// b có hàm toString()// b.toString() tồn tại
2.4 Auto-boxing và Auto-unboxing
- Auto-boxing là quá trình chuyển đổi từ kiểu dữ liệu nguyên thuỷ sang kiểu dữ liệu lưu dạng object.
- Auto-unboxing là quá trình chuyển đổi từ kiểu dữ liệu lưu dạng object sang kiểu dữ liệu nguyên thuỷ.
- Ví dụ:
inta=1;
Integerb=1;
// Auto-boxingIntegerc= a;
// Auto-unboxingintd= b;
- Auto-boxing và Auto-unboxing là 2 quá trình tự động, do Java tự động thực hiện. Vậy nên các em không cần phải quan tâm đến nó.
- Tuy nhiên, khi sử dụng Auto-boxing và Auto-unboxing, các em cần phải cẩn thận, vì nó có thể gây ra lỗi.
Integera=null;
intb= a; // NullPointerException
- Vậy nên, khi sử dụng Auto-boxing và Auto-unboxing, các em cần phải kiểm tra giá trị null trước khi sử dụng.
Integera=null;
intb= a == null ? 0 : a;
- Auto-boxing và Auto-unboxing có thể gây ra lỗi khi sử dụng toán tử
==
.
Integera=1;
Integerb=1;
Integerc=128;
Integerd=128;
System.out.println(a == b); // true
System.out.println(c == d); // false
- Autoboxing và Auto-unboxing giúp cho việc sử dụng các kiểu dữ liệu nguyên thuỷ và object trở nên dễ dàng hơn. Ví dụ, khi ta muốn sử dụng
HashMap
, thì ta không thể sử dụng kiểu dữ liệu nguyên thuỷ làint
, mà phải sử dụngInteger
.
HashMap<Integer, String> map = newHashMap<>();
map.put(1, "Hello");
map.put(2, "World");
System.out.println(map.get(1)); // Hello
System.out.println(map.get(2)); // World
- Nếu không có Auto-boxing và Auto-unboxing, thì ta phải làm như sau:
HashMap<Integer, String> map = newHashMap<>();
map.put(Integer.valueOf(1), "Hello");
map.put(Integer.valueOf(2), "World");
System.out.println(map.get(Integer.valueOf(1))); // Hello
System.out.println(map.get(Integer.valueOf(2))); // World
- Tuy nhiên, trong nhiều trường hợp nên hạn chế dùng các kiểu Object do chúng tốn bộ nhớ
for (Integeri=0; i < 1000000; i++) {
// ...
}
// Tốn bộ nhớ hơn// Vì i là object, nên phải tạo ra 1 object mới mỗi lần tăng i// Còn nếu dùng int, thì chỉ cần tạo ra 1 biến int i, và tăng i mỗi lần
3. Các phương thức khởi tạo (Constructor) trong Java
- Constructor là một phương thức đặc biệt, nó được gọi khi ta khởi tạo một đối tượng. Constructor có tên giống với tên class, và không có kiểu trả về.
- Constructor được sử dụng để khởi tạo các giá trị ban đầu cho đối tượng. Ví dụ như ta có một class
Student
, trong đó có các thuộc tínhname
,age
,address
,math
,literature
,english
. Ta có thể tạo một constructor như sau:
classStudent {
String name;
int age;
String address;
float math;
float literature;
float english;
Student(String name, int age, String address, float math, float literature, float english) {
this.name = name;
this.age = age;
this.address = address;
this.math = math;
this.literature = literature;
this.english = english;
}
}
- Constructor có thể có hoặc không có tham số. Nếu không có tham số, thì constructor đó được gọi là constructor mặc định. Nếu có tham số, thì constructor đó được gọi là constructor có tham số.
- Constructor có thể có nhiều tham số, và các tham số đó có thể là bất kỳ kiểu dữ liệu nào.
- Constructor có thể có nhiều hơn một, và các constructor đó có thể có số lượng tham số khác nhau.
- Constructor có thể gọi constructor khác của cùng một class bằng từ khóa
this()
. Ví dụ:
classStudent {
String name;
int age;
String address;
float math;
float literature;
float english;
Student(String name, int age, String address, float math, float literature, float english) {
this.name = name;
this.age = age;
this.address = address;
this.math = math;
this.literature = literature;
this.english = english;
}
Student(String name, int age, String address) {
this(name, age, address, 0, 0, 0);
}
}
4. Các khái niệm cơ bản về Garbage Collector (GC)
- Trong quá trình thực thi chương trình, Java sẽ liên tục tìm các Object không được tham chiếu tới Heap Memory (mang giá trị
null
hoặc một vài trường hợp khác). Sau đó Garbage Collector sẽ tự động giải phóng bộ nhớ cho các ô nhớ chứa tham chiếu đó. - Điều này kiến lập trình viên không cần phải giải phóng bộ nhớ thủ công như C/C++. Thay vào đó, để giải phóng bộ nhớ, lập trình viên chỉ cần loại bỏ tham chiếu của Object.
5. Cách Java truyền tham số | Pass-by-value
Cách Java truyền tham số
5.1. Pass-by-value
- Trong Java, khi ta truyền tham số vào một hàm, thì tham số đó sẽ được copy ra một vùng nhớ khác, và hàm sẽ thao tác với tham số ở vùng nhớ mới này.
- Vậy nên, khi ta thay đổi giá trị của tham số trong hàm, thì giá trị của tham số bên ngoài hàm không bị thay đổi.
publicclassMain {
publicstaticvoidmain(String[] args) {
inta=1;
System.out.println(a); // 1
change(a);
System.out.println(a); // 1
}
publicstaticvoidchange(int a) {
a = 2;
}
}
- Tuy nhiên, nó sẽ lại thay đổi được các thuộc tính của object.
publicclassMain {
publicstaticvoidmain(String[] args) {
Studentstudent=newStudent();
student.name = "A";
System.out.println(student.name); // A
change(student);
System.out.println(student.name); // B
}
publicstaticvoidchange(Student student) {
student.name = "B";
}
}
classStudent {
String name;
}
5.2. Tại sao pass-by-value mà String lại thay đổi được?
- Ta hãy quay trở lại khái niệm vừa nhắc tới Khi ta truyền một biến nguyên thuỷ, hay trong trường hợp trên là age, ta đang như tạo một bản sao của một tờ giấy bình thường, tờ giấy đó ghi số là 20, ta đưa cho người khác. Họ sửa chúng thành 90, 100 hay gì đi chăng nữa, thì tờ giấy ban đầu của ta vẫn là 20
- Tuy nhiên, khi ta truyền một biến reference (tham chiếu) của Object person, ta đang truyền bản sao của “tham chiếu” của nó. Hay đơn giản là, ta đang tạo ra bản sao của một cái điều khiển, vậy thì cái điều khiển bản sao này khi đưa cho người khác, vẫn sẽ bật tắt được tivi ban đầu của mình.