最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java單例模式實(shí)現(xiàn)的方法

 更新時(shí)間:2020年06月02日 11:04:21   作者:蝴蝶刀刀  
這篇文章主要介紹了如何在JAVA中實(shí)現(xiàn)單例模式,文中代碼簡(jiǎn)單易懂,供大家參考學(xué)習(xí),感興趣的小伙伴可以了解下

1.最基本的單例模式

/**
 * @author LearnAndGet
 * @time 2018年11月13日
 * 最基本的單例模式 */public class SingletonV1 { 
 private static SingletonV1 instance = new SingletonV1();; 
 //構(gòu)造函數(shù)私有化
 private SingletonV1() {} public static SingletonV1 getInstance() 
 { return instance;
 }
}
import org.junit.Test;public class SingletonTest {
 
 @Test public void test01() throws Exception
 {
 SingletonV1 s1 = SingletonV1.getInstance();
 SingletonV1 s2 = SingletonV1.getInstance();
 System.out.println(s1.hashCode());
 System.out.println(s2.hashCode());
 }
}//運(yùn)行結(jié)果如下:589873731
589873731

2.類加載時(shí)不初始化實(shí)例的模式

上述單例模式在類加載的時(shí)候,就會(huì)生成實(shí)例,可能造成空間浪費(fèi),如果需要修改成,在需要使用時(shí)才生成實(shí)例,則可修改代碼如下:

public class SingletonV2 
{ private static SingletonV2 instance; //構(gòu)造函數(shù)私有化 
 private SingletonV2() {} 
	 public static SingletonV2 getInstance()
	 { if(instance == null) 
 { instance = new SingletonV2();
	} 
	return instance; } 
	}

然而,上述方案雖然在類加載時(shí)不會(huì)生成實(shí)例,但是存在線程安全問題,如果線程A在執(zhí)行到第10行時(shí),線程B也進(jìn)入該代碼塊,恰好也執(zhí)行好第10行,此時(shí)如果實(shí)例尚未生成,則線程A和線程B都會(huì)執(zhí)行第12行的代碼,各自生成一個(gè)實(shí)例,此時(shí)就違背了單例模式的設(shè)計(jì)原則。實(shí)際測(cè)試代碼如下:

public class SingletonTest {

 @Test public void test02() throws Exception
 { 
 for(int i=0;i<1000;i++) 
 {
 Thread th1 = new getInstanceThread();
 th1.start();
 }
 
 } 
 class getInstanceThread extends Thread
 { public void run() 
 { try 
 {
 SingletonV2 s = SingletonV2.getInstance();
 System.out.println(Thread.currentThread().getName()+" get Instance "+s.hashCode()+" Time: "+System.currentTimeMillis());
 }catch(Exception e) 
 {
 e.printStackTrace();
 }
 }
 }
 
}

經(jīng)過多次測(cè)試,可能產(chǎn)生如下輸出結(jié)果:

3.線程安全的單例模式

在上述單例模式下進(jìn)行改進(jìn),在getInstance方法前加入 Sychronized關(guān)鍵字,來實(shí)現(xiàn)線程安全,修改后代碼如下:

 public class SingletonV3 { 
 
 private static SingletonV3 instance; 
 
 //構(gòu)造函數(shù)私有化 
 private SingletonV3() {} 
 
    //synchronized關(guān)鍵字在靜態(tài)方法上,鎖定的是當(dāng)前類: 
 public static synchronized SingletonV3 getInstance() 
 {
 if(instance == null) 
 
{
 instance = new SingletonV3();
 }
 return instance;
 }
 }

 增加sychronized關(guān)鍵字后,確實(shí)能夠改善線程安全問題,但是也帶來了額外的鎖開銷。性能受到一定影響。舉例來說,此時(shí)如果有1000個(gè)線程都需要使用SingletonV3實(shí)例,因?yàn)榧渔i的位置在getInstance上,因此,每個(gè)線程都必須等待其他獲取了鎖的線程完全執(zhí)行完鎖中的方法后,才能夠進(jìn)入該方法并獲取自己的實(shí)例。

4.雙重校檢+線程安全單例模式

  于是可以在上述代碼的基礎(chǔ)上,只有當(dāng)Singleton實(shí)例未被初始化時(shí),對(duì)實(shí)例化方法加鎖即可。在Singleton實(shí)例已經(jīng)被初始化時(shí),無需加鎖,直接返回當(dāng)前Singleton對(duì)象。代碼如下:

 private static SingletonV4 instance; 
 
 //構(gòu)造函數(shù)私有化 
 private SingletonV4() {}
 
 public static SingletonV4 getInstance() 
 { 
 if(instance == null) 
 
 {
 synchronized(SingletonV4.class) 
 {
 //雙重校檢
 if(instance == null) 
 {
  instance = new SingletonV4();
  }
 }
 }
 return instance;
 }

5.內(nèi)部類單例模式 

盡管上述方案解決了同步問題,雙重校檢也使得性能開銷大大減小,但是,只有有synchronized關(guān)鍵字的存在。性能多多少少還是會(huì)有一些影響,此時(shí),我們想到了 "內(nèi)部類"的用法。

 ?、?內(nèi)部類不會(huì)隨著類的加載而加載

 ?、?一個(gè)類被加載,當(dāng)且僅當(dāng)其某個(gè)靜態(tài)成員(靜態(tài)域、構(gòu)造器、靜態(tài)方法等)被調(diào)用時(shí)發(fā)生。

  靜態(tài)內(nèi)部類隨著方法調(diào)用而被加載,只加載一次,不存在并發(fā)問題,所以是線程安全。基于此,修改代碼如下:

 public class SingletonV5 {
 //構(gòu)造函數(shù)私有化
 private SingletonV5() {} 
 
 static class SingetonGet 
 { 
 private static final SingletonV5 instance = new SingletonV5(); 
 } 
 
 public static SingletonV5 getInstance() 
 {
 return SingetonGet.instance;
 }
 }

6.反射都不能破壞的單例模式

靜態(tài)內(nèi)部類實(shí)現(xiàn)的單例模式,是目前比較推薦的方式,但是在java功能強(qiáng)大反射的機(jī)制下,它就是個(gè)弟弟,此時(shí)利用反射仍然能夠創(chuàng)建出多個(gè)實(shí)例,以下是創(chuàng)建實(shí)例的代碼:

 @Test
 public void test4() 
 { 
 //普通方式獲取實(shí)例s1,s2 
 SingletonV5 s1 = SingletonV5.getInstance(); 
 SingletonV5 s2 = SingletonV5.getInstance(); 
 //利用反射獲取實(shí)例s3,s4 
 SingletonV5 s3 = null; 
 SingletonV5 s4 = null;
 try 
 {
 Class<SingletonV5> clazz = SingletonV5.class;
 Constructor<SingletonV5> constructor = clazz.getDeclaredConstructor();
 constructor.setAccessible(true);
 s3 = constructor.newInstance();
 s4 = constructor.newInstance();
 }catch(Exception e) 
 {
 e.printStackTrace();
 }
 
 System.out.println(s1.hashCode());
 System.out.println(s2.hashCode());
 System.out.println(s3.hashCode());
 System.out.println(s4.hashCode()); 
 }

輸出結(jié)果如下:

589873731
589873731
200006406
2052001577

 可以看到,s1和s2擁有相同的哈希碼,因此他們是同一個(gè)實(shí)例,但是s3、s4,是通過反射后用構(gòu)造函數(shù)重新構(gòu)造生成的實(shí)例,他們均與s1,s2不同。此時(shí)單例模式下產(chǎn)生了多個(gè)不同的對(duì)象,違反了設(shè)計(jì)原則。

基于上述反射可能造成的單例模式失效,考慮在私有的構(gòu)造函數(shù)中添加是否初始化的標(biāo)記位,使私有構(gòu)造方法只可能被執(zhí)行一次。

public class SingletonV6 { //是否已經(jīng)初始化過的標(biāo)記位
 private static boolean isInitialized = false; 
 //構(gòu)造函數(shù)中,當(dāng)實(shí)例已經(jīng)被初始化時(shí),不能繼續(xù)獲取新實(shí)例
 private SingletonV6() 
 { synchronized(SingletonV6.class) 
 { if(isInitialized == false) 
 {
 isInitialized = !isInitialized;
 }else 
 { throw new RuntimeException("單例模式被破壞...");
 }
 } 
 } static class SingetonGet
 { private static final SingletonV6 instance = new SingletonV6();
 } 
 public static SingletonV6 getInstance() 
 { return SingetonGet.instance;
 }
}

測(cè)試代碼如下:

 @Test public void test5()
 { 
 SingletonV6 s1 = SingletonV6.getInstance();
 SingletonV6 s2 = null; try 
 {
 Class<SingletonV6> clazz = SingletonV6.class;
 Constructor<SingletonV6> constructor = clazz.getDeclaredConstructor();
 constructor.setAccessible(true);
 s2 = constructor.newInstance();

 }catch(Exception e) 
 {
 e.printStackTrace();
 } 
 System.out.println(s1.hashCode());
 System.out.println(s2.hashCode());
 }

運(yùn)行上述代碼時(shí),會(huì)拋出異常:

java.lang.reflect.InvocationTargetException
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
 at java.lang.reflect.Constructor.newInstance(Unknown Source)
 at SingletonTest.SingletonTest.test5(SingletonTest.java:98)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
 at java.lang.reflect.Method.invoke(Unknown Source)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: java.lang.RuntimeException: 單例模式被破壞...
 at SingletonTest.SingletonV6.<init>(SingletonV6.java:26)
 ... 28 more2052001577

7.序列化反序列化都不能破壞的單例模式

經(jīng)過上述改進(jìn),反射也不能夠破壞單例模式了。但是,依然存在一種可能造成上述單例模式產(chǎn)生兩個(gè)不同的實(shí)例,那就是序列化。當(dāng)一個(gè)對(duì)象A經(jīng)過序列化,然后再反序列化,獲取到的對(duì)象B和A是否是同一個(gè)實(shí)例呢,驗(yàn)證代碼如下:

/**
 * @Author {LearnAndGet}
 * @Time 2018年11月13日
 * @Discription:測(cè)試序列化并反序列化是否還是同一對(duì)象 */package SingletonTest;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInput;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;public class Main { /**
 * @param args */
 public static void main(String[] args) { // TODO Auto-generated method stub
 SingletonV6 s1 = SingletonV6.getInstance();
 
 ObjectOutput objOut = null; 
 try { //將s1序列化(記得將Singleton實(shí)現(xiàn)Serializable接口)
 objOut = new ObjectOutputStream(new FileOutputStream("c:\\a.objFile"));
 objOut.writeObject(s1);
 objOut.close(); 
 //反序列化得到s2
 ObjectInput objIn = new ObjectInputStream(new FileInputStream("c:\\a.objFile"));
 SingletonV6 s2 = (SingletonV6) objIn.readObject();
 objIn.close();
 
 System.out.println(s1.hashCode());
 System.out.println(s2.hashCode());
 
 } catch (Exception e) 
 { // TODO Auto-generated catch block e.printStackTrace();
 }
 }

}

輸出結(jié)果如下:

1118140819
990368553

可見,此時(shí)序列化前的對(duì)象s1和經(jīng)過序列化->反序列化步驟后的到的對(duì)象s2,并不是同一個(gè)對(duì)象,因此,出現(xiàn)了兩個(gè)實(shí)例,再次違背了單例模式的設(shè)計(jì)原則。

為了消除問題,在單例模式類中,實(shí)現(xiàn)Serializable接口之后 添加對(duì)readResolve()方法的實(shí)現(xiàn):當(dāng)從I/O流中讀取對(duì)象時(shí),readResolve()方法都會(huì)被調(diào)用到。實(shí)際上就是用readResolve()中返回的對(duì)象直接替換在反序列化過程中創(chuàng)建的對(duì)象,而被創(chuàng)建的對(duì)象則會(huì)被垃圾回收掉。這就確保了在序列化和反序列化的過程中沒人可以創(chuàng)建新的實(shí)例,修改后的代碼如下:

package SingletonTest;import java.io.Serializable;/**
 * @author LearnAndGet
 *
 * @time 2018年11月13日
 * 
 */public class SingletonV6 implements Serializable{ //是否已經(jīng)初始化過的標(biāo)記位
 private static boolean isInitialized = false; 
 //構(gòu)造函數(shù)中,當(dāng)實(shí)例已經(jīng)被初始化時(shí),不能繼續(xù)獲取新實(shí)例
 private SingletonV6() 
 { synchronized(SingletonV6.class) 
 { if(isInitialized == false) 
 {
 isInitialized = !isInitialized;
 }else 
 { throw new RuntimeException("單例模式被破壞...");
 }
 } 
 } static class SingetonGet
 { private static final SingletonV6 instance = new SingletonV6();
 } 
 public static SingletonV6 getInstance() 
 { return SingetonGet.instance;
 } //實(shí)現(xiàn)readResolve方法
 private Object readResolve() 
 { return getInstance();
 }
}

重新運(yùn)行上述序列化和反序列過程,可以發(fā)現(xiàn),此時(shí)得到的對(duì)象是同一對(duì)象。

1118140819
1118140819

8.總結(jié)

在實(shí)際開發(fā)中,根據(jù)自己的需要,選擇對(duì)應(yīng)的單例模式即可,不一樣非要實(shí)現(xiàn)第7節(jié)中那種無堅(jiān)不摧的單例模式。畢竟不是所有場(chǎng)景下都需要實(shí)現(xiàn)序列化接口, 也并不是所有人都會(huì)用反射來破壞單例模式。因此比較常用的是第5節(jié)中的,內(nèi)部類單例模式,代碼簡(jiǎn)潔明了,且節(jié)省空間。

以上就是java單例模式實(shí)現(xiàn)的方法的詳細(xì)內(nèi)容,更多關(guān)于java單例模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot Controller中的常用注解

    SpringBoot Controller中的常用注解

    這篇文章主要介紹了SpringBoot Controller中的常用注解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • JVM調(diào)優(yōu)實(shí)戰(zhàn)

    JVM調(diào)優(yōu)實(shí)戰(zhàn)

    本文主要介紹了JVM調(diào)優(yōu)實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Spring的Bean注入解析結(jié)果BeanDefinition詳解

    Spring的Bean注入解析結(jié)果BeanDefinition詳解

    這篇文章主要介紹了Spring的Bean注入解析結(jié)果BeanDefinition詳解,BeanDefinition描述了一個(gè)bean實(shí)例,擁有屬性值、構(gòu)造參數(shù)值和具體實(shí)現(xiàn)的其他信息,其是一個(gè)bean的元數(shù)據(jù),xml中配置的bean元素會(huì)被解析成BeanDefinition對(duì)象,需要的朋友可以參考下
    2023-12-12
  • Java用POI解析excel并獲取所有單元格數(shù)據(jù)的實(shí)例

    Java用POI解析excel并獲取所有單元格數(shù)據(jù)的實(shí)例

    下面小編就為大家?guī)硪黄狫ava用POI解析excel并獲取所有單元格數(shù)據(jù)的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • springboot整合flowable框架入門步驟

    springboot整合flowable框架入門步驟

    最近工作中有用到工作流的開發(fā),引入了flowable工作流框架,在此記錄一下springboot整合flowable工作流框架的過程,感興趣的朋友一起看看吧
    2022-04-04
  • maven如何查看jar的pom引入來源

    maven如何查看jar的pom引入來源

    這篇文章主要介紹了maven查看jar的pom引入來源,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • Java實(shí)現(xiàn)將導(dǎo)出帶格式的Excel數(shù)據(jù)到Word表格

    Java實(shí)現(xiàn)將導(dǎo)出帶格式的Excel數(shù)據(jù)到Word表格

    在Word中制作報(bào)表時(shí),我們經(jīng)常需要將Excel中的數(shù)據(jù)復(fù)制粘貼到Word中,這樣則可以直接在Word文檔中查看數(shù)據(jù)而無需打開另一個(gè)Excel文件。本文將通過Java應(yīng)用程序詳細(xì)介紹如何把帶格式的Excel數(shù)據(jù)導(dǎo)入Word表格。希望這篇文章能對(duì)大家有所幫助
    2022-11-11
  • 一步步教你把SpringBoot項(xiàng)目打包成Docker鏡像

    一步步教你把SpringBoot項(xiàng)目打包成Docker鏡像

    Docker可以讓開發(fā)者打包他們的應(yīng)用以及依賴包到一個(gè)輕量級(jí)、可移植的容器中,然后發(fā)布到任何流行的 Linux 機(jī)器上,也可以實(shí)現(xiàn)虛擬化,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目打包成Docker鏡像的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • SpringBoot環(huán)境下junit單元測(cè)試速度優(yōu)化方式

    SpringBoot環(huán)境下junit單元測(cè)試速度優(yōu)化方式

    這篇文章主要介紹了SpringBoot環(huán)境下junit單元測(cè)試速度優(yōu)化方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動(dòng)填充案例講解

    SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動(dòng)填充案例講解

    每一次在Controller層中封裝改動(dòng)數(shù)據(jù)的方法時(shí)都要重新設(shè)置一些共性字段,顯得十分冗余。為了解決此問題也是在項(xiàng)目中第一次利用到線程,總的來說還是讓我眼前一亮,也開闊了視野,對(duì)以后的開發(fā)具有深遠(yuǎn)的意義
    2022-10-10

最新評(píng)論

鄂托克旗| 河津市| 句容市| 邯郸县| 永善县| 阿巴嘎旗| 荣成市| 合川市| 高州市| 临沂市| 扬州市| 安阳市| 陕西省| 衡阳市| 普兰店市| 营口市| 保定市| 临泽县| 罗山县| 简阳市| 大石桥市| 赣榆县| 合山市| 盐城市| 沛县| 三门县| 桐梓县| 繁峙县| 永城市| 桂林市| 温州市| 都昌县| 陇西县| 名山县| 永平县| 皮山县| 即墨市| 昌平区| 长岛县| 北川| 延长县|