Mini-challenge: Máy tính BMI
Viết chương trình Java tính chỉ số BMI và phân loại theo WHO — luyện Scanner nhập liệu, String.format xuất 2 chữ số thập phân, và enum có field với static factory method.
Đây là bài thực hành khép lại Module 2. Không có lý thuyết mới — chỉ có một bài toán thực tế để bạn kết hợp những gì vừa học: double cho tính toán thực, String.format xuất 2 chữ số thập phân, và enum có field để phân loại BMI theo WHO.
🎯 Đề bài
Viết chương trình Java tính chỉ số BMI (Body Mass Index) và phân loại theo WHO. Người dùng nhập cân nặng và chiều cao từ console.
Output mẫu:
Nhap can nang (kg): 65
Nhap chieu cao (m): 1.70
BMI: 22.49
Phan loai: Binh thuong
Yêu cầu:
- Nhập cân nặng (kg) và chiều cao (m) từ
Scanner - Tính BMI theo công thức WHO
- Hiển thị BMI làm tròn 2 chữ số thập phân
- Phân loại đúng theo ngưỡng WHO
- Dùng
enumcó field để mã hoá phân loại và tên hiển thị
🔍 Phân tích I-P-O
Input: Cân nặng double (kg), chiều cao double (m) — nhập từ Scanner
Processing:
- Tính BMI = cân nặng / (chiều cao × chiều cao)
- Phân loại theo ngưỡng WHO:
| BMI | Phân loại |
|---|---|
| < 18.5 | Gầy |
| 18.5 – 24.9 | Bình thường |
| 25.0 – 29.9 | Thừa cân |
| ≥ 30.0 | Béo phì |
Output: BMI dạng %.2f + tên phân loại
📦 Concept dùng trong bài
| Concept | Bài đã học | Dùng ở đây |
|---|---|---|
Scanner nhập từ console | Module 1, bài 5 | Đọc canNang, chieuCao |
double tính toán thực | Module 2, bài 2 | Tính BMI tránh integer division |
String.format("%.2f", x) | Module 2, bài 6 | In BMI 2 chữ số thập phân |
enum có field + static factory | Module 2, bài 7 | PhanLoai với tên hiển thị và tuBmi() |
if/else if rẽ nhánh | Sẽ học Module 3 | Dùng tạm trong static factory |
| try-with-resources | Sẽ học sau | Tự đóng Scanner khi xong |
▶️ Starter code
Copy file này, điền vào các TODO:
import java.util.Scanner;
public class BmiCalculator {
// TODO: dinh nghia enum PhanLoai voi 4 values: GAY, BINH_THUONG, THUA_CAN, BEO_PHI
// moi value co field String hienThi (ten hien thi tieng Viet ASCII)
// them static method tuBmi(double bmi) tra ve PhanLoai tuong ung
public static void main(String[] args) {
try (Scanner sc = new Scanner(System.in)) {
System.out.print("Nhap can nang (kg): ");
double canNang = sc.nextDouble();
System.out.print("Nhap chieu cao (m): ");
double chieuCao = sc.nextDouble();
// TODO: tinh BMI
// TODO: lay PhanLoai tu BMI
// TODO: in BMI voi 2 chu so thap phan
// TODO: in phan loai
}
}
}
Dành 15–20 phút thử tự làm trước khi xem gợi ý.
javac BmiCalculator.java
java BmiCalculator
💡 Gợi ý
Công thức BMI:
double bmi = canNang / (chieuCao * chieuCao);
Định nghĩa enum với field:
enum PhanLoai {
GAY("Gay"),
BINH_THUONG("Binh thuong"),
THUA_CAN("Thua can"),
BEO_PHI("Beo phi");
private final String hienThi;
PhanLoai(String hienThi) {
this.hienThi = hienThi;
}
public String getHienThi() { return hienThi; }
}
Static factory method bên trong enum:
public static PhanLoai tuBmi(double bmi) {
if (bmi < 18.5) return GAY;
if (bmi < 25) return BINH_THUONG;
if (bmi < 30) return THUA_CAN;
return BEO_PHI;
}
In BMI 2 chữ số thập phân:
System.out.printf("BMI: %.2f%n", bmi);
// hoac:
System.out.println("BMI: " + String.format("%.2f", bmi));
try-with-resources cho Scanner — Scanner implement Closeable, dùng được với try-with-resources để tự đóng khi ra khỏi block:
try (Scanner sc = new Scanner(System.in)) {
// dung sc o day
}
// sc tu dong dong -- khong can sc.close()
✅ Lời giải
import java.util.Scanner;
public class BmiCalculator {
enum PhanLoai {
GAY("Gay"),
BINH_THUONG("Binh thuong"),
THUA_CAN("Thua can"),
BEO_PHI("Beo phi");
private final String hienThi;
PhanLoai(String hienThi) {
this.hienThi = hienThi;
}
public String getHienThi() {
return hienThi;
}
public static PhanLoai tuBmi(double bmi) {
if (bmi < 18.5) return GAY;
if (bmi < 25) return BINH_THUONG;
if (bmi < 30) return THUA_CAN;
return BEO_PHI;
}
}
public static void main(String[] args) {
try (Scanner sc = new Scanner(System.in)) {
System.out.print("Nhap can nang (kg): ");
double canNang = sc.nextDouble();
System.out.print("Nhap chieu cao (m): ");
double chieuCao = sc.nextDouble();
double bmi = canNang / (chieuCao * chieuCao);
PhanLoai pl = PhanLoai.tuBmi(bmi);
System.out.printf("BMI: %.2f%n", bmi);
System.out.println("Phan loai: " + pl.getHienThi());
}
}
}
Giải thích từng phần:
enum PhanLoaivới fieldhienThi— mỗi value mang tên hiển thị riêng, tách biệt với tên khai báo (BINH_THUONGvs"Binh thuong"). Không cầnswitchrải rác khắp nơi.static PhanLoai tuBmi(double bmi)— static factory method ngay trong enum: truyền vào BMI, nhận về phân loại đúng kiểu. Logic phân loại đóng gói trong một chỗ.try (Scanner sc = ...)— try-with-resources: Java tự gọisc.close()khi ra khỏi block dù có exception hay không. Tránh resource leak.printf("BMI: %.2f%n", bmi)—%.2flàm tròn đến 2 chữ số thập phân theo standard rounding;%nxuống dòng platform-independent.- Tại sao
doublechocanNangvàchieuCao? —sc.nextDouble()đọc số thực. Nếu dùngint, người nhập1.70sẽ gây lỗi parse. BMI = cân/cao² cần tính toán thực — haidoubleđảm bảo không xảy ra integer division.
🎓 Mở rộng
Đã hoàn thành bài cơ bản? Thử tiếp:
Mức 1 — Validate input:
if (canNang <= 0 || chieuCao <= 0 || chieuCao > 3.0) {
System.out.println("Input khong hop le. Can nang > 0 va chieu cao 0 < h <= 3.0");
return;
}
Mức 2 — Xử lý exception khi nhập sai định dạng:
import java.util.InputMismatchException;
try {
double canNang = sc.nextDouble();
} catch (InputMismatchException e) {
System.out.println("Loi: vui long nhap so thuc, vi du 65.5");
}
Mức 3 — Lặp lại cho đến khi người dùng gõ quit:
System.out.print("Nhap can nang (kg) hoac 'quit' de thoat: ");
String input = sc.next();
if ("quit".equalsIgnoreCase(input)) break;
double canNang = Double.parseDouble(input);
Mức 4 — Tính khoảng cân nặng lý tưởng:
// BMI ly tuong: 18.5 -- 24.9
double canNangMin = 18.5 * chieuCao * chieuCao;
double canNangMax = 24.9 * chieuCao * chieuCao;
System.out.printf("Can nang ly tuong: %.1f -- %.1f kg%n", canNangMin, canNangMax);
✨ Điều bạn vừa làm được
Hoàn thành mini-challenge này, bạn đã:
- Kết hợp
Scannernhập liệu từ console vớidoublecho tính toán thực — tránh integer division khi tính BMI - Dùng
String.format("%.2f", ...)/printfđể kiểm soát số chữ số thập phân trong output - Định nghĩa
enumcó field và static factory method — đóng gói phân loại + tên hiển thị + logic lookup trong một chỗ - Áp dụng try-with-resources để
Scannertự đóng, không lo resource leak - Thực hành tư duy I-P-O: phân tích bài toán thành Input, Processing, Output trước khi viết code
Chúc mừng — bạn đã hoàn thành Module 2! Module 3 sẽ đi vào cấu trúc điều kiện và vòng lặp: if/else, switch, for, while — những công cụ để điều khiển luồng chạy của chương trình.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên