編輯:關於Android編程
今天我來全面總結一下Android開發中最常用的設計模式 - 單例模式。

之前說過,設計模式 = 某類特定問題的解決方案,那麼單例模式是解決什麼問題的解決方案呢?
含義:單例 =一個實例; 解決的問題:降低對象之間的耦合度 解決方法:單例模式,即實現一個類只有一個實例化對象,並提供一個全局訪問點接下來我用一個實例來對單例模式進行引入
背景:小成有一個塑料生產廠,但裡面只有一個倉庫。 目的:想用代碼來實現倉庫的管理 現有做法: 建立倉庫類和工人類其中,倉庫類裡的quantity=商品數量;工人類裡有搬運方法MoveIn(int i)和MoveOut(int i)。出現的問題:通過測試發現,每次工人搬運操作都會新建一個倉庫,就是貨物都不是放在同一倉庫,這是怎麼回事呢?(看下面代碼)
package scut.designmodel.SingletonPattern;
//倉庫類
class StoreHouse {
private int quantity = 100;
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getQuantity() {
return quantity;
}
}
//搬貨工人類
class Carrier{
public StoreHouse mStoreHouse;
public Carrier(StoreHouse storeHouse){
mStoreHouse = storeHouse;
}
//搬貨進倉庫
public void MoveIn(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i);
}
//搬貨出倉庫
public void MoveOut(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i);
}
}
//工人搬運測試
public class SinglePattern {
public static void main(String[] args){
StoreHouse mStoreHouse1 = new StoreHouse();
StoreHouse mStoreHouse2 = new StoreHouse();
Carrier Carrier1 = new Carrier(mStoreHouse1);
Carrier Carrier2 = new Carrier(mStoreHouse2);
System.out.println("兩個是不是同一個?");
if(mStoreHouse1.equals(mStoreHouse2)){//這裡用equals而不是用 == 符號,因為 == 符號只是比較兩個對象的地址
System.out.println("是同一個");
}else {
System.out.println("不是同一個");
}
//搬運工搬完貨物之後出來匯報倉庫商品數量
Carrier1.MoveIn(30);
System.out.println("倉庫商品余量:"+Carrier1.mStoreHouse.getQuantity());
Carrier2.MoveOut(50);
System.out.println("倉庫商品余量:"+Carrier2.mStoreHouse.getQuantity());
}
}
結果:
兩個是不是同一個? 不是同一個 倉庫商品余量:130 倉庫商品余量:50
在Java中,我們通過使用對象(類實例化後)來操作這些類,類實例化是通過它的構造方法進行的,要是想實現一個類只有一個實例化對象,就要對類的構造方法下功夫:

單例模式的一般實現:(含使用步驟)
public class Singleton {
//1. 創建私有變量 ourInstance(用以記錄 Singleton 的唯一實例)
//2. 內部進行實例化
private static Singleton ourInstance = new Singleton();
//3. 把類的構造方法私有化,不讓外部調用構造方法實例化
private Singleton() {
}
//4. 定義公有方法提供該類的全局唯一訪問點
//5. 外部通過調用getInstance()方法來返回唯一的實例
public static Singleton newInstance() {
return ourInstance;
}
}
好了,單例模式的介紹和原理應該了解了吧?那麼我們現在來解決上面小成出現的“倉庫不是一個”的問題吧!
小成使用單例模式改善上面例子的代碼:
package scut.designmodel.SingletonPattern;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//單例倉庫類
class StoreHouse {
//倉庫商品數量
private int quantity = 100;
//自己在內部實例化
private static StoreHouse ourInstance = new StoreHouse();;
//讓外部通過調用getInstance()方法來返回唯一的實例。
public static StoreHouse getInstance() {
return ourInstance;
}
//封閉構造函數
private StoreHouse() {
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getQuantity() {
return quantity;
}
}
//搬貨工人類
class Carrier{
public StoreHouse mStoreHouse;
public Carrier(StoreHouse storeHouse){
mStoreHouse = storeHouse;
}
//搬貨進倉庫
public void MoveIn(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i);
}
//搬貨出倉庫
public void MoveOut(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i);
}
}
//工人搬運測試
public class SinglePattern {
public static void main(String[] args){
StoreHouse mStoreHouse1 = StoreHouse.getInstance();
StoreHouse mStoreHouse2 = StoreHouse.getInstance();
Carrier Carrier1 = new Carrier(mStoreHouse1);
Carrier Carrier2 = new Carrier(mStoreHouse2);
System.out.println("兩個是不是同一個?");
if(mStoreHouse1.equals(mStoreHouse2)){
System.out.println("是同一個");
}else {
System.out.println("不是同一個");
}
//搬運工搬完貨物之後出來匯報倉庫商品數量
Carrier1.MoveIn(30);
System.out.println("倉庫商品余量:"+Carrier1.mStoreHouse.getQuantity());
Carrier2.MoveOut(50);
System.out.println("倉庫商品余量:"+Carrier2.mStoreHouse.getQuantity());
}
}
結果:
兩個是不是同一個? 是同一個 倉庫商品余量:130 倉庫商品余量:80
從結果分析,使用了單例模式後,倉庫類就只有一個倉庫實例了,再也不用擔心搬運工人進錯倉庫了!!!
餓漢式(最簡單的單例實現方式)
class Singleton {
private static Singleton ourInstance = new Singleton();
private Singleton() {
}
public static Singleton newInstance() {
return ourInstance;
}
}
應用場景:
要求直接在應用啟動時加載並初始化 單例對象要求初始化速度非常快且占用內存非常小懶漢式
懶漢式與餓漢式最大的區別是單例的初始化操作的時機:
餓漢式:自動進行單例的初始化 懶漢式:有需要的時候才手動調用newInstance()進行單例的初始化操作
class Singleton {
private static Singleton ourInstance = null;
private Singleton() {
}
public static Singleton newInstance() {
if( ourInstance == null){
ourInstance = new Singleton();
}
return ourInstance;
}
}
應用場景:
單例初始化的操作耗時比較長而應用對於啟動速度又有要求 單例的占用內存比較大 單例只是在某個特定場景的情況下才會被使用,即按需延遲加載單例。在多線程的情況下:
對於“餓漢式單例模式”:適用,因為JVM只會加載一次單例類; 對於“懶漢式單例模式”:不適用,因為“懶漢式”在創建單例時是線程不安全的,多個線程可能會並發調用 newInstance 方法從而出現重復創建單例對象的問題。使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成instance被多次實例化。
class Singleton {
private static Singleton ourInstance = null;
private Singleton() {
}
public static Singleton newInstance() {
synchronized (Singleton.class){
if( ourInstance == null){
ourInstance = new Singleton();
}
}
return ourInstance;
}
}
在同步鎖的基礎上( synchronized (Singleton.class) 外)添加了一層if,這是為了在Instance已經實例化後下次進入不必執行 synchronized (Singleton.class) 獲取對象鎖,從而提高性能。
class Singleton {
private static Singleton ourInstance = null;
private Singleton() {
}
public static Singleton newInstance() {
if( ourInstance == null){
synchronized (Singleton.class){
if( ourInstance == null){
ourInstance = new Singleton();
}
}
}
return ourInstance;
}
}
在JVM進行類加載的時候會保證數據是同步的,我們采用內部類實現:在內部類裡面去創建對象實例。
只要應用中不使用內部類 JVM 就不會去加載這個單例類,也就不會創建單例對象,從而實現“懶漢式”的延遲加載和線程安全。
class Singleton {
//在裝載該內部類時才會去創建單例對象
private static class Singleton2{
private static Singleton ourInstance = new Singleton();
}
private Singleton() {
}
public static Singleton newInstance() {
return Singleton2.ourInstance;
}
}
最簡潔、易用的單例實現方式,(《Effective Java》推薦)
public enum Singleton{
//定義一個枚舉的元素,它就是Singleton的一個實例
instance;
public void doSomething(){
}
}
使用方式如下:
Singleton singleton = Singleton.instance; singleton.doSomething();
本文主要對單例模式進行了全面介紹,包括原理和實現方式,接下來我會繼續講解其他設計模式
微信換手機號了怎麼辦 換手機號微信怎麼辦
如今我們大部分人都在玩微信,都用手機綁定了微信號,手機的功能太強大了,如果手機丟了,或者要換手機號碼怎麼辦?沒關系啦,騰訊公司也會想到這個問題,下面我來為大
Android存儲系統如何優化?
Android存儲系統如何優化?答案是我也不知道…那為什麼會想到要寫這篇文章哪?主要是因為有天晚上和以前一個同事討論到Android手機存儲系統的優化問題,
Android ViewPager 重復數據問題的解決方法
最近在做的新聞客戶端用到了ViewPager,Android Studio如今集成的很好了,自動創建很多不必要寫的代碼。在使用過程中碰到了一個很奇怪的事:ViewPage
android 二級聯動列表,仿eleme點餐頁面
寫在前面最近手感不錯,老想寫點輪子。正好周末外賣點得多,就仿一仿“餓了麼”好了。先上圖吧,這樣的訂單頁面是不是很眼熟:右邊的listview分好組