Java — Từ Zero đến Senior/Cú pháp Java & Kiểu dữ liệu/Mini-challenge: Máy tính BMI
8/8
~18 phútCú pháp Java & Kiểu dữ liệu

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 enum có 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:

  1. Tính BMI = cân nặng / (chiều cao × chiều cao)
  2. Phân loại theo ngưỡng WHO:
BMIPhân loại
< 18.5Gầy
18.5 – 24.9Bình thường
25.0 – 29.9Thừa cân
≥ 30.0Béo phì

Output: BMI dạng %.2f + tên phân loại

📦 Concept dùng trong bài

ConceptBài đã họcDùng ở đây
Scanner nhập từ consoleModule 1, bài 5Đọc canNang, chieuCao
double tính toán thựcModule 2, bài 2Tính BMI tránh integer division
String.format("%.2f", x)Module 2, bài 6In BMI 2 chữ số thập phân
enum có field + static factoryModule 2, bài 7PhanLoai với tên hiển thị và tuBmi()
if/else if rẽ nhánhSẽ học Module 3Dùng tạm trong static factory
try-with-resourcesSẽ học sauTự đó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 ý

💡 💡 Gợi ý — đọc khi bị kẹt

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 ScannerScanner 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

ℹ️ ✅ Lời giải — xem sau khi đã thử

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 PhanLoai với field hienThi — mỗi value mang tên hiển thị riêng, tách biệt với tên khai báo (BINH_THUONG vs "Binh thuong"). Không cần switch rả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ọi sc.close() khi ra khỏi block dù có exception hay không. Tránh resource leak.
  • printf("BMI: %.2f%n", bmi)%.2f làm tròn đến 2 chữ số thập phân theo standard rounding; %n xuống dòng platform-independent.
  • Tại sao double cho canNangchieuCao?sc.nextDouble() đọc số thực. Nếu dùng int, người nhập 1.70 sẽ gây lỗi parse. BMI = cân/cao² cần tính toán thực — hai double đả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 Scanner nhập liệu từ console với double cho 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 enum có 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 để Scanner tự đó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.