好吧,今天一定要把面向對象的最後一個特性:多態,給說完。不過我們先來聊一聊設計模式,因爲它很重要。
設計模式
官方的解釋是,設計模式是:一套被反覆使用,多數人知曉的,經過分類編目,代碼設計經驗的總結。說人話就是:軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。
常見的設計模式可以參看這張圖片:
我們可以對其按照作用來進行分類:: 關注對象創建過程的:創建型模式; 類和對象組合:結構型模式; 對象之間的通信過程:行爲型模式;
單例模式
單例模式: 一個類有且僅有一個實例,並且自行實例化向整個系統提供,它的目的就是使得類的一個對象成爲該類系統中的唯一實例。
要點:
- 某個類只能有一個實例;;
- 必須自行創建實例;
- 必須自行向整個系統提供這個實例;
實現: 1、只提供私有的構造方法; 2、只含有一個該類的靜態私有對象; 3、提供一個靜態的公有方法用於創建、獲取靜態私有對象。
對於1的理解:private是訪問限制能力最強的修飾符,只能在當前類內被使用。也就是說經過private修飾,該類的對象在類外無法通過new關鍵字直接實例化,這樣可以做到限制類實例化產生;
對於2的理解:1可以實現有且僅有一個實例,static修飾的靜態成員可以滿足該類有且僅有一個,所有的對象都共享這一個靜態成員;
對於3的理解:類似於封裝,必須向外部系統提供唯一的公有訪問方法。
在java中實現單例模式有2種方式:餓漢式和懶漢式。
餓漢式:在類中私有對象創建的過程中立刻進行實例化操作(言外之意,不管你用不用,我先把這個給做了)如此看來確實挺餓的;
懶漢式::對象創建時並不立刻進行實例化操作,而是在靜態公有方法中進行實例化操作(言外之意,你不需要我就不做)如此看來確實挺懶的。
餓漢式
餓漢式:在類中私有對象創建的過程中立刻進行實例化操作(言外之意,不管你用不用,我先把這個給做了):
package SingleExample;
// 餓漢式:創建對象實例的時候直接初始化;(空間換時間)
public class SingletonOne {
//1、創建類中私有的構造方法
private SingletonOne() {
};
//2、創建該類型的私有靜態實例
private static SingletonOne instance = new SingletonOne();
//3、創建公有的靜態方法,返回靜態實例對象
public static SingletonOne getinstance() {
return instance;
};
}
測試代碼:
package SingleExample;
public class SingleOneTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
SingletonOne one =SingletonOne.getinstance();
SingletonOne two =SingletonOne.getinstance();
System.out.println(one==two); //輸出結果爲true
}
}
懶漢式
懶漢式::對象創建時並不立刻進行實例化操作,而是在靜態公有方法中進行實例化操作(言外之意,你不需要我就不做):
package SingleExample;
//懶漢式:創建對象實例的時候並不初始化;(時間換空間)
public class SingletonTwo {
// 1、創建類中私有的構造方法
private SingletonTwo() {
};
// 2、創建靜態的該類實例對象
private static SingletonTwo instance = null;
// 3、創建公有的靜態方法,提供實例對象
public static SingletonTwo getinstance() {
if (instance == null) {
instance = new SingletonTwo();
}
return instance;
};
}
相應的測試代碼爲:
package SingleExample;
public class SingleTwoTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
SingletonTwo one = SingletonTwo.getinstance();
SingletonTwo two = SingletonTwo.getinstance();
System.out.println(one == two); //輸出結果爲true
}
}
單例模式兩種實現總結
餓漢式在類加載時就創建實例,第一次加載速度快; 懶漢式在第一次使用時才進行實例化,第一次加載速度慢;
餓漢式:空間換時間 懶漢式:時間換空間
餓漢式,類在加載時進行了對象的實例化創建,即使多個進程進行併發操作,訪問的實例也是唯一的,餓漢式線程安全。
懶漢式,第一次使用纔會實例化,多個線程併發操作時,由於時間片的切換,可能導致線程風險。
但是懶漢式的線程危險是可以規避的,通過關鍵字Synchronized實現線程的鎖定,也可以通過靜態內部類和枚舉保證操作時的線程唯一。
單例模式優缺點及使用場景
單例模式的優點: 1、在內存中只有一個對象,節省內存空間; 2、避免頻繁的創建銷燬對象, 提高性能; 3、避免對共享資源的多重佔用。
單例模式的缺點: 1、擴展比較困難; 2、如果實例化後的對象長期不利用,系統將默認爲垃圾進行回收,造成對象狀態丟失。
使用場景: 1、創建對象時佔用資源過多,但同時又需要用到該類對象; 2、對系統內資源要求統一讀寫,如讀寫配置信息; 3、當多個實例存在可能引起程序邏輯錯誤,如號碼生成器;
每一種設計模式都是針對場景,針對某種具體問題的,具體場景應當進行具體分析,選用合適的設計模式。
多態
終於開始進入多態的世界了,在這裏你將全面瞭解多態的特點及使用。
多態你可以理解爲不同類的對象對同一消息做出不同的響應。
一般而言,多態分爲編譯時多態和運行時多態這兩種。
編譯時多態:也稱設計時多態,它是通過方法重載來實現的,編譯器在編譯狀態可以進行不同行爲的區分。
而運行時多態,則必須要求程序運行時,動態決定調用哪個方法。
我們通常在Java中的多態指的就是運行時多態。
實現多態的必要條件: 滿足繼承;父類引用指向子類對象
向上轉型
所謂的向上轉型也指隱式轉型(自動轉型)。說通俗一點就是父類引用指向子類實例,它可以調用子類重寫父類的方法以及父類派生的方法,但是無法調用子類特有方法。
舉個例子,假如Dog這個類繼承了我們Animal這個類,我們不僅可以這樣:
Dog dog =new Dog();
Animal animal =new Animal();
你還可以這樣:
Animal dog2 =new Dog();
這就是將一個子類對象轉型爲一個父類對象,這個很好理解,對吧。
接下來我們來說一下,向下轉型,顧名思義就是和向上轉型相反的操作了,是的,你很聰明。
向下轉型
向下轉型也稱強制類型轉換。它是子類引用指向父類實例,我們在之前就用過了,還記得我們在重寫Object類的equals方法時,就將父類Object強制轉換,然後才調用子類特有的方法。
向下轉型並不是可以隨便轉換的,需要滿足一定的轉換條件。我們可以通過instanceof這個運算符來判斷是否能進行強制類型轉換。
通過上面的圖片,我們可以很清楚的知道instanceof的作用就是判斷左邊對象是否是右邊這個類的實例,如果是就返true,否則就返回false。
因此,我們在進行向下轉型的時候,可以用instanceof來判斷一個對象是否滿足某個類的實例特徵。滿足,我們才進行類型轉換,否則強制轉換會報錯。
總結一下: 向上轉型: 父類引用指向子類對象。即小變大。
向下轉型: 子類引用指向父類對象。即大變小。
需要注意的是:父類中static修飾的方法允許被子類使用,但是不允許被子類重寫,所以向上轉型之後,只能調用到父類原有的靜態方法。如果此時要用子類中的,只能通過向下轉型來實現。
抽象類
某個父類只是知道其子類應該包含怎樣的方法,但無法準確知道這些子類如何實現這些方法,這樣我們的抽象類就派上用場了。
抽象類可以避免子類設計的隨意性,還可以避免父類無意義的實例化。
你只需知道,修飾抽象類要用abstract這個關鍵詞,抽象類不可以直接被實例化。
抽象方法
我們前面說過,父類只是規定子類擁有該項能力,但在父類中具體實現它是沒有任何意義的,因此該方法應設置爲抽象方法。
public abstract void test();
你記住,抽象方法是不允許有方法體的,也就是不能有花括號。而且此時子類必須實現父類的抽象方法,如果你不實現,那麼這個類就必須被設置爲抽象類(不設置就會報錯),然後由繼承它的類去具體實現相應的方法。簡單來說就是一句話:抽象方法中不允許包含方法體,子類需要重寫父類的抽象方法。
一般抽象類適用於這種情況:1、父類中的實現沒有意義;2、提醒子類必須要去自己實現自己的這個方法。
通常子類變多了之後,你新建一個類只要繼承了抽象的父類,IDE會自動提醒你實現父類中的抽象方法的,你不實現就會報錯。
抽象類和抽象方法的使用
你可以使用abstract關鍵詞來定義抽象類,抽象類不能被直接實例化,你可以通過向上轉型完成對象實例,只能被繼承。
abstract關鍵詞定義抽象方法 ,你不需要具體實現也不能具體實現,也就是花括號不能有。
需要注意的是:抽象類可以沒有抽象方法,但包含抽象方法的類一定是抽象類。
我們前面說過,當一個類繼承抽象類,必須實現類中的抽象方法。如果不重寫,則必須將該子類也變爲抽象類,由其子類來實現,否則會報錯。
注意:static final private 不可以和abstract同時出現(因爲抽象方法是要在子類中進行重寫的,而private只能在當前類被訪問,final方法不允許被子類重寫,static靜態不允許被子類重寫。)