關於繼承,你瞭解多少?

classic123發表於2022-03-20

繼承

語法格式: class 子類 extends 父類{ }

  • 子類到底繼承了父類哪些內容呢?
  • 很多小夥伴會以為子類只能訪問父類的public和protected屬性和方法,所以子類繼承了父類中訪問修飾符為public和protected的部分。
  • 這麼理解是錯誤的,準確來說繼承是無關訪問修飾符的。
  • 當用子類的構造方法建立一個子類的物件時,不僅子類中宣告的成員變數被分配了記憶體,而且父類子物件的成員變數也都被分配了記憶體空間。
  • 我們用以下程式碼驗證:
/**
 * 父類Animals,子類為Dog,在主函式中呼叫子類Dog的構造方法
 */
public class Animals {
    public int age;
    private String name;
    Animals(){
        age = 1 ;
        name = "動物" ;
        System.out.println("Animals_age:"+age) ;
        System.out.println("Animals_name:"+name) ;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        Dog dog1 = new Dog();
    }
}
class Dog extends Animals{
    Dog() {
        age = 2 ;
        setName("狗");
        System.out.println("Dog_age:"+age);
        System.out.println("Dog_name:"+getName());
    }
}

結果為:
Animals_age:1
Animals_name:動物
Dog_age:2
Dog_name:狗

  • 我們發現呼叫子類的構造方法會首先呼叫父類的構造方法,所以說子類在進行new操作時,父類子物件的成員變數也都被分配了記憶體空間。

訪問修飾符: 限定修飾的主體的使用範圍 -檔案域範圍

  • public 整個專案下
  • private 本類{}中
  • protected 同一個包中,其子類中
  • 預設 同包下
  • 反射 可以直接拿到所有的方法 屬性 包括私有的

方法重寫:

  • 基本格式: 除方法體外與父類的方法一致
    • 方法名與引數型別必須一致 其他的都可以在一定程度上修改
      • 如果我們將父類中的高訪問許可權的方法在子類中重寫為低訪問許可權的方法,編譯時會報錯:子類中的方法無法覆蓋父類中的方法,
      • 如果我們將重寫函式的返回值型別改為與父類返回值不同的型別,也會顯示無法覆蓋。
    • 那麼許可權修飾符和返回值型別什麼時候可以被修改呢?
      • 許可權修飾符修改的條件:子類方法的訪問許可權比父類方法的訪問許可權高
      • 返回值修改的條件:子類方法的返回值型別為父類方法返回值型別的子類
      • 我們可以根據里氏代換原則進行理解: 在一個軟體系統中,子類應該可以替換任何基類能夠出現的地方,並且經過替換以後,程式碼還能正常工作。
  • 重寫的物件:子類重寫父類中不受限制的方法(不能重寫final以及受訪問許可權限制的方法)
  • 重寫方法: 子類以及物件(不管是否向上轉型)可以優先呼叫重寫後的方法
  • 子類可以在父類方法的基礎上,改造 擴充套件方法

轉型

  • 基本資料型別

    • 強制轉型
      • int x = (int)1.2; //會造成精度損失,資料不安全
    • 自動轉型
      • byte b = 100;
      • int a = b;
  • 引用資料型別轉型:(類 介面)

    • 自動轉型: 子類物件名向上轉型為父類型別
    • 程式碼驗證如下
/**
 * 父類Animals,子類Dog,子類重寫父類show方法
 */
public class Animals {

    public void show() {
        System.out.println(50);
    }

    public static void main(String[] args) {
        Animals dog = new Dog(); //子類物件名向上轉型,但是在記憶體中仍然是Dog物件
        dog.show();     //輸出100
        dog.show2();     //Error,編譯錯誤,在Animals類中無法找到show2方法
    }
}
class Dog extends Animals {
    @Override
    public void  show() {
        System.out.println(100);
    }
    public void  show2() {
        System.out.println(20);
    }
}

子類物件自動轉型之後不能呼叫子類獨有的屬性和方法

  • 強制轉型 :不安全,必須驗證物件的原始型別
  • 錯誤案例:
public class Animals {
    
    public static void main(String[] args) {
        Dog dog = new Dog(); 
        Animals a = dog;  //dog自動轉型為Animals
        Bird bird = (Bird) a;  //a強制轉型為Bird,但是轉型後的bird存放的仍然是Dog物件
        bird.fly(); //此時bird裡沒有fly方法,執行時出現ClassCastException(轉型異常)
    }
}
class Dog extends Animals {
    public void run(){
        System.out.println("狗會奔跑");
    }
}
class Bird extends Animals{
    public void fly(){
        System.out.println("鳥會飛");
    }
}
  • 解決方法:在強制轉型之前,使用instanceof來判斷父類是否是子類的例項
  • 語法: bird instanceof Bird
        Dog dog = new Dog();
        Animals a1 = dog;
        if(a1 instanceof Dog){ // 判斷a1是否為Dog的例項物件
            Dog dog2 = (Dog)a1;  //是的話,就可以進行強制轉換了
            System.out.println("ok");
        }

屬性的”重寫“

  • 方法可以重寫,那屬性可不可以重寫呢?
  • 屬性的“重寫”,實際上是用子類的屬性覆蓋父類的屬性
  • 程式碼驗證:
public class Animals {
    int age;
    String name = "動物";

    public static void main(String[] args) {
        Dog dog1 = new Dog();
        //dog1.name = "狗";  ①
        System.out.println(dog1.name);
    }
}
class Dog extends Animals{
    //String name;   ②
}
  • 無①② 輸出:動物
  • 有② 輸出:null
  • 有①② 輸出:狗

相關文章