Java設計模式學習01——單例模式(轉)
阿新 • • 發佈:2017-08-27
多線程 調用 直接 private 延時加載 stream 構造 漏洞 天然
原地址:http://blog.csdn.net/xu__cg/article/details/70182988
Java單例模式是一種常見且較為簡單的設計模式。單例模式,顧名思義一個類僅能有一個實例,並且向整個系統提供這一個實例。
單例模式的特點:
- 單例類僅能有一個實例。
- 單例類必須為自己創建實例。
- 單例類必須向外界提供獲取實例的方法。
以下是幾種實現方法
一、懶漢式單例(能夠延時加載)
public class SingleTon {
private static SingleTon instance = null ;
private SingleTon(){
}
public static SingleTon getInstance(){
if(instance==null){
instance=new SingleTon();
}
return instance ;
}
}
通過是構造方法私有化使該類不能在外界實例化,只能通過public方法獲取實例,從而保證實例唯一。
但是以上的代碼是線程不安全的,多線程並發的情況下,SingleTon可能產生多個實例,可以通過以下方法對getInstance方法進行改進從而保證懶漢式單例線程安全。
1.在getInstance()方法上加上同步
public class SingleTon {
private static SingleTon instance = null ;
private SingleTon(){
}
//方法同步,調用效率低!
public static synchronized SingleTon getInstance(){
if(instance==null){
instance=new SingleTon();
}
return instance ;
}
}
2.靜態內部類式
public class SingleTon {
private static class SingletonClassInstance {
private static final SingleTon instance=new SingleTon();
}
private SingleTon(){
}
//方法沒有同步,調用效率高!
public static SingleTon getInstance(){
return SingletonClassInstance.instance ;
}
}
3.雙重校驗鎖式
public class SingleTon {
private volatile static SingleTon instance ;
private SingleTon(){
}
public static SingleTon getInstance(){
if(instance==null){
synchronized(SingleTon.class ){
if(instance==null){
instance=new SingleTon();
}
}
}
return instance ;
}
}
二、餓漢式單例 (不能夠延時加載)
public class SingleTon {
//類初始化時,立即加載這個對象,加載類時,天然的是線程安全的!
private static SingleTon instance=new SingleTon();
private SingleTon(){
}
//方法沒有同步,調用效率高!
public static SingleTon getInstance(){
return instance ;
}
}
餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不再改變,所以天生是線程安全的。
三、枚舉式單例
public enum SingleTon {
//這個枚舉元素,本身就是單例對象!
INSTANCE;
//添加自己需要的操作!
public void singletonOperation(){
}
}
枚舉式單例是線程安全的,調用效率高,並且可以天然的防止反射和反序列化漏洞。
四、防止反射和反序列化
事實上,通過Java反射或反序列化能夠獲取構造方法為private的類實例,那麽所有的單例都會失效。所以為了避免這種後果,需要采取相應措施。
/**
*懶漢式單例模式(如何防止反射和反序列化漏洞)
*
*/
public class SingleTon implements Serializable {
//類初始化時,不初始化這個對象(延時加載,真正用的時候再創建)。
private static SingleTon instance ;
private SingleTon(){ //私有化構造器
if(instance!=null){
throw new RuntimeException(); //防止反射
}
}
//方法同步,調用效率低!
public static synchronized SingleTon getInstance(){
if(instance==null){
instance=new SingleTon();
}
return instance ;
}
//反序列化時,如果定義了readResolve()則直接返回此方法指定的對象。而不需要單獨再創建新對象!
private Object readResolve() throws ObjectStreamException {
return instance ;
} }
五、如何選用單例模式實現方式
- 單例對象占用資源少,且不需要延時加載:枚舉式 好於 餓漢式
- 單例對象占用資源大,且需要延時加載:靜態內部類式 好於 一般懶漢式
Java設計模式學習01——單例模式(轉)