Java Bài 18: Phương Thức Của Lớp – YellowCode.Books

Rate this item :

Rating: 4.9/5. From 45 votes.

Please wait…

Được chỉnh sửa ngày 14/06/2021 .
Chào mừng những bạn đến với bài học kinh nghiệm Java số 18, bài học kinh nghiệm về Phương thức của Lớp. Bài học này nằm trong chuỗi bài học kinh nghiệm lập trình ngôn từ Java của Yellow Code Books .

Như đã hứa, hôm nay mình sẽ nói đến các kiến thức liên quan đến Phương thức trong một lớp.

Phương Thức Của Lớp

Phương thức, hay còn gọi là Method. Một số tài liệu khác gọi là Hàm. Các phương thức này là các hành động của một lớp. Sau này khi các đối tượng được tạo ra từ lớp, thì các hành động này cũng chính là các hành động hay các hành vi của đối tượng đó.

Cú pháp của một phương pháp như sau .

[kả_năng_truy_cập] kiểu_trả_về tên_phương_thức ( [tham_số] ) {
     // Các dòng code
}
  • khả_năng_truy_cập

    mình sẽ nói ở bài sau, tuy nhiên, bạn cũng nên biết

    khả_năng_truy_cập của một phương thức hoàn toàn giống với khả_năng_truy_cập của thuộc tính ở bài học trước

    , nên chúng ta sẽ chỉ cần nói một lần cho cả hai ở bài học sau mà thôi.

  • tên_phương_thức là tên của phương thức, các quy tắc đặt tên cho phương thức tương tự như quy tắc đặt tên cho tên_thuộc_tính ở bài hôm trước, hay tên_biến ở bài học về biến và hằng.
  • tham_số là các đối số truyền vào cho phương thức, sẽ được nói rõ trong bài học hôm nay.

Thực Hành Tạo Các Phương Thức

Tiếp tục với project OOPLearning hôm trước, khi mà chúng ta đã tạo một lớp HinhTron ở một file .java tách biệt, rồi thêm vào cho lớp này các thuộc tính như những gì chúng ta cùng nhau thực hành ở bài trước nữa, bài học số 16.

Hôm nay tất cả chúng ta sẽ thêm vào những phương pháp quen thuộc từ bài học kinh nghiệm số 16 mà tất cả chúng ta cũng đã làm quen. Một lần nữa hoàn toàn có thể bạn sẽ chưa hiểu hết từng thành phần trong khai báo một phương pháp, bạn chỉ đã hiểu tường tận những dòng code bên trong từng phương pháp mà thôi. Nhưng không sao, bạn cứ code đi nhé, tất cả chúng ta sẽ dần quen thuộc với phương pháp ở những phần sau của bài học kinh nghiệm ngày hôm nay .
Và code của bài hôm trước sẽ trông như thế này của bài học kinh nghiệm ngày hôm nay ( tuy hình ảnh này được chụp từ Eclipse nhưng bạn hoàn toàn có thể code tương tự như nếu dùng InteliJ ) .
Hãy thêm các dòng định nghĩa các phương thức cho lớp HinhTronHãy thêm các dòng định nghĩa các phương thức cho lớp HinhTronHãy thêm các dòng định nghĩa các phương thức cho lớp HinhTron

Kiểu Trả Về Của Phương Thức

Khi khai báo một phương thức, bạn buộc phải chỉ định một kiểu_trả_về. Kiểu trả về này thường là kết quả cuối cùng mà phương thức đó thực hiện, nó có thể là một kiểu dữ liệu nguyên thủy, nó có thể là một giá trị boolean, nó có thể là một mảng, hoặc thậm chí nó có thể là một đối tượng. Mục đích của việc trả về này, là giúp cho các đối tượng nào đó bên ngoài lớp, hoặc các phương thức khác bên trong lớp, có thể nhận được kết quả đó để thực hiện một mục đích nào đó. Chúng ta có thể xem kết quả trả về là “đầu ra” của phương thức, khi mà “đầu vào” chính là tham_số mình sẽ nói ở mục sau.

Và như mình có nói, một phương thức bắt buộc phải khai báo một kiểu_trả_về. Khi bắt đầu trả về một kết quả, chúng ta dùng từ khóa return. Bạn xem các ví dụ sau để hiểu rõ cách sử dụng của lệnh này.

Thực chất các kiểu_trả_về đều có cách sử dụng như nhau, nhưng với từng ví dụ sau mình sẽ tách các kiểu_trả_về ra làm từng mục, để các bạn dễ tiếp cận.

Ví Dụ Kiểu Trả Về Là Một Kiểu Nguyên Thủy

Bạn có còn nhớ các kiểu dữ liệu nguyên thủy là gì không, nếu quên thì xem lại mục Kiểu Dữ Liệu Của Biến ở bài học số 4 nhé.

Với ví dụ ở mục này, mình xây dựng ba phương thức bên trong lớp HinhTron có khai báo các kiểu_trả_về, bạn chú ý các source code có comment nhé.

public class HinhTron {
 
    /**
     * Demo các phương thức có khai báo kiểu trả về.
     * Bạn hãy chú ý các phương thức sau:
     * - getBanKinh()
     * - tinhChuVi()
     * - vongTronLon()
     */
 
    final float PI = 3.14f;
 
    float r;
    float cv;
    float dt;
 
    void nhapBanKinh() {
        System.out.println("Hãy nhập vào Bán kính Hình tròn: ");
        Scanner scanner = new Scanner(System.in);
        r = scanner.nextFloat();
    }
 
    void tinhDienTich() {
        dt = PI * r * r;
    }
 
    // Phương thức này có kiểu trả về là một dữ liệu kiểu float,
    // bạn chú ý dòng code return, dòng này chỉ định giá trị mà hàm sẽ trả về,
    // giá trị trả về phải cùng kiểu với khai báo trả về của hàm.
    float getBanKinh() {
        return r;
    }
 
    // Phương thức tinhChuVi() được chuyển sang có kiểu trả về.
    // Bạn có thể để bao nhiêu dòng code trước khi quyết định gọi return.
    float tinhChuVi() {
        cv = 2 * PI * r;
        return cv;
    }
 
    // Phương thức này trả về kết quả là một kiểu boolean,
    // nếu bán kính lớn hơn 10 sẽ trả về true (vòn tròn lớn),
    // ngược lại sẽ trả về false (vòn tròn không lớn).
    // Bạn thiết kế bao nhiêu return bên trong một hàm có kiểu trả về cũng được,
    // chỉ cần đảm bảo kết thúc một hàm kiểu này luôn có return là được.
    boolean vongTronLon() {
        if (r > 10) {
            return true;
        } else {
            return false;
        }
    }
}

Và rồi bước tiếp theo ở hàm main() bên trong lớp MainClass, chúng ta sẽ sử dụng kết quả trả về của các hàm bên trong lớp HinhTron để làm một vài tác vụ nào đó (như in ra console chẳng hạn).

public class MainClass {
 
    public static void main(String[] args) {
        // Khai báo đối tượng hinhTron, từ lớp HinhTron
        HinhTron hinhTron = new HinhTron();
 
        // hinhTron kêu người dùng nhập bán kính và lưu lại
        hinhTron.nhapBanKinh();
 
        // Nếu hinhTron có bán kính lớn, in ra lỗi. Ngược lại sẽ tính chu vi hinhTron đó
        if (hinhTron.vongTronLon()) {
            // Lấy kết quả trả về từ phương thức getBanKinh() của HinhTron ra dùng
            System.out.println("Hình tròn có bán kính " + hinhTron.getBanKinh() + " quá lớn!");
        } else {
            // Lấy kết quả trả về từ phương thức tinhChuVi() của HinhTron ra dùng
            float chuvi = hinhTron.tinhChuVi();
            System.out.println("Chu vi Hình tròn: " + chuvi);
        }
    }
 
}

Đơn giản đúng không. Bạn chỉ cần nhớ một số nguyên tắc khi khai báo kiểu_trả_về của một phương thức:

  • Nếu như phương thức có khai báo

    kiểu_trả_về

    , thì hiển nhiên phương thức đó

    phải kết thúc với một hoặc nhiều câu lệnh return

    . Nếu không có

    return

    đối với phương thức này, hệ thống sẽ báo lỗi đấy nhé.

  • kiểu_trả_về của phương thức phải cùng kiểu với biểu thức (hoặc giá trị) sau câu lệnh

     

    return

    , như bạn đã thấy ở ví dụ trên. Nếu khác nhau về kiểu, sẽ bị báo lỗi luôn.

  • Bạn có thể ép kiểu tường minh một biểu thức (hoặc giá trị) trước khi

    return

    nó. Mình làm tạm một ví dụ sau.

/**
* Demo việc ép kiểu một biểu thức (hoặc một giá trị)
* trước khi return nó
*/
int getBanKinh() {
    return (int) r;
}
  • Với một phương thức có khai báo

    kiểu_trả_về

    , bạn có thể không cần sử dụng đến kiểu trả về của phương thức đó khi gọi đến từ bên ngoài. Mình lấy ví dụ trên đây, hàm 

    tinhChuVi()

    có khai báo kiểu trả về là một

    float

    , nhưng ở hàm

    main()

    bạn có thể không cần dùng đến kiểu trả về này. Mình minh họa bằng code như sau.

hinhTron.tinhChuVi();
hinhTron.inChuVi();

Ví Dụ Kiểu Trả Về Là Một Đối Tượng

Với ví dụ này, mọi thứ sẽ không khác gì so với bạn khai báo một kiểu_trả_về là một biến nguyên thủy cả, chỉ là lần này bạn thử trả về là một đối tượng. Bạn xem ví dụ bên dưới.

Giả sử HinhTron mà chúng ta xây dựng sẽ được vẽ đâu đó lên màn hình, như vậy phải cần người dùng nhập vào tọa độ (x, y) của Hình tròn đó. Và như tư duy của Hướng đối tượng, mình chỉ định Tọa độ cũng là một đối tượng cần quản lý. Đầu tiên mình sẽ định nghĩa một lớp có tên ToaDo, lớp này có hai thuộc tính là xy, và không có phương thức nào cả. Bạn thử tạo xem nhé.

Thử tạo thêm một lớp ToaDoThử tạo thêm một lớp ToaDoThử tạo thêm một lớp ToaDo

Sau đó ở lớp HinhTron, mình khai báo một thuộc tính với kiểu dữ liệu là ToaDo, HinhTron sẽ hỗ trợ nhập tọa độ x, y vào từ console. Và cuối cùng là hàm getToaDo() trả ra ngoài kiểu dữ liệu là ToaDo.

public class HinhTron {
 
    /**
     * Demo phương thức có khai báo kiểu trả về là một đối tượng.
     * Bạn hãy chú ý đến phương thức:
     * - getToaDo()
     */
 
    float r;
    ToaDo toaDo;
 
    // Hàm này quen thuộc từ mấy bài học rồi nhé
    void nhapBanKinh() {
        System.out.println("Hãy nhập vào Bán kính Hình tròn: ");
        Scanner scanner = new Scanner(System.in);
        r = scanner.nextFloat();
    }
 
    // Hàm này giúp nhập tọa độ vào lớp ToaDo, bạn nhớ khởi tạo nó nhé
    void nhapToaDo() {
        // Bạn phải khởi tạo lớp ToaDo, hay bất kỳ lớp nào bằng từ khóa new trước khi sử dụng
        toaDo = new ToaDo();
        Scanner scanner = new Scanner(System.in);
 
        System.out.println("Hãy nhập vào Tọa độ Hình tròn: ");
        System.out.println("x = ");
        toaDo.x = scanner.nextInt();
 
        System.out.println("y = ");
        toaDo.y = scanner.nextInt();
    }
 
    float getBanKinh() {
        return r;
    }
 
    // Phương thức này trả về một đối tượng ToaDo
    ToaDo getToaDo() {
        return toaDo;
    }
 
}

Còn ở hàm main(), mình thử xuất ra xem kết quả nhập tọa độ vào cho HinhTron có đúng chưa.

public class MainClass {
 
    public static void main(String[] args) {
        // Khai báo đối tượng hinhTron, từ lớp HinhTron
        HinhTron hinhTron = new HinhTron();
 
        // hinhTron kêu người dùng nhập bán kính và lưu lại
        hinhTron.nhapBanKinh();
 
        // hinhTron kêu người dùng nhập tọa độ x, y và lưu lại
        hinhTron.nhapToaDo();
 
        // Xuất kết quả mà người dùng vừa nhập
        float banKinh = hinhTron.getBanKinh();
        ToaDo toaDo = hinhTron.getToaDo(); // Không cần từ khóa new, vì đối tượng đã được khởi tạo bên trong HinhTron rồi
        System.out.println("Bạn vừa nhập một Hình tròn có Bán kính: " + hinhTron.getBanKinh() + "\n" +
                "Và tọa độ:\n" +
                "- x = " + toaDo.x + "\n" +
                "- y = " + toaDo.y);
    }
 
}

Ví Dụ Kiểu Trả Về Là void

Chắc chắn là các bạn sẽ thắc mắc nhiều lắm nếu mình không đề cập đến mục này. Vì trong quá trình thực hành bạn nhìn thấy kiểu trả về nhiều nhất đó chính là kiểu void!?!

Như mình có khẳng định chắc nịch bên trên rằng một phương thức bắt buộc phải khai báo một kiểu_trả_về. Nhưng với nhu cầu thực tế, không phải lúc nào chúng ta cũng cần phải trả ra khỏi phương thức một kết quả, minh chứng rõ ràng nhất là các hàm inChuVi(), inDienTich() mà chúng ta đã làm quen, các hàm này chỉ cần các câu lệnh in ra console, và rồi hết.

Vậy để giải quyết vấn đề không cần kiểu trả về này, Java (hay C/C++ cũng thế) đã định nghĩa cho chúng ta một kiểu dữ liệu hơi lạ, kiểu void. Kiểu dữ liệu này thực chất là kiểu rỗng, nó không là gì cả, nó không chứa bất kỳ dữ liệu nào. Do đó chúng ta cứ hiểu rằng nếu một phương thức có kiểu_trả_vềvoid, thì có nghĩa là nó không có kiểu trả về. Và vì là một phương thức không có kiểu trả về, nên bạn cũng không cần câu lệnh return bên trong phương thức này.

Chắc có lẽ mình không cần đưa thêm ví dụ nào về kiểu void này, các bài thực hành đều dùng nhiều đến nó. Mình chỉ có một ý nho nhỏ, rằng trong một phương thức với kiểu void, bạn hoàn toàn có thể dùng từ khóa return để kết thúc. Cách sử dụng câu lệnh return đã được mình nhắc sơ qua từ bài học số 10, và ý nghĩa của return trong một phương thức void không khác với break trong một Câu lệnh điều khiển luồng cả.

Để ví dụ về cách sử dụng return trong phương thức void, mình xin mượn ý tưởng tìm số nguyên tố từ Bài Thực Hành Số 1 của bài học số 10. Mình biến chế một xíu, đó là khi nào tìm thấy số nguyên tố đầu tiên, thì sẽ in số đó ra console và kết thúc ngay phương thức đó.

void timSoNguyenTo() {
    for (int number = 2; number <= 10000; number++) {
        boolean isPrime = true; // Thay vì biến count, dùng biến này để kiểm tra số nguyên tố
        for(int j = 2; j <= Math.sqrt(number); j++) {
            if (number % j == 0) {
                // Chỉ cần một giá trị được tìm thấy trong khoảng này,
                // thì number không phải số nguyên tố
                isPrime = false;
                break; // Thoát ngay và luôn khỏi for (vòng for bên ngoài vẫn chạy)
            }
        }
        if (isPrime) {
            // Nếu isPrime còn giữ được sự "trong trắng" đến cùng thì đó là số nguyên tố,
            // in nó ra rồi thoát
            System.out.println(number);
            return;
        }
    }
}

Các Tham Số Truyền Vào Của Phương Thức

Chúng ta vừa đi qua kiểu_trả_về, điều tiếp theo chúng ta quan tâm đó là các tham_số truyền vào.

Bạn nên biết là, một phương thức có thể không có tham số truyền vào. Điều này mình không cần đưa thêm ví dụ nào cả, vì các phương thức mà bạn thực hành từ mấy bài học OOP đều như vậy cả. Khi đó sẽ không có gì bên trong cặp dấu () sau tên phương thức, như là tinhChuVi(), tinhDienTich().

Còn với thể loại phương pháp có tham số truyền vào. Bạn hoàn toàn có thể truyền vào nó bất kỳ kiểu tài liệu nào, từ một kiểu nguyên thủy, một mảng, hay một đối tượng người tiêu dùng nào đó. Bạn hoàn toàn có thể truyền vào nó nhiều tham số, mỗi tham số như vậy cách nhau bởi dấu phẩy. Bạn hoàn toàn có thể xem ví dụ sau .

public class HinhTron {
 
    /**
     * Demo phương thức có tham số truyền vào.
     * Bạn hãy chú ý đến phương thức:
     * - setBanKinh(float r)
     * - setToaDo(ToaDo toaDo)
     * - setToaDo(int x, int y)
     */
 
    float r;
    ToaDo toaDo;
 
    // Phương thức này có một tham số truyền vào là kiểu nguyên thủy
    void setBanKinh(float r) {
        // Gán dữ liệu từ tham số r vào thuộc tính r (this.r) của lớp
        this.r = r;
    }
 
    // Phương thức này có một tham số truyền vào là kiểu đối tượng
    void setToaDo(ToaDo toaDo) {
        this.toaDo = toaDo;
    }
 
    // Phương thức này có hai tham số truyền vào
    void setToaDo(int x, int y) {
        // Phải new một đối tượng trước khi dùng đến các thuộc tính
        this.toaDo = new ToaDo();
 
        // Gán dữ liệu từ từng tham số vào cho this.toaDo
        this.toaDo.x = x;
        this.toaDo.y = y;
    }
 
    void xuatBanKinh() {
        System.out.println("Bán kính Hình tròn: " + this.r);
    }
 
    void xuatToaDo() {
        System.out.println("Tọa độ Hình tròn: ");
        System.out.println("x = " + this.toaDo.x);
        System.out.println("y = " + this.toaDo.y);
    }
 
}

Có thể có bạn lần tiên phong làm quen với phương pháp có tham số truyền vào này. Vậy mình có 1 số ít ý nhỏ sau đây giúp bạn nào còn kinh ngạc sẽ có một cách tiếp cận tốt hơn .

  • Tham số truyền vào cho từng phương thức chính là “cửa ngõ” của phương thức. Nó giúp cho các thành phần bên ngoài lớp có cơ hội truyền dữ liệu vào bên trong một lớp, giúp lớp đó có “nguyên liệu” để thực hiện các logic. Như ví dụ ngay trên đây, nếu bạn xây dựng lớp

    HinhTron

    tự nó kêu người dùng nhập bán kính và các tọa độ thì không sao, nhưng nếu bạn muốn các hàm nhập nằm ở ngoài lớp, rồi sau đó truyền dữ liệu này vào trong

    HinhTron

    , thì bạn xây dựng như ví dụ trên.

  • Bạn có quyền khai báo nhiều tham số truyền vào cho một phương thức, mỗi tham số được định nghĩa một kiểu dữ liệu. Nhưng khi ở đâu đó gọi đến phương thức này, bạn phải truyền đầy đủ số lượng tham số và đúng với kiểu dữ liệu đã khai báo. Ở mục sau nữa chúng ta sẽ xem đến cách gọi đến phương thức có tham số truyền vào.
  • Nếu như với ngôn ngữ khác, như C/C++ chẳng hạn, có quan tâm đến hai loại tham số, đó là

    Tham chiếu

    Tham trị

    . Với tham số là Tham chiếu, thì khi ở đâu đó gọi đến phương thức và truyền biến vào phương thức, nếu bên trong phương thức đó có “lỡ tay” làm thay đổi giá trị của tham số, thì ở nơi gọi đến phương thức đó, biến truyền vào cũng sẽ bị thay đổi. Còn với tham số là Tham trị, thì biến truyền vào sẽ độc lập với tham số bên trong phương thức, có nghĩa là phương thức đó cứ thay đổi giá trị của tham số thoải mái, mà biến bên ngoài vẫn không đổi.

    Với Java, chỉ có duy nhất một loại tham số kiểu Tham trị, không có tham số kiểu Tham chiếu bạn nhé

    . Nếu bạn nào còn mơ hồ mục này, hãy đọc bài viết này, mình đã giải thích cặn kẽ hai loại tham số này rồi.

  • Trong một lớp, có thể có nhiều phương thức có cùng tên nhưng khác tham số truyền vào, như các phương thức 

    setToaDo(ToaDo toaDo)

    và 

    setToaDo(int x, int y)

    . Điều này rất bình thường trong một lớp, và bạn đừng lo lắng nhiều quá, chúng ta sẽ có một bài học viết riêng về trường hợp này.

Truy Xuất Đến Phương Thức Của Lớp Từ Bên Ngoài

Cũng tương tự như như bên thuộc tính. Đó là :

  • Khi bạn ở đâu đó bên ngoài một lớp, và muốn truy xuất đến các phương thức của lớp đó, bạn vẫn sẽ sử dụng toán tử

    “.”

    .

  • Tuy nhiên không phải lúc nào một lớp cũng cho phép bạn truy xuất đến các phương thức của nó đâu. Nó tùy thuộc vào

    khả_năng_truy_cập

    mà mình sẽ nói cụ thể ở bài sau.

Như các ví dụ ở mục Kiểu Trả Về Của Phương Thức trên đây, các bạn đã hình dung đến việc truy xuất đến một phương thức không có tham số truyền vào rồi. Nên mình sẽ tập trung vào việc truy xuất đến một phương thức có tham số truyền vào cho ví dụ bên dưới nhé. Mình lấy lại ví dụ trên, khi khai báo một HinhTron với các phương thức có tham số truyền vào, giờ là lúc chúng ta thử xem việc gọi đến nó ở hàm main() là như thế nào.

public class MainClass {
 
    public static void main(String[] args) {
        // Khai báo đối tượng hinhTron, từ lớp HinhTron
        HinhTron hinhTron = new HinhTron();
 
        // Kêu người dùng nhập vào Bán kính Hình tròn, rồi truyền vào cho HinhTron
        System.out.println("Hãy nhập vào Bán kính Hình tròn: ");
        Scanner scanner = new Scanner(System.in);
        float bk = scanner.nextFloat();
        hinhTron.setBanKinh(bk);
 
        // Kêu người dùng nhập vào Tọa độ Hình tròn, rồi truyền vào cho HinhTron, với 2 cách
        System.out.println("Hãy nhập vào Tọa độ Hình tròn: ");
        System.out.println("x = ");
        ToaDo toaDo = new ToaDo();
        toaDo.x = scanner.nextInt();
        System.out.println("y = ");
        toaDo.y = scanner.nextInt();
        hinhTron.setToaDo(toaDo); // Cách 1, gọi đến phương thức có tham số là một đối tượng
        hinhTron.setToaDo(toaDo.x, toaDo.y); // Cách 2, gọi đến phương thức có tham số là hai kiểu nguyên thủy
 
        // In kết quả vừa nhập
        hinhTron.xuatBanKinh();
        hinhTron.xuatToaDo();
    }
 
}

Mình vừa trình bày xong kiến thức về sử dụng phương thức trong một lớp. Tất nhiên đây chưa phải tất cả những gì liên quan đến phương thức mà OOP mang lại cho chúng ta. Còn rất nhiều điều thú vị xoay quanh cách sử dụng phương thức nữa, các bạn chờ xem nhé.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:
– Đánh giá 5 sao ở mỗi bài viết nếu thấy thích.
– Comment bên dưới mỗi bài viết nếu có thắc mắc.
– Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
– Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.
– Ủng hộ blog theo hướng dẫn ở thanh bên phải để blog ngày càng phát triển hơn.

Bài Kế Tiếp

Mình tạm nợ lại kiến thức về khả_năng_truy_cập của các thuộc tính và phương thức trong một lớp. Bài học kế tếp sẽ là bài học về cách khai báo và sử dụng package bên trong Java, kiến thức về package này cũng quyết định không nhỏ đến khả_năng_truy_cập khi mà chúng ta cùng nói đến nó.