Được chỉnh sửa ngày 30/12/2022.
Chào mừng các bạn đến với bài học Java số 21, bài học về Kế thừa. Bài học này nằm trong chuỗi bài học lập trình ngôn ngữ Java của Yellow Code Books.
Vậy là chúng ta đã bước qua lần lượt nhiều kiến thức quan trọng trong lập trình hướng đối tượng, như thuộc tính, phương thức, constructor. Nhưng có một loại kiến thức có thể nói là tinh hoa của hướng đối tượng, mà chúng ta sẽ tiếp cận bắt đầu từ bài học hôm nay, sẽ làm bạn có một cách sử dụng và tổ chức các lớp trong ứng dụng theo một cách thức hoàn toàn nâng cao và hiệu quả hơn so với các cách mà bạn đã làm quen từ các bài học trước, đó là kiến thức về kế thừa.
Nếu bạn là người đang tập làm quen với Java và hướng đối tượng, thì kế thừa sẽ khiến bạn càng thêm khó khăn hơn một chút. Đến lúc này, Java code không còn quan trọng bằng việc bạn tổ chức cấu trúc cho các lớp hay đối tượng bên trong ứng dụng nữa. Theo kinh nghiệm của mình, thì sau khi làm quen đến kế thừa, sẽ có vô số thắc mắc mang đến với các bạn. Có bạn sẽ thắc mắc không biết khi nào nên kế thừa. Cũng có bạn thì hiểu rõ kế thừa nhưng lại tổ chức kế thừa sai “họ hàng” của chúng, khiến việc kế thừa trở nên rối hơn. Rồi thì khi nào nên chặn tính kế thừa của bất kỳ một lớp nào đó. Vân vân và vân vân. Những thắc mắc đó mình sẽ cố gắng trình bày kỹ càng trong loạt bài viết về kế thừa này, để bạn có một cách thức vận dụng tốt nhất tính kế thừa vào trong sản phẩm của bạn.
Làm Quen Với Kế Thừa
Kế Thừa Là Gì?
Kế thừa trong lập trình hướng đối tượng ám chỉ đến một mối quan hệ giữa các đối tượng, có người thì nói mối quan hệ này là cha-con, có người thì nói là quan hệ mở rộng Người ta có vẻ thích cái khái niệm cha-con hơn, nhưng mình thấy mở rộng sẽ sát với ý nghĩa của kế thừa hơn. Bởi vì nó thực chất là một sự dùng lại, một số trường hợp là mở rộng hơn các đặc tính, của một đối tượng từ một đối tượng nào đó khác.
Như vậy, để hiểu một cách thực tế, giả sử đầu tiên chúng ta có một lớp nào đó, lớp này có thể là do chúng ta viết ra, hay “lượm lặt” ở đâu đó, mình tạm gọi tên lớp sẵn có này là A. Sau đó, để tận dụng lại các phương thức hay các thuộc tính của A mà không cần phải viết lại (hoặc copy lại, có thể vi phạm bản quyền), thì chúng ta xây dựng một lớp mới kế thừa từ A, mình gọi lớp mới này là B. Khi đó B của chúng ta sẽ có sẵn các phương thức và thuộc tính mà A có. Cũng có lúc không vì mục đích dùng lại các giá trị của A, mà là vì một vài giá trị của A không phù hợp với nhu cầu của B, thế là việc kế thừa từ A còn giúp cho B có cơ hội được hoàn thiện lại (hay còn gọi là mở rộng) các giá trị chưa phù hợp đó của A mà không làm thay đổi bản chất của A. Hôm nay chúng ta tập trung vào mục đích dùng lại, mục đích mở rộng mình sẽ nói đến ở bài học sau.
Tại Sao Phải Kế Thừa?
Qua các ý trên đây của mình, có lẽ các bạn cũng đã hiểu lý do tại sao phải kế thừa đúng không nào.
Vâng, mục đích chính mà kế thừa mang lại, đó là việc tận dụng lại, và mở rộng hơn các thuộc tính và phương thức có sẵn từ một đối tượng nào đó.
Vậy thôi, mục đích của kế thừa không cao siêu gì cả, nhưng tác dụng mà nó mang lại là rất to lớn. Với việc dùng lại những cái sẵn có này, sẽ làm cho cấu trúc project của bạn trông chuyên nghiệp hơn, khi đó code của bạn sẽ dễ đọc hơn. Ngoài ra thì việc kế thừa còn giúp cho bạn giảm bớt gánh nặng phải code nhiều, vì đã tận dụng lại code đã có của các lớp khác.
Kế Thừa Trong Java
Nãy giờ chúng ta đang nói chung chung về các khái niệm. Vậy thì làm sao để thể hiện sự kế thừa trong Java? Trong Java, để thể hiện một lớp muốn kế thừa từ một lớp nào đó, bạn sử dụng từ khóa extends.
Bạn hãy nhìn vào ví dụ dưới đây, bạn đừng nên code vội, lát nữa vào phần thực hành chúng ta sẽ cùng code.
class HinhTron { float bk; float getBanKinh() { return bk; } } class HinhTru extends HinhTron { }
Code trên đây thể hiện rằng lớp HinhTru kế thừa HinhTron bằng từ khóa extends. Và theo quy luật kế thừa, thì HinhTru có thể sẽ thừa hưởng các giá trị (thuộc tính và phương thức) mà HinhTron đã khai báo. Qua mối quan hệ kế thừa như vậy, người ta có thể gọi lớp HinhTron là lớp cơ sở (base class), hay lớp cha (super class, parent class). Còn lớp HinhTru được gọi là lớp dẫn xuất (derived class) hay lớp con (sub class, child class).
Thông thường thì các lớp cha, hay lớp cơ sở, là các lớp chứa đựng các giá trị chung, hay các giá trị cơ sở nhất cho các lớp con. Nên nếu như bạn có nhiều lớp có sự tương đồng nhất định, như Hình tròn và Hình trụ ở ví dụ trên, các lớp này đều có mặt tròn (Hình trụ có hai mặt tròn), nên bạn có thể dùng Hình tròn làm lớp cơ sở (vì nó chứa các giá trị tối thiểu mà Hình trụ có thể tận dụng lại được, trong trường hợp này chính là mặt tròn). Hoặc có những trường hợp có nhiều các lớp có cùng các giá trị tương đồng, mà bạn có thể gom thành một lớp cơ sở duy nhất, rồi các lớp con chỉ việc kế thừa và sử dụng lại các giá trị tương đồng đó mà không cần phải khai báo gì thêm, như bài thực hành bên dưới mình sẽ tạo một lớp HinhHoc là cơ sở nhất cho các lớp HinhTron, HinhVuong, HinhChuNhat,…
Trên đây là một ví dụ cho việc khi nào thì bạn cần kế thừa, còn bây giờ mình xin liệt kê một số ý quan trọng trong quá trình bạn tổ chức kế thừa.
- Một lớp chỉ được phép kế thừa từ một và chỉ một lớp cha mà thôi.
- Tuy không được kế thừa từ nhiều lớp cha, nhưng lớp cha mà đối tượng đang kế thừa này có thể kế thừa từ một lớp cha khác, bạn có thể gọi chơi lớp cha khác này là “lớp ông nội” cho dễ nhớ, và chắc chắc có thể có cả “lớp ông cố” nữa nếu như “lớp ông nội” lại kế thừa một lớp khác nữa.
- Nếu một lớp không khai báo kế thừa gì hết (như các lớp mà chúng ta đã thực hành từ các bài học trước), thì khi này hệ thống sẽ mặc định xem là nó đang kế thừa từ lớp Object. Bài học sau chúng ta sẽ cùng nói rõ về lớp Object này nhé.
Làm Quen Với Sơ Đồ Lớp
Khái niệm kế thừa ở bài học hôm nay chỉ như vậy thôi. Nhưng trước khi đi vào thực hành cụ thể, mình mời các bạn cùng làm quen với một sơ đồ thần thánh, mà nếu là một dân lập trình chính thống, bạn không thể không biết đến. Cái chính của mục này là nói về cách thức để bạn nhìn và hiểu sơ đồ. Vì project của chúng ta ngày một phức tạp, sẽ có nhiều và rất nhiều lớp, chúng sử dụng nhau, kế thừa nhau. Và vì vậy mà nếu không có sơ đồ, chúng ta sẽ không thể nào diễn tả hết được các mối quan hệ này bằng lời.
Sơ đồ lớp của mục này được mình lấy ra từ nguyên tắc xây dựng sơ đồ lớp (class diagram) của UML. UML là một bộ nhiều các nguyên tắc khác nhau dành cho việc đặc tả và thiết kế hệ thống phần mềm. Nói như vậy để bạn hiểu đây là một sơ đồ tuân thủ theo các nguyên tắc chuẩn, nếu bạn hiểu và tuân thủ các nguyên tắc này, bạn sẽ tạo ra một mô hình có tiếng nói chung, mà ai đọc vào cũng hiểu bạn muốn thể hiện gì cho phần mềm của bạn.
Bạn biết không, bạn đã từng làm quen một chút với sơ đồ này rồi, vì mình đã từng dùng qua ở bài 16, khi đó mình muốn diễn đạt một lớp có ba thành phần chính như sau, và hình khối mà bạn trông thấy chính là cách mà sơ đồ lớp biểu diễn ra một thực thể lớp.

Hình dáng và màu sắc của các khối trong sơ đồ lớp có thể khác nhau ở bài học của mình và ở các tài liệu khác, tùy vào công cụ để vẽ nó. Nhưng cho dù chúng khác nhau về ngoại hình, thì chung quy lại các dữ liệu mà mỗi sơ đồ thể hiện phải đều tuân thủ một nguyên tắc hình khối với ba thành phần trên.
Mình lấy ví dụ lớp HinhTron như code trên kia, thì khi biểu diễn thành một thực thể trong sơ đồ lớp, sẽ trông như thế này.

Nhìn vào sơ đồ, bạn biết ngay cần xây dựng một lớp có tên HinhTron, lớp này có một thuộc tính tên bk có kiểu dữ liệu là float, và một phương thức getBanKinh() trả về kiểu float. Tuy mang giá trị tĩnh, tức là nó không thể hiện được nội dung hay mối quan hệ của các thành phần bên trong một lớp, nhưng chắc hẳn bạn cũng dễ hiểu và hình dung ra được cách thức xây dựng một lớp dựa trên sơ đồ này là như thế nào.
Tiếp theo, mình nói tiếp sơ đồ lớp này thể hiện sự kế thừa như thế nào. Với mong muốn lớp HinhTru kế thừa từ HinhTron, người ta thể hiện qua sơ đồ lớp mối quan hệ này bằng một dấu mũi tên như sau. Bạn chú ý phải là dấu mũi tên rỗng ruột như hình, nếu bạn dùng mũi tên kiểu khác, thì sẽ gây nhầm lẫn với các mối quan hệ khác đấy nhé.

Một ý nữa của sơ đồ, ví dụ như trường hợp ở bài 18, lớp HinhTron có sử dụng lớp ToaDo để làm một thuộc tính, thuộc tính này có tên là toaDo, vậy thì sơ đồ sẽ thể hiện sự sử dụng này như sau.

Mọi thứ thật sự rõ ràng đúng không nào. Xong rồi, với bài học hôm nay mình chỉ trình bày sơ lược về kế thừa và về sơ đồ lớp như vậy thôi. Chúng ta sẽ bổ sung các kiến thức, các ký hiệu cho sơ đồ này ở các bài học tiếp theo. Còn bây giờ chúng ta cần thực hành cho quen tay.
Thực Hành Kế Thừa
Chúng ta sẽ tiếp tục cùng nhau xây dựng ứng dụng tính toán các giá trị hình học cho HinhTron, HinhTru, HinhChuNhat, HinhVuong. Bạn cũng nên biết là, nếu không áp dụng kiến thức về kế thừa của bài hôm nay, thì bạn vẫn xây dựng được kết quả của bài thực hành này một cách hoàn hảo bằng các kiến thức về OOP ở các bài trước, bạn có thể thử. Nhưng với việc áp dụng tính kế thừa, như mình có nói, bạn sẽ tiết kiệm được các dòng code một cách đáng kể. Vậy thì mời bạn cùng thử xây dựng một project mới với mình nhé.
Trước hết mình mời bạn xem qua sơ đồ lớp của bài hôm nay (có áp dụng kế thừa) như sau, bạn hãy nghiền ngẫm một tí nhé (chú ý các dấu + ở trước mỗi phương thức hay thuộc tính trong sơ đồ là các khai báo với từ khóa public, đây là khả_năng_truy_cập vào các giá trị lớp mà chúng ta sẽ nói sau).

Trước khi đi vào chính thức, mình sẽ nhìn sơ đồ và nói chi tiết từng lớp, rồi cho bạn xem kết quả chạy chương trình, và cuối cùng là code của từng lớp. Bạn khoan hãy xem code của các lớp vội, mà hãy dựa vào sơ đồ và kết quả rồi thử code nhé, mỗi người sẽ có một cách code khác nhau, bạn không nhất thiết phải code giống như mình, miễn sao chương trình của bạn chạy ổn là được.
- Lớp HinhHoc. Đây là lớp cha của các lớp còn lại, hay mang ý nghĩa là lớp cơ bản nhất. Do là lớp cơ bản, nên tốt nhất nó phải chứa các thuộc tính hay phương thức mà sẽ hữu dụng cho các lớp con, hay có thể nói rằng lớp con hoàn toàn có thể kế thừa lại các giá trị đó một cách hiệu quả. Chẳng hạn hằng số PI mình sẽ khai báo ở lớp này. Thuộc tính ten tuy dùng chung nhưng sẽ được các lớp con định nghĩa cụ thể theo tên của chúng. Các chuVi, dienTich, theTich cũng vậy, tuy định nghĩa chung nhưng các lớp con sẽ chứa các giá trị khác nhau. Các phương thức của lớp cha này cũng mang ý nghĩa sẽ được các lớp con dùng đến, nên chúng sẽ có thân hàm cụ thể, như xuatTen() sẽ xuất biến ten ra consolse. Hay inChuVi(), inDienTich(), inTheTich() cũng sẽ xuất các biến chuVi, dienTich, theTich tương ứng.
- Lớp HinhTron. Là lớp con của HinhHoc. Như bạn biết HinhTron sẽ kế thừa các giá trị từ lớp cha của nó. Ngoài các thuộc tính mà nó có được từ lớp cha là ten, chuVi, dienTich, theTich, thì nó cũng định nghĩa thêm một thuộc tính banKinh đặc biệt của riêng nó. Bạn có thể khởi tạo biến ten cho HinhTron ở constructor HinhTron(). Các phương thức nhapBanKinh(), tinhChuVi(), tinhDienTich() không khai báo ở lớp cha, HinhTron tự nó thiết kế.
- Lớp HinhTru. Lớp này là con của HinhTron, bởi như mình nói trên kia, HinhTru có các mặt tròn, mặt tròn này không khác gì các đặc tính của một HinhTron, nên HinhTron nên là một lớp cơ bản của HinhTru. Chính vì HinhTru kế thừa các giá trị từ HinhTron, mà HinhTron kế thừa từ HinhHoc, nên HinhTru có đủ hết các giá trị của HinhHoc và HinhTron. Nó chỉ cần thêm thuộc tính chieuCao, và các phương thức nhapChieuCao(), tinhTheTich() của riêng nó nữa mà thôi.
- Tương tự cho các mối quan hệ của HinhChuNhat và HinhVuong. Có một điều đặc biệt ở lớp HinhVuong, đó là vì các cạnh dài và rộng của hình này bằng nhau, nên phương thức nhapCanh() của hình vuông chỉ kêu người dùng nhập một cạnh, sau đó bạn gán cùng giá trị cạnh này cho các biến dai và rong, và thế là HinhVuong không cần phải xây dựng thêm các phương thức nào cả, kế thừa hoàn toàn xuất sắc từ lớp cha của nó.
Kết quả chạy chương trình giống giống như sau.

Về phần project, mình tạo ra một project mới tên là InheritanceLearning. Với cách tổ chức các lớp vào trong các package như sau.

Sau đây là source code của các lớp tương ứng trong chương trình cho bạn tham khảo.
Lớp HinhHoc.
package shapes; public class HinhHoc { public final float PI = 3.14f; public String ten; public float chuVi; public float dienTich; public float theTich; public void xuatTen() { System.out.println("\n\n===== " + ten + " ====="); } public void inChuVi() { System.out.println("Chu vi = " + chuVi); } public void inDienTich() { System.out.println("Diện tích = " + dienTich); } public void inTheTich() { System.out.println("Thể tích = " + theTich); } }
Lớp HinhTron.
package shapes; import java.util.Scanner; public class HinhTron extends HinhHoc { public float banKinh; // Constructor public HinhTron() { ten = "Hình Tròn"; } public void nhapBanKinh() { System.out.println("Bán kính = "); Scanner scanner = new Scanner(System.in); banKinh = scanner.nextFloat(); } public void tinhChuVi() { chuVi = 2 * PI * banKinh; } public void tinhDienTich() { dienTich = PI * banKinh * banKinh; } }
Lớp HinhTru.
package shapes; import java.util.Scanner; public class HinhTru extends HinhTron { public float chieuCao; // Constructor public HinhTru() { ten = "Hình Trụ"; } public void nhapChieuCao() { nhapBanKinh(); System.out.println("Chiều cao = "); Scanner scanner = new Scanner(System.in); chieuCao = scanner.nextFloat(); } public void tinhTheTich() { tinhDienTich(); theTich = dienTich * chieuCao; } }
Lớp HinhChuNhat.
package shapes; import java.util.Scanner; public class HinhChuNhat extends HinhHoc { public float dai; public float rong; // Constructor public HinhChuNhat() { ten = "Hình Chữ Nhật"; } public void nhapChieuDai() { System.out.println("Chiều dài = "); Scanner scanner = new Scanner(System.in); dai = scanner.nextFloat(); } public void nhapChieuRong() { System.out.println("Chiều rộng = "); Scanner scanner = new Scanner(System.in); rong = scanner.nextFloat(); } public void tinhChuVi() { chuVi = 2 * (dai + rong); } public void tinhDienTich() { dienTich = dai * rong; } }
Lớp HinhVuong.
package shapes; import java.util.Scanner; public class HinhVuong extends HinhChuNhat { // Constructor public HinhVuong() { ten = "Hình Vuông"; } public void nhapCanh() { System.out.println("Cạnh = "); Scanner scanner = new Scanner(System.in); dai = rong = scanner.nextFloat(); } }
Và cuối cùng là lớp MainClass.
package main; import shapes.HinhChuNhat; import shapes.HinhTron; import shapes.HinhTru; import shapes.HinhVuong; public class MainClass { public static void main(String[] args) { // Thử nghiệm với lớp Hình tròn HinhTron hinhTron = new HinhTron(); hinhTron.xuatTen(); hinhTron.nhapBanKinh(); hinhTron.tinhChuVi(); hinhTron.tinhDienTich(); hinhTron.inChuVi(); hinhTron.inDienTich(); // Thử nghiệm với lớp Hình trụ HinhTru hinhTru = new HinhTru(); hinhTru.xuatTen(); hinhTru.nhapChieuCao(); hinhTru.tinhTheTich(); hinhTru.inTheTich(); // Thử nghiệm với lớp Hình chữ nhật HinhChuNhat hinhChuNhat = new HinhChuNhat(); hinhChuNhat.xuatTen(); hinhChuNhat.nhapChieuDai(); hinhChuNhat.nhapChieuRong(); hinhChuNhat.tinhChuVi(); hinhChuNhat.tinhDienTich(); hinhChuNhat.inChuVi(); hinhChuNhat.inDienTich(); // Thử nghiệm với lớp Hình vuông HinhVuong hinhVuong = new HinhVuong(); hinhVuong.xuatTen(); hinhVuong.nhapCanh(); hinhVuong.tinhChuVi(); hinhVuong.tinhDienTich(); hinhVuong.inChuVi(); hinhVuong.inDienTich(); } }
Kết Luận
Đấy bạn thấy càng ngày càng phải code nhiều rồi đúng không nào. Cái chính của việc học lập trình đó là bạn đừng ngại code, tuy kiến thức về kế thừa hôm nay giúp ích cho bạn khá nhiều về việc tiết kiệm các dòng code, nhưng không phải vì vậy mà các lập trình viên chúng ta hoàn toàn rảnh rỗi.
Bài tuy nhiều code, nhưng hi vọng các bạn hứng thú, và sẵn sàng để cùng mình đi qua các kiến thức “đau đầu” hơn (nhưng thú vị hơn) về OOP ở các bài sắp tới.
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
Bài học sau chúng ta sẽ nói qua cách sử dụng hai từ khóa this và super, hai từ khóa này sẽ giúp ích gì cho mối quan hệ kế thừa này, mời các bạn cùng đọc xem nhé.
Anh trai ơi , đọc bài anh em dễ hiểu lắm … em đã có thể tự code được bải như trên … nhưng khi so với source code của a thì chỉ thiếu mỗi public ở phần khai báo các thuộc tính . nhưng sao em chạy chương trình thì nó báo như này ạ : Error: Could not find or load main class main.MainClass
C:\Users\danht\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 0 seconds)
A giúp em với
Chắc bạn Quang dùng Netbeans đúng không. Nhìn đường dẫn project của bạn sao thấy lạ quá, nó nằm trong catche của Netbeans. Bạn thử copy project ra ổ đĩa khác thử xem, ổ E:\ chẳng hạn. Hoặc chuyển sang dùng Eclipse như các bài viết của mình cho chắc cú.
anh ơi em có câu hỏi là, nếu hàm con được kế thừa, lúc mình gọi hàm khởi tạo của hàm con, n có lôi cả hàm khởi tạo cha không ạk
Nếu lớp cha có hàm khởi tạo không tham số, thì hàm khởi tạo này sẽ được các lớp con kế thừa đến một cách mặc định. Tức là nếu bạn khai báo lớp con, thì nó cũng sẽ có hàm khởi tạo không tham số của cha nó trong đó. Nhưng nếu lớp con có khai báo super() trong hàm khởi tạo của nó, thì sự mặc định này không còn nữa. Để hiểu rõ bạn có thể tự code trong Eclipse vài trường hợp để hiểu rõ hơn, hoặc xem ví dụ ở link này cũng được nè https://www.geeksforgeeks.org/g-fact-67/
Bài viết hay ad ạ. cám ơn ad nhiều ạ
Lớp hình trụ mình extends lớp hình tròn rồi nhưng mình không dùng được thuộc tính tên của Hình học . Hai thuộc tính diện tích và thể tích cũng không dùng được.
Lớp hình tròn mình có extends lớp hình học rồi và dùng được thuộc tính tên bình thường.
À, sorry ad, mình quên đóng project bài trước nên tạo nhầm class ở project cũ :”))) giờ được rồi 😀
.
Ad cho mình hỏi là trong bài thực hành trên, mình có thể close cái scanner như nào nhỉ. Vì close ngay tại method sử dụng thì khi run sẽ bị lỗi như này:
Exception in thread “main” java.util.NoSuchElementException
at java.base/java.util.Scanner.throwFor(Scanner.java:937)
at java.base/java.util.Scanner.next(Scanner.java:1594)
at java.base/java.util.Scanner.nextDouble(Scanner.java:2564)
at HinhChuNhat.nhapChieuDai(HinhChuNhat.java:15)
at JavaLearning.main(JavaLearning.java:20)
Chào bạn, mình chưa hiểu, tại sao phải close cái scanner vậy bạn.
Theo mình biết là để giải phóng resource. Nên thông thường sau khi lấy được giữ liệu mình hay close scanner.
Bạn quan tâm đến giải phóng resource thì tốt rồi. Việc giải phóng scanner cũng được hệ thống hỗ trợ. Có chăng là thứ tự giải phóng của bạn có thể chưa đúng, bạn hãy gửi cho mình xem code lúc bạn nhận dữ liệu đầu vào, và code giải phóng nó nhé.
Anh ơi cho em hỏi tại sao không khởi tạo và khai báo Scanner ở Class Hình Học luôn 1 lần để khỏi phải khai báo lại trong các class con?
Đó cũng là một ý hay. Bạn có thể khai báo Scanner ở lớp cha HinhHoc rồi dùng chung, về mặt thực tế thì điều này đúng, nếu với project thực tế của bạn, mình nghĩ bạn nên áp dụng ý tưởng này. Tuy nhiên, về mặt bài học thì mình sẽ không tổ chức vậy đâu bạn, vì Scanner khi này cũng chỉ là biến phụ để nhập dữ liệu thôi. Cái mình cần tổ chức theo kế thừa là những thứ liên quan đến Hình Học, để các bạn có sự phân biệt giữa ten, chuVi, dienTich, theTich là cái chung nên để lớp cha, còn canh, banKinh, chieuDai, chieuRong là cái riêng của từng lớp con. Scanner lúc này không ăn nhập gì với các Hình Học của chúng ta, nên mình sẽ để mỗi thằng con tự quản lý luôn nhé.
Cảm ơn ạ
Xin cảm ơn bạn rất nhiều, mình mới tập tọe học lập trình nhưng đọc bài viết của bạn có thể hiểu được ngay, mỗi tội mình dùng Intelli IDEA nên 1 số chỗ ko giống nhau
Chào bạn, thật sự thiếu sót khi mình chậm cập nhật các bài viết để có thể hỗ trợ InteliJ. Dù vậy mình cũng muốn các bạn biết rằng hiện tại lựa chọn code trên InteliJ cũng đang là lựa chọn tốt, và cũng không khác nhiều với Eclipse lắm đâu. Mình cũng đã có cập nhật các bài đầu sao cho có thêm kiến thức về InteliJ rồi, có gì bạn và các bạn khác hãy xem lại để cùng học hỏi và trao đổi nhé.
hàm tính diện tích và chu vi của hình tròn và hình trụ không giống nhau nên hình trụ có vẻ cần override lại 2 hàm này thì mới có kq chính xác được.
Mình hiểu ý bạn, ý bạn không sai. Diện tích và Chu vi của Hình tròn và Hình trụ tất nhiên là khác nhau rồi. Tuy nhiên do ở Hình trụ (là con của Hình tròn), mình không xuất ra Diện tích và Chu vi của nó, mình chỉ mượn Diện tích và Chu vi của Hình tròn để tính và xuất Thể tích ra thôi bạn. Nên nếu như chúng ta cần xuất Diện tích và Chu vi của Hình trụ nữa thì dĩ nhiên là sẽ phải cần override lại rồi, còn với ví dụ của bài học thì chưa cần thiết lắm.
Em cảm ơn ad rất nhiều, bài học này rất dễ hiểu ạ. Em cũng thử code và check lại thì console lại hiện ra lỗi như thế này ạ của Scanner mà không biết tại sao:
===== Hinh Tron =====
Ban Kinh =
2
Chu vi = 12.56
Dien tich = 12.56
===== Hình Trụ =====
Ban Kinh =
Exception in thread “main” java.util.NoSuchElementException
at java.base/java.util.Scanner.throwFor(Scanner.java:937)
at java.base/java.util.Scanner.next(Scanner.java:1594)
at java.base/java.util.Scanner.nextFloat(Scanner.java:2496)
at shapes.HinhTron.nhapBanKinh(HinhTron.java:17)
at shapes.HinhTru.nhapChieuCao(HinhTru.java:14)
at main.MainClass.main(MainClass.java:27)
à em tự sửa được lỗi rồi ạ.