物件導向高階(二)
多型
多型是在繼承/實現情況下的一種現象,表現為:物件多型、行為多型。
多型的具體程式碼體現
//使用同一個類名建立了不同型別的物件,體現了物件多型
People p1 = new Student();
People p2 = new Teacher();
//不同型別的物件呼叫了同一個名字的方法,體現了行為多型
p1.run();
p2.run();
識別技巧:編譯看左邊,行為看右邊
多型的前提
-
有繼承/實現關係。
-
存在父類引用子類物件。
-
存在方法重寫。
多型的注意事項
- 多型是物件、行為的多型,Java中的屬性(成員變數)不談多型。
- 多型的物件作為引數傳入方法時,是看建立時的等號左邊;而使用.呼叫方法時,是看等號右邊。
多型有什麼用
-
在多型形式下,右邊物件是解耦合(松耦合)的,更便於擴充套件和維護。
-
定義方法時,使用父類型別的形參,可以接收一切子類物件,擴充套件性更強、更便利。
多型的問題
-
多型下不能使用子類的獨有功能。
-
可以使用強制型別轉換,把物件轉換成其真正的型別,從而解決多型下不能呼叫子類獨有方法的問題。
多型下的型別轉換問題
型別轉換
自動型別轉換:
父類 變數名 = new 子類();
強制型別轉換:
子類 變數名 = (子類) 父類變數;
強制型別轉換的一個注意事項
- 存在繼承/實現關係就可以在編譯階段進行強制型別轉換,編譯階段不會報錯。
- 執行時,如果發現物件的真實型別與強轉後的型別不同,就會報型別轉換異常(ClassCastException)的錯誤出來。
- Java建議:強轉前,使用instanceof關鍵字,判斷當前物件的真實型別,再進行強轉。
final
final 關鍵字是最終的意思,可以修飾類、方法和變數。
- 修飾類:該類被稱為最終類,特點是不能被繼承了。
- 修飾方法:該方法被稱為最終方法,特點是不能被重寫了。
- 修飾變數:該變數只能被賦值一次,且當修飾成員變數時必須要在定義時同時賦值。
final修飾變數的注意事項
- final修飾基本型別的變數,變數儲存的資料不能被改變。
- final修飾引用型別的變數,變數儲存的地址不能被改變,但地址所指向物件的內容是可以被改變的。
變數的分類
變數:
-
區域性變數
-
成員變數:
- 靜態成員變數
- 例項成員變數
常量
-
使用了static final修飾的成員變數就被稱為常量。其作用是通常用於記錄系統的配置資訊。
-
常量名的命名規範:建議使用大寫英文單詞,多個單詞使用下劃線連線起來。
常量的好處
- 程式碼可讀性更好,可維護性也更好。
- 程式編譯後,常量會被“宏替換”:出現常量的地方全部會被替換成其記住的字面量,這樣可以保證使用常量和直接用字面量的效能是一樣的。
抽象類
使用關鍵字abstract修飾類,這個類就是抽象類;修飾方法,這個方法就是抽象方法。
格式
修飾符 abstract class 類名{
修飾符 abstract 返回值型別 方法名稱(形參列表);
}
抽象類的注意事項和特點
- 抽象類中不一定有抽象方法,有抽象方法的類一定是抽象類。
- 類有的成員(成員變數、方法、構造器、程式碼塊和內部類)抽象類都可以有。
- 抽象類最主要的特點:抽象類不能建立物件,僅作為一種特殊的父類,讓子類繼承並實現。
- 一個類繼承抽象類,必須重寫完抽象類的全部抽象方法,否則這個類也必須定義成抽象類。
抽象類的應用場景和好處
抽象類由兩種主要的應用場景:
- 用抽象類,我們可以把子類中相同的程式碼,包括方法簽名都抽上來,這樣能更好的支援多型,以提高程式碼的靈活性,並且減少重複程式碼的書寫,提高開發效率。
- 反過來用,我們不知道系統未來具體的業務實現時,我們可以先定義抽象類,將來讓子類去繼承實現,以方便系統的擴充套件。
抽象類的常見應用場景
經常用來設計模板方法設計模式。
模板方法設計模式解決了什麼問題
解決方法中存在重複程式碼的問題。
模板方法設計模式的寫法
- 定義一個抽象類。
- 在裡面定義2個方法:
- 一個是模板方法:把相同程式碼放裡面去。
- 一個是抽象方法:具體實現交給子類完成。
- 定義子類繼承抽象類,重寫抽象方法。
- 建立子類物件,呼叫模板方法完成功能。
- 建議使用final關鍵字修飾模板方法,因為:
- 模板方法是給物件直接使用的,不能被子類重寫。
- 一旦子類重寫了模板方法,模板方法就失效了。
介面
- Java提供了一個關鍵字interface,用這個關鍵字我們可以定義出一個特殊的結構:介面。
- 介面不能建立物件;介面是用來被類實現(implements)的,實現介面的類稱為實現類。
- 一個類可以實現多個介面,實現類實現多個介面,必須重寫完全部介面的全部抽象方法,否則實現類需要定義成抽象類。
- 介面只有成員物件和成員方法,且預設是常量和抽象方法。
- 介面也不能建立物件。
- 介面不繼承Object類。
介面和實現的格式
介面的格式:
public interface 介面名 {
// 成員變數(常量)
// 成員方法(抽象方法)
}
實現的格式:
修飾符 class 實現類 implements 介面1, 介面2, 介面3 , ... {
}
//在實現介面時,可以同時繼承父類,但是需要先寫繼承再寫介面
修飾符 class 實現類 entends 父類 implements 介面1, 介面2, 介面3 , ... {
}
介面的好處
- 彌補了類單繼承的不足,類可以同時實現多個介面。
- 讓程式可以面向介面程式設計,這樣既不用關心實現的細節,也可以靈活方便的切換各種實現。
- 透過面向介面程式設計,別人透過實現類implements的介面,就可以顯性的知道實現類實現的是什麼誰,從而也就可以放心的把實現類當作誰來用了,同時也能提高程式碼的可讀性。
JDK8開始,介面中新增的三種方法
預設方法
預設方法(例項方法)使用default修飾,預設會被加上public修飾,只能使用介面的實現類物件呼叫。
私有方法(JDk9開始才支援的)
私有方法必須用private修飾,只能在介面內部被呼叫。
靜態方法
類方法(靜態方法)使用static修飾,預設會被加上public修飾,只能用介面名來呼叫。
這些方法的作用
增強了介面的能力,更便於專案的擴充套件和維護。
介面的多繼承
介面也可以繼承,一個介面可以同時繼承多個介面。
格式
public interface C extends B, A, ... {
}
介面多繼承的作用
便於實現類去實現。
介面的其他注意事項
- 一個介面繼承多個介面,如果多個介面中存在方法簽名衝突,則此時不支援多繼承。
- 一個類實現多個介面,如果多個介面中存在方法簽名衝突,則此時不支援多實現。
- 一個類繼承了父類,又同時實現了介面,父類中和介面中有同名的預設方法,實現類會優先用父類的。
- 一個類實現了多個介面,多個介面中存在同名且同返回值型別的預設方法,可以不衝突,這個類重寫該方法即可。