java中單例模式是一種常見的設計模式,單例模式分三種:懶漢式單例、餓漢式單例、登記式單例三種。
單例模式有一下特點:
1、單例類只能有一個實例。
2、單例類必須自己自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
java主要使用的單例模式有:
1.餓漢式
public class Singleton {
/**
* 單例對象()
*/
private static fianl Singleton uniqueInstance=new Singleton();
/**
* 構造方法私有
*/
private Singleton() {
}
public static Singleton getUniqueInstance() {
return uniqueInstance;
}
}
優點:簡單暴力。沒有線程安全問題。
缺點:過於簡單,面試官不買賬,沒有延遲加載,初期內存使用增加
2.懶漢式(普遍版)
public class Singleton {
/**
* 單例對象(volatile實現變量的可見性)
*/
private static volatile Singleton uniqueInstance;
/**
* 構造方法私有
*/
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
// new Singleton()可能會因爲重排序而產生線程安全問題
uniqueInstance = new Singleton();
System.out.println(Thread.currentThread().getName() + " :get a new singleton");
}
}
}
return uniqueInstance;
}
}
優點:看起來高大上- -。 用了延遲加載+雙重檢驗+加鎖+volatile保證有序性,大部分面試希望看到的代碼
缺點:比較麻煩,效率又不是最高的。由於是大多數的普遍會寫的版本有些細節還不到位,下文會繼續優化。
3. 懶漢式(可序列化版)
public class Singleton implements Serializable{
/**
* 單例對象(volatile實現變量的可見性)
*/
private static volatile Singleton uniqueInstance;
/**
* 構造方法私有
*/
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
//new Singleton()可能會因爲重排序而產生線程安全問題
uniqueInstance = new Singleton();
System.out.println(Thread.currentThread().getName() + " :get a new singleton");
}
}
}
return uniqueInstance;
}
private Object readResolve() throws ObjectStreamException {
//反序列化調用該方法產生新的實例,反正產生多個實例。
return uniqueInstance;
}
}
加個方法反正反序列化對象時產生多個實例。保證單例模式。(面試官看到了可能會加分哦)
最後由於反射的存在,即使類的構造方法設成私有還是會可以通過反射進行構造出第二個實例。所以要在構造方法上加個判斷
(一般不建議在構造方法中加邏輯哦)
public class Singleton implements Serializable {
/**
* 單例對象(volatile實現變量的可見性)
*/
private static volatile Singleton uniqueInstance;
/**
* 構造方法私有 (用判斷防止反射創建實例)
*/
private Singleton() {
if (uniqueInstance != null) {
throw new RuntimeException("不能創建第二個實例");
}
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
//new Singleton()可能會因爲重排序而產生線程安全問題
uniqueInstance = new Singleton();
System.out.println(Thread.currentThread().getName() + " :get a new singleton");
}
}
}
return uniqueInstance;
}
private Object readResolve() throws ObjectStreamException {
//反序列化調用該方法產生新的實例,反正產生多個實例。
return uniqueInstance;
}
}
當面試官考你單例模式的時候,寫這個代碼會讓面試官眼前一亮,很有效果哦。
但是,這個卻不是最好的單例辦法。性能安全性都比較高的單例模式有以下兩種實現:
1.枚舉類實現 。由於比較簡單,雖然安全和效率都很高,但是大多數人都不喜歡這個方式,也很少人用,個人不建議使用。
2.靜態內部類實現。安全效率也得到保障,大多數人喜歡使用這種方法,效率比懶漢式高一些。
靜態內部類實現
/**
* @author Liquid
* @類名: Singleton2
* @描述: 內部類單例模式,安全效率都有保障
* @date 2019/3/26
*/
public class Singleton2 {
/**
* 內部類創建實例(內部類只在線程第一次訪問時初始化化(延遲加載)
*/
private static class SingletonClass {
private static Singleton2 uniqueInstance = new Singleton2();
}
/**
* 構造方法私有 (用判斷防止反射創建實例)
*/
private Singleton2() {
if (SingletonClass.uniqueInstance != null) {
throw new RuntimeException("不能創建第二個實例");
}
}
/**
* 獲取單例對象,jvm會自動保證內部類中實例的線程安全
*/
public static Singleton2 getUniqueInstance() {
return SingletonClass.uniqueInstance;
}
private Object readResolve() throws ObjectStreamException {
//反序列化調用該方法產生新的實例,反正產生多個實例。
return SingletonClass.uniqueInstance;
}
}
個人推薦這種方法哦,如果面試官問你單例模式,別忘了告訴他這個安全又高效的解決方案.
------Liquid