1. 程式人生 > >關於JAVA你必須知道的那些事(四):單例模式和多型

關於JAVA你必須知道的那些事(四):單例模式和多型

好吧,今天一定要把面向物件的最後一個特性:多型,給說完。不過我們先來聊一聊設計模式,因為它很重要。

設計模式

官方的解釋是,設計模式是:一套被反覆使用,多數人知曉的,經過分類編目,程式碼設計經驗的總結。說人話就是:軟體開發人員在軟體開發過程中面臨的一般問題的解決方案。

常見的設計模式可以參看這張圖片:

我們可以對其按照作用來進行分類:: 關注物件建立過程的:建立型模式; 類和物件組合:結構型模式; 物件之間的通訊過程:行為型模式

單例模式

單例模式: 一個類有且僅有一個例項,並且自行例項化向整個系統提供,它的目的就是使得類的一個物件成為該類系統中的唯一例項。

要點:

  1. 某個類只能有一個例項;;
  2. 必須自行建立例項;
  3. 必須自行向整個系統提供這個例項;

實現: 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靜態不允許被子類重寫。)