主頁 > 軟體設計 > 設計模式 - 單例模式(詳解)看看和你理解的是否一樣?

設計模式 - 單例模式(詳解)看看和你理解的是否一樣?

2020-09-15 04:26:46 軟體設計

一、概述

單例模式是設計模式中相對簡單且非常常見的一種設計模式,但是同時也是非常經典的高頻面試題,相信還是有很多人在面試時會掛在這里,本篇文章主要針對單例模式做一個回顧,記錄單例模式的應用場景、常見寫法、針對執行緒安全進行除錯(看得見的執行緒)以及總結,相信大家看完這篇文章之后,對單例模式有一個非常深刻的認識,

文章中按照常見的單例模式的寫法,由淺入深進行講解記錄;以及指出該寫法的不足,從而進行演進改造,

秉承廢話少說的原則,我們下面快速開始

二、定義

單例模式(Singleton Pattern)是指確保一個類在任何情況下都絕對只有一個實體,并提供一個全域訪問點,

單例模式是創建型模式,

三、應用場景

  1. 生活中的單例:例如,國家主席、公司 CEO、部門經理等,
  2. Java世界中:ServletContextServletContextConfig 等;
  3. Spring 框架應用中:ApplicationContext、資料庫的連接池也都是單例形式,

四、常見的單例模式寫法

單例模式主要有:餓漢式單例、懶漢式單例(執行緒不安全型、執行緒安全型、雙重檢查鎖型別、靜態內部型別別)、注冊式(登記式)單例(列舉式單例、容器式單例)、ThreadLocal執行緒單例

下面我們來看看各種模式的寫法,

1、餓漢式單例

餓漢式單例是在類加載的時候就立即初始化,并且創建單例物件,絕對執行緒安全,在執行緒還沒出現以前就是實體化了,不可能存在訪問安全問題,

Spring 中 IOC 容器 ApplicationContext 就是典型的餓漢式單例

優缺點

優點:沒有加任何的鎖、執行效率比較高,在用戶體驗上來說,比懶漢式更好,

缺點:類加載的時候就初始化,不管用與不用都占著空間,浪費了記憶體,有可能占著茅坑不拉屎,

寫法

/**
 * @author eamon.zhang
 * @date 2019-09-30 上午9:26
 */
public class HungrySingleton {
    // 1.私有化構造器
    private HungrySingleton (){}
    // 2.在類的內部創建自行實體
    private static final HungrySingleton instance = new HungrySingleton();
    // 3.提供獲取唯一實體的方法(全域訪問點)
    public static HungrySingleton getInstance(){
        return instance;
    }
}

還有另外一種寫法,利用靜態代碼塊的機制:

/**
 * @author eamon.zhang
 * @date 2019-09-30 上午10:46
 */
public class HungryStaticSingleton {
    // 1. 私有化構造器
    private HungryStaticSingleton(){}

    // 2. 實體變數
    private static final HungryStaticSingleton instance;

    // 3. 在靜態代碼塊中實體化
    static {
        instance = new HungryStaticSingleton();
    }

    // 4. 提供獲取實體方法
    public static HungryStaticSingleton getInstance(){
        return instance;
    }
}

測驗代碼,我們創建 10 個執行緒(具體執行緒發令槍 ConcurrentExecutor 在文末原始碼中獲取):

/**
 * @author eamon.zhang
 * @date 2019-09-30 上午11:17
 */
public class HungrySingletonTest {
    @Test
    public void test() {
        try {
            ConcurrentExecutor.execute(() -> {
                HungrySingleton instance = HungrySingleton.getInstance();
                System.out.println(Thread.currentThread().getName() + " : " + instance);
            }, 10, 10);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

測驗結果:

pool-1-thread-6 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-1 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-9 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-10 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-2 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-7 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-5 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-3 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-4 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
pool-1-thread-8 : com.eamon.javadesignpatterns.singleton.hungry.HungrySingleton@5e37cce6
...

可以看到,餓漢式每次獲取實體都是同一個,

使用場景

這兩種寫法都非常的簡單,也非常好理解,餓漢式適用在單例物件較少的情況,

下面我們來看性能更優的寫法——懶漢式單例,


2、懶漢式單例

懶漢式單例的特點是:被外部類呼叫的時候內部類才會加載,

懶漢式單例可以分為下面這幾種寫法來,

簡單懶漢式(執行緒不安全)

這是懶漢式單例的簡單寫法

/**
 * @author eamon.zhang
 * @date 2019-09-30 上午10:55
 */
public class LazySimpleSingleton {
    private LazySimpleSingleton(){}
    private static LazySimpleSingleton instance = null;

    public static LazySimpleSingleton getInstance(){
        if (instance == null) {
            instance = new LazySimpleSingleton();
        }
        return instance;
    }
}

我們創建一個多執行緒來測驗一下,是否執行緒安全:

/**
 * @author eamon.zhang
 * @date 2019-09-30 上午11:12
 */
public class LazySimpleSingletonTest {

    @Test
    public void test() {
        try {
            ConcurrentExecutor.execute(() -> {
                LazySimpleSingleton instance = LazySimpleSingleton.getInstance();
                System.out.println(Thread.currentThread().getName() + " : " + instance);
            }, 5, 5);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

運行結果:

pool-1-thread-3 : com.eamon.javadesignpatterns.singleton.lazy.LazySimpleSingleton@abe194f
pool-1-thread-5 : com.eamon.javadesignpatterns.singleton.lazy.LazySimpleSingleton@abe194f
pool-1-thread-1 : com.eamon.javadesignpatterns.singleton.lazy.LazySimpleSingleton@748e48d0
pool-1-thread-2 : com.eamon.javadesignpatterns.singleton.lazy.LazySimpleSingleton@abe194f
pool-1-thread-4 : com.eamon.javadesignpatterns.singleton.lazy.LazySimpleSingleton@abe194f

從測驗結果來看,一定幾率出現創建兩個不同結果的情況,意味著上面的單例存在執行緒安全隱患,

至于為什么?由于篇幅問題,我們在后面一篇文章中利用測驗工具進行詳細的分析(這可能也是面試中面試官會問到的問題),大家現在只需要知道簡單的懶漢式會存在這么一個問題就行了,

簡單懶漢式(執行緒安全)

通過對上面簡單懶漢式單例的測驗,我們知道存在執行緒安全隱患,那么,如何來避免或者解決呢?

我們都知道 java 中有一個synchronized可以來對共享資源進行加鎖,保證在同一時刻只能有一個執行緒拿到該資源,其他執行緒只能等待,所以,我們對上面的簡單懶漢式進行改造,給getInstance() 方法加上synchronized

/**
 * @author eamon.zhang
 * @date 2019-09-30 上午10:55
 */
public class LazySimpleSyncSingleton {
    private LazySimpleSyncSingleton() {
    }

    private static LazySimpleSyncSingleton instance = null;

    public synchronized static LazySimpleSyncSingleton getInstance() {
        if (instance == null) {
            instance = new LazySimpleSyncSingleton();
        }
        return instance;
    }
}

然后使用發令槍進行測驗:

@Test
public void testSync(){
    try {
        ConcurrentExecutor.execute(() -> {
            LazySimpleSyncSingleton instance = LazySimpleSyncSingleton.getInstance();
            System.out.println(Thread.currentThread().getName() + " : " + instance);
        }, 5, 5);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

進行多輪測驗,并觀察結果,發現能夠獲取同一個示例:

pool-1-thread-3 : com.eamon.javadesignpatterns.singleton.lazy.simple.LazySimpleSyncSingleton@1a7e99de
pool-1-thread-2 : com.eamon.javadesignpatterns.singleton.lazy.simple.LazySimpleSyncSingleton@1a7e99de
pool-1-thread-5 : com.eamon.javadesignpatterns.singleton.lazy.simple.LazySimpleSyncSingleton@1a7e99de
pool-1-thread-1 : com.eamon.javadesignpatterns.singleton.lazy.simple.LazySimpleSyncSingleton@1a7e99de
pool-1-thread-4 : com.eamon.javadesignpatterns.singleton.lazy.simple.LazySimpleSyncSingleton@1a7e99de

執行緒安全問題是解決了,但是,用synchronized加鎖,在執行緒數量比較多情況下,如果CPU分配壓力上升,會導致大批量執行緒出現阻塞,從而導致程式運行性能大幅下降,

那么,有沒有一種更好的方式,既兼顧執行緒安全又提升程式性能呢?答案是肯定的,

我們來看雙重檢查鎖的單例模式,

雙重檢查鎖懶漢式

上面的執行緒安全方式的寫法,synchronized鎖是鎖在 getInstance() 方法上,當多個執行緒過來拿資源的時候,其實需要拿的不是getInstance()這個方法,而是getInstance()方法里面的instance 實體物件,而如果這個實體物件一旦被初始化之后,多個執行緒到達時,就可以利用方法中的 if (instance == null) 去判斷是否實體化,如果已經實體化了就直接回傳,就沒有必要再進行實體化一遍,所以對上面的代碼進行改造:

第一次改造:

/**
 * @author eamon.zhang
 * @date 2019-09-30 下午2:03
 */
public class LazyDoubleCheckSingleton {
    private LazyDoubleCheckSingleton() {
    }

    private static LazyDoubleCheckSingleton instance = null;

    public static LazyDoubleCheckSingleton getInstance() {
        // 這里判斷是為了過濾不必要的同步加鎖,因為如果已經實體化了,就可以直接回傳了
        if (instance == null) {
            // 如果未初始化,則對資源進行上鎖保護,待實體化完成之后進行釋放
            synchronized (LazyDoubleCheckSingleton.class) {
                instance = new LazyDoubleCheckSingleton();
            }
        }
        return instance;
    }
}

這種方法行不行?答案肯定是不行,代碼中雖然是將同步鎖添加到了實體化操作中,解決了每個執行緒由于同步鎖的原因引起的阻塞,提高了性能;但是,這里會存在一個問題:

  • 執行緒X執行緒Y同時呼叫getInstance()方法,他們同時判斷instance == null,得出的結果都是為null,所以進入了if代碼塊了
  • 此時執行緒X得到CPU的控制權 -> 進入同步代碼塊 -> 創建物件 -> 回傳物件
  • 執行緒X執行完成了以后,釋放了鎖,然后執行緒Y得到了CPU的控制權,同樣是 -> 進入同步代碼塊 -> 創建物件 -> 回傳物件

所以我們明顯可以分析出來:LazyDoubleCheckSingleton 類回傳了不止一個實體!所以上面的代碼是不行的!大家可以自行測驗,我這里就不進行測驗了!

我們再進行改造,經過分析,由于執行緒X已經實體化了物件,在執行緒Y再次進入的時候,我們再加一層判斷不就可以解決 “這個” 問題嗎?確實如此,來看代碼:

/**
 * @author eamon.zhang
 * @date 2019-09-30 下午2:03
 */
public class LazyDoubleCheckSingleton {
    private LazyDoubleCheckSingleton() {
    }
    private static LazyDoubleCheckSingleton instance = null;
    public static LazyDoubleCheckSingleton getInstance() {
        // 這里判斷是為了過濾不必要的同步加鎖,因為如果已經實體化了,就可以直接回傳了
        if (instance == null) {
            // 如果未初始化,則對資源進行上鎖保護,待實體化完成之后進行釋放(注意,可能多個執行緒會同時進入)
            synchronized (LazyDoubleCheckSingleton.class) {
                // 這里的if作用是:如果后面的行程在前面一個執行緒實體化完成之后拿到鎖,進入這個代碼塊,
                // 顯然,資源已經被實體化過了,所以需要進行判斷過濾
                if (instance == null) {
                    instance = new LazyDoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

大家覺得經過這樣改造是不是就完美了呢?在我們習慣性的“講道理”的思維模式看來,好像確實沒什么問題,但是,程式是計算機在執行;什么意思呢?

instance = new LazyDoubleCheckSingleton(); 這段代碼執行的時候,計算機內部并非簡單的一步操作,也就是非原子操作,在JVM中,這一行代碼大概做了這么幾件事情:

  1. instance 分配記憶體
  2. 呼叫 LazyDoubleCheckSingleton 的建構式來初始化成員變數
  3. instance物件指向分配的記憶體空間(執行完這步 instance 就為非 null 了)

但是在 JVM 中的即時編譯器中存在指令重排序的優化;通俗的來說就是,上面的第二步和第三步的順序是不能保證的,如果執行順序是 1 -> 3 -> 2 那么在 3 執行完畢、2 未執行之前,被另外一個執行緒 A 搶占了,這時 instance 已經是非 null 了(但卻沒有初始化),所以執行緒 A 會直接回傳 instance,然后被程式呼叫,就會報錯,

當然,這種情況是很難測驗出來的,但是確實會存在這么一個問題,所以我們必須解決它,解決方式也很簡單,就是 j 將 instance 加上 volatile 關鍵字,

所以相對較完美的實作方式是:

/**
 * @author eamon.zhang
 * @date 2019-09-30 下午2:03
 */
public class LazyDoubleCheckSingleton {
    private LazyDoubleCheckSingleton() {
    }

    private static volatile LazyDoubleCheckSingleton instance = null;

    public static LazyDoubleCheckSingleton getInstance() {
        // 這里判斷是為了過濾不必要的同步加鎖,因為如果已經實體化了,就可以直接回傳了
        if (instance == null) {
            // 如果未初始化,則對資源進行上鎖保護,待實體化完成之后進行釋放(注意,可能多個執行緒會同時進入)
            synchronized (LazyDoubleCheckSingleton.class) {
                // 這里的if作用是:如果后面的行程在前面一個執行緒實體化完成之后拿到鎖,進入這個代碼塊,
                // 顯然,資源已經被實體化過了,所以需要進行判斷過濾
                if (instance == null) {
                    instance = new LazyDoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

測驗代碼見文末說明

靜態內部類懶漢式

上面的雙重鎖檢查形式的單例,對于日常開發來說,確實夠用了,但是在代碼中使用synchronized關鍵字 ,總歸是要上鎖,上鎖就會存在一個性能問題,難道就沒有更好的方案嗎?別說,還真有,我們從類初始化的角度來考慮,這就是這里所要說到的靜態內部類的方式,

廢話不多說,直接看代碼:

/**
 *
 * @author eamon.zhang
 * @date 2019-09-30 下午2:55
 */
public class LazyInnerClassSingleton {

    private LazyInnerClassSingleton() {
    }

    // 注意關鍵字final,保證方法不被重寫和多載
    public static final LazyInnerClassSingleton getInstance() {
        return LazyHolder.INSTANCE;
    }

    private static class LazyHolder {
        // 注意 final 關鍵字(保證不被修改)
        private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
    }
}

進行多執行緒測驗:

pool-1-thread-9 : com.eamon.javadesignpatterns.singleton.lazy.inner.LazyInnerClassSingleton@88b7fa2
pool-1-thread-1 : com.eamon.javadesignpatterns.singleton.lazy.inner.LazyInnerClassSingleton@88b7fa2
pool-1-thread-6 : com.eamon.javadesignpatterns.singleton.lazy.inner.LazyInnerClassSingleton@88b7fa2
...

結果都是同一個物件實體,

結論

這種方式即解決了餓漢式的記憶體浪費問題,也解決了synchronized 所帶來的性能問題

原理

利用的原理就是類的加載初始化順序:

  1. 當類不被呼叫的時候,類的靜態內部類是不會進行初始化的,這就避免了記憶體浪費問題;
  2. 當有方法呼叫 getInstance()方法時,會先初始化靜態內部類,而靜態內部類中的成員變數是 final 的,所以即便是多執行緒,其成員變數是不會被修改的,所以就解決了添加 synchronized 所帶來的性能問題

首先感謝也恭喜大家能夠看到這里,因為我想告訴你,上面所有的單例模式似憾訓存在一點小問題 —— 暴力破壞,解決這一問題的方式就是下面提到的列舉型別單例,

至于緣由和為何列舉能夠解決這個問題,同樣,篇幅原因,我將在后面單獨開一篇文章來說明,


下面我們先來講講注冊式單例,

3、注冊式(登記式)單例

注冊式單例又稱為登記式單例,就是將每一個實體都登記到某一個地方,使用唯一的標識獲取實體,

注冊式單例有兩種寫法:一種為容器快取,一種為列舉登記,

先來看列舉式單例的寫法,

列舉單例

廢話少說,直接看代碼,我們先創建EnumResource 類:

/**
 * @author eamon.zhang
 * @date 2019-09-30 下午3:53
 */
public class EnumResource {
}

然后創建EnumSingleton :

/**
 * @author eamon.zhang
 * @date 2019-09-30 下午3:42
 */
public enum EnumSingleton {
    INSTANCE;

    private Object instance;

    EnumSingleton() {
        instance = new EnumResource();
    }

    public Object getInstance() {
        return instance;
    }

}

來看測驗代碼:

/**
 * @author eamon.zhang
 * @date 2019-09-30 下午3:47
 */
public class EnumSingletonTest {

    @Test
    public void test() {
        try {
            ConcurrentExecutor.execute(() -> {
                EnumSingleton instance = EnumSingleton.INSTANCE;
                System.out.println(instance.getInstance());
            }, 10, 10);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

測驗結果:

com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7
com.eamon.javadesignpatterns.singleton.enums.EnumResource@3eadb1e7

結果都一樣,說明列舉類單例是執行緒安全的,且是不可破壞的;在 JDK 列舉的語法特殊性,以及反射也為列舉保駕護航,讓列舉式單例成為一種比較優雅的實作,

列舉類單例也是《Effective Java》中所建議使用的,

容器式單例

注冊式單例還有另外一種寫法,利用容器快取,直接來看代碼:

創建ContainerSingleton類:

/**
 * @author EamonZzz
 * @date 2019-10-06 18:28
 */
public class ContainerSingleton {
    private ContainerSingleton() {
    }

    private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getBean(String className) {
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                Object object = null;
                try {
                    object = Class.forName(className).newInstance();
                    ioc.put(className, object);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return object;
            } else {
                return ioc.get(className);
            }
        }
    }
}

測驗代碼:

@Test
    public void test() {
        try {
            ConcurrentExecutor.execute(() -> {
                Object bean = ContainerSingleton
                        .getBean("com.eamon.javadesignpatterns.singleton.container.Resource");
                System.out.println(bean);
            }, 5, 5);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

測驗結果:

com.eamon.javadesignpatterns.singleton.container.Resource@42e7420f
com.eamon.javadesignpatterns.singleton.container.Resource@42e7420f
com.eamon.javadesignpatterns.singleton.container.Resource@42e7420f
com.eamon.javadesignpatterns.singleton.container.Resource@42e7420f
com.eamon.javadesignpatterns.singleton.container.Resource@42e7420f

容器式寫法適用于創建實體非常多的情況,便于管理,但是,是非執行緒安全的,

其實 Spring 中也有相關容器史丹利的實作代碼,比如 AbstractAutowireCapableBeanFactory 介面

至此,注冊式單例介紹完畢,


五、拓展

ThreadLocal 執行緒單例

ThreadLocal 不能保證其創建的物件是唯一的,但是能保證在單個執行緒中是唯一的,并且在單個執行緒中是天生的執行緒安全,

看代碼:

/**
 * @author EamonZzz
 * @date 2019-10-06 21:40
 */
public class ThreadLocalSingleton {
    private ThreadLocalSingleton() {
    }

    private static final ThreadLocal<ThreadLocalSingleton> instance = ThreadLocal.withInitial(ThreadLocalSingleton::new);

    public static ThreadLocalSingleton getInstance() {
        return instance.get();
    }
}

測驗程式:

@Test
public void test() {
    System.out.println("-------------- 單執行緒 start ---------");
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println(ThreadLocalSingleton.getInstance());
    System.out.println("-------------- 單執行緒 end ---------");
    System.out.println("-------------- 多執行緒 start ---------");
    try {
        ConcurrentExecutor.execute(() -> {
            ThreadLocalSingleton singleton = ThreadLocalSingleton.getInstance();
            System.out.println(Thread.currentThread().getName() + " : " + singleton);

        }, 5, 5);
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("-------------- 多執行緒 end ---------");
}

測驗結果:

-------------- 單執行緒 start ---------
com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@1374fbda
com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@1374fbda
com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@1374fbda
com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@1374fbda
com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@1374fbda
-------------- 單執行緒 end ---------
-------------- 多執行緒 start ---------
pool-1-thread-5 : com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@2f540d92
pool-1-thread-1 : com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@3ef7ab4e
pool-1-thread-2 : com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@604ffe2a
pool-1-thread-3 : com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@50f41c9f
pool-1-thread-4 : com.eamon.javadesignpatterns.singleton.threadlocal.ThreadLocalSingleton@40821a7a
-------------- 多執行緒 end ---------

從測驗結果來看,我們不難發現,在主執行緒中無論呼叫多少次,獲得到的實體都是同一個;在多執行緒環境下,每個執行緒獲取到了不同的實體,

所以,在單執行緒環境中,ThreadLocal 可以達到單例的目的,這實際上是以空間換時間來實作執行緒間隔離的,

六、總結

單例模式可以保證記憶體里只有一個實體,減少了記憶體的開銷;可避免對資源的浪費,

單例模式看起來非常簡單,實作起來也不難,但是在面試中卻是一個高頻的面試題,希望大家能夠徹底理解,


本篇文章所涉及的源代碼:

github.com/eamonzzz

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/42136.html

標籤:設計模式

上一篇:行為型設計模式(中)

下一篇:記錄敲代碼生活:資料演算法(求最大公約數和最小公倍數)Java語言

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more