2022-07-21 第四組 java之繼承

2022-07-21 21:00:46

一、繼承

1.概念

  • 繼承的基本思想是,基於已有的類創造新的類。繼承已存在的類就是複用這些類的方法,而且可以增加一些新的方法和欄位,使新類能夠適應新的情況,繼承是Java程式設計中一項核心技術,它主要解決的問題是:共性的抽取,實現程式碼複用


上圖中,講師類和助教類都繼承員工類,講師類和助教類可以稱為員工類的子類或者派生類,繼承之後,子類可以複用父類別的方法和屬性,子類在實現時只關心自己新增加的成員即可。

2.語法

  • 定義父類別的格式:(一個普通的類定義)
    public class 父類別名稱{
    //...
    }
  • 定義子類的格式:
    public class 子類名稱 extends 父類別名稱{
    //...
    }
//Employee.java
public class Employee {
    public void method() {
        System.out.println("方法執行!");
    }
}
//Teacher.java
//定義了一個員工的子類--講師
public class Teacher extends Employee {
}
//Assistant.java
//定義另一個子類---助教
public class Assistant extends Employee {
}
//主方法
public class Demo01Extends {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.method();
        Assistant assistant = new Assistant();
        assistant.method();
    }
}


注意:

  • 子類將繼承父類別的成員變數和成員方法
  • 子類繼承父類別之後,需要新增自己特有的成員,體現出與基礎類別的不同

3.父類別成員存取

3.1 子類存取父類別的成員變數

3.1.1 子類和父類別中不存在同名的成員變數

public class Base {
    int a;
    int b;
}
 
public class Derived extends Base{
    int c;
    public void method(){
        a = 10;  //從父類別繼承
        b = 20;  //從父類別繼承
        c = 30;  //存取自己
    }
}

3.1.2 子類和父類別中不存在同名的成員變數

public class Base {
    int a;
    int b;
    int c;
}
 
public class Derived extends Base {
    char b;   //與父類別同名,不同型別
    int c;    //與父類別同名,相同型別
    public void method(){
        a = 10;   //存取父類別繼承
        b = 20;   //存取誰的?
        c = 30;   //存取誰的?
        //d = 40;  //編譯器報錯
    }
}

注意:

  • 如果存取的成員變數子類中有,則優先存取子類本身的

  • 如果存取的成員變數子類中無,父類別中有,則存取繼承下來的

  • 如果子類與父類別中有同名的成員變數,則優先存取子類自己的,即子類將父類別的同名變數隱藏

成員變數的存取遵循就近原則,自己有就優先存取自己的

3.2 子類中存取父類別的成員方法

3.2.1 成員方法名字不同

public class Base {
    public void method1(){
        System.out.println("我是父類別方法");
    }
}
 
 
 
public class Derived extends Base {
   public void method2(){
       System.out.println("我是子類方法");
   }
   public void method(){
       method1();   //父類別方法
       method2();   //子類方法
   }
}

3.2.2 成員方法名字相同

public class Base {
    public void method1(){
        System.out.println("我是父類別方法");
    }
    public void method2(){
        System.out.println("我是父類別方法");
    }
}
 

public class Derived extends Base {
    public void method1(int a){
        System.out.println("我是子類方法");
    }
    public void method2(){
       System.out.println("我是子類方法");
   }
    public void method(){
        method1();   //父類別方法
        method1(10); //子類方法
        method2();   //子類方法
   }
}

說明:

  • 通過子類存取成員方法,先看子類本身有沒有,如果有存取自己的,如果沒有,存取父類別的

  • 通過子類存取與父類別同名方法時,如果子類和父類別方法的參數列不同則構成過載,根據呼叫方法傳遞的引數選擇合適的方法存取

  • 如果子類和父類別同名方法的原型一致,則只能存取到子類的

4.重寫(override)

1. 概念

在繼承關係中,方法的名稱不一樣,參數列也一樣。

2.過載和重寫的區別

重寫(override),方法名稱一樣,參數列也一樣,覆蓋,覆寫
過載(overload),方法名稱一樣,參數列不一樣

  • 方法過載是一個類中定義了多個方法名相同,而他們的引數的數量不同或數量相同而型別和次序不同,則稱為方法的過載 (overload)。

  • 方法重寫是在子類存在方法與父類別的方法的名字相同,而且引數的個數與型別一樣,返回值也一樣的方法,就稱為重寫 (override)。

  • 方法過載是一個類的多型性表現,而方法重寫是子類與父類別的一種多型性表現。

方法過載的要求是參數列不同。具體包括以下三種情形。
①引數的數量不同。
②引數的型別不同。
③引數的順序不同。

3.方法重寫的注意事項

  • 必須保證父子類之間方法的名稱相同,參數列也相同
    @override;寫在方法前面,用來檢測是否是正常的覆蓋重寫

  • 子類方法的返回值必須小於等於父類別的返回值範圍
    前提:object類是所有類的最高父類別(祖宗類)

  • 子類方法的許可權必須大於等於父類別的許可權修飾符
    public > protected >(default) >private
    備註:(default)不是關鍵字default,而是空

5.super關鍵字

super關鍵字用來存取父類別內容,而this關鍵字用來存取本類內容,用法也有三種:

  • 在本類的成員方法中,存取本類的成員變數
  • 在本類的成員方法中,存取本類的另一個成員方法
  • 在本類的構造方法中存取另一個構造方法。
    第三點注意:this(...)呼叫也必須是呼叫方法的第一個語句
    this和super兩種構造方法,不能同時使用

6.super和this的記憶體圖

public class Demo01Extends {
    public static void main(String[] args) {
        ZI zi = new ZI();
        zi.Method();

    }
}
-----------------------------
public class Fu {
    int num = 10;
}
-----------------------------
public class ZI extends Fu {
    int num = 20;

    public void Method() {
        int num = 30;
        System.out.println(num);
        System.out.println(this.num);//本類的成員變數
        System.out.println(super.num);//父類別的成員變數
    }
}

7.子類構造方法

構造哪個類的物件,就呼叫哪個類的構造方法,呼叫構造方法時先呼叫基礎類別,在呼叫子類(即在子類中隱藏super() )

public class Base {
    public Base(){
        System.out.println("Base()");
    }
}
 
public class Derived extends Base {
    public Derived(){
        System.out.println("Derived()");
    }
}
 
public class Text {
    public static void main(String[] args) {
        Derived d = new Derived();
    }
}
輸出結果:Base()
         Derived()

在子類構造方法中,並沒有寫任何關於基礎類別構造的程式碼,但是在構造子類物件時,先執行基礎類別的構造方法,然後執行子類的構造方法

注意:

  • 若父類別顯示定義無參或者預設的構造方法,在子類構造方法的第一行預設有隱含的super呼叫,即呼叫基礎類別的構造方法

  • 如果父類別的構造方法是帶有引數的,此時編譯器不會給子類生成預設的構造方法,此時需要使用者在子類中顯示定義構造方法,並在子類構造方法中選取合適的父類別構造方法呼叫

  • 在子類構造方法中,super(...)呼叫父類別構造時,必須是子類構造方法中的第一條語句

  • super(...)只能在子類的構造方法中出現一次,並不能和this同時出現

8. 執行順序

無繼承關係時的執行順序:

public class Person {
    String name;
    String gender;
    int age;
    public Person(String name,String gender,int age){
        this.name = name;
        this.gender = gender;
        this.age = age;
        System.out.println("我是構造方法");
    }
    {
        System.out.println("我是範例程式碼塊");
    }
    static {
        System.out.println("我是靜態程式碼塊");
    }
 
    public static void main(String[] args) {
        Person p1 = new Person("xiaoHua","男",12);
        System.out.println("=====================");
        Person p2 = new Person("xiaoHong","女",15);
    }
}

執行結果:

說明:

靜態程式碼塊先執行,且只執行一次,在類載入階段執行

當有物件建立時,才會執行範例程式碼塊,範例程式碼塊執行完後,再執行構造方法

有繼承關係時的執行順序:

public class Person {
    String name;
    String gender;
    int age;
    public Person(String name,String gender,int age){
        this.name = name;
        this.gender = gender;
        this.age = age;
        System.out.println("person的構造方法");
    }
    {
        System.out.println("person的範例程式碼塊");
    }
    static {
        System.out.println("person的靜態程式碼塊");
    }
}

 
public class Student extends Person{
    public Student(String name, String gender, int age) {
        super(name, gender, age);
        System.out.println("student的構造方法");
    }
    {
        System.out.println("student的範例程式碼塊");
    }
    static {
        System.out.println("student的靜態程式碼塊");
    }
}
 
public class Text {
    public static void main(String[] args) {
        Student s1 = new Student("張三","男",35);
        System.out.println("=====================");
        Student s2 = new Student("李四","男",30);
    }
}

執行結果:

結論:

  • 父類別靜態程式碼塊優先子類靜態程式碼塊執行,都是最早執行的

  • 父類別範例程式碼塊和父類別構造方法緊接著執行

  • 子類的範例程式碼塊和子類構造方法在接著執行

  • 第二次範例化物件時,父類別和子類的靜態程式碼塊都不會在執行

9. 繼承方式

Java中只支援以下幾種方式:

注意:Java中不支援多支援

10. final關鍵字

final關鍵字可以用來修飾變數,成員方法以及類。

1.修飾變數或欄位,表示常數(即不能修改)

final int a = 10;
a = 20;//編譯報錯

2.修飾類,表示類不能繼承

final public class Person{
     
}
public class Student extends Person{
     
}//編譯報錯

3.修飾方法,表示方法不能被重寫(後續在介紹)