JAVA破壞單例模式的方式以及避免方法
單例模式,大家恐怕再熟悉不過(guò)了,其作用與實(shí)現(xiàn)方式有多種,這里就不啰嗦了。但是,咱們?cè)谑褂眠@些方式實(shí)現(xiàn)單例模式時(shí),程序中就真的會(huì)只有一個(gè)實(shí)例嗎?
聰明的你看到這樣的問(wèn)話,一定猜到了答案是NO。這里筆者就不賣關(guān)子了,開(kāi)門見(jiàn)山吧!實(shí)際上,在有些場(chǎng)景下,如果程序處理不當(dāng),會(huì)無(wú)情地破壞掉單例模式,導(dǎo)致程序中出現(xiàn)多個(gè)實(shí)例對(duì)象。
下面筆者介紹筆者已知的三種破壞單例模式的方式以及避免方法。
1、反射對(duì)單例模式的破壞
我們先通過(guò)一個(gè)例子,來(lái)直觀感受一下
(1)案例
DCL實(shí)現(xiàn)的單例模式:
public class Singleton{
private static volatile Singleton mInstance;
private Singleton(){}
public static Singleton getInstance(){
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
測(cè)試代碼:
public class SingletonDemo {
public static void main(String[] args){
Singleton singleton = Singleton.getInstance();
try {
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton reflectSingleton = constructor.newInstance();
System.out.println(reflectSingleton == singleton);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
執(zhí)行結(jié)果:
false
運(yùn)行結(jié)果說(shuō)明,采用反射的方式另辟蹊徑實(shí)例了該類,導(dǎo)致程序中會(huì)存在不止一個(gè)實(shí)例。
(2)解決方案
其思想就是采用一個(gè)全局變量,來(lái)標(biāo)記是否已經(jīng)實(shí)例化過(guò)了,如果已經(jīng)實(shí)例化過(guò)了,第二次實(shí)例化的時(shí)候,拋出異常。實(shí)現(xiàn)代碼如下:
public class Singleton{
private static volatile Singleton mInstance;
private static volatile boolean mIsInstantiated = false;
private Singleton(){
if (mIsInstantiated){
throw new RuntimeException("Has been instantiated, can not do it again!");
}
mIsInstantiated = true;
}
public static Singleton getInstance(){
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
執(zhí)行結(jié)果:

2、clone()對(duì)單例模式的破壞
當(dāng)需要實(shí)現(xiàn)單例的類允許clone()時(shí),如果處理不當(dāng),也會(huì)導(dǎo)致程序中出現(xiàn)不止一個(gè)實(shí)例。
(1)案例
一個(gè)實(shí)現(xiàn)了Cloneable接口單例類:
public class Singleton implements Cloneable{
private static volatile Singleton mInstance;
private Singleton(){
}
public static Singleton getInstance(){
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
測(cè)試代碼:
public class SingletonDemo {
public static void main(String[] args){
try {
Singleton singleton = Singleton.getInstance();
Singleton cloneSingleton;
cloneSingleton = (Singleton) Singleton.getInstance().clone();
System.out.println(cloneSingleton == singleton);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
執(zhí)行結(jié)果:
false
(2)解決方案:
解決思想是,重寫clone()方法,調(diào)clone()時(shí)直接返回已經(jīng)實(shí)例的對(duì)象
public class Singleton implements Cloneable{
private static volatile Singleton mInstance;
private Singleton(){
}
public static Singleton getInstance(){
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return mInstance;
}
}
執(zhí)行結(jié)果:
true
3、序列化對(duì)單例模式的破壞
在使用序列化/反序列化時(shí),也會(huì)出現(xiàn)產(chǎn)生新實(shí)例對(duì)象的情況。
(1)案例
一個(gè)實(shí)現(xiàn)了序列化接口的單例類:
public class Singleton implements Serializable{
private static volatile Singleton mInstance;
private Singleton(){
}
public static Singleton getInstance(){
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
測(cè)試代碼:
public class SingletonDemo {
public static void main(String[] args){
try {
Singleton singleton = Singleton.getInstance();
FileOutputStream fos = new FileOutputStream("singleton.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(singleton);
oos.close();
fos.close();
FileInputStream fis = new FileInputStream("singleton.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Singleton serializedSingleton = (Singleton) ois.readObject();
fis.close();
ois.close();
System.out.println(serializedSingleton==singleton);
} catch (Exception e) {
e.printStackTrace();
}
}
}
運(yùn)行結(jié)果:
false
(2)解決方案
在反序列化時(shí)的回調(diào)方法 readResolve()中返回單例對(duì)象。
public class Singleton implements Serializable{
private static volatile Singleton mInstance;
private Singleton(){
}
public static Singleton getInstance(){
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
protected Object readResolve() throws ObjectStreamException{
return mInstance;
}
}
結(jié)果:
true
以上就是筆者目前已知的三種可以破壞單例模式的場(chǎng)景以及對(duì)應(yīng)的解決辦法,讀者如果知道還有其他的場(chǎng)景,記得一定要分享出來(lái)噢,正所謂“獨(dú)樂(lè)樂(lè)不如眾樂(lè)樂(lè)”?。?!
單例模式看起來(lái)是設(shè)計(jì)模式中最簡(jiǎn)單的一個(gè),但“麻雀雖小,五臟俱全”,其中有很多細(xì)節(jié)都是值得深究的。即便是本篇介紹的這幾個(gè)場(chǎng)景,也只是介紹了一些梗概而已,很多細(xì)節(jié)還需要讀者自己去試驗(yàn)和推敲的,比如:通過(guò)枚舉方式實(shí)現(xiàn)單例模式,就不存在上述問(wèn)題,而其它的實(shí)現(xiàn)方式似乎都存在上述問(wèn)題!
后記
本篇參(剽)考(竊)了如下資料:http://www.dbjr.com.cn/article/143047.htm
以上就是JAVA破壞單例模式的方式以及避免方法的詳細(xì)內(nèi)容,更多關(guān)于JAVA 單例模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解springboot如何更新json串里面的內(nèi)容
這篇文章主要為大家介紹了springboot 如何更新json串里面的內(nèi)容,文中有詳細(xì)的解決方案供大家參考,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-10-10
SpringBoot如何切換成其它的嵌入式Servlet容器(Jetty和Undertow)
這篇文章主要介紹了SpringBoot如何切換成其它的嵌入式Servlet容器(Jetty和Undertow),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Spring boot監(jiān)控Actuator-Admin實(shí)現(xiàn)過(guò)程詳解
這篇文章主要介紹了Spring boot監(jiān)控Actuator-Admin實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
java后臺(tái)調(diào)用接口及處理跨域問(wèn)題的解決
這篇文章主要介紹了java后臺(tái)調(diào)用接口,處理跨域的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
MyBatis開(kāi)啟二級(jí)緩存實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了MyBatis開(kāi)啟二級(jí)緩存實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

