單例模式的創建、破壞和防破壞
- 前言
- 單例模式
- 單例模式的幾種實作方式
- 懶漢式,執行緒不安全
- 懶漢式,執行緒安全
- 餓漢式
- 雙檢鎖/雙重校驗鎖
- 登記式/靜態內部類
- 列舉
- 破壞單例模式
- 未破壞的情況
- 破壞后的情況
- 單例模式的防破壞
前言
大家所熟知的單例模式只能創建唯一一個實體,今天我們介紹幾種常見的單例模式,同時說一說如何破壞單例模式,同時又怎么來防破壞,
單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一,這種型別的設計模式屬于創建型模式,它提供了一種創建物件的最佳方式,
這種模式涉及到一個單一的類,該類負責創建自己的物件,同時確保只有單個物件被創建,這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要實體化該類的物件,
1、單例類只能有一個實體,
2、單例類必須自己創建自己的唯一實體,
3、單例類必須給所有其他物件提供這一實體,
單例模式的幾種實作方式
懶漢式,執行緒不安全
下面的懶漢式是執行緒不安全的,支持懶加載,因為沒有加鎖 synchronized,所以嚴格意義上它并不算單例模式,
樣例代碼:
public class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
return new Singleton();
}
return instance;
}
}
懶漢式,執行緒安全
下面的這種方式可以保證執行緒安全,支持懶加載,優點是第一次呼叫才初始化,避免記憶體浪費,缺點是必須加鎖synchronized 才能保證單例,但加鎖會影響效率,
樣例代碼:
public class Singleton{
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance == null){
return new Singleton();
}
return instance;
}
}
餓漢式
餓漢式,比較常用,但是容易參生垃圾物件,這種方式不支持懶加載,執行緒安全,優點是沒有加鎖,執行效率會提高,缺點是類加載時就初始化,浪費記憶體,
樣例代碼:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}
雙檢鎖/雙重校驗鎖
這種方式支持懶加載,執行緒安全,這種方式采用雙鎖機制,安全且在多執行緒情況下能保持高性能,
樣例代碼:
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 class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){
}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
列舉
這種實作方式不支持懶加載,執行緒安全,不過還沒有被廣泛采用,但這是實作單例模式的最佳方法,它更簡潔,自動支持序列化機制,絕對防止多次實體化,這種方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不僅能避免多執行緒同步問題,而且還自動支持序列化機制,防止反序列化重新創建新的物件,絕對防止多次實體化,
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
模擬一個資料庫連接類:
public enum SingletonEnum {
INSTANCE;
private DBConnection connection = null;
SingletonEnum(){
connection = new DBConnection();
}
public DBConnection getConnection(){
return connection;
}
}
public class DBConnection{
}
public class TestConnection {
public static void main(String[] args) {
DBConnection con1 = DataSourceEnum.DATASOURCE.getConnection();
DBConnection con2 = DataSourceEnum.DATASOURCE.getConnection();
System.out.println(con1 == con2); //輸出結果為true,
}
}
破壞單例模式
破壞單例模式主要有兩種方法:反射、反序列化
我們就拿最經典的餓漢式來演示破壞和防破壞,
未破壞的情況
Singleton:
/**
* Keafmd
*
* @ClassName: Singleton
* @Description: 單例模式
* @author: 牛哄哄的柯南
* @date: 2021-09-07 10:53
*/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}
測驗類(未破壞):
/**
* Keafmd
*
* @ClassName: SigletonTest
* @Description: 測驗類
* @author: 牛哄哄的柯南
* @date: 2021-09-07 11:04
*/
public class SingletonTest {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1); //com.keafmd.Study.designPatterns.Blog.Singleton@610455d6
System.out.println(instance2); //com.keafmd.Study.designPatterns.Blog.Singleton@610455d6
System.out.println(instance1==instance2); //true
}
}
破壞后的情況
Singleton:(不改變)
/**
* Keafmd
*
* @ClassName: Singleton
* @Description: 單例模式
* @author: 牛哄哄的柯南
* @date: 2021-09-07 10:53
*/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}
測驗類(通過反射破壞):
package com.keafmd.Study.designPatterns.Blog;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* Keafmd
*
* @ClassName: SigletonTest
* @Description: 測驗類
* @author: 牛哄哄的柯南
* @date: 2021-09-07 11:04
*/
public class SingletonTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1); //com.keafmd.Study.designPatterns.Blog.Singleton@610455d6
System.out.println(instance2); //com.keafmd.Study.designPatterns.Blog.Singleton@610455d6
System.out.println(instance1==instance2); //true
//=====================破壞單例模式===================
//通過反射獲取實體,破壞單例
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance11 = constructor.newInstance();
Singleton instance22 = constructor.newInstance();
System.out.println(instance11); //com.keafmd.Study.designPatterns.Blog.Singleton@511d50c0
System.out.println(instance22); //com.keafmd.Study.designPatterns.Blog.Singleton@60e53b93
System.out.println(instance11==instance22); //false 證明單例模式已經被破壞
}
}
輸出結果:
com.keafmd.Study.designPatterns.Blog.Singleton@610455d6
com.keafmd.Study.designPatterns.Blog.Singleton@610455d6
true
com.keafmd.Study.designPatterns.Blog.Singleton@511d50c0
com.keafmd.Study.designPatterns.Blog.Singleton@60e53b93
false
Process finished with exit code 0
這種破壞是通過java的反射機制,創建一個實體,這種破壞方法通過setAccessible(true)的方法是java跳過檢測語法,可以臨時改變訪問權限,就可以獲取私有成員變數,
單例模式的防破壞
其實防止破壞最簡單的一種方式就是判斷下有沒有創建過實體,如果是第二次創建實體物件的時候,直接拋出例外,阻止創建即可,
重寫Singleton類:
package com.keafmd.Study.designPatterns.Blog;
/**
* Keafmd
*
* @ClassName: Singleton
* @Description: 單例模式
* @author: 牛哄哄的柯南
* @date: 2021-09-07 10:53
*/
public class Singleton {
//阻止實體化
private static boolean flag=true;
private static Singleton instance = new Singleton();
private Singleton (){
if(!flag){
throw new RuntimeException("這個單例模式類不能創建更多的物件了");
}
}
public static Singleton getInstance() {
if(flag){
flag=false; //第一次創建時就會改變flag的值,導致后面創建不成功
}
return instance;
}
}
測驗類(未改變):
package com.keafmd.Study.designPatterns.Blog;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* Keafmd
*
* @ClassName: SigletonTest
* @Description: 測驗類
* @author: 牛哄哄的柯南
* @date: 2021-09-07 11:04
*/
public class SingletonTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance1==instance2);
//=====================破壞單例模式===================
//通過反射獲取實體,破壞單例
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance11 = constructor.newInstance();
Singleton instance22 = constructor.newInstance();
System.out.println(instance11);
System.out.println(instance22);
System.out.println(instance11==instance22);
}
}
輸出結果:
com.keafmd.Study.designPatterns.Blog.Singleton@610455d6
com.keafmd.Study.designPatterns.Blog.Singleton@610455d6
true
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.keafmd.Study.designPatterns.Blog.SingletonTest.main(SingletonTest.java:28)
Caused by: java.lang.RuntimeException: 這個單例模式類不能創建更多的物件了
at com.keafmd.Study.designPatterns.Blog.Singleton.<init>(Singleton.java:28)
... 5 more
Process finished with exit code 1
這樣在執行到
Singleton instance22 = constructor.newInstance();這行的時候就會拋出例外,這樣就防止了破壞,
著作權宣告:
原創博主:牛哄哄的柯南
博主原文鏈接:https://keafmd.blog.csdn.net/
看完如果對你有幫助,感謝點擊下面的一鍵三連支持!
[哈哈][抱拳]


加油!
共同努力!
Keafmd
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/298656.html
標籤:java
