Backend

99 Ngày Java — Ngày 10: Phương thức & truyền tham số

SSite Admin
6 tháng 08, 2026 7 phút đọc 70 lượt xem
99 Ngày Java — Ngày 10: Phương thức & truyền tham số

Chín ngày qua, mọi code của chúng ta đều chen chúc trong main — tính điểm cũng main, đảo mảng cũng main. Hôm nay ta học cách đóng gói logic thành phương thức (method): đặt tên cho một việc, gọi lại trăm lần không copy-paste. Kèm theo là hai chủ đề phỏng vấn kinh điển: overloading — nhiều method trùng tên, và sự thật đằng sau câu thần chú "Java luôn pass-by-value" mà Ngày 09 đã hứa.

Sketchnote Ngày 10: phương thức trong Java — khai báo, overloading, pass-by-value, phạm vi biến

Khai báo method: đặt tên cho một việc

Method là một cỗ máy nhỏ có tên: nhận nguyên liệu (tham số), làm đúng một việc, và có thể trả về thành phẩm (giá trị trả về). Cú pháp gồm bốn mảnh:

// [modifier] [kiểu trả về] [tên](tham số) { thân — và return nếu có trả về }
static int tong(int a, int b) {      // nhận 2 int, TRẢ VỀ int
    return a + b;                    // return: trả kết quả VÀ thoát method
}

static void chao(String ten) {       // void = không trả về gì
    System.out.println("Chào " + ten + "!");
    // return; — được phép, chỉ để thoát sớm
}

public static void main(String[] args) {
    int kq = tong(3, 4);             // GỌI: đối số 3, 4 → tham số a, b
    chao("Mọt Dev");                 // 7 được gán vào kq
    System.out.println(tong(kq, 10)); // 17 — kết quả dùng trực tiếp được
}
  • Tham số (parameter) là biến khai trong ngoặc; đối số (argument) là giá trị thật lúc gọi — 34 rót vào ab theo đúng thứ tự.

  • return làm hai việc cùng lúc: trả kết quảthoát ngay — code sau return không bao giờ chạy. Method khai kiểu trả về thì mọi nhánh (nhớ if–else Ngày 06) đều phải dẫn tới một return.

  • Tên method dùng camelCase động từ: tinhTong, inBangDiem — đọc phát biết làm gì. Một method chỉ nên làm một việc; dài quá một màn hình là dấu hiệu nên tách.

  • Khoan thắc mắc chữ static — nó nghĩa là "thuộc về lớp, gọi không cần đối tượng". Ngày 11–18 sẽ trả lời trọn vẹn; tạm thời cứ viết static cho method gọi từ main.

Overloading — trùng tên, khác chữ ký

Java cho phép nhiều method cùng tên miễn danh sách tham số khác nhau (khác số lượng hoặc khác kiểu) — gọi là overloading. Compiler nhìn đối số lúc gọi để chọn bản khớp nhất, ngay tại thời điểm biên dịch:

static int    max(int a, int b)       { return a > b ? a : b; }
static double max(double a, double b) { return a > b ? a : b; }
static int    max(int a, int b, int c) { return max(max(a, b), c); }

max(3, 7)        // → bản int:    7
max(2.5, 1.5)    // → bản double: 2.5
max(1, 9, 4)     // → bản 3 tham số: 9
max(3, 7.0)      // → bản double! int 3 được widening (Ngày 05) thành 3.0

// ✗ KHÔNG phải overload — chỉ khác kiểu trả về thì compiler chịu:
// static double max(int a, int b) { ... }   → lỗi biên dịch!
  • Bộ "tên + danh sách kiểu tham số" gọi là chữ ký (signature)kiểu trả về không thuộc chữ ký, nên chỉ khác kiểu trả về là lỗi biên dịch.

  • Trường hợp max(3, 7.0) cho thấy overloading bắt tay với ép kiểu tự động Ngày 05: không có bản (int, double) nên 3 được widening lên double.

  • Bạn đã dùng overloading từ Ngày 01 mà không biết: println có hơn 10 bản — nhận int, String, char[]… đều in được là nhờ vậy.

  • Còn "một method nhận bao nhiêu đối số cũng được" kiểu String.join Ngày 09? Đó là varargs — hẹn Ngày 25.

Pass-by-value — sự thật đằng sau câu thần chú

Câu hỏi phỏng vấn quốc dân: "Java pass-by-value hay pass-by-reference?" Đáp án gọn: luôn luôn pass-by-value — mọi đối số đều bị copy khi rót vào tham số. Nhưng cái được copy là gì mới là chỗ thú vị: với primitive là giá trị, còn với object (mảng Ngày 08, chuỗi Ngày 09…) là tham chiếu — và bản sao tham chiếu vẫn trỏ về cùng một đối tượng:

static void tangSo(int x)      { x = x + 100; }          // sửa BẢN SAO
static void suaMang(int[] m)   { m[0] = 99; }            // sửa NỘI DUNG chung
static void thayMang(int[] m)  { m = new int[] { 7 }; }  // gán lại BẢN SAO tham chiếu

public static void main(String[] args) {
    int so = 1;
    tangSo(so);
    System.out.println(so);        // 1 — primitive: copy GIÁ TRỊ, bản gốc bình an

    int[] mang = { 1, 2, 3 };
    suaMang(mang);
    System.out.println(mang[0]);   // 99 — copy THAM CHIẾU: hai bên nhìn CÙNG mảng!

    thayMang(mang);
    System.out.println(mang[0]);   // vẫn 99 — gán lại chỉ đổi bản sao tham chiếu
}
  • Sửa nội dung qua bản sao tham chiếu → bản gốc thấy thay đổi (cùng nhìn một mảng trên heap).

  • Gán lại tham số → chỉ xoay bản sao sang chỗ khác, bản gốc không suy chuyển — vì thế không thể viết method "swap hai biến int" trong Java!

  • Ghép với Ngày 09: String bất biến nên truyền đi đâu cũng an toàn tuyệt đối — không ai "sửa nội dung" được, chỉ có thể gán lại bản sao của họ.

Bàn phím và màn hình code — method là những cỗ máy nhỏ được đặt tên, lắp ghép thành chương trình lớn

Phạm vi biến (scope)

Method sinh ra ranh giới sống cho biến: biến khai ở đâu thì chỉ sống trong cặp ngoặc nhọn đó — gọi là biến local:

public static void main(String[] args) {
    int diem = 9;                    // sống từ đây đến hết main

    if (diem >= 5) {
        String xepLoai = "Đậu";      // sống TRONG khối if — ra ngoài là biến mất
        System.out.println(xepLoai);
    }
    // System.out.println(xepLoai);  // ✗ lỗi biên dịch: ngoài phạm vi!

    for (int i = 0; i < 3; i++) { }  // i chỉ sống trong vòng for (Ngày 07)
    // System.out.println(i);        // ✗ i đã "hết đời"

    int chuaGan;
    // System.out.println(chuaGan);  // ✗ biến local KHÔNG có giá trị mặc định!
}
  • Tham số cũng là biến local của method — x trong tangSo chết khi method kết thúc, đó chính là lý do pass-by-value "vô hại" ở trên.

  • Biến local không có giá trị mặc định — khác với mảng Ngày 08 (phần tử tự về 0). Dùng trước khi gán là lỗi biên dịch, không phải lỗi runtime.

  • Phạm vi càng hẹp càng tốt: khai biến sát nơi dùng, vòng đời ngắn thì bug ít chỗ trốn.

Bài tập nhỏ

  • Viết static boolean laSoNguyenTo(int n) rồi dùng nó in mọi số nguyên tố dưới 100 (vòng lặp Ngày 07 tái xuất).

  • Overload dienTich(double canh) (hình vuông) và dienTich(double dai, double rong) (chữ nhật) — gọi cả hai.

  • Viết static void tangLuong(double[] luong, double phanTram) tăng lương cả mảng — giải thích vì sao bản gốc thay đổi dù Java pass-by-value.

  • Thử viết method "hoán đổi hai biến int" — chứng kiến nó bất lực, rồi giải thích bằng bài học hôm nay.

Kết luận

Bạn vừa có công cụ tổ chức code đầu tiên: method — khai báo với tham số và giá trị trả về, overload để một tên phục vụ nhiều kiểu dữ liệu, hiểu pass-by-value đủ sâu để trả lời phỏng vấn lẫn tránh bug, và scope giữ biến sống đúng chỗ. Đây cũng là viên gạch cuối của Giai đoạn 1 — nền tảng ngôn ngữ. Từ Ngày 11, ta bước vào vùng đất lớn nhất của Java: lập trình hướng đối tượng — bắt đầu với lớp và đối tượng, nơi dữ liệu và method về chung một nhà. Hẹn gặp lại!

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Java — Ngày 58: Optional

Optional là công cụ cho kiểu trả về — không field, không tham số, không bọc collection; chuỗi map/flatMap/filter thay kim tự tháp if; orElse luôn tính tham số còn orElseGet lười, orElseThrow cho “không có là lỗi” — và các anti-pattern isPresent + get, orElse(null).

23 thg 9, 20269 phút9
99 Ngày Spring — Ngày 58: Spring Boot Actuator

Boot 3 mặc định chỉ lộ health; bản đồ endpoint an toàn và nguy hiểm; HealthIndicator tự viết với liveness tách khỏi readiness cho Kubernetes, info từ build và git, metrics Micrometer với tag hữu hạn — và ba lớp khóa: cổng riêng, SecurityFilterChain với EndpointRequest, exclude env/heapdump/threaddump.

23 thg 9, 202610 phút5
So sánh EasyExcel và Apache Fesod: khác biệt thật nằm ở đâu?

Fesod là hậu duệ của EasyExcel, cùng engine SAX/SXSSF. Khác biệt kiểm chứng được: vòng đời dự án, POI 5.2.5 vs 5.5.1, các API mới (numRows, csv(), headerMergeStrategy) và độ bền với file xấu — không phải hiệu năng gấp nhiều lần.

23 thg 9, 20268 phút1