031:Java繼承(extends)簡明教程


Java 繼承(extends)簡明教程

(轉自:http://c.biancheng.net/view/940.html

1.類的封裝

       繼承是面向對象的三大特征之一。繼承和現實生活中的“繼承”的相似之處是保留一些父輩的特性,從而減少代碼冗余,提高程序運行效率。
Java 中的繼承就是在已經存在類的基礎上進行擴展,從而產生新的類。已經存在的類稱為父類、基類或超類,而新產生的類稱為子類或派生類。在子類中,不僅包含父類的屬性和方法,還可以增加新的屬性和方法
Java 中子類繼承父類的語法格式如下:

  1. 修飾符 class class_name extends extend_class {
  2.     // 類的主體
  3. }

其中,class_name 表示子類(派生類)的名稱;extend_class 表示父類(基類)的名稱;extends 關鍵字直接跟在子類名之后,其后面是該類要繼承的父類名稱。例如:

  1. // Student是子類,extends后面是要繼承的父類Person
  2. public class Student extends Person{}

Java 的繼承通過 extends 關鍵字來實現,extends 的英文意思是擴展,而不是繼承。extends 很好的體現了子類和父類的關系,即子類是對父類的擴展,子類是一種特殊的父類。從這個角度看,使用繼承來描述子類和父類的關系是錯誤的,用擴展更恰當。
那么為什么國內把 extends 翻譯為“繼承”呢?子類擴展父類之后就可以獲得父類的屬性和方法,這與漢語中的繼承(子類從父類獲得一筆財富稱為繼承)具有相似性。

Java 與 C++ 定義繼承類的方式十分相似。Java 用關鍵字 extends 代替了 C++ 中的冒號(:)。在 Java 中,所有的繼承都是公有繼承, 而沒有 C++ 中的私有繼承和保護繼承。

類的繼承不改變類成員的訪問權限,也就是說,如果父類的成員是公有的、被保護的或默認的,它的子類仍具有相應的這些特性,並且子類不能獲得父類的構造方法。

例 1

教師和學生都屬於人,他們具有共同的屬性:姓名、年齡、性別和身份證號,而學生還具有學號和所學專業兩個屬性,教師還具有教齡和所教專業兩個屬性。下面編寫 Java 程序代碼,使教師(Teacher)類和學生(Student)類都繼承於人(People)類,具體的實現步驟如下。
1)創建人類 People,並定義 name、age、sex、sn 屬性,代碼如下:

  1. //創建People 類
  2. public class People {
  3.     public String name; // 姓名
  4.     public int age; // 年齡
  5.     public String sex; // 性別
  6.     public String sn; // 身份證號
  7. //添加類的屬性,構造方法
  8.     public People(String name, int age, String sex, String sn) {
  9.           //this用於任何實例方法內指向當前對象
  10. 10.         this.name = name;
  11. 11.         this.age = age;
  12. 12.         this.sex = sex;
  13. 13.         this.sn = sn;
  14. 14.     }

15. //添加方法

  1. 16.     public String toString() {
  2. 17.         return "姓名:" + name + "\n年齡:" + age + "\n性別:" + sex + "\n身份證號:" + sn;
  3. 18.     }

19. }

如上述代碼,在 People 類中包含 4 個公有屬性、一個構造方法和一個 toString() 方法。
2)創建 People 類的子類 Student 類,並定義 stuNo 和 department 屬性,代碼如下:

  1. //添加People的子類Student,Student繼承了People
  2. public class Student extends People {
  3.     private String stuNo; // 學號
  4.     private String department; // 所學專業
  5.  
  6.     public Student(String name, int age, String sex, String sn, String stuno, String department) {
  7.           // 調用父類中的構造方法:super()
  8.         super(name, age, sex, sn);
  9.         this.stuNo = stuno;
  10. 10.         this.department = department;
  11. 11.     }
  12. 12.  
  13. 13.     public String toString() {
  14. 14.         return "姓名:" + name + "\n年齡:" + age + "\n性別:" + sex + "\n身份證號:" + sn + "\n學號:" + stuNo + "\n所學專業:" + department;
  15. 15.     }

16. }

由於 Student 類繼承自 People 類,因此,在 Student 類中同樣具有 People 類的屬性和方法,這里重寫了父類中的 toString() 方法。
注意:如果在父類中存在有參的構造方法而並沒有重載無參的構造方法,那么在子類中必須含有有參的構造方法,因為如果在子類中不含有構造方法,默認會調用父類中無參的構造方法,而在父類中並沒有無參的構造方法,因此會出錯。
3)創建 People 類的另一個子類 Teacher,並定義 tYear 和 tDept 屬性,代碼如下:

  1. //添加子類
  2. public class Teacher extends People {
  3.     private int tYear; // 教齡
  4.     private String tDept; // 所教專業
  5.  
  6.     public Teacher(String name, int age, String sex, String sn, int tYear, String tDept) {
  7.         super(name, age, sex, sn); // 調用父類中的構造方法
  8.         this.tYear = tYear;
  9.         this.tDept = tDept;
  10. 10.     }
  11. 11.  
  12. 12.     public String toString() {
  13. 13.         return "姓名:" + name + "\n年齡:" + age + "\n性別:" + sex + "\n身份證號:" + sn + "\n教齡:" + tYear + "\n所教專業:" + tDept;
  14. 14.     }

15. }

Teacher 類與 Student 類相似,同樣重寫了父類中的 toString() 方法。
4)編寫測試類 PeopleTest,在該類中創建 People 類的不同對象,分別調用它們的 toString() 方法,輸出不同的信息。具體的代碼如下:

  1. //創建子類PeopleTest
  2. public class PeopleTest {
  3.     public static void main(String[] args) {
  4.         // 創建Student類對象
  5.         People stuPeople = new Student("王麗麗", 23, "女", "410521198902145589", "00001", "計算機應用與技術");
  6.         System.out.println("----------------學生信息---------------------");
  7.         System.out.println(stuPeople);
  8.  
  9.         // 創建Teacher類對象
  10. 10.         People teaPeople = new Teacher("張文", 30, "男", "410521198203128847", 5, "計算機應用與技術");
  11. 11.         System.out.println("----------------教師信息----------------------");
  12. 12.         System.out.println(teaPeople);
  13. 13.     }

14. }

運行程序,輸出的結果如下:

----------------學生信息---------------------

姓名:王麗麗

年齡:23

性別:女

身份證號:410521198902145589

學號:00001

所學專業:計算機應用與技術

----------------教師信息----------------------

姓名:張文

年齡:30

性別:男

身份證號:410521198203128847

教齡:5

所教專業:計算機應用與技術

1      單繼承

Java 語言摒棄了 C++ 中難以理解的多繼承特征,即 Java 不支持多繼承,只允許一個類直接繼承另一個類,即一個子類只能有一個直接父類,extends 關鍵字后面只能有一個類名。例如,如下代碼會導致編譯錯誤:

  1. class Student extends Person,Person1,Person2{…}
  2. class Student extends Person,extends Person1,extends Person2{…}
    很多地方在介紹 Java 的單繼承時,可能會說 Java 類只能有一個父類,嚴格來講,這種說法是錯誤的,應該是一個類只能有一個直接父類,但是它可以有多個間接的父類。例如,Student 類繼承 Person 類,Person 類繼承 Person1 類,Person1 類繼承 Person2 類,那么 Person1 和 Person2 類是 Student 類的間接父類。圖 1 展示了單繼承的關系。


圖 1  圖形類之間的關系
從圖 1 中可以看出,三角形、四邊形和五邊形的直接父類是多邊形類,它們的間接父類是圖形類。圖形類、多邊形類和三角形、四邊形、五邊形類形成了一個繼承的分支。在這個分支上,位於下層的子類會繼承上層所有直接或間接父類的屬性和方法。如果兩個類不在同一個繼承樹分支上,就不會存在繼承關系,例如多邊形類和直線。
如果定義一個 Java 類時並未顯式指定這個類的直接父類,則這個類默認繼承 java.lang.Object 類。因此,java.lang.Object 類是所有類的父類,要么是其直接父類,要么是其間接父類。因此所有的 Java 對象都可調用 java.lang.Object 類所定義的實例方法。
使用繼承的注意點:

  1. 子類一般比父類包含更多的屬性和方法。
  2. 父類中的 private 成員在子類中是不可見的,因此在子類中不能直接使用它們。
  3. 父類和其子類間必須存在“是一個”即“is-a”的關系,否則不能用繼承。但也並不是所有符合“is-a”關系的都應該用繼承。例如,正方形是一個矩形,但不能讓正方形類來繼承矩形類,因為正方形不能從矩形擴展得到任何東西。正確的繼承關系是正方形類繼承圖形類。
  4. Java 只允許單一繼承(即一個子類只能有一個直接父類),C++ 可以多重繼承(即一個子類有多個直接父類)。

2        繼承的優缺點

在面向對象語言中,繼承是必不可少的、非常優秀的語言機制,它有如下優點:

  1. 實現代碼共享,減少創建類的工作量,使子類可以擁有父類的方法和屬性。
  2. 提高代碼維護性和可重用性。
  3. 提高代碼的可擴展性,更好的實現父類的方法。


自然界的所有事物都是優點和缺點並存的,繼承的缺點如下:

  1. 繼承是侵入性的。只要繼承,就必須擁有父類的屬性和方法。
  2. 降低代碼靈活性。子類擁有父類的屬性和方法后多了些約束。
  3. 增強代碼耦合性(開發項目的原則為高內聚低耦合)。當父類的常量、變量和方法被修改時,需要考慮子類的修改,有可能會導致大段的代碼需要重構。

例 1

下面以一個員工類的封裝為例介紹封裝過程。一個員工的主要屬性有姓名、年齡、聯系電話和家庭住址。假設員工類為 Employee,示例如下:

  1. public class Employee {
  2.     private String name; // 姓名
  3.     private int age; // 年齡
  4.     private String phone; // 聯系電話
  5.     private String address; // 家庭住址
  6.     public String getName () {
  7.         return name;
  8.     }
  9. // setXxx() 方法來對其進行賦值
  10. 10.     public void setName(String name) {
  11. 11.         this.name = name;
  12. 12.     }

13. //通過 getXxx() 方法來訪問這些屬性

  1. 14.     public int getAge() {
  2. 15.         return age;
  3. 16.     }
  4. 17.     public void setAge(int age) {
  5. 18.         // 對年齡進行限制
  6. 19.         if (age < 18 || age > 40) {
  7. 20.             System.out.println("年齡必須在18到40之間!");
  8. 21.             this.age = 20; // 默認年齡
  9. 22.         } else {
  10. 23.             this.age = age;
  11. 24.         }
  12. 25.     }
  13. 26.     public String getPhone() {
  14. 27.         return phone;
  15. 28.     }
  16. 29.  
  17. 30.     public void setPhone(String phone) {
  18. 31.         this.phone = phone;
  19. 32.     }
  20. 33.  
  21. 34.     public String getAddress() {
  22. 35.         return address;
  23. 36.     }
  24. 37.     public void setAddress(String address) {
  25. 38.         this.address = address;
  26. 39.     }

40. }

如上述代碼所示,使用 private 關鍵字修飾屬性,這就意味着除了 Employee 類本身外,其他任何類都不可以訪問這些屬性。但是,可以通過這些屬性的 setXxx() 方法來對其進行賦值,通過 getXxx() 方法來訪問這些屬性。
在 age 屬性的 setAge() 方法中,首先對用戶傳遞過來的參數 age 進行判斷,如果 age 的值不在18 到 40 之間,則將 Employee 類的 age 屬性值設置為20,否則為傳遞過來的參數值。
編寫測試類 EmployeeTest,在該類的 main() 方法中調用 Employee 屬性的 setXxx() 方法對其相應的屬性進行賦值,並調用 getXxx() 方法訪問屬性,代碼如下:

  1. //新建測試類
  2. public class EmployeeTest {
  3.     public static void main(String[] args) {
  4.           //創建一個實例
  5.         Employee people = new Employee();
  6.           //使用setXxx()對屬性進行賦值
  7.         people.setName("王麗麗");
  8.         people.setAge(35);
  9.         people.setPhone("13653835964");
  10. 10.         people.setAddress("河北省石家庄市");
  11. 11.           //使用getXxx()對屬性進行訪問
  12. 12.         System.out.println("姓名:" + people.getName());
  13. 13.         System.out.println("年齡:" + people.getAge());
  14. 14.         System.out.println("電話:" + people.getPhone());
  15. 15.         System.out.println("家庭住址:" + people.getAddress());
  16. 16.     }

17. }

運行該示例,輸出結果如下:

姓名:王麗麗

年齡:35

電話:13653835964

家庭住址:河北省石家庄市

       通過封裝,實現了對屬性的數據訪問限制,滿足了年齡的條件。在屬性的賦值方法中可以對屬性進行限制操作,從而給類中的屬性賦予合理的值,並通過取值方法獲取類中屬性的值(也可以直接調用類中的屬性名稱來獲取屬性值)。

2.封裝圖書信息類

       了解有關封裝的知識后,通過完整的例子再次實現封裝。要求編寫表示圖書的 Book 類,實現以下需求:

  • 基本信息包括圖書名稱(bookName)、總頁數(pagelbtalNum),其中頁數不能少於 200 頁,否則輸出錯誤信息,並賦予默認值 200。
  • 為各個屬性設置賦值和取值方法。
  • 具有 details() 方法,該方法在控制台輸出每本圖書的名稱和總頁數。
    編寫 BookTest 測試類,為 Book 對象的屬性賦予初始值,並調用 details() 方法輸出詳細信息。根據上面的描述添加代碼,步驟如下。
    1)創建 Book 類,首先向該類添加 bookName 變量,並封裝該變量。代碼如下:
  1. //創建類
  2. public class Book {
  3. //添加 bookName 變量
  4.     private String bookName; // 圖書名稱
  5. //封裝變量,使用了getXxx()訪問
  6.     public String getBookName() {
  7.         return bookName;
  8.     }
  9. //封裝變量,使用了setXxx()賦值
  10. 10.     public void setBookName(String bookName) {
  11. 11.         this.bookName = bookName;
  12. 12.     }

13. }

2)在 Book 類中添加 bookTotalNum 變量,並封裝該變量,在封裝的 setter 方法中判斷頁數的值是否小於 200。代碼如下:

  1. private int bookTotalNum; // 圖書總頁數
  2. //封裝變量
  3. public int getBookTotaiNum() {
  4.     return bookTotalNum;
  5. }
  6. //在封裝的 setXxx()方法中判斷頁數的值是否小於 200
  7. public void setBookTotalNum(int bookTotalNum) {
  8.     if (bookTotalNum < 200) {
  9.         System.out.println(this.bookName + "這本書的頁數不能少於 200 頁");
  10. 10.         this.bookTotalNum = 200;
  11. 11.     } else {
  12. 12.         this.bookTotalNum = bookTotalNum;
  13. 13.     }

14. }

3)在 Book 類中添加公有的 details() 方法,輸出圖書的名稱和總頁數。代碼如下:

  1. //創建 details()方法,輸出圖書信息
  2. public void details() {
  3.     System.out.println(this.bookName + "這本書的總頁數是:" + this.bookTotalNum);
  4. }

4)創建 BookTest 測試類,在該類的 main() 方法中創建 Book 類的兩個實例對象,然后分別為類中的兩個屬性賦值,最后調用 details() 方法輸出信息。代碼如下:

  1. //創建類
  2. public class BookTest {
  3.     public static void main(String[] args) {
  4.           //創建Book的實例對象
  5.         Book book1 = new Book();
  6.          
  7.         book1.setBookName("《紅與黑》");
  8.         book1.setBookTotalNum(190);
  9.         book1.details();
  10. 10.         System.out.println("************************************");
  11. 11.         Book book2 = new Book();
  12. 12.         book2.setBookName("《格林童話》");
  13. 13.         book2.setBookTotalNum(520);
  14. 14.         book2.details();
  15. 15.     }

16. }

5)執行上述代碼,輸出結果如下:

《紅與黑》這本書的頁數不能少於 200 頁

《紅與黑》這本書的總頁數是:200

************************************

《格林童話》這本書的總頁數是:520

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM