天天看點

圖圖細談設計模式——單例模式

圖圖在為畢業設計的事忙來忙去,有時間就回來講故事。

圖圖在為面試做準備,今天圖圖給大家細說一番單例模式。其實最簡單的是工廠模式而不是單例(工廠後續會說)

設計模式是一種思想,最早用于建築,後來也用到我們軟體開發上來,是牛人總結的一些常見的架構思想,按照六中原則設計出來的23中設計模式

單例模式:是23中設計模式稍簡單的設計模式,也是很常見的設計模式, java中一般展現在架構的整體架構,面向接口程式設計,用這種思想完成某個領域的解決方案。

當然實作單例功能後,保證線程安全,并發效率快,消耗資源率少的原則才是最perfect。

詳情請往下看。

單例模式自身的初衷在于應用程式一啟動,單例資源一次性永久駐留記憶體的思想,

package org.huey.pattern.singleton1;
/**
 * 懶漢式單例模式
 * @author huey
 *
 */
public class HungryStyle {
     //public Integer number;
     //private Integer number;
     /**
      * 類屬性,天然線程安全
      */
     private static HungryStyle hungryStyle = new HungryStyle();
     private HungryStyle() {
     }
     public static HungryStyle getInstance() {
          return hungryStyle;
     }
}           

複制

餓漢式

缺點:上來就建立對象,立刻加載,如果我不用getInstance方法的話,那麼就浪費了建立所耗的資源。

優點:類屬性線程安全,方法不用同步鎖也沒有并發問題,調用效率高。

package org.huey.pattern.singleton1;
/**
 * 懶漢式加載
 * @author huey
 *
 */
public class LazyStyle {
     private static LazyStyle lazyStyle;
     private LazyStyle() {
     }
     public synchronized static LazyStyle getInstance() {
          if (lazyStyle == null) {
 //---------------此處如果有多個線程通路,一個線程挂機,當另一個線程進來時,lazyStyle依然為null
              //那麼就都去new對象了,失去了初衷(隻建立一次),故加上synchronized同步鎖
              lazyStyle = new LazyStyle();
          }
          return lazyStyle;
     }
}           

複制

懶漢式

缺點:由于存在并發問題,需要加上同步鎖解決線程安全問題,故調用效率低。

優點:使用的時候再建立,資源使用率高。

package org.huey.pattern.singleton1;
/**
 * 靜态内部類的單例模式
 * @author huey
 *
 */
public class StaticInnerStyle {
     //靜态内部類不會随這類加載而加載,當你調用getInstance方法才會加載該内部類,也就是懶加載
     private static class SingletionClass{
           //類屬性,線程安全
           private static final StaticInnerStyle instance = new StaticInnerStyle();
     }
     //不用同步鎖,調用效率高
     public static StaticInnerStyle getInstance(){
          return SingletionClass.instance;
     }
     private StaticInnerStyle(){}
}           

複制

靜态内部類的單例:

優點:高效(不用同步鎖) 線程安全(靜态類屬性) 懶加載(使用時我才建立)

package org.huey.pattern.singleton1;
/**
 * 枚舉類型的單例模式
 * @author huey
 *
 */
public enum EnumStyle {
  //枚舉天然是單例,jvm的底層實作,不過用枚舉實作單例,要求隻有這一個對象
     INSTANCE;
  //INSTANCE,SPRING;這樣如果測試INSTANCE與SPRING不是通一個對象
     public void service(){
          System.out.println("業務操作....");
     }
}
借助jad工具進行反編譯,代碼如下
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name:   EnumStyle.java
//反編譯
package org.huey.pattern.singleton1;
import java.io.PrintStream;
public final class EnumStyle extends Enum
{
 private EnumStyle(String s, int i)
    {
        super(s, i);
    }
   public void service()
    {
        System.out.println("\u4E1A\u52A1\u64CD\u4F5C....");
    }
    public static EnumStyle[] values()
    {
        EnumStyle aenumstyle[];
        int i;
        EnumStyle aenumstyle1[];
 System.arraycopy(aenumstyle = ENUM$VALUES, 0, aenumstyle1 = new EnumStyle[i = aenumstyle.length], 0, i);
        return aenumstyle1;
    }
    public static EnumStyle valueOf(String s)
    {
        return (EnumStyle)Enum.valueOf(org/huey/pattern/singleton1/EnumStyle, s);
    }
 public static final EnumStyle INSTANCE;
    private static final EnumStyle ENUM$VALUES[];
   static
    {
        INSTANCE = new EnumStyle("INSTANCE", 0);
        ENUM$VALUES = (new EnumStyle[] {
            INSTANCE,
        });
    }
}           

複制

反編譯後,會學到很多知識,數組的最佳複制操作,字元串轉為Unicode的碼,還有一個是目标匿名内部類的數組形式,頭一次發現,學到了不少東西,很開心。

其枚舉類型的單例 優點 線程安全,高效, 缺點是 沒有延遲加載(可通過反編譯代碼看出),還有就是隻有這種方式,可以避免反射(不考慮特殊處理)和反序列化漏洞,其他的形式的單例模式做不到這一點,換句話說反射和反編譯能破解其他形式的單例模式

注:

一般單例用途就是用在他的功能,隻加載一次就夠。比如springmvc的前端控制器,spring的ioc,加載配置檔案的配置器類,建立架構内部對象的工廠類,處理日志類等。

還有一種好像叫雙重内部鎖,由于該方式雙同步可能次序不易可能存在問題,就不說了,主要是沒研究過。。。。

附加Enum抽象類jdk源碼

/*
 * @param <E> The enum type subclass
 * @author  Josh Bloch
 * @author  Neal Gafter
 * @see     Class#getEnumConstants()
 * @see     java.util.EnumSet
 * @see     java.util.EnumMap
 * @since   1.5
 */
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
    private final String name;
    public final String name() {
        return name;
    }
    private final int ordinal;
    public final int ordinal() {
        return ordinal;
    }
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }
    public String toString() {
        return name;
    }
    public final boolean equals(Object other) {
        return this==other;
    }
    public final int hashCode() {
        return super.hashCode();
    }
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
    public final int compareTo(E o) {
        Enum other = (Enum)o;
        Enum self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }
    public final Class<E> getDeclaringClass() {
        Class clazz = getClass();
        Class zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? clazz : zuper;
    }
    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }
    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize enum");
    }
    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }
}           

複制