設計模式 --面試高頻之享元模式

前言

享元模式是很是經常使用的一種結構性設計模式。java

特別是在面試的時候。當咱們把這一節內容掌握,我相信不論是工做中仍是面試中這一塊內容絕對是一大亮點。面試

什麼是享元模式

所謂「享元」,顧名思義就是被共享的單元。享元模式的意圖是複用對象,節省內存,前提是享元對象是不可變對象。設計模式

具體來說,當一個系統中存在大量重複對象的時候,若是這些重複的對象是不可變對象,咱們就能夠利用享元模式將對象設計成享元,在內存中只保留一份實例,供多處代碼引用。這樣能夠減小內存中對象的數量,起到節省內存的目的。緩存

這裏值得注意的是只保留一份實例,供多人使用。ide

面試最多見的面試題

我相信大夥在面試的時候常常會被問到String,Integer相關的面試題。函數

那咱們就從這兩塊內容開始講解。優化

享元模式在Integer中的應用

咱們先來看下面這樣一段代碼。ui

Integer i1 = 56;
Integer i2 = 56;
Integer i3 = 129;
Integer i4 = 129;
System.out.println(i1 == i2); //true
System.out.println(i3 == i4); //false

我相信不少人在面試的時候會遇到這種題目。答案可能會出乎咱們的意料。第一個爲true,第二個爲false。線程

這正是由於 Integer,用到了享元模式來複用對象,才致使了這樣的運行結果。當咱們經過自動裝箱,也就是調用 valueOf() 來建立 Integer 對象的時候,若是要建立的 Integer 對象的值在 -128 到 127 之間,會從 IntegerCache 類中直接返回,不然才調用 new 方法建立。看代碼更加清晰一些,Integer 類的 valueOf() 函數的具體代碼以下所示:設計

//從這裏的源碼咱們能看到,當咱們執行Integer i2 = 56;
//這行代碼的時候。實際上是經過自動裝箱機制,調用的valueOf。
//當數據在IntegerCache.low~IntegerCache.high之間的時候,咱們是直接從緩存中拿取的數據。
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

那這個IntegerCache是什麼呢?這個實際上是Integer的內部類。

咱們挑選重點代碼來看看,源碼以下:

/**
 * Cache to support the object identity semantics of autoboxing for values between
 * -128 and 127 (inclusive) as required by JLS.
 *
 * The cache is initialized on first usage.  The size of the cache
 * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
 * During VM initialization, java.lang.Integer.IntegerCache.high property
 * may be set and saved in the private system properties in the
 * sun.misc.VM class.
 */
private static class IntegerCache {
    static final int low = -128; //緩存的最小值
    static final int high; //緩存的最大值
    static final Integer cache[];  //緩存

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

這個是Integer的靜態內部類,當咱們加載Ineger的時候該類也會被加載進去。能夠看到他緩存了-128 到 127 之間的整型值。

實際上,除了 Integer 類型以外,其餘包裝器類型,好比 Long、Short、Byte 等,也都利用了享元模式來緩存 -128 到 127 之間的數據。好比,Long 類型對應的 LongCache 享元工廠類及 valueOf() 。

其實jdk考慮的很周到,咱們大部分時間建立出來的Ineger對象,其實都是存儲整型都不是特別大。因此乾脆取一段大小合理的數據直接緩存下來。

舉一個極端一點的例子,假設程序須要建立 1 萬個 -128 到 127 之間的 Integer 對象。使用第一種建立方式,咱們須要分配 1 萬個 Integer 對象的內存空間;使用後兩種建立方式,咱們最多隻須要分配 256 個 Integer 對象的內存空間。

享元模式在String中的應用

咱們都知道String是被final修飾的,你們又仔細想過這其中的原因嗎?

這最大的緣由就是爲了實現字符串池化技術。其核心思想就是享元模式。

咱們前面提到過享元對象都是不可變的。這樣咱們才能保證你們在共同使用的時候不會出現問題。因此String是被final修飾的。

咱們再來看一下這段代碼:

String s1 = "享元模式";
String s2 = "享元模式";
String s3 = new String("享元模式");

System.out.println(s1 == s2); //ture
System.out.println(s1 == s3); //false

前兩個s1和s2都是指向的字符串常量池的"享元模式"。而s3指向的是堆的String。

String 類的享元模式的設計,跟 Integer 類稍微有些不一樣。
Integer 類中要共享的對象,是在類加載的時候,就集中一次性建立好的。

可是,對於字符串來講,咱們無法事先知道要共享哪些字符串常量,因此沒辦法事先建立好。

只能在某個字符串常量第一次被用到的時候,存儲到常量池中,當以後再用到的時候,直接引用常量池中已經存在的便可,就不須要再從新建立了

實際運用

咱們想一想,什麼狀況咱們應該使用享元模式。

我總結了一下:

  1. 首先這個對象在不少地方都得使用,不然就是過分設計。
  2. 其次這個對象是不可變的,可讓多個線程同時使用。

我舉一個具體的例子。

好比咱們開發一個麻將遊戲。沒一局遊戲是否是要new一個麻將桌,new一副麻將。假如同時在線100w人,那咱們就new了25w個麻將桌和25w副麻。

咱們仔細想一想能不能用享元模式來優化,首先麻將桌應該是不能優化的,由於他得記錄咱們每一局遊戲得狀態,桌上麻將的狀況,等等信息。可是麻將咱們卻能夠緩存一副,讓他不可變。全部人共用這一副緩存的麻將。

總結

享元模式其實開發中咱們用的不是特別多,可是當須要時,卻很是的有效。包括面試中關於String,基本類型的包裝類關於享元模式的運用。當面試管再拋出這個問題,若是你能回答清楚而且提出其設計模式是享元模式,我相信必定會讓面試官眼前一亮。

相關文章
相關標籤/搜索