Java之繼承和抽象類

gonghr發表於2021-03-25

繼承

繼承的實現

繼承通過extends實現
格式:class 子類 extends 父類 { }
舉例:class Dog extends Animal { }
 
繼承帶來的好處
繼承可以讓類與類之間產生關係,子父類關係,產生子父類後,子類則可以使用父類中非私有的成員。
public class Fu { 
public void show() { System.out.println("show方法被呼叫"); } } public class Zi extends Fu { public void method() { System.out.println("method方法被呼叫"); } } public class Demo { public static void main(String[] args) { //建立物件,呼叫方法 Fu f = new Fu(); f.show(); Zi z = new Zi(); z.method(); z.show(); } }

繼承的好處和弊端

繼承好處

提高了程式碼的複用性(多個類相同的成員可以放到同一個類中)  提高了程式碼的維護性(如果方法的程式碼需要修改,修改一處即可)繼承弊端

 

繼承讓類與類之間產生了關係,類的耦合性增強了,當父類發生變化時子類實現也不得不跟著變化,削弱了子類的獨立性

 

繼承的應用場景:

使用繼承,需要考慮類與類之間是否存在is..a的關係,不能盲目使用繼承is..a的關係:誰是誰的一種,例如:老師和學生是人的一種,那人就是父類,學生和老師就是子類

Java中繼承的特點

1. Java中類只支援單繼承,不支援多繼承
錯誤範例:class A extends B, C { }
2. Java中類支援多層繼承
 
多層繼承示例程式碼
public class Granddad {
    public void drink() { 
        System.out.println("爺爺愛喝酒");
    }
}
public class Father extends Granddad { 
    public void smoke() {
        System.out.println("爸爸愛抽菸");
    }
}
public class Mother { 
    public void dance() {
        System.out.println("媽媽愛跳舞");
    }
}
public class Son extends Father {
// 此時,Son類中就同時擁有drink方法以及smoke方法
}            

繼承中的成員訪問特點

繼承中變數的訪問特點

在子類方法中訪問一個變數,採用的是就近原則。

  1. 子類區域性範圍找
  2. 子類成員範圍找
  3. 父類成員範圍找
  4. 如果都沒有就報錯(不考慮父親的父親…)
class Fu {
    int num = 10;
}
class Zi {
    int num = 20; public void show(){
    int num = 30; 
    System.out.println(num);
    }
}
public class Demo1 {
    public static void main(String[] args) { 
        Zi z = new Zi();
        z.show();    // 輸出show方法中的區域性變數30
    }
}

super

this&super關鍵字:

this:代表本類物件的引用

super:代表父類儲存空間的標識(可以理解為父類物件引用)

 

this和super的使用分別成員變數:

this.成員變數 -   訪問本類成員變數

super.成員變數 -  訪問父類成員變數

 

成員方法:

this.成員方法 - 訪問本類成員方法

super.成員方法 - 訪問父類成員方法

 

構造方法:

this(…) - 訪問本類構造方法

super(…) - 訪問父類構造方法

繼承中構造方法的訪問特點

注意:子類中所有的構造方法預設都會訪問父類中無參的構造方法

子類會繼承父類中的資料,可能還會使用父類的資料。所以,子類初始化之前,一定要先完成父類資料的初始化, 原因在於,每一個子類構造方法的第一條語句預設都是:super()

問題:如果父類中沒有無參構造方法,只有帶參構造方法,該怎麼辦呢?

1. 通過使用super關鍵字去顯示的呼叫父類的帶參構造方法
2. 子類通過this去呼叫本類的其他構造方法,本類其他構造方法再通過super去手動呼叫父類的帶參的構造方法
注意: this(…)super(…) 必須放在構造方法的第一行有效語句,並且二者不能共存

繼承中成員方法的訪問特點

通過子類物件訪問一個方法
1. 子類成員範圍找
2. 父類成員範圍找
3. 如果都沒有就報錯(不考慮父親的父親…)

方法重寫

1、方法重寫概念
子類出現了和父類中一模一樣的方法宣告(方法名一樣,引數列表也必須一樣)
 
2、方法重寫的應用場景
當子類需要父類的功能,而功能主體子類有自己特有內容時,可以重寫父類中的方法,這樣,即沿襲了父類的功能,又定義了子類特有的內容
 
3、Override註解
用來檢測當前的方法,是否是重寫的方法,起到【校驗】的作用

方法重寫的注意事項

1. 私有方法不能被重寫(父類私有成員子類是不能繼承的)
2. 子類方法訪問許可權不能更低(public > 預設 > 私有)
3. 靜態方法不能被重寫,如果子類也有相同的方法,並不是重寫的父類的方法,是將父類的方法隱藏
public class Fu { 
    private void show() {
    System.out.println("Fu中show()方法被呼叫");
    }
    void method() {
    System.out.println("Fu中method()方法被呼叫");
    }
}
public class Zi extends Fu {
/* 編譯【出錯】,子類不能重寫父類私有的方法*/ 
    @Override
    private void show() {
    System.out.println("Zi中show()方法被呼叫");
    }

/* 編譯【出錯】,子類重寫父類方法的時候,訪問許可權需要大於等於父類 */ @Override
    private void method() {
    System.out.println("Zi中method()方法被呼叫");
    }

/* 編譯【通過】,子類重寫父類方法的時候,訪問許可權需要大於等於父類 */ 
    @Override
    public void method() {
    System.out.println("Zi中method()方法被呼叫");
    }
}

許可權修飾符

super記憶體圖

物件在堆記憶體中,會單獨存在一塊super區域,用來存放父類的資料(父類的空參建構函式必須寫)

父類沒有空參構造

1.在子類通過super呼叫父類有參建構函式

public Child(int age){
        super(age);
        this.age=age;
        System.out.println("Child parameter constructor called");
}

2.子類通過this呼叫本類其他構造方法,本類其他構造方法再通過呼叫super去手動呼叫父類有參構造方法

 

    public Child(){
        this(10);
        System.out.println("Child non_parameter constructor called");
    }
    public Child(int age){
        super(age);
        System.out.println("Child parameter constructor called");
    }

注意this和super都必須放在構造方法第一行才能作為有效語句,並且二者不能共存

抽象類

抽象類的概述

當我們在做子類共性功能抽取時,有些方法在父類中並沒有具體的體現,這個時候就需要抽象類了!

在Java中,一個沒有方法體的方法應該定義為抽象方法,而類中如果有抽象方法,該類必須定義為抽象類!

抽象類的特點

抽象類和抽象方法必須使用 abstract 關鍵字修飾 
//抽象類的定義
public abstract class 類 名 {}

//抽象方法的定義
public abstract void eat();
抽象類中不一定有抽象方法,有抽象方法的類一定是抽象類
抽象類不能例項化
抽象類可以有構造方法
抽象類的子類,要麼重寫抽象類中的所有抽象方法,要麼是自己變成抽象類 

抽象類的案例

案例需求
定義貓類(Cat)和狗類(Dog)
貓類成員方法:eat(貓吃魚)drink(喝水…)
狗類成員方法:eat(狗吃肉)drink(喝水…)
實現步驟
1. 貓類和狗類中存在共性內容,應向上抽取出一個動物類(Animal)
2. 父類Animal中,無法將 eat 方法具體實現描述清楚,所以定義為抽象方法
3. 抽象方法需要存活在抽象類中,將Animal定義為抽象類
4. 讓 Cat 和 Dog 分別繼承 Animal,重寫eat方法
5. 測試類中建立 Cat 和 Dog 物件,呼叫方法測試 
 

動物類

public abstract class Animal { 
    public void drink(){
    System.out.println("喝水");
    }
    public Animal(){
    }
    public abstract void eat();
}

貓類

public class Cat extends Animal { 
    @Override
    public void eat() { 
        System.out.println("貓吃魚");
    }
}

狗類

public class Dog extends Animal { 
    @Override
    public void eat() { 
        System.out.println("狗吃肉");
    }
}    

測試類

public static void main(String[] args) { 
    Dog d = new Dog();
    d.eat();
    d.drink();

    Cat c = new Cat(); c.drink();
    c.eat();

    //Animal a = new Animal();    //抽象類不能例項話物件
    //a.eat();
}

模板設計模式

設計模式

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性、程式的重用性。

 

模板設計模式

把抽象類整體就可以看做成一個模板,模板中不能決定的東西定義成抽象方法  讓使用模板的類(繼承抽象類的類)去重寫抽象方法實現需求

模板設計模式的優勢

模板已經定義了通用結構,使用者只需要關心自己需要實現的功能即可

 

示例程式碼

模板類

/*
作文模板類
*/
public abstract class CompositionTemplate {

    public final void write(){ 
    System.out.println("<<我的爸爸>>");
    body();
    System.out.println("啊~ 這就是我的爸爸");
    }
    public abstract void body();
}

實現類A

 

public class Tom extends CompositionTemplate {
    @Override
    public void body() {
    System.out.println("那是一個秋天, 風兒那麼纏綿,記憶中, " +
    "那天爸爸騎車接我放學回家,我的腳卡在了自行車鏈當中, 爸爸蹬不動,他就            
    站起來蹬...");
    }
}

測試類

public class Test {
    public static void main(String[] args) { 
        Tom t = new Tom();
        t.write();
    }
}

final

fianl關鍵字的作用

final代表最終的意思,可以修飾成員方法,成員變數,類final修飾類、方法、變數的效果

 

fianl修飾類:該類不能被繼承(不能有子類,但是可以有父類)

public final class Parent{};

final修飾方法:該方法不能被重寫

public final void show{};

final修飾變數:表明該變數是一個常量,不能再次賦值。變數是基本型別,不能改變值

final int a=10;  //正確
a=20;    //錯誤

變數是引用型別,不能改變的是地址值,但地址裡面的內容是可以改變的

public static void main(String[] args){ 
    final Student s = new Student(23); 
    s = new Student(24); // 錯 誤
    s.setAge(24); // 正 確
}

程式碼塊 

程式碼塊概述

在Java中,使用 { } 括起來的程式碼被稱為程式碼塊 

程式碼塊分類

區域性程式碼塊
位置: 方法中定義
作用: 限定變數的生命週期,及早釋放,提高記憶體利用率
public class Test {
/*
區域性程式碼塊
位置:方法中定義
作用:限定變數的生命週期,及早釋放,提高記憶體利用率
*/
    public static void main(String[] args) {
        {
            int a = 10; 
            System.out.println(a);
        }

    // System.out.println(a);
    }
}

構造程式碼塊

位置: 類中方法外定義

特點: 每次構造方法執行的時,都會執行該程式碼塊中的程式碼,並且在構造方法執行前執行作用: 將多個構造方法中相同的程式碼,抽取到構造程式碼塊中,提高程式碼的複用性


public static void main(String[] args) {

  Student stu1 = new Student();

  Student stu2 = new Student(10);

}

class Student {
    {
    System.out.println("好好學習");
    }
    
    public Student(){
        System.out.println("空引數構造方法");
    }

    public Student(int a){
        System.out.println("帶引數構造方法");
    }
}

輸出:

好好學習
空引數構造方法
好好學習
帶引數構造方法

靜態程式碼塊

位置: 類中方法外定義

特點: 需要通過static關鍵字修飾,隨著類的載入而載入,並且只執行一次作用: 在類載入的時候做一些資料初始化的操作


public static void main(String[] args) { 
    Person p1 = new Person();
    Person p2 = new Person(10);
}
class Person { 
    static {
        System.out.println("我是靜態程式碼塊, 我執行了");
    }
    public Person(){
    System.out.println("我是Person類的空引數構造方法");
    }
    public Person(int a){
    System.out.println("我是Person類的帶引數構造方法");
    }  
}

輸出:

我是靜態程式碼塊, 我執行了
我是Person類的空引數構造方法
我是Person類的帶引數構造方法
 

相關文章