1. 程式人生 > >反射 序列化 克隆對單例模式的破壞

反射 序列化 克隆對單例模式的破壞

--尋找遠方的自己,開創圈外的生命。

本文主要總結一下單例模式,以及其他物件建立方式可能對單例模式的破壞和解決方式。

所謂單例模式就是,某一個類只能有一個例項,實現的核心就是將類的建構函式私有化,只能由該類建立物件,其他物件就不能呼叫該類的建構函式,即不能建立物件了。

現在看一個問題:物件的建立方式有哪幾種?
四種:new 、克隆、序列化、反射。

其實上面的說法有點問題,改為:……其他物件就不能呼叫該類的建構函式,即不能通過new 來建立物件了。那麼是否還有可以通過其他的三種方式建立物件呢,即其他三種方式會不會破壞單例模式呢?

  • 克隆可能對單例模式的破壞

    由克隆我們可以想到原型模式,原型模式就是通過clone方法實現物件的建立的,clone方式是Object方法,每個物件都有,那我使用一個單例模式類的物件,呼叫clone方法,再建立一個新的物件了,那豈不是上面說的單例模式失效了。當然答案是否定,某一個物件直接呼叫clone方法,會丟擲異常,即並不能成功克隆一個物件。呼叫該方法時,必須實現一個Cloneable 介面。這也就是原型模式的實現方式。還有即如果該類實現了cloneable介面,儘管建構函式是私有的,他也可以建立一個物件。即clone方法是不會呼叫建構函式的,他是直接從記憶體中copy記憶體區域的。所以單例模式的類是不可以實現cloneable介面的。

  • 序列化可能對單例模式的破壞

    一是可以實現資料的持久化;二是可以物件資料的遠端傳輸。
    如果過該類implements Serializable,那麼就會在反序列化的過程中再創一個物件。這個問題的解決辦法就是在反序列化時,指定反序化的物件例項。新增如下方法:

 private static final long serialVersionUID = -3979059770681747301L;

    private volatile static Singleton singleton;

    private Object readResolve() {
        return
singleton; }
  • 反射可能對單例模式的破壞

反射是可以獲取類的建構函式,再加一行 setAccessible(true);就可以呼叫私有的建構函式,建立物件了。那麼防止反射破壞Java單例模式的方法就是:當第二次呼叫建構函式時丟擲異常。程式碼如下:

private volatile static Singleton1 singleton;

    private  static boolean  flag = true;
    private Singleton1 (){
        if(flag){
        flag = false;   
        }else
{ throw new RuntimeException("單例模式險些被破壞,第二個物件未建立成功"); } }

還有一種方式是通過列舉實現。

下面開始正式講單例模式方式:
最好的就是雙重檢測校檢鎖,不過下面將全部的幾種方式都寫出來。
1、執行緒不安全:懶漢: (懶漢就是延遲載入)

 package com.txc.singleton;

import java.io.Serializable;
//執行緒不安全:懶漢 
public class Singleton2 implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 2857896192197905033L;

    private volatile static Singleton2 singleton;
    private  static boolean  flag = true;
    private Singleton2 (){
        if(flag){
        flag = false;   
        }else{
            throw new RuntimeException("單例模式險些被破壞,第二個物件未建立成功");
        }

    }

    public static Singleton2 getInstance() {  
        if (singleton == null) {  
            singleton = new Singleton2();  
        }  
        return singleton;  
        }  
    private Object readResolve() {
        return singleton;
    }
}

2、執行緒安全:懶漢:

package com.txc.singleton;

import java.io.Serializable;
//執行緒不安全:懶漢 
public class Singleton3 implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 2857896192197905033L;

    private volatile static Singleton3 singleton;

    private  static boolean  flag = true;
    private Singleton3 (){
        if(flag){
        flag = false;   
        }else{
            throw new RuntimeException("單例模式險些被破壞,第二個物件未建立成功");
        }

    }

    public static synchronized  Singleton3 getInstance() {  
        if (singleton == null) {  
            singleton = new Singleton3();  
        }  
        return singleton;  
        }  
    private Object readResolve() {
        return singleton;
    }
}

3、餓漢:(非延遲載入)是執行緒安全的。

package com.txc.singleton;

import java.io.Serializable;
//執行緒不安全:懶漢 
public class Singleton4 implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 2857896192197905033L;

    private  static Singleton4 singleton= new Singleton4(); 

   private  static boolean  flag = true;
    private Singleton4 (){
        if(flag){
        flag = false;   
        }else{
            throw new RuntimeException("單例模式險些被破壞,第二個物件未建立成功");
        }

    }

    public static  Singleton4 getInstance() {  
        return singleton;  
    }  
    private Object readResolve() {
        return singleton;
    }
}

4、列舉

public enum Singleton {  
    INSTANCE;  

} 

5、雙重校檢鎖

package com.txc.singleton;

import java.io.Serializable;

public class Singleton1 implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -3979059770681747301L;

    private volatile static Singleton1 singleton;


    private  static boolean  flag = true;
    private Singleton1 (){
        if(flag){
        flag = false;   
        }else{
            throw new RuntimeException("單例模式險些被破壞,第二個物件未建立成功");
        }

    }

    public static Singleton1 getSingleton() {
        if (singleton == null) {
            synchronized (Singleton1.class) {
                if (singleton == null) {
                    singleton = new Singleton1();
                }
            }
        }
        return singleton;
    }

    private Object readResolve() {
        return singleton;
    }

}

如有錯誤,請指出。