Chào mừng các bạn trở lại! Hôm nay chúng ta sẽ học hai khái niệm quan trọng giúp code Java của bạn trở nên chuyên nghiệp và an toàn hơn: Constructor và Tính đóng gói (Encapsulation).
I. PHẦN LÝ THUYẾT
1. Vấn đề với cách tạo đối tượng cũ
Ở bài trước, chúng ta tạo đối tượng như thế này:
SinhVien sv = new SinhVien();
sv.ten = "Nguyễn Văn A"; // Phải gán từng thuộc tính
sv.tuoi = 20;
sv.maSV = "SV001";Vấn đề:
- Dễ quên gán giá trị cho thuộc tính
- Không kiểm soát được giá trị hợp lệ
- Code dài dòng, dễ sai sót
2. Constructor – “Nhà xây dựng” thông minh
Constructor là phương thức đặc biệt:
- Tên trùng với tên lớp
- Không có kiểu trả về (không có
voidhayreturn type) - Được gọi tự động khi tạo đối tượng với
new - Có thể có nhiều constructor (overloading)
public class SinhVien {
String ten;
int tuoi;
String maSV;
// Constructor mặc định (không tham số)
public SinhVien() {
System.out.println("Đã tạo một sinh viên mới!");
}
// Constructor có tham số
public SinhVien(String ten, int tuoi, String maSV) {
this.ten = ten;
this.tuoi = tuoi;
this.maSV = maSV;
}
}3. Từ khóa this – Đại diện cho đối tượng hiện tại
this tham chiếu đến đối tượng hiện tại đang được xử lý. Nó giúp:
- Phân biệt thuộc tính và tham số cùng tên
- Truy cập thuộc tính của đối tượng hiện tại
- Gọi constructor khác trong cùng lớp
4. Tính đóng gói (Encapsulation) – Nguyên lý bảo mật
Tính đóng gói là nguyên lý:
- Ẩn dữ liệu (thuộc tính) bằng
private - Cung cấp phương thức công khai (
public) để truy cập an toàn - Kiểm soát và validate dữ liệu trước khi gán
Tại sao cần đóng gói?
- Bảo vệ dữ liệu khỏi truy cập trái phép
- Kiểm soát tính hợp lệ của dữ liệu
- Dễ dàng thay đổi cài đặt bên trong mà không ảnh hưởng code bên ngoài
5. Access Modifiers – Các mức độ truy cập
| Modifier | Trong lớp | Trong package | Lớp con | Mọi nơi |
|---|---|---|---|---|
private | ✓ | ✗ | ✗ | ✗ |
default | ✓ | ✓ | ✗ | ✗ |
protected | ✓ | ✓ | ✓ | ✗ |
public | ✓ | ✓ | ✓ | ✓ |
II. PHẦN THỰC HÀNH
Ví dụ 1:
- Định nghĩa Lớp SinhVien với Constructor và Encapsulation (SinhVien.java)
- Sử dụng lớp (MainSinhVien.java)
// file SinhVien.java
public class SinhVien {
// Thuộc tính PRIVATE - chỉ truy cập trong lớp
private String ten;
private int tuoi;
private String maSV;
private double diemTB;
// Constructor mặc định
public SinhVien() {
this.ten = "Chưa có tên";
this.tuoi = 18;
this.maSV = "SV000";
this.diemTB = 0.0;
}
// Constructor có tham số
public SinhVien(String ten, int tuoi, String maSV) {
this.ten = ten;
this.setTuoi(tuoi); // Sử dụng setter để validate
this.maSV = maSV;
this.diemTB = 0.0;
}
// GETTER - Lấy giá trị thuộc tính
public String getTen() {
return ten;
}
public int getTuoi() {
return tuoi;
}
public String getMaSV() {
return maSV;
}
public double getDiemTB() {
return diemTB;
}
// SETTER - Gán giá trị với kiểm tra
public void setTen(String ten) {
if (ten != null && !ten.trim().isEmpty()) {
this.ten = ten;
} else {
System.out.println("Tên không hợp lệ!");
}
}
public void setTuoi(int tuoi) {
if (tuoi >= 18 && tuoi <= 50) {
this.tuoi = tuoi;
} else {
System.out.println("Tuổi phải từ 18 đến 50!");
this.tuoi = 18; // Giá trị mặc định
}
}
public void setMaSV(String maSV) {
if (maSV != null && maSV.startsWith("SV")) {
this.maSV = maSV;
} else {
System.out.println("Mã SV phải bắt đầu bằng 'SV'!");
}
}
public void setDiemTB(double diemTB) {
if (diemTB >= 0.0 && diemTB <= 10.0) {
this.diemTB = diemTB;
} else {
System.out.println("Điểm TB phải từ 0.0 đến 10.0!");
}
}
// Phương thức công khai
public void hienThiThongTin() {
System.out.println("=== THÔNG TIN SINH VIÊN ===");
System.out.println("Tên: " + this.ten);
System.out.println("Tuổi: " + this.tuoi);
System.out.println("Mã SV: " + this.maSV);
System.out.println("Điểm TB: " + this.diemTB);
System.out.println("Xếp loại: " + this.xepLoai());
}
// Phương thức private - chỉ dùng nội bộ
private String xepLoai() {
if (diemTB >= 8.0) return "Giỏi";
else if (diemTB >= 6.5) return "Khá";
else if (diemTB >= 5.0) return "Trung bình";
else return "Yếu";
}
}// Chương trình chính: File MainSinhVien.java
public class MainSinhVien {
public static void main(String[] args) {
// Tạo sinh viên với constructor mặc định
SinhVien sv1 = new SinhVien();
sv1.setTen("Nguyễn Văn A");
sv1.setTuoi(20);
sv1.setMaSV("SV001");
sv1.setDiemTB(8.5);
sv1.hienThiThongTin();
System.out.println("\n---\n");
// Tạo sinh viên với constructor có tham số
SinhVien sv2 = new SinhVien("Trần Thị B", 21, "SV002");
sv2.setDiemTB(7.2);
sv2.hienThiThongTin();
System.out.println("\n---\n");
// Test validation
SinhVien sv3 = new SinhVien();
sv3.setTuoi(15); // Tuổi không hợp lệ
sv3.setDiemTB(12.5); // Điểm không hợp lệ
sv3.setMaSV("ABC123"); // Mã SV không hợp lệ
// Truy cập thông qua getter
System.out.println("\nTên sv1: " + sv1.getTen());
System.out.println("Điểm TB sv2: " + sv2.getDiemTB());
}
}Ví dụ 2: Lớp TaiKhoanNganHang với Constructor Overloading
//TaiKhoanNganHang.java
public class TaiKhoanNganHang {
private String soTaiKhoan;
private String tenChuTaiKhoan;
private double soDu;
// Constructor 1: Chỉ có số tài khoản
public TaiKhoanNganHang(String soTaiKhoan) {
this.soTaiKhoan = soTaiKhoan;
this.tenChuTaiKhoan = "Chưa xác định";
this.soDu = 0.0;
}
// Constructor 2: Có số tài khoản và tên chủ
public TaiKhoanNganHang(String soTaiKhoan, String tenChuTaiKhoan) {
this.soTaiKhoan = soTaiKhoan;
this.tenChuTaiKhoan = tenChuTaiKhoan;
this.soDu = 0.0;
}
// Constructor 3: Đầy đủ thông tin
public TaiKhoanNganHang(String soTaiKhoan, String tenChuTaiKhoan, double soDu) {
this.soTaiKhoan = soTaiKhoan;
this.tenChuTaiKhoan = tenChuTaiKhoan;
this.soDu = (soDu >= 0) ? soDu : 0.0;
}
// Getter/Setter
public String getSoTaiKhoan() { return soTaiKhoan; }
public String getTenChuTaiKhoan() { return tenChuTaiKhoan; }
public double getSoDu() { return soDu; }
public void setTenChuTaiKhoan(String ten) {
if (ten != null && ten.length() > 0) {
this.tenChuTaiKhoan = ten;
}
}
// Phương thức gửi tiền
public boolean guiTien(double soTien) {
if (soTien > 0) {
this.soDu += soTien;
System.out.println("Gửi tiền thành công! Số dư mới: " + this.soDu);
return true;
}
System.out.println("Số tiền gửi phải > 0!");
return false;
}
// Phương thức rút tiền
public boolean rutTien(double soTien) {
if (soTien > 0 && soTien <= this.soDu) {
this.soDu -= soTien;
System.out.println("Rút tiền thành công! Số dư mới: " + this.soDu);
return true;
}
System.out.println("Số tiền rút không hợp lệ!");
return false;
}
}III. CÂU HỎI VÀ BÀI TẬP
Phần A: Câu hỏi lý thuyết (5 phút)
- Câu 1: Constructor khác phương thức thường ở những điểm nào?
- Câu 2: Tại sao cần sử dụng từ khóa
thistrong constructor? - Câu 3: Tính đóng gói mang lại lợi ích gì cho việc phát triển phần mềm?
- Câu 4: Sự khác biệt giữa
public,private,protectedvàdefault? - Câu 5: Tại sao nên đặt thuộc tính là
privatevà cung cấp getter/setter?
Phần B: Bài tập thực hành (25 phút)
Bài 1: Lớp Sản phẩm với Encapsulation
Tạo lớp SanPham với:
- Thuộc tính private:
maSP,tenSP,gia,soLuong - 2 constructor: mặc định và có tham số
- Getter/Setter với validation:
- Giá phải > 0
- Số lượng phải ≥ 0
- Mã SP không được rỗng
- Phương thức
tinhThanhTien(): tính giá * số lượng - Phương thức
hienThiThongTin()
Bài 2: Lớp Nhân viên
Tạo lớp NhanVien với:
- Thuộc tính private:
maNV,hoTen,luongCoBan,heSoLuong - Constructor nhận đầy đủ thông tin
- Getter/Setter
- Phương thức
tinhLuong(): luongCoBan * heSoLuong - Phương thức
xepLoai():- Nếu lương ≥ 20,000,000: “Xuất sắc”
- Nếu lương ≥ 15,000,000: “Tốt”
- Còn lại: “Khá”
Bài 3: Lớp Điểm trong mặt phẳng Oxy
Tạo lớp Diem với:
- Thuộc tính private:
x,y(tọa độ) - Constructor mặc định tạo điểm (0,0)
- Constructor có tham số
- Phương thức:
tinhKhoangCach(Diem d): tính khoảng cách giữa 2 điểmkiemTraTrungNhau(Diem d): kiểm tra 2 điểm có trùng nhau khôngdoiXungQuaGoc(): trả về điểm đối xứng qua gốc tọa độ
Code mẫu gợi ý:
public class Diem {
private double x;
private double y;
public Diem(double x, double y) {
this.x = x;
this.y = y;
}
public double tinhKhoangCach(Diem d) {
return Math.sqrt(Math.pow(this.x - d.x, 2) +
Math.pow(this.y - d.y, 2));
}
}Phần C: Thử thách nâng cao (15 phút)
Bài 4: Hệ thống quản lý thư viện (phần 1)
Tạo lớp Sach cho hệ thống thư viện:
- Thuộc tính:
maSach,tenSach,tacGia,namXuatBan,trangThai(đang mượn/ có sẵn) - Constructor với validation:
- Năm xuất bản phải > 1900 và ≤ năm hiện tại
- Trạng thái mặc định là “có sẵn”
- Phương thức:
muonSach(): chỉ cho mượn nếu sách có sẵntraSach(): trả sách về trạng thái có sẵnkiemTraSachCu(): trả về true nếu sách xuất bản trước 2000
Bài 5: Quản lý tài khoản email
Tạo lớp EmailAccount với:
- Thuộc tính:
email,password,storageLimit(GB),usedStorage(GB) - Validation:
- Email phải có định dạng hợp lệ (chứa @ và .)
- Password ít nhất 8 ký tự
- StorageLimit > 0
- Phương thức:
changePassword(String oldPass, String newPass): đổi mật khẩuuploadFile(double sizeGB): tải file lên nếu còn đủ dung lượngcalculateStoragePercentage(): tính % dung lượng đã dùng
IV. TÓM TẮT BÀI HỌC
- Constructor khởi tạo đối tượng một cách thông minh
- Tính đóng gói bảo vệ dữ liệu với
private+ getter/setter thistham chiếu đến đối tượng hiện tại- Access modifiers kiểm soát mức độ truy cập
- Validation trong setter đảm bảo tính hợp lệ của dữ liệu
