Java Foundations/Mini-challenge: Máy tính BMI
15/35
Bài 15 / 35~18 phútCú pháp Java & Kiểu dữ liệuMiễn phí lượt xem

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.

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

Đặt 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