Java實現單例模式(餓漢式、懶漢式、枚舉式,帶測試)

 

具體代碼以下:java

/**
 * 
 * 餓漢式單例,無論之後用不用這個對象,咱們一開始就建立這個對象的實例,
 * 須要的時候就返回已建立好的實例對象,因此比較飢餓,故此叫餓漢式單例。
 *
 */
public class SingletonHanger {
	private static final SingletonHanger instance = new SingletonHanger();
	private SingletonHanger() {
	}
	public static SingletonHanger getInstance(){
		return instance;
	}
}
/**
 * 懶漢漢式單例,在須要單例對象的時候,才建立惟一的單例對象,之後再次調用,返回的也是第一建立的單例對象
 * 將靜態成員初始化爲null,在獲取單例的時候才建立,故此叫懶漢式。

 *
 */
class SingletonLazy{
	private static SingletonLazy instance = null;
	private SingletonLazy() {
	}
	/**
	 * 此方法實現的單例,沒法在多線程中使用,多線能夠同時進入if方法,會致使生成多個單例對象。

	 */
	public static SingletonLazy getInstance1(){
		if(instance==null){
			instance = new SingletonLazy();
		}
		return instance;
	}
	/**
	 * 你們都會想到同步,能夠同步方法實現多線程的單例
	 * 可是這種方法不可取,嚴重影響性能,由於每次去取單例都要檢查方法,因此只能用同步代碼塊的方式實現同步。

	 */
	public static synchronized SingletonLazy getInstance2(){
		if(instance==null){
			instance = new SingletonLazy();
		}
		return instance;
	}
	/**
	 * 用同步代碼塊的方式,在判斷單例是否存在的if方法裏使用同步代碼塊,在同步代碼塊中再次檢查是否單例已經生成,
	 * 這也就是 雙重檢查加鎖的方法

	 */
	public static synchronized SingletonLazy getInstance3(){
		if(instance==null){
			synchronized (SingletonLazy.class) {
				if(instance==null){
					instance = new SingletonLazy();
				}
			}
		}
		return instance;
	}
}
/**
 * 
 * 使用枚舉實現單例模式,也是Effective Java中推薦使用的方式
 * 根據具體狀況進行實例化,對枚舉不熟悉的同窗,能夠參考個人博客 JAVA 枚舉類的初步理解。
 * 它的好處:更加簡潔,無償提供了序列化機制,絕對防止屢次實例化,即便面對複雜的序列和反射攻擊。

 *
 */
enum SingletionEnum{
	SingletionEnum("單例的枚舉方式");
	private String str ;
	private SingletionEnum(String str){
		this.setStr(str);
	}
	public String getStr() {
		return str;
	}
	public void setStr(String str) {
		this.str = str;
	}
	
}

 

惡漢式、懶漢式的方式還不能防止反射來實現多個實例,經過反射的方式,設置ACcessible.setAccessible方法能夠調用私有的構造器,能夠修改構造器,讓它在被要求建立第二個實例的時候拋出異常。多線程

其實這樣還不能保證單例,當序列化後,反序列化是還能夠建立一個新的實例,在單例類中添加readResolve()方法進行防止。函數

代碼以下:性能

 

package com.zhf.demo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 懶漢漢式單例,在須要單例對象的時候,才建立惟一的單例對象,之後再次調用,返回的也是第一建立的單例對象
 * 將靜態成員初始化爲null,在獲取單例的時候才建立,故此叫懶漢式。

 *
 */
public class Singleton implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -5271537207137321645L;
	private static Singleton instance = null;
	private static int i = 1;
	private Singleton() {
		/**
		 * 防止反射攻擊,只運行調用一次構造器,第二次拋異常
		 */
		if(i==1){
			i++;
		}else{
			throw new RuntimeException("只能調用一次構造函數");
		}
		System.out.println("調用Singleton的私有構造器");
		
	}
	/**
	 * 用同步代碼塊的方式,在判斷單例是否存在的if方法裏使用同步代碼塊,在同步代碼塊中再次檢查是否單例已經生成,
	 * 這也就是網上說的 雙重檢查加鎖的方法

	 */
	public static synchronized Singleton getInstance(){
		if(instance==null){
			synchronized (Singleton.class) {
				if(instance==null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
	/**
	 * 
	 * 防止反序列生成新的單例對象,這是effective Java 一書中說的用此方法能夠防止,具體細節我也不明白

	 */
	private Object readResolve(){
		return instance;
	}
	public static void main(String[] args) throws Exception {
		test1();
		test2();
	}
	/**
	 * 測試 反序列 仍然爲單例模式

	 */
	public static void test2() throws Exception{
		Singleton s  = Singleton.getInstance();
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("E:\\Singleton.txt")));
		objectOutputStream.writeObject(s);
		ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("E:\\Singleton.txt")));
		Object readObject = objectInputStream.readObject();
		Singleton s1 = (Singleton)readObject;
		System.out.println("s.hashCode():"+s.hashCode()+",s1.hashCode():"+s1.hashCode());
		
		objectOutputStream.flush();
		objectOutputStream.close();
		objectInputStream.close();
	}
	/**
	 * 測試反射攻擊

	 */
	public static void test1(){
		Singleton s  = Singleton.getInstance();
		Class c = Singleton.class;
		Constructor privateConstructor;
		try {
			privateConstructor = c.getDeclaredConstructor();
			privateConstructor.setAccessible(true);
			privateConstructor.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
相關文章
相關標籤/搜索