Lab 01 cho người mới: mảng và vòng lặp giảng từng dòng

Giả định duy nhất: bạn đã đọc phần 0 (Java từ số 0) trong trang người mới. Mỗi bài dưới đây đi theo đúng 6 bước: đề nói gì, từ mới, ý tưởng, code giải thích từng dòng, chạy tay, bẫy.

Bài 1. Chao2 — chào theo tên gõ lúc chạy

Đề nói gì

Viết chương trình chào. Tên không viết cứng trong code mà gõ lúc chạy: java Chao2 Nguyen Van An thì in Chao Nguyen Van An. Chạy không gõ tên thì nhắc cách dùng rồi dừng.

Từ mới

Ý tưởng

  1. Không có tên (length == 0) thì in hướng dẫn, dừng.
  2. Có tên thì nối các chữ lại, giữa hai chữ chèn 1 dấu cách.
  3. In câu chào.

Code giải thích từng dòng

public class Chao2 {
    public static void main(String[] args) {
        if (args.length == 0) {                  // bước 1: không gõ tên?
            System.out.println("Cach dung: java Chao2 <ten>");
            return;                              // dừng chương trình tại đây
        }
        String ten = "";                         // hộp rỗng để hứng tên
        for (int i = 0; i < args.length; i++) {  // duyệt từng chữ đã gõ
            ten += args[i];                      // nối chữ thứ i vào
            if (i < args.length - 1)             // chưa phải chữ cuối?
                ten += " ";                      // thì chèn 1 dấu cách
        }
        System.out.println("Chao " + ten);       // bước 3: chào
    }
}

ten += args[i] là viết gọn của ten = ten + args[i]. Điều kiện i < args.length - 1 (chữ cuối có i bằng length - 1) để kết quả không thừa dấu cách ở đuôi.

Chạy tay

java Chao2 Nguyen Van An: args = ["Nguyen", "Van", "An"], length = 3, khác 0 nên qua được bước 1. Vòng lặp: i=0 → ten="Nguyen", 0 < 2 nên thêm cách; i=1 → ten="Nguyen Van", thêm cách; i=2 → ten="Nguyen Van An", 2 < 2 sai nên không thêm cách. In ra Chao Nguyen Van An.

java Chao2 (không tên): length = 0 → in hướng dẫn, return dừng. Không bao giờ chạm tới vòng lặp.

Bẫy hay gặp

Tự luyện

  1. Sửa để chỉ chào từ đầu tiên (args[0]), bỏ qua phần còn lại.
  2. Sửa để nếu tên có đúng 1 chữ thì chào Chao ban <ten>, nhiều chữ thì chào như cũ.

Bài 2. DocSo — đọc số 0–9 thành chữ

Đề nói gì

Nhập 1 số, in cách đọc: 7 → bay, 0 → khong. Số ngoài 0–9 thì in Khong hop le.

Từ mới

Ý tưởng

  1. Nhập n.
  2. Ngoài 0–9 → báo sai, xong.
  3. Trong 0–9 → switch in chữ tương ứng.

Code giải thích từng dòng

Scanner sc = new Scanner(System.in);
System.out.print("Nhap mot chu so (0-9): ");
int n = sc.nextInt();
if (n < 0 || n > 9) {                 // bước 2: chặn số lạ trước
    System.out.println("Khong hop le");
} else {                              // số sạch mới cho vào switch
    switch (n) {
        case 7: System.out.println("bay"); break; // n=7: in rồi THOÁT
        default: System.out.println("chin"); break;
    }
}
sc.close();

Vì chỉ viết case 0 đến case 8 nên số 9 không khớp nhánh nào, rơi vào default in chin. if chặn ngoài giúp switch khỏi lo số lạ.

Chạy tay

Nhập 7: 7 < 0 sai, 7 > 9 sai → cả || sai → vào else, switch(7) nhảy case 7, in bay, gặp break thoát. Nhập 12: 12 > 9 đúng → in Khong hop le, bỏ qua switch.

Bẫy hay gặp

Tự luyện

  1. Thêm case 9 tường minh, default báo Khong hop le (chuyển nhiệm vụ chặn số lạ cho default).
  2. Đọc số 0–99: gợi ý dùng n / 10 (chục) và n % 10 (đơn vị).

Bài 3. HinhTron — kiểm tra rồi mới tính

Đề nói gì

Nhập bán kính r, in diện tích và chu vi làm tròn 2 chữ số. r không dương thì báo Ban kinh phai duong rồi dừng.

Từ mới

Ý tưởng

  1. Nhập r.
  2. r <= 0 → báo lỗi, đóng Scanner, dừng.
  3. Tính và in, làm tròn 2 chữ số.

Code giải thích từng dòng

double r = sc.nextDouble();
if (r <= 0) {                                // bước 2: số sai thì đuổi về
    System.out.println("Ban kinh phai duong");
    sc.close();
    return;                                  // không tính tiếp
}
double dienTich = Math.PI * r * r;           // pi × r²
double chuVi    = 2 * Math.PI * r;           // 2 × pi × r
System.out.println("Dien tich: " + Math.round(dienTich * 100) / 100.0);

Mẹo Math.round(x * 100) / 100.0: với r = 3, diện tích 28.2743... nhân 100 → 2827.43, làm tròn → 2827, chia 100.0 → 28.27. Chia cho 100.0 (số thực) chứ không phải 100 (số nguyên) — chia nguyên là mất hết phần lẻ.

Chạy tay

r = 3: 3 <= 0 sai → tính tiếp. Diện tích 28.2743 → 28.27, chu vi 18.8495 → 18.85. Khớp output mẫu từng chữ số. r = 0: 0 <= 0 đúng → báo lỗi, dừng, không có phép tính nào chạy.

Bẫy hay gặp

Tự luyện

  1. In thêm đường kính (2 * r).
  2. Chỉ nhận r trong khoảng 0–1000, ngoài thì báo lỗi.

Bài 4. GiaiThua — int tràn số

Đề nói gì

Tính n! (n gõ lúc chạy). 5! = 120, 0! = 1, số âm báo lỗi. 13! phải ra 6227020800.

Từ mới

Ý tưởng

  1. Không gõ n thì nhắc cách dùng, dừng.
  2. Đổi chữ thành số; âm thì báo lỗi, dừng.
  3. Nhân dồn từ 2 tới n.

Code giải thích từng dòng

int n = Integer.parseInt(args[0]);  // "5" thành số 5
if (n < 0) { System.out.println("n phai >= 0"); return; }
long kq = 1;                        // bắt đầu từ 1 (trung hòa phép nhân)
for (int i = 2; i <= n; i++) {      // nhân 2, 3, ..., n
    kq *= i;                        // kq = kq * i
}
System.out.println(n + "! = " + kq);

Bắt đầu từ 2 vì nhân thêm 0, 1 không đổi kết quả. n = 0: vòng lặp 2 <= 0 sai ngay từ đầu, kq giữ 1 → 0! = 1. Đúng.

Chạy tay

n = 5: kq=1 → ×2=2 → ×3=6 → ×4=24 → ×5=120. In 5! = 120. Vì sao 13! cần long? 12! = 479 triệu (vừa int), 13! = 6.2 tỉ (vượt 2.1 tỉ) → int tràn thành số âm linh tinh.

Bẫy hay gặp

Tự luyện

  1. Tính tổng 1 + 2 + ... + n (khởi đầu kq bằng mấy?).
  2. In cả dãy 1! 2! ... n! trên một dòng.

Bài 5. NguyenToTrongMang — tách hàm kiểm tra

Đề nói gì

Nhập n rồi nhập n số, in ra các số nguyên tố và đếm có bao nhiêu cái. Ví dụ 6 số 2 3 4 5 10 11 → nguyên tố là 2 3 5 11, có 4 số.

Từ mới

Ý tưởng

  1. Nhập n, chặn n <= 0 trước khi cấp phát mảng.
  2. Nhập n số vào mảng.
  3. Duyệt mảng, số nào qua được laNguyenTo thì in và đếm.

Code giải thích từng dòng

public static boolean laNguyenTo(int n) {
    if (n < 2) return false;                 // 0, 1, số âm: loại ngay
    for (int i = 2; i <= Math.sqrt(n); i++)  // thử ước tới căn bậc hai
        if (n % i == 0) return false;        // có ước → kết luận luôn
    return true;                             // hết ước mà sống → nguyên tố
}

Vì sao chỉ tới căn? Ước đi theo cặp (100 = 4 × 25), ước nhỏ không bao giờ vượt căn. return false giữa vòng lặp nghĩa là "biết đáp án rồi, khỏi kiểm tiếp".

Chạy tay

n = 11: thử i = 2 (11 % 2 = 1), i = 3 (11 % 3 = 2), i <= 3.31 hết vòng → true. n = 10: i = 2, 10 % 2 = 0 → false ngay. Dãy 2 3 4 5 10 11 → giữ 2 3 5 11, dem = 4. Khớp output mẫu.

Bẫy hay gặp

Tự luyện

  1. Đếm số không nguyên tố trong mảng.
  2. In ra số nguyên tố lớn nhất (gợi ý: giữ biến max).

Bài 6. FibonacciTrongMang — sinh dãy để đối chiếu

Đề nói gì

Giống bài 5 nhưng lọc số thuộc dãy Fibonacci (0, 1, 1, 2, 3, 5, 8, 13...). Ví dụ 1 2 4 5 8 13 20 → giữ 1 2 5 8 13.

Từ mới

Ý tưởng

Hàm laFibonacci(x) sinh dãy từ đầu tới khi chạm/xuyên x, trúng thì true. main nhập mảng rồi lọc y hệt bài 5.

Code giải thích từng dòng

public static boolean laFibonacci(int x) {
    if (x < 0) return false;
    int a = 0, b = 1;                 // hai số đầu dãy
    while (a < x) {                   // chưa tới x thì bước tiếp
        int tiep = a + b;             // số mới = tổng hai số cũ
        a = b;                        // trượt cửa sổ lên 1 nấc
        b = tiep;
    }
    return a == x;                    // dừng vì bằng (trúng) hay vượt (trượt)
}

Chạy tay

x = 5: (0,1) → (1,1) → (1,2) → (2,3) → (3,5), a = 5 dừng, 5 == 5 → true. x = 4: ... → (3,5), a = 3 < 4? 3 < 4 đúng nên bước tiếp → (5,8), a = 5, dừng, 5 == 4 sai → false. Dãy mẫu giữ đúng 1 2 5 8 13.

Bẫy hay gặp

Tự luyện

  1. In 10 số Fibonacci đầu tiên.
  2. Đếm số vừa nguyên tố vừa Fibonacci trong mảng.

Bài 7. SapXepNgauNhien — sắp xếp nổi bọt

Đề nói gì

Sinh 10 số ngẫu nhiên 0–99, in ra, sắp xếp tăng dần, in lại.

Từ mới

Ý tưởng

  1. Sinh mảng.
  2. In mảng chưa sắp.
  3. Nổi bọt n - 1 lượt.
  4. In mảng đã sắp.

Code giải thích từng dòng

for (int i = 0; i < n - 1; i++) {          // lượt i: còn n-1-i cặp cần xét
    for (int j = 0; j < n - 1 - i; j++) {  // đi từng cặp kề nhau
        if (a[j] > a[j + 1]) {             // sai thứ tự?
            int tmp = a[j];                // đổi chỗ qua biến tạm
            a[j] = a[j + 1];
            a[j + 1] = tmp;
        }
    }
}

Đổi chỗ cần tmp như đổi nước hai cốc cần cốc thứ ba. j < n - 1 - i vì a[j+1] không được vượt mảng, và i số cuối đã đúng chỗ khỏi xét lại.

Chạy tay (mảng 4 số cho gọn)

[3, 1, 2]: lượt 0: (3,1) đổi → [1, 3, 2], (3,2) đổi → [1, 2, 3]; lượt 1: (1,2) đúng, bỏ qua. Xong sau 2 lượt = n - 1.

Bẫy hay gặp

Tự luyện

  1. Sắp giảm dần (đổi 1 dấu).
  2. Đếm số lần đổi chỗ đã xảy ra.

Phụ lục OOP: vì sao Lab 01 chưa có object

Bài nào cũng static — nghĩa là gì

Để ý 7 bài Lab 01: mọi hàm đều static, không có new ở đâu cả. static nghĩa là "của lớp, không của object nào". laNguyenTo(11) gọi thẳng tên hàm vì nó không đụng thuộc tính của object nào — nó chỉ nhận số, trả đúng/sai.

Khi nào cần object

Quy tắc ngón tay cái: hàm nào không nhớ gì giữa các lần gọi (nguyên tố, giai thừa, Fibonacci) thì static là đủ. Khi dữ liệu cần sống lâu và đi cùng nhau (tử+mẫu của một phân số, tên+điểm của một sinh viên), phải gói thành object — đó là Lab 02. Lab 01 là nền: vòng lặp, mảng, hàm — gạch nền để Lab 02 xây nhà OOP lên.

Tham chiếu null là gì (đọc trước để khỏi sốc Lab 03)

Biến object không cầm object thật mà cầm địa chỉ (tham chiếu). Khai báo PS p; mà chưa new thì p là null — tờ giấy ghi địa chỉ trống. Gọi p.cong(q) lúc này là đi theo địa chỉ trống → nổ NullPointerException. Mảng new SV[5] cũng vậy: 5 tờ giấy trống, phải new từng bạn rồi mới gọi phương thức. Gặp lại ở Lab 03–05.