📬 BÀI 5B: QUẢN LÝ THÙNG THƯ & BƯU PHẨM (LAB 05 - PHẦN 2.2 THỰC HÀNH Ở NHÀ)
Dựa theo bài tập ở nhà Lab 05, ta sẽ xây dựng cấu trúc các lớp như sau:
BuuPham (Lớp cha trừu tượng): Định nghĩa phương thức tinhTien().
ThuThuong (Giá 800đ), ThuBaoDam (Giá 2000đ), BuuKien (Giá 5000đ) kế thừa từ BuuPham.
ThungThu: Chứa mảng 100 bưu phẩm đa hình.
Cài đặt nhanh bài Quản lý Thùng thư:
// 1. Lớp cha BuuPham
abstract class BuuPham {
protected String maSo;
public BuuPham(String maSo) { this.maSo = maSo; }
public abstract double tinhTien();
}
// 2. Thư thường (800 đ)
class ThuThuong extends BuuPham {
public ThuThuong(String maSo) { super(maSo); }
@Override
public double tinhTien() { return 800; }
@Override
public String toString() { return "Thư Thường [" + maSo + "] - 800 đ"; }
}
// 3. Thư bảo đảm (2000 đ)
class ThuBaoDam extends BuuPham {
public ThuBaoDam(String maSo) { super(maSo); }
@Override
public double tinhTien() { return 2000; }
@Override
public String toString() { return "Thư Bảo Đảm [" + maSo + "] - 2000 đ"; }
}
// 4. Bưu kiện (5000 đ)
class BuuKien extends BuuPham {
public BuuKien(String maSo) { super(maSo); }
@Override
public double tinhTien() { return 5000; }
@Override
public String toString() { return "Bưu Kiện [" + maSo + "] - 5000 đ"; }
}
// 5. Thùng Thư chứa được 100 bưu phẩm
class ThungThu {
private BuuPham[] ds;
private int soLuong;
public ThungThu() {
ds = new BuuPham[100]; // Tối đa 100 bưu phẩm
soLuong = 0;
}
public void boVaoThung(BuuPham bp) {
if (soLuong < ds.length) {
ds[soLuong++] = bp;
}
}
public void lietKeToanBo() {
System.out.println("--- TOÀN BỘ BƯU PHẨM TRONG THÙNG ---");
for (int i = 0; i < soLuong; i++) {
System.out.println(ds[i]);
}
}
public void lietKeThuBaoDam() {
System.out.println("\n--- DANH SÁCH THƯ BẢO ĐẢM ---");
for (int i = 0; i < soLuong; i++) {
// Kiểm tra xem bưu phẩm có thuộc lớp ThuBaoDam hay không bằng từ khóa instanceof
if (ds[i] instanceof ThuBaoDam) {
System.out.println(ds[i]);
}
}
}
public double tongTien() {
double tong = 0;
for (int i = 0; i < soLuong; i++) {
tong += ds[i].tinhTien();
}
return tong;
}
}
⚠️ Lỗi sai phổ biến người mới bắt đầu cần tránh!
Bổ sung: hiểu sâu từng dòng (Bài 5B)
Từ mới trong bài này (giải từ số 0):
- instanceof: toán tử hỏi "object này có phải loại X không?".
ds[i] instanceof ThuBaoDamtrả về true chỉ với thư bảo đảm, dùng để lọc đúng loại trong mảng lẫn lộn. - Mảng đa hình: mảng kiểu cha (
BuuPham[]) đựng được mọi object con. Một thùng mà chứa cả thư thường, thư bảo đảm, bưu kiện — mỗi thứ tính tiền theo cách riêng. - Tổng tiền đa hình: vòng lặp gọi
ds[i].tinhTien()— cùng một dòng code mà mỗi object tự trả giá của mình (800/2000/5000). Thêm loại mới không sửa vòng lặp.
Mổ xẻ lọc thư bảo đảm:
if (ds[i] instanceof ThuBaoDam) { // hỏi trước khi in
System.out.println(ds[i]); // in gọi toString() riêng của từng loại
}
Không có instanceof thì hoặc in nhầm cả thùng, hoặc phải thêm cờ loại cho từng bưu phẩm — rối và dễ quên cập nhật.
Trace tay: thùng có ThuThuong + ThuBaoDam + BuuKien → tổng = 800 + 2000 + 5000 = 7800. Liệt kê bảo đảm chỉ in đúng 1 dòng thư bảo đảm.
Tự kiểm tra:
- Vì sao
tinhTien()ở lớp cha phải là abstract mà không cho sẵn giá 0?
Đáp án
Để ép mọi lớp con phải tự định nghĩa giá — quên là biên dịch lỗi ngay, không bao giờ lọt bưu phẩm giá 0 vào tổng tiền.- Bỏ đầy quá 100 bưu phẩm thì sao?
Đáp án
boVaoThung có điều kiện soLuong < ds.length nên cái thứ 101 bị lặng lẽ bỏ qua, không crash mảng.- Muốn thêm "Thư hỏa tốc giá 10000" cần sửa mấy chỗ?
Đáp án
Một chỗ duy nhất: thêm lớp con extends BuuPham vớitinhTien() trả 10000. Thùng thư, liệt kê, tính tổng giữ nguyên.Cố tình dùng từ khóa new khởi tạo Lớp Trừu Tượng:
Mắc phải: Viết DiemHP hp = new DiemHP("Toan", 3);.
Sửa lại: Lớp abstract không thể tạo đối tượng trực tiếp. Bạn phải tạo đối tượng thông qua các lớp con của nó (ví dụ: new HPLyThuyet(...)).
Quên từ khóa @Override:
Khi viết lại hàm của lớp cha ở lớp con, hãy luôn thêm annotation @Override. Việc này giúp trình biên dịch nhắc lỗi nếu bạn gõ sai tên phương thức hoặc tham số truyền vào.
📝 BÀI TẬP TỰ RÈN LỰC (Mở rộng bài 5A)
Đề bài: Bổ sung lớp HPDuan (Học phần làm đồ án) kế thừa lớp DiemHP. Điểm học phần đồ án là trung bình cộng điểm của 5 thành viên hội đồng bảo vệ.
public class HPDuan extends DiemHP {
private double[] diemHoiDong; // Mảng 5 điểm của 5 thành viên hội đồng
public HPDuan(String tenHP, int soTinChi, double[] diemHoiDong) {
super(tenHP, soTinChi);
this.diemHoiDong = diemHoiDong;
}
@Override
public double tinhDiem() {
double tong = 0;
for (double d : diemHoiDong) {
tong += d;
}
return tong / diemHoiDong.length;
}
}
🎉 CHÚC MỪNG BẠN! Bạn đã hoàn thành trọn vẹn chương trình giảng giải 4 bài thực hành OOP Java từ Lab 02 đến Lab 05.
Nếu có bất kỳ dòng code hay thuật toán nào bạn muốn thầy giải thích lại chi tiết hơn, hãy cứ thoải mái đặt câu hỏi nhé!
Phụ lục: Prompt giảng giải dùng lại với AI
Muốn AI khác giảng lại theo đúng phong cách này, copy đoạn prompt dưới đây dán vào ô chat:
Bạn là một Giảng viên Lập trình Hướng đối tượng (OOP) Java cực kỳ tận tâm, kiên nhẫn và giàu kinh nghiệm sư phạm. Mục tiêu của bạn là giúp một người mới bắt đầu (Chưa từng tiếp xúc hoặc mới học cơ bản về OOP) hiểu sâu sắc bản chất tư duy OOP, hiểu rõ sơ đồ UML và viết được code hoàn chỉnh cho toàn bộ các Bài Thực Hành (Lab 02, Lab 03, Lab 04, Lab 05).
Hãy tuân thủ các nguyên tắc giảng dạy sau:
1. NGUYÊN TẮC GIẢNG DẠY (ƯU TIÊN NGƯỜI MỚI BẮT ĐẦU):
- "Trực quan hóa tư duy": Dùng các ví dụ đời thực gần gũi (ví dụ: Phân số như chiếc bánh, ATM như cây rút tiền thật, Thùng thư, Sinh viên, Học phần...) trước khi đi vào thuật ngữ lập trình.
- "Đọc hiểu Sơ đồ UML": Giải thích ý nghĩa của từng ký hiệu (+, -, #, tên thuộc tính, phương thức khởi tạo Constructor, Getter/Setter, Override...) trước khi bảo người học gõ code.
- "Phân tích từng dòng code": Viết mã nguồn Java có chú thích (comments) cực kỳ chi tiết ở từng dòng quan trọng. Đừng viết code tắt hoặc viết quá phức tạp.
- "Chỉ ra lỗi sai phổ biến": Cảnh báo các lỗi cú pháp và lỗi logic người mới hay gặp (như chia cho 0 trong Phân số, NullPointerException, ép kiểu, lỗi trùng tên biến).
2. CẤU TRÚC BÀI HỌC THEO LỘ TRÌNH (Từ dễ đến khó):
PART 1: KHAI BÁO CLASS VÀ OBJECT CƠ BẢN (LAB 02 & LAB 03)
- Lớp Phân Số (PS): Hướng dẫn cách tạo các Constructor (không tham số, có tham số, sao chép), các phép toán +, -, *, / phân số, rút gọn phân số (thuật toán UCLN), và tạo mảng 5 phân số để sắp xếp.
- Lớp Cây ATM: Khai báo tài khoản, thao tác nạp tiền, rút tiền, chuyển tiền giữa các đối tượng ATM.
- Lớp Điểm (Diem) & Hình Chữ Nhật (HCN): Cách tạo lớp Diem làm thuộc tính cho lớp HCN (Tính khoảng cách, chu vi, diện tích).
3. PART 2: THAO TÁC NÂNG CAO VÀ XỬ LÝ CHUỖI/NGÀY THÁNG (LAB 04)
- Lớp SinhVien (SV) & Dạng Danh Sách Sinh Viên (DSSV):
+ Hướng dẫn xử lý chuỗi Việt Nam: Cách cắt Họ, Tên, Tên Đệm từ chuỗi Họ Tên full.
+ Hướng dẫn so sánh tên kiểu Việt Nam để sắp xếp danh sách (Tên -> Họ -> Đệm).
+ Thao tác Quản lý: Thêm, xóa SV theo tên, tính tuổi (năm hiện tại - năm sinh), chuẩn hóa chuỗi họ tên.
4. PART 3: THỪA KẾ (INHERITANCE) & ĐA HÌNH / LIÊN KẾT MUỘN (LAB 05)
- Giải thích thế nào là Trừu tượng (Abstract class), Kế thừa (Inheritance) và Đa hình (Polymorphism).
- Lớp DiemHP (Lớp trừu tượng cơ sở): Định nghĩa `tinhDiem()` trừu tượng.
- Lớp HPLyThuyet và HPThucHanh (Lớp con kế thừa DiemHP): Cài đặt công thức tính điểm riêng cho từng loại học phần.
- Lớp SinhVien có 2 học phần: Cách SinhVien quản lý học phần và tính DTB theo số tín chỉ.
- Mở rộng Đồ án & Thùng thư / Bưu phẩm: Hướng dẫn tư duy chia lớp BưuPhẩm -> ThưThường, ThưBảoĐảm, BưuKiện và thùng thư chứa mảng đa hình.
5. ĐẦU RÚT VÀ BÀI TẬP TỰ RÈN LỰC:
- Sau mỗi bài giải mẫu, hãy cung cấp cho tôi 1 bài tập nhỏ tự luyện kèm gợi ý đáp án để tôi tự kiểm tra mức độ hiểu bài.
Bắt đầu bằng lời chào mừng ấm áp và đi ngay vào BÀI 1: Lớp Phân Số (PS) từ cơ bản nhất!
Cách dùng prompt
- Copy toàn bộ đoạn mã trong khung text ở trên.
- Dán vào ô chat với AI.
- Khi AI giảng từng bài, tương tác thêm theo từng bước.
- Nếu chỗ nào chưa hiểu: gõ "Giải thích lại đoạn code này giúp mình" hoặc "Tại sao lại dùng this ở đây?".
- Sau khi đọc xong, gõ "Cho mình xem phần tiếp theo: Lớp ATM".