java設計模式1——單例模式
1、單例模式介紹
1.1、核心作用:保證一個類只有一個實體,并且提供一個訪問該實體的全域訪問點
1.2、常見場景

1.3、單例模式的優點

1.4、常見的五種單例模式實作方式

2、餓漢式
2.1、第一步:私有化構造器,(防止外部直接new物件)
//保證類只有一個實體,私有其構造器
private SingletonDemo01() {
}
2.2、第二步:創建自身物件,
//創建自身物件
private static SingletonDemo01 instance = new SingletonDemo01();
2.3、第三步:提夠對外全域公開的方法
//全域公開的方法
public static SingletonDemo01 getInstance() {
return instance;
}
2.4、測驗是否為單例
class SingletonDemo01Test {
public static void main(String[] args) {
SingletonDemo01 instance = SingletonDemo01.getInstance();
SingletonDemo01 instance2 = SingletonDemo01.getInstance();
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
System.out.println(instance == instance2);
}
}
輸出的結果為:
356573597
356573597
true
2.5、弊端分析:
餓漢式一上來就會對物件進行創建,不管后續有沒有用到,如果對于較大記憶體的物件而后續也都沒有用到,則會造成較大的記憶體空間的浪費,
2.6、本類全部代碼
package com.xgp.company.第一種_單例模式.餓漢式;
/**
*
* 核心:保證一個類只有一個實體,并且提供一個范圍該實體的全域訪問點
*/
public class SingletonDemo01 {
//保證類只有一個實體,私有其構造器
private SingletonDemo01() {
}
//創建自身物件
private static SingletonDemo01 instance = new SingletonDemo01();
//全域公開的方法
public static SingletonDemo01 getInstance() {
return instance;
}
}
class SingletonDemo01Test {
public static void main(String[] args) {
SingletonDemo01 instance = SingletonDemo01.getInstance();
SingletonDemo01 instance2 = SingletonDemo01.getInstance();
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
System.out.println(instance == instance2);
}
}
3、懶漢式
目的:解決餓漢式可能存在的記憶體空間浪費的問題進行該進,不一上來就創建物件,而是在使用時再來創建物件,
3.1、懶漢式的代碼如下:
public class SingletonDemo02 {
//保證類只有一個實體,私有其構造器
private SingletonDemo02() {
}
//創建自身物件,當時不用立即加載
private static SingletonDemo02 instance;
//全域公開的方法 synchronized作用:加鎖 多執行緒進來時會不安全,效率較低
public static synchronized SingletonDemo02 getInstance() {
if(instance == null) {
instance = new SingletonDemo02();
}
return instance;
}
}
3.2、分析:代碼中為什要使用synchronized關鍵字來進行上鎖
考率一下多執行緒的情況下,如果沒有上鎖,兩個執行緒A、B一前以后的很緊密的執行該方法,而此時A完成了初始化操作,但是還沒有進行回傳,B此時進入判斷陳述句中,此時也為null,這樣也會進行初始化操作,于是乎,就得到了兩個物件了,違反了單例模式設計得原則,
3.3、弊端分析:
該方法使用了synchronized對一個回傳得方法進行了上鎖,該方法得執行效率會較慢,
4、DCL_懶漢式
目的:DCL_懶漢式又稱為雙重檢測懶漢式,為了改進懶漢式效率不高的問題
4.1、該類的1版本的代碼如下:
public class SingletonDemo03 {
//保證類只有一個實體,私有其構造器
private SingletonDemo03() {
}
//創建自身物件,當時不用立即加載 volatile作用:盡大可能的解決極端情況的問題
private volatile static SingletonDemo03 instance;
//全域公開的方法 synchronized作用:加鎖 多執行緒進來時會不安全,效率較低
public static SingletonDemo03 getInstance() {
if(instance == null) {
//定一次進來時加鎖,后面進來時就不加鎖了,提高了效率
synchronized (SingletonDemo03.class) {
if(instance == null) {
instance = new SingletonDemo03();
}
}
}
return instance;
}
}
4.2、分析1版本代碼:
同樣考率多執行緒的情況下,A、B兩執行緒相繼的進入方法中,A率先獲得初始化權力,進行上鎖,進行對物件的創建,并且因為有volatile關鍵字,能夠快速的將物件更新給B,如果B未進入判斷陳述句中,則此時B中有該類物件了,直接回傳了,如果B進入了判斷陳述句中,但是A已經上鎖了,也無法進入了,只有回傳了,
4.3、1版本的弊端
1、再考慮多執行緒的極端情況,如果該類比較龐大,創建物件需要花費很長時間,B已經進入函式中了,而A創建物件的時間會比B走完該函式的時間長,則此時該函式將會回傳B,而B=NULL,
2、該模式無法防止反射
4.4、版本2代碼:
public class SingletonDemo03 {
//破壞兩次都用反射創建物件
private static boolean flag = false;
//保證類只有一個實體,私有其構造器
private SingletonDemo03() {
//防治被反射
synchronized (SingletonDemo03.class) {
if(flag == false) {
flag = true;
}else {
throw new RuntimeException("不要試圖用反射破壞單例");
}
}
}
//創建自身物件,當時不用立即加載 volatile作用:盡大可能的解決極端情況的問題
private volatile static SingletonDemo03 instance;
//全域公開的方法 synchronized作用:加鎖 多執行緒進來時會不安全,效率較低
public static SingletonDemo03 getInstance() {
if(instance == null) {
//定一次進來時加鎖,后面進來時就不加鎖了,提高了效率
synchronized (SingletonDemo03.class) {
if(instance == null) {
instance = new SingletonDemo03();
}
}
}
return instance;
}
}
4.5、弊端分析
該版本同樣未解決上面的問題,只是加大了反射獲取物件的難度,反射破壞單例的代碼如下:
class SingletonDemo03Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
/*
SingletonDemo03 instance1 = SingletonDemo03.getInstance();
SingletonDemo03 instance2 = SingletonDemo03.getInstance();
System.out.println(instance1 == instance2);
*/
Class<SingletonDemo03> clazz = SingletonDemo03.class;
//反射破壞單例
Constructor<SingletonDemo03> declaredConstructor = clazz.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
SingletonDemo03 instance1 = declaredConstructor.newInstance();
//破壞flag
Field flag = clazz.getDeclaredField("flag");
flag.setAccessible(true);
flag.set(clazz,false);
System.out.println(flag.get(clazz));
SingletonDemo03 instance2 = declaredConstructor.newInstance();
System.out.println(instance1 == instance2);
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
運行結果:
false
false
21685669
2133927002
5、靜態內部類實作
該方式能夠不適用synchronized提高效率,并且能夠保證在多執行緒的情況下依舊是單例,代碼如下:
public class SingletonDemo04 {
private SingletonDemo04() {
//防治被反射
synchronized (SingletonDemo04.class) {
if(InnerClass.instance != null) {
throw new RuntimeException("不要試圖用反射破壞單例");
}
}
}
private static class InnerClass {
private static final SingletonDemo04 instance = new SingletonDemo04();
}
public static SingletonDemo04 getInstance() {
return InnerClass.instance;
}
}
6、利用列舉來實作
java中最為推薦的是使用列舉類來創建單例物件,因為列舉類有這純天然的優勢,無法被反射,點擊進反射創建物件的newInstance()方法的原始碼中可以發現:
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
此外,列舉類也本身就是單例的,所以使用列舉類來創建單例物件最為適合,而如今大多數的框架的單例也都是通過這樣的方法進行創建的,代碼如下:
/**
* 反射不能破壞列舉型別,列舉類純天然的單例,最簡單
*/
public enum SingletonDemo05 {
INSTANCE;
public SingletonDemo05 getInstance() {
return INSTANCE;
}
public String hello() {
return "Hello World!";
}
}
class SingletonDemo05Test {
public static void main(String[] args) {
SingletonDemo05 instance1 = SingletonDemo05.INSTANCE;
SingletonDemo05 instance2 = SingletonDemo05.INSTANCE.getInstance();
System.out.println(instance1 == instance2);
String hello = SingletonDemo05.INSTANCE.hello();
System.out.println(hello);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/26072.html
標籤:設計模式
上一篇:備忘錄模式
