Java Bài 20: Phương Thức Khởi Tạo – Constructor

Posted by

Được chỉnh sửa ngày 28/6/2017.

Chào mừng các bạn đến với bài học Java thứ 20 trong chuỗi bài học về lập trình ngôn ngữ Java của Yellow Code Books.

Hôm nay thật là một ngày đẹp trời để cùng nhau xem qua khái niệm và cách sử dụng về Phương thức khởi tạo trong Java.

Tất nhiên bạn sẽ phát hiện ra rằng bài học này đang nói về một loại phương thức, vậy thì tại sao mình không kết hợp vào bài học về phương thức luôn cho rồi. Vâng, không riêng gì bạn đâu, có rất nhiều bạn đã thắc mắc với mình như vậy. Nhưng bạn biết không, phương thức mà bạn làm quen hôm nay sẽ hơi đặc biệt hơn một chút so với các phương thức mà chúng ta đã nói qua, đặc biệt như thế nào thì bạn hãy xem nội dung bên dưới nhé. Và vì nó đặc biệt, nó hơi khác, nó lại quan trọng nữa, nên mình tách loại phương thức này ra một bài học riêng, để các bạn có một sự tiếp cận thoải mái hơn, độc lập hơn, không bị nhập nhằng giữa phương thức bình thường và phương thức khởi tạo này.

Khái Niệm Phương Thức Khởi Tạo – Constructor

Phương thức khởi tạo, hay gọi Hàm khởi tạo cũng được, bạn cũng có thể gọi là Constructor, mình thì mình sẽ dùng constructor luôn cho ngắn gọn.

Thực chất thì constructor này cũng là một phương thức, nhưng nó đặc biệt ở chỗ là, ngay khi mà bạn khởi tạo một đối tượng bằng từ khóa new, thì constructor của đối tượng đó sẽ lập tức được gọi đến một cách tự động. Có nghĩa là nếu với phương thức bình thường, bạn phải gọi đến nó thông qua toán tử chấm (“.”) thì phương thức đó mới được thực thi, còn với constructor, ngay khi biên dịch đến từ khóa new, hệ thống sẽ thực thi một constructor tương ứng của đối tượng, tùy vào constructor nào mà bạn chỉ định.

Mục đích chính mà constructor mang lại, không gì khác ngoài tác dụng Khởi tạo. Constructor giúp đối tượng vừa được tạo ra đó, có cơ hội được khởi tạo các giá trị cho các thuộc tính bên trong nó. Hoặc có thể giúp đối tượng đó gọi đến các phương thức tương ứng khác nhằm khởi tạo các logic bên trong đối tượng.

Trước khi hiểu rõ hơn về cách sử dụng một constructor, chúng ta hãy xem cách khai báo chúng.

Khai Báo Constructor

Trước hết mình xin nói qua cú pháp cho một constructor, để bạn có thể mang ra so sánh với việc khai báo một phương thức bình thường ở bài 18, xem có khác gì không nhé. Cú pháp của một constructor như sau.

[khả_năng_truy_cập]  tên_phương_thức  () {
     // Các dòng code
}

Như vậy bạn cũng có thể thấy sự khác biệt, tuy nhiên mình cũng điểm qua các thành phần bên trong cú pháp trên cho nó rõ ràng.

– Đầu tiên, constructor không có kiểu_trả_về như phương thức bình thường nhé.

Khả_năng_truy_cập – Chúng ta sẽ nói về vấn đề này ở một bài khác, cùng với khả_năng_truy_cập vào các thuộc tính và phương thức bình thường của một lớp. Tuy nhiên trong bài học hôm nay, mình đều sẽ dùng public cho các constructor, nó có nghĩa là ở đâu cũng có thể dùng đến các constructor này.

tên_phương_thức – Khác với các phương thức bình thường, tên của constructor phải cùng với tên lớp. Để giúp phân biệt đâu là constructor và đâu là phương thức bình thường í mà.

các_tham_số_truyền_vào – Phần này thì giống với phương thức bình thường, không có gì để nói thêm.

Thực Hành Khai Báo Các Constructor Cho Lớp HinhTron

Chúng ta vẫn lấy project PackageLearning từ bài học trước, để cùng nhau tạo các constructor cho các đối tượng.

Bạn hãy lấy lớp HinhTron ra làm chuột bạch. Bạn chú ý đến hai constructor mà mình đưa ra ở ví dụ sau. Mình chỉ mới khai báo ra thôi, chưa code gì bên trong các constructor này cả, quy tắc khai báo các constructor này hoàn toàn tuân theo các gạch đầu dòng trên đây.

Bạn hãy thử nhìn vào hai constructor này rồi ghi nhớ và tự code lại nhé, nhớ là phải code lại, đừng copy/paste, bạn sẽ học được nhiều điều thông qua các dòng code cho constructor này đấy, tin mình đi.

public class HinhTron {

	/**
	 * Demo cách khai báo các constructor,
	 * các thuộc tính và phương thức vẫn được giữ nguyên như bài 19,
	 * bạn hãy chú ý các constructor được comment
	 */

	final float PI = 3.14f;

    float r;
    float cv;
    float dt;

    // Một constructor, chú ý không có kiểu trả về,
    // và constructor này không có tham số truyền vào
    public HinhTron() {
    	// Chúng ta khởi tạo gì đó sau
    }

    // Một constructor khác, cũng không có kiểu trả về,
    // nhưng có một tham số truyền vào
    public HinhTron(float r) {
    	// Chúng ta khởi tạo gì đó sau
    }

    public 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();
    }

    public void tinhChuVi() {
        cv = 2 * PI * r;
    }

    public void tinhDienTich() {
        dt = PI * r * r;
    }

    public void inChuVi() {
        System.out.println("Chu vi Hình tròn: " + cv);
    }

    public void inDienTich() {
        System.out.println("Diện tích Hình tròn: " + dt);
    }
}

Thông qua ví dụ trên đây, mình có thêm một vài ghi chú nữa bổ sung cho bốn cái gạch đầu dòng trên kia. Mình muốn các bạn code qua constructor xong rồi mới nói đến điều này để khỏi nhầm lẫn.

Trong một lớp, bạn hoàn toàn có thể có nhiều constructor, mỗi constructor như vậy phải khác tham số truyền vào (chứ không phải khác tên nhé, như trên kia có nói rằng constructor phải cùng tên với tên lớp, như vậy các constructor đều phải có tên giống nhau rồi). Bạn có thể xem lại ví dụ trên sẽ thấy có hai constructor, nhưng bạn có thể tạo thêm nhiều constructor khác cũng được.

Với một lớp có nhiều constructor, bạn hoàn toàn có thể từ constructor này gọi đến constructor khác, việc gọi đến này không tạo thêm một thể hiện mới của lớp, mà mục đích chính của việc này là để tận dụng lại các dòng code khởi tạo của các constructor mà thôi. Vấn đề này bạn sẽ hiểu rõ hơn ở bài học về cách sử dụng từ khóa this ở bài học sau,

– Dù cho lớp đó có bao nhiêu constructor đi nữa, thì khi khai báo đối tượng, bạn phải chỉ định một và chỉ một constructor mà thôi. Điều này khác với các phương thức bình thường khác, khi mà bạn có thể gọi đến bao nhiêu phương thức cũng được. Ở bước dưới đây nữa chúng ta cùng xem cách chỉ định một constructor cho đối tượng như thế nào nhé.

Một constructor chỉ được thực thi một lần khi từ khóa new được gọi. Bạn không thể nào thực thi lại một constructor trong suốt đời sống của đối tượng được nữa. Nếu như bạn muốn thực thi lại một constructor, thì bạn lại phải dùng từ khóa new, như vậy là bạn đã tạo ra một đối tượng mới rồi. Điều này cũng khác với các phương thức bình thường khác có khả năng gọi lại hoài được. Chính vì vậy mà nếu bạn có nhu cầu cần khởi tạo các giá trị thì cứ khởi tạo hết trong một constructor đi nhé.

– Và một ý nữa cũng khá quan trọng. Nếu bạn quên không khai báo constructor cho một lớp thì sao? Cũng giống như là từ các bài học trước tới giờ, bạn chỉ tạo các thuộc tính và các phương thức cho lớp, có tạo constructor cho chúng đâu! Thì khi này, hệ thống sẽ luôn ngầm tạo cho chúng ta một constructor không có tham số truyền vào, không có nội dung gì bên trong constructor đó, y như constructor đầu tiên của lớp HinhTron ở ví dụ trên đây. Như vậy mặc định luôn luôn lúc nào chúng ta cũng sẽ có được một constructor từ hệ thống.

Bài thực hành kế tiếp các bạn sẽ thêm code vào trong các constructor.

Thực Hành Khởi Tạo Các Giá Trị Thông Qua Constructor

Giờ thì bạn đảm bảo lớp HinhTron đang mở, bạn thử code các dòng lệnh sau vào hai constructor.

public class HinhTron {

	/**
	 * Demo cách khai báo các constructor,
	 * các thuộc tính và phương thức vẫn được giữ nguyên như bài 19,
	 * bạn hãy chú ý các constructor được comment
	 */

	final float PI = 3.14f;

    float r;
    float cv;
    float dt;

    // Constructor không có tham số truyền vào
    public HinhTron() {
    	nhapBanKinh();	// Thử gọi hàm nhapBanKinh()
    }

    // Constructor có một tham số r truyền vào
    public HinhTron(float r) {
    	this.r = r; // Gán biến r vào thuộc tính r
    }

    public 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();
    }

    public void tinhChuVi() {
        cv = 2 * PI * r;
    }

    public void tinhDienTich() {
        dt = PI * r * r;
    }

    public void inChuVi() {
        System.out.println("Chu vi Hình tròn: " + cv);
    }

    public void inDienTich() {
        System.out.println("Diện tích Hình tròn: " + dt);
    }
}

Bạn có thể thấy rằng.

Ở constructor thứ nhất không có tham số truyền vào, trong này mình gọi đến hàm kêu nhập bán kính. Bạn nhớ nội dung này của constructor thứ nhất nhé, để một lát nữa thực thi chương trình bạn sẽ dễ hiểu tại sao.

Ở constructor thứ hai có một tham số truyền vào là biến r, khi nhận được biến này, chúng ta gán nó vào cho thuộc tính r luôn.

Nếu bạn vẫn chưa rõ constructor có tác dụng gì, thì đừng vội nản, tiếp tục đọc phần tiếp theo, phần thực thi hàm khởi tạo, bạn sẽ ngày càng hiểu rõ hơn thôi.

Khai Báo Đối Tượng Thông Qua Constructor

Nhớ lại đi, bạn đã khai báo đối tượng hinhTronhinhChuNhat ở các bài học trước như thế nào? Có phải như vậy không?

HinhTron hinhTron = new HinhTron();
HinhChuNhat hinhChuNhat = new HinhChuNhat();

Như mình nói, nếu như bạn không khai báo bất kỳ constructor nào cho lớp HinhTronHinhChuNhat, thì thực ra hệ thống đã khai báo cho bạn một constructor cho từng lớp đó như sau.

public HinhTron() {
}
public HinhChuNhat() {
}

Và vì vậy, nên khi bạn khai báo hai đối tượng này, bạn đã gọi đến chúng thông qua các constructor mặc định, đó là new HinhTron(), và new HinhChuNhat(). Bạn thấy có mối liên hệ không nào.

Vậy quay lại với lớp HinhTron mà bạn đã thực hành trên kia, chúng ta đã khai báo hai constructor vào bên trong lớp. Và mình cũng có nói rằng bạn chỉ được thực thi một và chỉ một constructor mà thôi, vậy bạn sẽ có một trong hai cách khởi tạo HinhTron như sau.

// Cách khai báo HinhTron dựa vào constructor thứ nhất
HinhTron hinhTron1 = new HinhTron();

// Cách khai báo HinhTron dựa vào constructor thứ hai
HinhTron hinhTron2 = new HinhTron(10);

Với hai cách khởi tạo này, hinhTron1hinhTron2 sẽ khác nhau như thế nào, chúng ta cùng qua bài thực hành.

Thực Hành Khai Báo HinhTron Thông Qua Các Constructor

Chúng ta cùng về lại lớp MainClass để mà khởi tạo HinhTron thông qua các constructor nhé.

Chúng ta cùng nhau code như sau.

public class MainClass {

	public static void main(String[] args) {
		// Cách khai báo HinhTron dựa vào constructor thứ nhất
        HinhTron hinhTron1 = new HinhTron();

        // Cách khai báo HinhTron dựa vào constructor thứ hai
        HinhTron hinhTron2 = new HinhTron(10);

        // Tính toán và in ra kết quả cho hinhTron1
        System.out.println("======== Kết quả hinhTron1 ========");
        hinhTron1.tinhChuVi();
        hinhTron1.tinhDienTich();
        hinhTron1.inChuVi();
        hinhTron1.inDienTich();
        
        // Tính toán và in ra kết quả cho hinhTron2
        System.out.println("======== Kết quả hinhTron2 ========");
        hinhTron2.tinhChuVi();
        hinhTron2.tinhDienTich();
        hinhTron2.inChuVi();
        hinhTron2.inDienTich();
}

Điều này có nghĩa là chúng ta cùng tạo ra hai đối tượng hinhTron1hinhTron2 từ lớp HinhTron. Trong lớp HinhTron chúng ta có hai constructor, hinhTron1 được tạo ra thông qua constructor không có tham số truyền vào, hinhTron2 được tạo ra thông qua constructor có tham số truyền vào là một biến float.

Giờ đây nếu bạn thực thi chương trình này, bạn sẽ thấy có một lần console yêu cầu nhập bán kính hình tròn. Đó là bởi vì constructor của hinhTron1 gọi đến phương thức nhapBanKinh(). Ngay khi hinhTron1 được khai báo thông qua HinhTron hinhTron1 = new HinhTron();, constructor tương ứng (constructor không có tham số truyền vào) lập tức được triển khai.

Tương tự, với hinhTron2 được khai báo bằng HinhTron hinhTron2 = new HinhTron(10);, có nghĩa đối tượng này đã lựa chọn constructor có một tham số float truyền vào để khởi tạo, và vì bạn truyền vào giá trị 10 ngay constructor đó, nên bên trong thân của constructor, nó gán giá trị này vào thuộc tính r, và rồi khi bạn gọi tinhChuVi()tinhDienTich() trên đối tượng hinhTron2 này, thì giá trị 10 sẽ được sử dụng.

Kết quả in ra console như hình sau, khi mà bạn chỉ nhập 5 cho bán kính hinhTron1, hinhTron2 đã được khởi tạo bán kính bằng 10 rồi.

Screen Shot 2017-06-28 at 12.48.52

Bạn đã hiểu constructor rồi đúng không nào.

Trên đây là các kiến thức về constructor. Bạn nên hiểu một constructor cũng là một phương thức, nhưng nó có một số điểm khác biệt như mình có trình bày trên đây. Constructor được sử dụng rất phổ biến, nhằm mang đến một giá trị khởi tạo cho đối tượng nào đó một cách tức thời ngay khi đối tượng đó được khởi tạo.

Có thể những ý mình nêu trên chưa hoàn toàn đầy đủ với một constructor. Hoặc cách thức mình trình bày có phần khó hiểu. Thì các bạn hãy để lại comment bên dưới bài học hôm nay cho mình nhé.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy đánh giá 5 sao nếu thấy thích bài viết, hãy comment bên dưới nếu có thắc mắc, hãy để lại địa chỉ email của bạn để nhận được thông báo mới nhất khi có bài viết mới, và nhớ chia sẻ các bài viết của Yellow Code Books đến nhiều người khác nữa nhé.

Bài Kế Tiếp

Chúng ta sẽ xem đến một mối quan hệ rất quan trọng giữa các lớp bên trong một ứng dụng Java, đó là quan hệ Kế thừa.

Advertisements
Rating: 5.0/5. From 11 votes.
Please wait...

Gửi phản hồi