Java — Từ Zero đến Senior/Nhập môn & Tư duy lập trình/Tư duy lập trình — input, xử lý, output
5/7
~14 phútNhập môn & Tư duy lập trình

Tư duy lập trình — input, xử lý, output

Nắm vững mô hình input-processing-output, phân rã bài toán thành các bước nhỏ, và 3 construct cơ bản của lập trình: sequence, selection, iteration.

Trước khi học thêm syntax, bạn cần một khung tư duy để tiếp cận mọi bài toán lập trình. Không có khung này, bạn sẽ biết nhiều lệnh Java nhưng không biết bắt đầu từ đâu khi đứng trước một bài toán mới.

Bài này trang bị cho bạn mô hình đơn giản nhất mà lập trình viên giàu kinh nghiệm vẫn dùng mỗi ngày: input → processing → output.

1. Analogy — "Máy xay sinh tố"

Hình dung chiếc máy xay sinh tố:

  1. Nhét trái cây vào (input) — chuối, dâu, sữa
  2. Máy xay (processing) — xay nhuyễn, trộn đều
  3. Đổ ra cốc (output) — cốc sinh tố hoàn chỉnh

Mọi chương trình máy tính đều theo đúng 3 bước này. Dù là app điện thoại, trang web, hay hệ thống ngân hàng — tất cả đều nhận input, xử lý, rồi tạo ra output.

💡 💡 Cách nhớ

Máy xay sinh tố — input là trái cây, processing là lưỡi dao quay, output là cốc sinh tố. Mọi chương trình bạn viết là một dạng máy xay: nhận nguyên liệu, biến đổi, trả kết quả.

2. Mô hình I-P-O — ba câu hỏi đầu tiên

Khi đứng trước bất kỳ bài toán nào, hỏi ngay 3 câu:

flowchart LR
  inp["INPUT<br/>Du lieu dau vao"]
  proc["PROCESSING<br/>Bien doi du lieu"]
  out["OUTPUT<br/>Ket qua dau ra"]

  inp --> proc --> out

Câu 1 — Input là gì?

  • User nhập từ bàn phím?
  • Đọc từ file?
  • Nhận từ API?
  • Dữ liệu cứng trong code?

Câu 2 — Processing làm gì?

  • Lọc, tìm kiếm?
  • Tính toán, biến đổi?
  • Sắp xếp, nhóm?
  • Kiểm tra điều kiện?

Câu 3 — Output đi đâu?

  • In ra console?
  • Ghi vào file?
  • Trả về cho người gọi?
  • Hiển thị trên màn hình?

Trả lời 3 câu này trước khi viết một dòng code — bạn đã có 80% bản thiết kế.

3. Ví dụ 1 — Tính diện tích hình chữ nhật

Bài toán: Nhập chiều dài và chiều rộng, tính và in diện tích.

Phân tích I-P-O:

  • Input: 2 số thực (chiều dài, chiều rộng) — người dùng nhập
  • Processing: nhân 2 số với nhau
  • Output: in kết quả ra console
import java.util.Scanner;

public class TinhDienTich {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in); // cong cu doc input tu keyboard

        // --- INPUT ---
        System.out.print("Nhap chieu dai: ");
        double dai = sc.nextDouble();         // doc so thap phan tu user

        System.out.print("Nhap chieu rong: ");
        double rong = sc.nextDouble();

        // --- PROCESSING ---
        double dienTich = dai * rong;         // tinh toan

        // --- OUTPUT ---
        System.out.println("Dien tich: " + dienTich);

        sc.close(); // giai phong tai nguyen
    }
}

Chạy thử:

Nhap chieu dai: 5.0
Nhap chieu rong: 3.5
Dien tich: 17.5

Scanner là class trong java.util — công cụ đọc input từ bàn phím (hoặc file). sc.nextDouble() đọc một số thập phân mà user nhập rồi nhấn Enter.

4. Ví dụ 2 — Chào user bằng tên

Bài toán: Hỏi tên người dùng, in lời chào cá nhân hoá.

Phân tích I-P-O:

  • Input: tên (chuỗi) — người dùng nhập
  • Processing: ghép chuỗi "Xin chao, " + ten + "!"
  • Output: in ra console
import java.util.Scanner;

public class Chao {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // --- INPUT ---
        System.out.print("Ban ten gi? ");
        String ten = sc.nextLine(); // doc ca dong -- bao gom khoang trang

        // --- PROCESSING ---
        String loiChao = "Xin chao, " + ten + "! Chuc ban hoc tot.";

        // --- OUTPUT ---
        System.out.println(loiChao);

        sc.close();
    }
}

Chạy thử:

Ban ten gi? Nguyen Van An
Xin chao, Nguyen Van An! Chuc ban hoc tot.

sc.nextLine() vs sc.nextDouble():

  • nextDouble() đọc một số, dừng lại trước Enter
  • nextLine() đọc cả dòng kể cả khoảng trắng, tiêu thụ cả ký tự Enter

5. Phân rã bài toán lớn

Bài toán thực tế không đơn giản như 2 ví dụ trên. Nhưng dù phức tạp đến đâu, bạn vẫn phân rã thành nhiều I-P-O nhỏ hơn, xử lý từng bước.

Ví dụ: Tính trung bình của danh sách số nguyên.

BướcLoạiMô tả
Đọc N số từ userInputScanner.nextInt() mỗi lần
Cộng tất cả vào biến tongProcessingLặp và cộng dồn
Chia tong cho NProcessingMột phép tính
In kết quảOutputSystem.out.println
import java.util.Scanner;

public class TinhTrungBinh {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // --- INPUT ---
        System.out.print("Nhap so luong phan tu: ");
        int n = sc.nextInt();

        // --- PROCESSING (phan 1) -- doc va cong don ---
        int tong = 0;
        for (int i = 1; i <= n; i++) {
            System.out.print("Nhap phan tu " + i + ": ");
            int soNguyen = sc.nextInt();
            tong += soNguyen;  // tong = tong + soNguyen
        }

        // --- PROCESSING (phan 2) -- tinh trung binh ---
        double trungBinh = (double) tong / n;

        // --- OUTPUT ---
        System.out.println("Trung binh: " + trungBinh);

        sc.close();
    }
}

Chú ý (double) tong / n: ép kiểu tong sang double trước khi chia để tránh integer division (chia lấy phần nguyên). 5 / 2 = 2 (int), nhưng (double)5 / 2 = 2.5.

6. Ba construct cơ bản của lập trình

Mọi thuật toán — dù đơn giản hay phức tạp — đều được xây từ 3 construct và chỉ 3:

💡 💡 Định lý Böhm-Jacopini

Năm 1966, hai nhà khoa học máy tính chứng minh rằng bất kỳ thuật toán nào cũng có thể biểu diễn bằng đúng 3 construct: sequence, selection, iteration. Đây là nền tảng của mọi ngôn ngữ lập trình mệnh lệnh (imperative).

ConstructTiếng ViệtTrong JavaDùng khi
SequenceTuần tựCác câu lệnh từ trên xuống dướiLuôn luôn — mặc định
SelectionRẽ nhánhif, else if, else, switchKhi cần chọn giữa các trường hợp
IterationLặpfor, while, do-whileKhi cần làm đi làm lại nhiều lần

6.1 Sequence — tuần tự

// Cac dong chay lan luot tu tren xuong duoi
int a = 5;          // buoc 1
int b = 10;         // buoc 2
int c = a + b;      // buoc 3 -- phai sau buoc 1 va 2
System.out.println(c); // buoc 4

Mặc định Java chạy tuần tự — dòng trước xong rồi mới đến dòng sau.

6.2 Selection — rẽ nhánh

int diem = 75;

if (diem >= 90) {
    System.out.println("Xuat sac");
} else if (diem >= 70) {
    System.out.println("Kha");     // chay dong nay vi 75 >= 70
} else {
    System.out.println("Trung binh");
}

Selection cho phép chương trình đưa ra quyết định — chỉ chạy đoạn code phù hợp với điều kiện. Chi tiết sẽ có ở module câu lệnh điều kiện.

6.3 Iteration — lặp

// In so tu 1 den 5
for (int i = 1; i <= 5; i++) {
    System.out.println(i);  // chay 5 lan
}
// Output: 1 2 3 4 5 (moi so tren 1 dong)

Iteration cho phép lặp đi lặp lại mà không cần viết code nhiều lần. Chi tiết về for, while sẽ có ở module vòng lặp.

7. Tư duy test — nghĩ edge case trước khi code

Một thói quen quan trọng của lập trình viên giỏi: trước khi viết code, nghĩ trước 2-3 input đặc biệt:

Bài toánInput bình thườngEdge case cần nghĩ
Tính diện tíchchiều dài = 5, rộng = 3Chiều dài = 0? Số âm?
Tính trung bình5 số dươngN = 0 (chia cho 0!)? Số âm?
Tìm max trong mảng[3, 1, 4, 1, 5]Mảng rỗng? Mảng 1 phần tử?
Đếm ký tự trong chuỗi"hello"Chuỗi rỗng ""? Chuỗi null?

Vì sao quan trọng? Code chạy đúng với input bình thường là điều hiển nhiên. Code xử lý đúng edge case là thước đo của lập trình viên giỏi. Bug trong production thường đến từ những trường hợp "không ai nghĩ tới".

// Vi du: tinh trung binh co xu ly edge case
if (n == 0) {
    System.out.println("Khong co phan tu nao de tinh trung binh.");
} else {
    double trungBinh = (double) tong / n;
    System.out.println("Trung binh: " + trungBinh);
}

8. Bài toán đầy đủ — kết hợp I-P-O

Dưới đây là ví dụ kết hợp tất cả: input từ user, xử lý có điều kiện, output rõ ràng.

import java.util.Scanner;

public class PhanLoaiDiem {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // --- INPUT ---
        System.out.print("Nhap diem (0-100): ");
        int diem = sc.nextInt();

        // --- PROCESSING + SELECTION ---
        String xepLoai;
        if (diem < 0 || diem > 100) {
            xepLoai = "Diem khong hop le";    // edge case
        } else if (diem >= 90) {
            xepLoai = "Xuat sac";
        } else if (diem >= 70) {
            xepLoai = "Kha";
        } else if (diem >= 50) {
            xepLoai = "Trung binh";
        } else {
            xepLoai = "Yeu";
        }

        // --- OUTPUT ---
        System.out.println("Xep loai: " + xepLoai);

        sc.close();
    }
}

Chạy thử:

Nhap diem (0-100): 85
Xep loai: Kha

Nhap diem (0-100): -5
Xep loai: Diem khong hop le

9. ✅/❌ Áp dụng tư duy I-P-O

Nên làm:

  • Vẽ I-P-O ra giấy trước khi gõ code — 1 phút vẽ tiết kiệm 30 phút debug
  • Nghĩ edge case ngay từ đầu — số 0, số âm, chuỗi rỗng, mảng rỗng
  • Đặt comment // INPUT, // PROCESSING, // OUTPUT khi code phức tạp
  • Kiểm tra từng phần — test input đúng chưa trước khi lo processing

Tránh:

  • ❌ Nhảy thẳng vào code khi chưa hiểu output cần là gì
  • ❌ Viết code dài rồi mới test — bug sẽ khó tìm hơn
  • ❌ Bỏ qua edge case vì "chắc không ai nhập số âm đâu"

10. Self-check — phân tích 3 bài toán

Hãy tự phân tích I-P-O cho 3 bài toán sau trước khi đọc gợi ý:

Bài 1: Sắp xếp danh sách tên theo thứ tự alphabet.

  • Input: ?
  • Processing: ?
  • Output: ?

Bài 2: Tìm giá trị lớn nhất trong mảng số nguyên.

  • Input: ?
  • Processing: ?
  • Output: ?

Bài 3: Đếm số lần ký tự 'a' xuất hiện trong một chuỗi.

  • Input: ?
  • Processing: ?
  • Output: ?

💡 💡 Gợi ý

Bài 1: Input = danh sách N tên (String), Processing = thuật toán sắp xếp (so sánh, đổi chỗ), Output = danh sách đã sắp xếp.

Bài 2: Input = mảng số nguyên, Processing = duyệt từng phần tử, giữ lại số lớn nhất tìm được, Output = 1 số nguyên.

Bài 3: Input = 1 chuỗi String, Processing = duyệt từng ký tự, đếm số ký tự bằng 'a', Output = 1 số nguyên (bộ đếm).

11. 📚 Deep Dive Oracle

ℹ️ 📚 Deep Dive Oracle (optional)

Tài liệu tham khảo:

Ghi chú thực tế: Scanner là class hay gặp khi mới học nhưng không dùng nhiều trong production (thường đọc input qua HTTP request, file, database thay vì bàn phím). Tuy nhiên, hiểu Scanner giúp bạn nắm khái niệm stream input/output — nền tảng để sau này làm việc với InputStream, BufferedReader, và Files.readAllLines.

12. Tóm tắt

  • Mọi chương trình = Input → Processing → Output. Hỏi 3 câu này trước khi code.
  • Scanner đọc input từ bàn phím: nextInt(), nextDouble(), nextLine().
  • Bài toán lớn = nhiều bước I-P-O nhỏ xếp lại — phân rã từng phần.
  • 3 construct cơ bản: sequence (tuần tự), selection (rẽ nhánh), iteration (lặp) — đủ để xây mọi thuật toán.
  • Tư duy test: nghĩ edge case (số 0, số âm, chuỗi rỗng) trước khi viết code — thói quen của dev giỏi.

13. Tự kiểm tra

  1. Phân tích I-P-O cho bài toán: "Kiểm tra một số có phải số chẵn không". Input là gì, processing làm gì, output là gì?
  2. Vì sao cần (double) tong / n thay vì chỉ tong / n? Điều gì xảy ra nếu không ép kiểu?
  3. sc.nextLine() khác sc.nextInt() ở điểm nào? Khi nào dùng cái nào?
  4. 3 construct sequence/selection/iteration — cái nào mặc định luôn có? Cái nào chỉ xuất hiện khi cần?
  5. Bạn viết chương trình tính căn bậc hai. Edge case nào cần xử lý trước khi tính?

Bài tiếp theo: Biến và khai báo — cái tên gắn với một giá trị