Java 從入門到進階之路(十三)
在之前的文章我們介紹了一下 Java 類的 private,static,final,本章我們來看一下 Java 中的抽象類和抽象方法。
我們先來看下面一段程式碼:
1 // 根據周長求面積 2 class Square { // 方形類 3 double c; 4 5 double area() { // 方形面積 6 return 0.0625 * c * c; 7 } 8 } 9 10 class Circle { // 圓形類 11 double c; 12 13 double area() { // 圓形面積 14 return 0.0796 * c * c; 15 } 16 }
在上面的程式碼中我們分別定義了一個方形類和圓形類,然後根據周長計算出各自的面積。咋上面的程式碼中可以看出這兩段程式碼都有相同的地方,我們第一感覺就是將相同的部分單獨封裝成一個父類,然後通過整合的方式完成,如下:
1 class Shape { // 圖形 2 double c; 3 4 double area() { // 如果不 return 的話會編譯錯誤 5 return 係數 * c * c; // 不同形狀係數不同 6 } 7 8 void area() { // 不會出現編譯錯誤,但是這樣寫這個方法沒有任何意義 9 10 } 11 } 12 13 // 根據周長求面積 14 class Square extends Shape { // 方形類 15 16 } 17 18 class Circle extends Shape { // 圓形類 19 20 }
在上面的程式碼中我們單獨封裝了一個 Shape 類,但是有一個問題,周長 c 是公共的沒問題,但是面積 area() 方法卻由於不同圖形的面積係數不同,沒法解決,此時就需要利用抽象方法來解決了,即在方法體前面加 abstract 關鍵字,如下:
1 class Shape { // 圖形 2 double c; 3 4 abstract double area(); // 抽象方法 --不完整 5 6 }
抽象方法的定義:
1、由 abstract 修飾;
2、只有方法的定義,沒有方法的實現(大括號都沒有);
但是抽象方法是不完整的,所以我們需要將類也變成抽象類,如下:
1 abstract class Shape { // 圖形 --不完整 2 double c; 3 4 abstract double area(); // 抽象方法 --不完整 5 6 }
抽象類的定義:
1、由 abstract 修飾;
2、包含抽象方法的類必須是抽象類;
3、抽象類不能被例項化;
4、抽象類是需要被繼承的,所以子類需要:
1)重寫所有抽象方法 --- 常用
2)也宣告未抽象類 ---不常用
在第 4 點鐘抽象類是需要被繼承的,如下程式碼:
1 abstract class Shape { // 圖形 --不完整 2 double c; 3 4 abstract double area(); // 抽象方法 --不完整 5 6 } 7 8 9 class Square extends Shape { // 編譯錯誤,因為繼承了 Shape 類,Shape 類中包含 抽象方法 10 11 } 12 13 // 改進 14 abstract class Square extends Shape { // 不報錯誤,但是 Square 類也變成了抽象類 15 16 } 17 18 // 改進 19 class Square extends Shape { // 不報錯誤,將 Square 類中的抽象類重寫為非抽象類 20 double area() { 21 return 0.0625 * c * c; 22 } 23 }
在上面的程式碼中我們實現了抽象類和抽象方法,但是貌似看上去多此一舉,如下:
1 class Shape { // 圖形 只提取公共變數 c 2 double c; 3 } 4 5 abstract class Shape { // 圖形 --不完整 6 double c; 7 8 abstract double area(); // 抽象方法 --不完整 9 }
在上面的程式碼中,我們完全可以只提取公共變數 周長c 就可以了,因為 area() 方法反正是要重寫的,何必多此一舉呢。
接下來我們看下面的需求,就是分別以 周長 c = 1,2,3 來建立三個 Square 和 Circle 類,然後找出這 6 個圖形中面積最大的一個。我們首先想到的方法應該是這樣:
1 public class HelloWorld { 2 public static void main(String[] args) { 3 Square[] squares = new Square[3]; 4 squares[0] = new Square(1); 5 squares[1] = new Square(2); 6 squares[2] = new Square(3); 7 8 Circle[] circles = new Circle[3]; 9 circles[0] = new Circle(1); 10 circles[1] = new Circle(2); 11 circles[2] = new Circle(3); 12 13 /** 14 * 1、找到 squares 中最大面積 sMax 15 * 2、找到 circles 中最大面積 cMax 16 * 3、比較 sMax 與 cMax 的最大值 17 */ 18 19 } 20 } 21 22 class Shape { 23 double c; 24 } 25 26 class Square extends Shape { 27 Square(double c) { 28 this.c = c; 29 } 30 31 double area() { 32 return 0.0625 * c * c; 33 } 34 } 35 36 37 class Circle extends Shape { 38 Circle(double c) { 39 this.c = c; 40 } 41 42 double area() { 43 return 0.0796 * c * c; 44 } 45 }
在上面的程式碼中,我們先定義一個數組,裡面存數三個周長 c = 1,2,3 的 Square,再定義一個數組,裡面存放三個周長 c = 1,2,3 的 Circle,然後我們分別計算出兩個陣列中面積的最大值再比較出面積最大的一個,這個可以解決我們的問題,但是如果再價格六邊形,就需要再定義一個數組,那麼我們上面寫的程式碼就出現了 程式碼重複,擴充套件性差,維護性差 的問題。
接下來我們用抽象類的方法來寫一下:
1 public class HelloWorld { 2 public static void main(String[] args) { 3 Shape[] shapes = new Shape[6]; // 向上造型 4 shapes[0] = new Square(1); 5 shapes[1] = new Square(2); 6 shapes[2] = new Square(3); 7 shapes[3] = new Circle(1); 8 shapes[4] = new Circle(2); 9 shapes[5] = new Circle(3); 10 11 double max = shapes[0].area(); // 先假設第一個為最大值 12 for(int i=1;i<shapes.length;i++){ 13 double area = shapes[i].area(); 14 if(area > max){ 15 max = area; 16 } 17 } 18 System.out.println(max); 19 } 20 } 21 22 abstract class Shape { 23 double c; 24 abstract double area(); 25 } 26 27 class Square extends Shape { 28 Square(double c) { 29 this.c = c; 30 } 31 32 double area() { 33 return 0.0625 * c * c; 34 } 35 } 36 37 38 class Circle extends Shape { 39 Circle(double c) { 40 this.c = c; 41 } 42 43 double area() { 44 return 0.0796 * c * c; 45 } 46 }
在上面的程式碼中我們先定義了一個 Shape[] 陣列,然後通過向上造型的方式向陣列中分別新增 Square 和 Circle,這樣就只在一個數組中比較最大值就可以了,當再新增五邊形,六邊形時只需要擴充 Shape[] 陣列就可以了。
抽象類的意義:
1、為其子類提供一個公共的型別 -- 向上造型;
2、封裝子類中的重複內容(成員變數和方法);
3、定義有抽象方法,子類雖然有不同的實現,但該方法的定義是一致的