Lab 04 — SV, DSSV, java.util.Date
LAB 04
— GIẢI CHI TIẾT (LỚP SV, DANH SÁCH DSSV,
java.util.Date)
Nguồn:
lab04.doc(Bài thực hành số 04). Bám lý thuyết buổi 2, 4, 10. Đây là lab "tổng hợp": lớp có ngày sinh, chuỗi, mảng, sắp xếp, tìm kiếm, xóa.
0. Bản đồ bài làm
| Phần | Lớp | Tệp | Kỹ thuật mới |
|---|---|---|---|
| Chính | SV |
SV.java |
java.util.Date, tách họ/tên, tuổi, xếp loại |
| Chính | DSSV |
DSSV.java |
mảng đối tượng, sắp xếp 3 tiêu chí, xóa dồn mảng |
| Chạy | — | QuanLyDSSV.java |
5 sinh viên, 6 thao tác theo đề |
| Ở nhà | SV mở rộng |
(thêm vào SV.java) |
lonHon, gioiHon, giongNhau,
chuanHoTen |
| Ở nhà | DSSV mở rộng |
(thêm vào DSSV.java) |
chuanHoTen, trungSV |
PHẦN 1 — LỚP SV
0. Đề bài
SV gồm: hoTen, ngaySinh
(Date), dtb. Phương thức:
SV(String, Date, double), layHoTen,
ganHoTen, layNgaySinh,
ganNgaySinh, layDTB, ganDTB,
hienThi, layHo, layTen,
layDem, layTuoi, layXepLoai.
1. Yêu cầu ẩn
hoTencó thể có 1, 2, 3 hoặc 4 từ →layHo/layDem/layTenphải chạy đúng cho mọi trường hợp.hoTencó khoảng trắng thừa (" Nguyen Van An ") → phảitrimvà tách theo một hoặc nhiều dấu cách.dtbngoài 0..10 phải bị từ chối.getYear()trả năm − 1900,getMonth()trả 0..11 — hai bẫy kinh điển củaDate.- Xếp loại theo mốc:
≥8Giỏi,[7,8)Khá,[5,7)Trung bình,<5Yếu.
2. Input → Output
| Phương thức | Vào | Ra | Ví dụ |
|---|---|---|---|
layHo() |
— | String |
"Nguyen Van An" → "Nguyen" |
layTen() |
— | String |
→ "An" |
layDem() |
— | String |
→ "Van" |
layTuoi() |
— | int |
sinh 2005 → 21 |
layXepLoai() |
— | String |
7.2 → "Kha" |
3. Thiết kế
SV
- hoTen : String
- ngaySinh : Date
- dtb : double
+ SV(String, Date, double)
+ layHoTen/ganHoTen layNgaySinh/ganNgaySinh layDTB/ganDTB
+ hienThi()
+ layHo() : String + layDem() : String + layTen() : String
+ layTuoi() : int + layXepLoai() : String
Quyết định thiết kế:
- Không lưu
ho,dem,tenthành 3 thuộc tính — nếu lưu, khiganHoTenphải cập nhật 3 chỗ, dễ lệch dữ liệu. Thay vào đó tính khi cần (derived data). dtbđểprivate, setter kiểm tra 0..10.layTuoi()dùng năm cố định 2026 (theo năm hiện tại) — ghi rõ giả định.
4. Code từng bước
Bước 1 — thuộc tính, constructor, getter/setter.
public class SV {
private String hoTen;
private Date ngaySinh;
private double dtb;
public SV(String hoTen, Date ngaySinh, double dtb) {
ganHoTen(hoTen);
this.ngaySinh = ngaySinh;
ganDTB(dtb);
}
public void ganDTB(double d) { if (d >= 0 && d <= 10) dtb = d; }Bước 2 — tách họ/tên bằng split.
private String[] tu() {
return hoTen.trim().split("\\s+"); // "\s+" = 1 hoac nhieu dau cach
}
public String layHo() { return tu()[0]; }
public String layTen() { String[] p = tu(); return p[p.length - 1]; }
public String layDem() {
String[] p = tu();
String dem = "";
for (int i = 1; i < p.length - 1; i++) {
dem += p[i];
if (i < p.length - 2) dem += " ";
}
return dem; // 1 tu -> ""
}Bước 3 — tuổi (getYear() + 1900).
public int layTuoi() { return 2026 - (ngaySinh.getYear() + 1900); }Bước 4 — xếp loại + hiển thị.
public String layXepLoai() {
if (dtb >= 8) return "Gioi";
if (dtb >= 7) return "Kha";
if (dtb >= 5) return "Trung binh";
return "Yeu";
}
public void hienThi() {
System.out.println(hoTen + " | " + ngaySinhS() + " | DTB " + dtb
+ " | " + layXepLoai() + " | " + layTuoi() + " tuoi");
}ngaySinhS() là phương thức phụ in ngày dạng
dd/M/yyyy (vì Date.toString() rất xấu):
public String ngaySinhS() {
return ngaySinh.getDate() + "/" + (ngaySinh.getMonth() + 1) + "/"
+ (ngaySinh.getYear() + 1900);
}5. Lời giải hoàn chỉnh
// ===== SV.java =====
import java.util.Date;
public class SV {
private String hoTen;
private Date ngaySinh;
private double dtb;
public SV(String hoTen, Date ngaySinh, double dtb) {
ganHoTen(hoTen);
this.ngaySinh = ngaySinh;
ganDTB(dtb);
}
public String layHoTen() { return hoTen; }
public void ganHoTen(String hoTen) {
this.hoTen = (hoTen == null) ? "" : hoTen.trim();
}
public Date layNgaySinh() { return ngaySinh; }
public void ganNgaySinh(Date ngaySinh) { this.ngaySinh = ngaySinh; }
public double layDTB() { return dtb; }
public void ganDTB(double d) { if (d >= 0 && d <= 10) dtb = d; }
private String[] tu() {
String s = (hoTen == null) ? "" : hoTen.trim();
if (s.length() == 0) return new String[]{""};
return s.split("\\s+");
}
public String layHo() { return tu()[0]; }
public String layTen() {
String[] p = tu();
return p[p.length - 1];
}
public String layDem() {
String[] p = tu();
String dem = "";
for (int i = 1; i < p.length - 1; i++) {
dem += p[i];
if (i < p.length - 2) dem += " ";
}
return dem;
}
public int layTuoi() {
return 2026 - (ngaySinh.getYear() + 1900);
}
public String layXepLoai() {
if (dtb >= 8) return "Gioi";
if (dtb >= 7) return "Kha";
if (dtb >= 5) return "Trung binh";
return "Yeu";
}
public String ngaySinhS() {
return ngaySinh.getDate() + "/" + (ngaySinh.getMonth() + 1) + "/"
+ (ngaySinh.getYear() + 1900);
}
// ---- cac phuong thuc cua phan "o nha" ----
public boolean lonHon(SV sv) {
return this.layTuoi() > sv.layTuoi();
}
public boolean gioiHon(SV sv) {
return this.layDTB() > sv.layDTB();
}
public boolean giongNhau(SV sv) {
if (sv == null) return false;
return this.hoTen.equalsIgnoreCase(sv.hoTen)
&& this.ngaySinh.equals(sv.ngaySinh);
}
public void chuanHoTen() {
String[] p = tu();
String kq = "";
for (int i = 0; i < p.length; i++) {
String tu = p[i].toLowerCase();
if (tu.length() > 0) {
tu = tu.substring(0, 1).toUpperCase() + tu.substring(1);
}
kq += tu;
if (i < p.length - 1) kq += " ";
}
hoTen = kq;
}
public void hienThi() {
System.out.println(hoTen + " | " + ngaySinhS() + " | DTB " + dtb
+ " | " + layXepLoai() + " | " + layTuoi() + " tuoi");
}
@Override
public String toString() { return hoTen; }
}6. Cách chạy
javac SV.java
(chưa chạy được một mình — cần DSSV ở phần 2)
7. Kết quả chạy mẫu
Nguyen Van An | 15/3/2005 | DTB 8.5 | Gioi | 21 tuoi
8. Bảng kiểm thử
| # | hoTen |
layHo |
layDem |
layTen |
|---|---|---|---|---|
| 1 | "Nguyen Van An" |
Nguyen | Van | An |
| 2 | "An" |
An | "" |
An |
| 3 | "Nguyen Thi Hoang Yen" |
Nguyen | Thi Hoang | Yen |
| 4 | " Tran Van Binh " |
Tran | Van | Binh |
9. Trace
hoTen = " Nguyen Van An ": trim() →
"Nguyen Van An"; split("\\s+") →
["Nguyen","Van","An"];
layHo = p[0] = "Nguyen"; layTen = p[2] = "An";
layDem: i chạy 1..1 → "Van".
10. Lỗi thường gặp
| Code sai | Thông báo / hiện tượng | Sửa |
|---|---|---|
hoTen.split(" ") với nhiều dấu cách |
Phần tử rỗng trong mảng | split("\\s+") |
hoTen.split(" ")[hoTen.length()] |
ArrayIndexOutOfBoundsException |
Dùng p.length - 1 |
2026 - ngaySinh.getYear() |
Tuổi sai 1900 | + 1900 |
ngaySinh.getMonth() in ra tháng 2 cho tháng 3 |
Lệch 1 | + 1 |
dtb public |
Gán được 15 | private + setter |
if (dtb >= 8) ... else if (dtb >= 7) ngược thứ
tự |
Phân loại sai | Từ cao xuống thấp |
11. Câu hỏi kiểm tra hiểu
- Vì sao không lưu
ho,dem,tenthành thuộc tính? layDem()trả gì với tên 1 từ? 2 từ?- Vì sao
2026nên là hằng số (static final int NAM_HIEN_TAI = 2026)?
12. Mở rộng
layTenDayDu()trả"Ho Dem Ten"đã chuẩn hoá.- Đổi
2026thành lấy năm hiện tại (new Date().getYear() + 1900).
PHẦN 2 — LỚP DSSV
0. Đề bài
DSSV: SV[] ds, int soSV.
DSSV(int), them(SV), laySV(int),
hienThi(), sapHoten(), sapTuoi(),
sapDTB(), timTen(String),
lietKeXepLoai(String), xoa(String).
1. Yêu cầu ẩn
themphải chặn khi mảng đầy, không được ghi tràn.laySV(i)ngoài phạm vi → trảnull, không ném ngoại lệ.timTenkhông thấy → trảnull(không in trong lớp này).xoaxóa sinh viên đầu tiên khớp tên, phải dồn mảng và giảmsoSV, đồng thời xóa tham chiếu ô cuối.- "Sắp xếp theo họ tên kiểu Việt": so sánh
ten + " " + ho + " " + dem, không so nguyên chuỗi họ tên. - Sắp theo tuổi/DTB: đề không nói chiều → chọn tuổi tăng dần, DTB giảm dần, ghi rõ giả định.
2. Input → Output
| Phương thức | Vào | Ra |
|---|---|---|
them(SV) |
SV |
boolean |
laySV(int) |
chỉ số | SV hoặc null |
timTen(String) |
tên | SV hoặc null |
xoa(String) |
tên | boolean |
3. Thiết kế
DSSV
- ds : SV[]
- soSV : int
+ DSSV(int)
+ them(SV) : boolean
+ laySV(int) : SV
+ hienThi() : void
+ sapHoten() / sapTuoi() / sapDTB() : void
+ timTen(String) : SV
+ lietKeXepLoai(String) : void
+ xoa(String) : boolean
Quyết định: soSV tách khỏi ds.length vì
mảng có thể còn chỗ trống — nếu dùng ds.length thì sẽ in cả
null.
4. Code từng bước
Bước 1 — thêm và truy xuất an toàn.
public boolean them(SV sv) {
if (sv == null || soSV >= ds.length) return false;
ds[soSV] = sv;
soSV++;
return true;
}
public SV laySV(int i) {
if (i < 0 || i >= soSV) return null;
return ds[i];
}Bước 2 — khoá sắp xếp kiểu Việt + bubble sort dùng lại.
private String khoaTen(SV sv) {
return sv.layTen() + " " + sv.layHo() + " " + sv.layDem();
}
public void sapHoten() {
for (int i = 0; i < soSV - 1; i++) {
for (int j = 0; j < soSV - 1 - i; j++) {
if (khoaTen(ds[j]).compareTo(khoaTen(ds[j + 1])) > 0) {
SV tmp = ds[j]; ds[j] = ds[j + 1]; ds[j + 1] = tmp;
}
}
}
}Bước 3 — sắp theo tuổi (tăng) và DTB (giảm).
public void sapTuoi() {
for (int i = 0; i < soSV - 1; i++)
for (int j = 0; j < soSV - 1 - i; j++)
if (ds[j].layTuoi() > ds[j + 1].layTuoi()) { /* doi cho */ }
}
public void sapDTB() {
for (int i = 0; i < soSV - 1; i++)
for (int j = 0; j < soSV - 1 - i; j++)
if (ds[j].layDTB() < ds[j + 1].layDTB()) { /* doi cho */ }
}Bước 4 — tìm, liệt kê, xóa.
public SV timTen(String ten) {
for (int i = 0; i < soSV; i++)
if (ds[i].layTen().equalsIgnoreCase(ten)) return ds[i];
return null;
}
public boolean xoa(String ten) {
for (int k = 0; k < soSV; k++) {
if (ds[k].layTen().equalsIgnoreCase(ten)) {
for (int i = k; i < soSV - 1; i++) ds[i] = ds[i + 1];
ds[soSV - 1] = null; // bo tham chieu cu
soSV--;
return true;
}
}
return false;
}Bước 5 — hai phương thức phục vụ đề bài chính (đặt
trong DSSV):
public double tuoiTrungBinh() {
if (soSV == 0) return 0;
int tong = 0;
for (int i = 0; i < soSV; i++) tong += ds[i].layTuoi();
return (double) tong / soSV;
}
public void tangDTBTruoc1980(double them) {
for (int i = 0; i < soSV; i++) {
int nam = ds[i].layNgaySinh().getYear() + 1900;
if (nam < 1980) ds[i].ganDTB(ds[i].layDTB() + them);
}
}5. Lời giải hoàn chỉnh
// ===== DSSV.java =====
public class DSSV {
private SV[] ds;
private int soSV;
public DSSV(int sucChua) {
ds = new SV[sucChua];
soSV = 0;
}
public int getSoSV() { return soSV; }
public boolean them(SV sv) {
if (sv == null || soSV >= ds.length) return false;
ds[soSV] = sv;
soSV++;
return true;
}
public SV laySV(int i) {
if (i < 0 || i >= soSV) return null;
return ds[i];
}
public void hienThi() {
for (int i = 0; i < soSV; i++) {
System.out.print((i + 1) + ". ");
ds[i].hienThi();
}
}
private String khoaTen(SV sv) {
return sv.layTen() + " " + sv.layHo() + " " + sv.layDem();
}
public void sapHoten() {
for (int i = 0; i < soSV - 1; i++) {
for (int j = 0; j < soSV - 1 - i; j++) {
if (khoaTen(ds[j]).compareTo(khoaTen(ds[j + 1])) > 0) {
SV tmp = ds[j];
ds[j] = ds[j + 1];
ds[j + 1] = tmp;
}
}
}
}
public void sapTuoi() {
for (int i = 0; i < soSV - 1; i++) {
for (int j = 0; j < soSV - 1 - i; j++) {
if (ds[j].layTuoi() > ds[j + 1].layTuoi()) {
SV tmp = ds[j];
ds[j] = ds[j + 1];
ds[j + 1] = tmp;
}
}
}
}
public void sapDTB() {
for (int i = 0; i < soSV - 1; i++) {
for (int j = 0; j < soSV - 1 - i; j++) {
if (ds[j].layDTB() < ds[j + 1].layDTB()) {
SV tmp = ds[j];
ds[j] = ds[j + 1];
ds[j + 1] = tmp;
}
}
}
}
public SV timTen(String ten) {
for (int i = 0; i < soSV; i++) {
if (ds[i].layTen().equalsIgnoreCase(ten)) return ds[i];
}
return null;
}
public void lietKeXepLoai(String loai) {
for (int i = 0; i < soSV; i++) {
if (ds[i].layXepLoai().equalsIgnoreCase(loai)) ds[i].hienThi();
}
}
public boolean xoa(String ten) {
for (int k = 0; k < soSV; k++) {
if (ds[k].layTen().equalsIgnoreCase(ten)) {
for (int i = k; i < soSV - 1; i++) ds[i] = ds[i + 1];
ds[soSV - 1] = null;
soSV--;
return true;
}
}
return false;
}
public double tuoiTrungBinh() {
if (soSV == 0) return 0;
int tong = 0;
for (int i = 0; i < soSV; i++) tong += ds[i].layTuoi();
return (double) tong / soSV;
}
public void tangDTBTruoc1980(double them) {
for (int i = 0; i < soSV; i++) {
int nam = ds[i].layNgaySinh().getYear() + 1900;
if (nam < 1980) ds[i].ganDTB(ds[i].layDTB() + them);
}
}
// ---- cac phuong thuc cua phan "o nha" ----
public void chuanHoTen() {
for (int i = 0; i < soSV; i++) ds[i].chuanHoTen();
}
public void trungSV() {
boolean co = false;
for (int i = 0; i < soSV - 1; i++) {
for (int j = i + 1; j < soSV; j++) {
if (ds[i].giongNhau(ds[j])) {
System.out.println("Trung: " + ds[i].layHoTen()
+ " va " + ds[j].layHoTen());
co = true;
}
}
}
if (!co) System.out.println("Khong co sinh vien trung");
}
}6. Cách chạy
javac SV.java DSSV.java QuanLyDSSV.java
java QuanLyDSSV
7. Kết quả chạy mẫu
(xem phần 3 — chương trình QuanLyDSSV)
8. Bảng kiểm thử
| # | Ca | Kỳ vọng |
|---|---|---|
| 1 | Thêm 6 SV vào mảng 5 chỗ | SV thứ 6 trả false |
| 2 | laySV(5) khi có 5 SV |
null |
| 3 | xoa("KhongCo") |
false, danh sách không đổi |
| 4 | xoa("Hoa") |
xóa đúng 1 người, soSV giảm 1 |
| 5 | tuoiTrungBinh() khi rỗng |
0 |
9. Trace xoa("Hoa") với 5
SV
| Bước | ds[0] |
ds[1] |
ds[2] |
ds[3] |
ds[4] |
soSV |
|---|---|---|---|---|---|---|
| trước | An | Binh | Hoa | Hung | Nam | 5 |
tìm được k = 2 |
5 | |||||
i=2: ds[2]=ds[3] |
An | Binh | Hung | Hung | Nam | 5 |
i=3: ds[3]=ds[4] |
An | Binh | Hung | Nam | Nam | 5 |
| dọn ô cuối | An | Binh | Hung | Nam | null | 4 |
10. Lỗi thường gặp
| Code sai | Thông báo / hiện tượng | Sửa |
|---|---|---|
for (i = k; i < soSV; i++) ds[i] = ds[i+1] |
ArrayIndexOutOfBoundsException khi
i = soSV-1 |
i < soSV - 1 |
Không ds[soSV-1] = null |
Giữ tham chiếu cũ (rò rỉ, in thừa) | Dọn ô cuối |
sapHoten so sánh cả hoTen |
Sai thứ tự kiểu Việt | Dùng khoaTen |
khoaTen dùng ho + ten |
Sắp theo họ, không theo tên | ten + ho + dem |
if (ds[j] > ds[j+1]) |
bad operand types |
So layDTB() |
them không kiểm tra đầy |
Ghi tràn mảng | Kiểm tra soSV >= ds.length |
11. Câu hỏi kiểm tra hiểu
- Vì sao cần cả
soSVlẫnds.length? - Vì sao khi xóa phải dồn từ trái sang phải (không phải ngược lại)?
- So sánh
equalsvàequalsIgnoreCase— dùng cái nào khi tìm tên?
12. Mở rộng
xoaHet(String ten)xóa tất cả người trùng tên.sapHoTenGiam(),timTheoDTB(double min).thongKe()đếm số lượng từng xếp loại.
PHẦN 3 — CHƯƠNG TRÌNH
QuanLyDSSV
0. Đề bài
Tạo danh sách 5 sinh viên; sắp theo họ tên; liệt kê sinh viên Giỏi; tìm sinh viên tên "Nam"; xóa sinh viên đầu tiên tên "Hòa"; tính tuổi trung bình; tăng DTB 0.5 cho SV sinh trước 1980.
1. Yêu cầu ẩn
- Thứ tự thao tác ảnh hưởng kết quả (xóa trước hay sau khi tính tuổi TB?) → chọn theo đúng thứ tự đề, ghi rõ.
- Sau khi xóa, không in lại phần tử
null. equalskhi tìm tên phải phân biệt "Hòa"/"Hoa" — đề không dấu, dùngequalsIgnoreCase("Hoa").
3. Thiết kế luồng chạy
tạo 5 SV → in danh sách → sắp họ tên → in
→ liệt kê Giỏi → tìm "Nam" → xóa "Hoa"
→ in danh sách còn lại → tuổi TB → tăng DTB → in
5. Lời giải hoàn chỉnh
// ===== QuanLyDSSV.java =====
import java.util.Date;
public class QuanLyDSSV {
public static void main(String[] args) {
DSSV dssv = new DSSV(10);
dssv.them(new SV("Nguyen Van An", new Date("03/15/2005"), 8.5));
dssv.them(new SV("Tran Thi Binh", new Date("07/22/2004"), 7.2));
dssv.them(new SV("Le Hoang Nam", new Date("11/02/2005"), 6.8));
dssv.them(new SV("Pham Thi Hoa", new Date("05/10/2003"), 9.1));
dssv.them(new SV("Do Van Hung", new Date("01/30/1978"), 4.5));
System.out.println("--- Danh sach ban dau ---");
dssv.hienThi();
System.out.println("--- Sap xep theo ho ten ---");
dssv.sapHoten();
dssv.hienThi();
System.out.println("--- Sinh vien Gioi ---");
dssv.lietKeXepLoai("Gioi");
System.out.println("--- Tim sinh vien ten Nam ---");
SV tim = dssv.timTen("Nam");
if (tim != null) tim.hienThi();
else System.out.println("Khong tim thay");
System.out.println("--- Xoa sinh vien dau tien ten Hoa ---");
System.out.println("Ket qua xoa: " + dssv.xoa("Hoa"));
dssv.hienThi();
System.out.println("Tuoi trung binh: " + dssv.tuoiTrungBinh());
System.out.println("--- Tang 0.5 DTB cho SV sinh truoc 1980 ---");
dssv.tangDTBTruoc1980(0.5);
dssv.hienThi();
}
}6. Cách chạy
javac SV.java DSSV.java QuanLyDSSV.java
java QuanLyDSSV
7. Kết quả chạy mẫu
--- Danh sach ban dau ---
1. Nguyen Van An | 15/3/2005 | DTB 8.5 | Gioi | 21 tuoi
2. Tran Thi Binh | 22/7/2004 | DTB 7.2 | Kha | 22 tuoi
3. Le Hoang Nam | 2/11/2005 | DTB 6.8 | Trung binh | 21 tuoi
4. Pham Thi Hoa | 10/5/2003 | DTB 9.1 | Gioi | 23 tuoi
5. Do Van Hung | 30/1/1978 | DTB 4.5 | Yeu | 48 tuoi
--- Sap xep theo ho ten ---
1. Nguyen Van An | 15/3/2005 | DTB 8.5 | Gioi | 21 tuoi
2. Tran Thi Binh | 22/7/2004 | DTB 7.2 | Kha | 22 tuoi
3. Pham Thi Hoa | 10/5/2003 | DTB 9.1 | Gioi | 23 tuoi
4. Do Van Hung | 30/1/1978 | DTB 4.5 | Yeu | 48 tuoi
5. Le Hoang Nam | 2/11/2005 | DTB 6.8 | Trung binh | 21 tuoi
--- Sinh vien Gioi ---
Nguyen Van An | 15/3/2005 | DTB 8.5 | Gioi | 21 tuoi
Pham Thi Hoa | 10/5/2003 | DTB 9.1 | Gioi | 23 tuoi
--- Tim sinh vien ten Nam ---
Le Hoang Nam | 2/11/2005 | DTB 6.8 | Trung binh | 21 tuoi
--- Xoa sinh vien dau tien ten Hoa ---
Ket qua xoa: true
1. Nguyen Van An | 15/3/2005 | DTB 8.5 | Gioi | 21 tuoi
2. Tran Thi Binh | 22/7/2004 | DTB 7.2 | Kha | 22 tuoi
3. Do Van Hung | 30/1/1978 | DTB 4.5 | Yeu | 48 tuoi
4. Le Hoang Nam | 2/11/2005 | DTB 6.8 | Trung binh | 21 tuoi
Tuoi trung binh: 28.0
--- Tang 0.5 DTB cho SV sinh truoc 1980 ---
1. Nguyen Van An | 15/3/2005 | DTB 8.5 | Gioi | 21 tuoi
2. Tran Thi Binh | 22/7/2004 | DTB 7.2 | Kha | 22 tuoi
3. Do Van Hung | 30/1/1978 | DTB 5.0 | Trung binh | 48 tuoi
4. Le Hoang Nam | 2/11/2005 | DTB 6.8 | Trung binh | 21 tuoi
Kiểm tra tay:
- Sắp theo tên:
An,Binh,Hoa,Hung,Nam✓ (đúng thứ tự alphabet của tên). - Tuổi TB sau khi xóa:
(21 + 22 + 48 + 21)/4 = 112/4 = 28.0✓. - Hung sinh 1978 < 1980 →
4.5 + 0.5 = 5.0, xếp loại đổi từ Yếu sang Trung bình ✓ (vì>= 5).
8. Bảng kiểm thử
| # | Thao tác | Kỳ vọng |
|---|---|---|
| 1 | In ban đầu | đúng 5 dòng, đúng thứ tự nhập |
| 2 | sapHoten |
An, Binh, Hoa, Hung, Nam |
| 3 | lietKeXepLoai("Gioi") |
2 dòng (An, Hoa) |
| 4 | timTen("Nam") |
Le Hoang Nam |
| 5 | xoa("Hoa") |
còn 4 SV, không có Hoa |
| 6 | tuoiTrungBinh |
28.0 |
| 7 | tangDTBTruoc1980(0.5) |
Hung 5.0 |
9. Trace
tangDTBTruoc1980
| SV | năm sinh | < 1980? |
DTB trước | DTB sau |
|---|---|---|---|---|
| An | 2005 | không | 8.5 | 8.5 |
| Binh | 2004 | không | 7.2 | 7.2 |
| Hung | 1978 | có | 4.5 | 5.0 |
| Nam | 2005 | không | 6.8 | 6.8 |
10. Lỗi thường gặp
| Code sai | Hiện tượng | Sửa |
|---|---|---|
Xóa rồi vẫn in soSV cũ |
In dòng null |
In theo soSV hiện tại |
new Date(2005, 3, 15) |
Năm 3905, tháng lệch | new Date("03/15/2005") |
timTen in luôn trong lớp DSSV |
Không tái sử dụng được | Trả SV, main quyết định in |
| Tăng DTB không kiểm tra trần 10 | DTB > 10 | Math.min(10, dtb + them) |
So sánh tên bằng == |
Không khớp | equals/equalsIgnoreCase |
11. Câu hỏi kiểm tra hiểu
- Vì sao
Ket qua xoa: truelà cần thiết (nếu không in, làm sao biết xóa thành công)? - Nếu đổi thứ tự "xóa Hoa" lên trước "tính tuổi TB" thì kết quả đổi thế nào?
- Vì sao Hung được nâng lên đúng 5.0 mà không vượt 10?
12. Mở rộng
- In bảng có cột căn lề bằng
\t. - Thêm chức năng menu
do-while(1: in, 2: thêm, 3: xóa, 0: thoát). - Xuất danh sách theo từng xếp loại.
PHẦN 4 (Ở NHÀ) — CÁC PHƯƠNG THỨC BỔ SUNG
4.1
SV.lonHon(SV), gioiHon(SV),
giongNhau(SV)
Phân tích: ba phương thức so sánh hai sinh
viên, nên phải nhận SV làm tham số và trả
boolean.
// them vao SV.java
public boolean lonHon(SV sv) {
return this.layTuoi() > sv.layTuoi();
}
public boolean gioiHon(SV sv) {
return this.layDTB() > sv.layDTB();
}
public boolean giongNhau(SV sv) {
if (sv == null) return false;
return this.hoTen.equalsIgnoreCase(sv.hoTen)
&& this.ngaySinh.equals(sv.ngaySinh);
}Lưu ý: giongNhau so cả
hoTen và ngaySinh — hai người
trùng tên nhưng khác ngày sinh thì không trùng. Nếu chỉ
so tên, bạn sẽ gộp nhầm người khác nhau.
4.2 SV.chuanHoTen()
Chuẩn hoá: bỏ khoảng trắng thừa, viết hoa chữ đầu mỗi từ, còn lại viết thường.
// them vao SV.java
public void chuanHoTen() {
String[] p = tu();
String kq = "";
for (int i = 0; i < p.length; i++) {
String tu = p[i].toLowerCase();
if (tu.length() > 0) {
tu = tu.substring(0, 1).toUpperCase() + tu.substring(1);
}
kq += tu;
if (i < p.length - 1) kq += " ";
}
hoTen = kq;
}Trace " nGUYEN vAN aN ": | Bước |
Kết quả | |---|---| | tu() |
["nGUYEN","vAN","aN"] | | từ 1 | "nGUYEN" →
"nguyen" → "Nguyen" | | từ 2 |
"vAN" → "van" → "Van" | | từ 3 |
"aN" → "an" → "An" | | ghép |
"Nguyen Van An" |
4.3 DSSV.chuanHoTen()
// them vao DSSV.java
public void chuanHoTen() {
for (int i = 0; i < soSV; i++) ds[i].chuanHoTen();
}4.4
DSSV.trungSV() — liệt kê sinh viên trùng nhau
Phân tích: so từng cặp (i, j) với
j > i; nếu giongNhau thì in. Dùng
j > i để mỗi cặp chỉ xét một lần.
// them vao DSSV.java
public void trungSV() {
boolean co = false;
for (int i = 0; i < soSV - 1; i++) {
for (int j = i + 1; j < soSV; j++) {
if (ds[i].giongNhau(ds[j])) {
System.out.println("Trung: " + ds[i].layHoTen()
+ " va " + ds[j].layHoTen());
co = true;
}
}
}
if (!co) System.out.println("Khong co sinh vien trung");
}Trace 4 SV: An(2005),
Binh(2004), An(2005),
Nam(2005):
i |
j |
So sánh | Kết quả |
|---|---|---|---|
| 0 | 1 | An vs Binh | khác |
| 0 | 2 | An vs An (cùng ngày) | trùng → in |
| 0 | 3 | An vs Nam | khác |
| 1 | 2 | Binh vs An | khác |
| 1 | 3 | Binh vs Nam | khác |
| 2 | 3 | An vs Nam | khác |