<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          好心封裝工具類,結(jié)果被領(lǐng)導(dǎo)怒懟:“會(huì)不會(huì)寫代碼!”

          共 8055字,需瀏覽 17分鐘

           ·

          2021-04-27 14:05


          先上腦圖,不理解的可以看下面文章!
          身為程序員,你可能沒有系統(tǒng)的學(xué)習(xí)過設(shè)計(jì)模式,但是你一定知道單例模式,因?yàn)樗鄬?duì)簡(jiǎn)單,而且最常被大家所用到。既然大家都用到過,也都知道為什么我還要單獨(dú)列出一篇文章來寫呢?

          因?yàn)榻^大部分開發(fā)者平時(shí)對(duì)單例模式的認(rèn)識(shí),可能僅僅停留在“會(huì)用”的階段。
          為什么會(huì)有這個(gè)模式?為什么要用這個(gè)模式?在哪里用單例模式最合適?亂用了會(huì)有什么負(fù)面影響?

          這些可能大多數(shù)人都一知半解。今天就讓我們大家一起來扒光單例模式的外衣,有深度的認(rèn)識(shí)一下單例模式。


          通過這篇文章你能學(xué)到什么


          (建議你可以帶著問題去學(xué)習(xí))
          1. 單例模式的定義

          2. 單例模式在Android源碼中的應(yīng)用

          3. 單例模式的九種寫法以及優(yōu)劣對(duì)比

          4. 單例模式的使用場(chǎng)景

          5. 單例模式存在的缺點(diǎn)

          接下來我們就一起進(jìn)入今天的學(xué)習(xí)了

          單例模式的定義


          在學(xué)單例模式之前,我想大家都會(huì)自己?jiǎn)栕约海?/span>“單例模式存在的意義是什么?我們?yōu)槭裁匆脝卫J剑俊?/span>
          眾所周知,在古代封建社會(huì),一個(gè)國(guó)家都只有一個(gè)國(guó)王或者叫皇帝。我們?cè)谶@個(gè)國(guó)家的任何一個(gè)地方,只要提起國(guó)王,大家都知道他是誰。因?yàn)閲?guó)王是唯一的。其實(shí)這個(gè)就是單例模式的核心思想:保證對(duì)象的唯一性。
          單例模式(Singleton Pattern):確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例,這個(gè)類稱為單例類,它提供全局訪問的方法。 單例模式是一種對(duì)象創(chuàng)建型模式。
          從其定義我們可以看出來單例模式存在三個(gè)要點(diǎn):
          1、實(shí)例唯一性
          2、自行創(chuàng)建
          3、全局訪問

          知道了單例什么,那么我們要如何設(shè)計(jì)單例呢?
          如何設(shè)計(jì)一個(gè)優(yōu)秀的單例模式其實(shí)也是圍繞著這三點(diǎn)來的。

          說了這么多了,還不知道單例模式到底啥樣呢?接下來我們一起來著手設(shè)計(jì)這個(gè)“國(guó)王”的單例類。我們先看一下單例模式的類圖:


          單例模式的類圖看起來很簡(jiǎn)單,一個(gè)私有的當(dāng)前類型的成員變量,一個(gè)私有的構(gòu)造方法,一個(gè) getInstance 方法,創(chuàng)建對(duì)象不再通過new 而通過 getInstance 讓該類自行創(chuàng)建。相信我們大多數(shù)人使用的單例模式都是這種,因?yàn)樘?jiǎn)單了。但是單例模式的寫法可不止這一種。接下來我們一起來看一下單例模式的九種寫法。

          單例模式的九種寫法


          一、餓漢式(靜態(tài)常量) 
          /**
          * 餓漢式(靜態(tài)常量)
          */
          class King {
          private static final King kingInstance = new King();

          static King getInstance() {
          return kingInstance;
          }

          private King() {
          }
          }
          • 優(yōu)點(diǎn):這種寫法比較簡(jiǎn)單,就是在類裝載的時(shí)候就完成實(shí)例化。避免了線程同步問題。

          • 缺點(diǎn):在類裝載的時(shí)候就完成實(shí)例化,沒有達(dá)到Lazy Loading的效果。如果從始至終從未使用過這個(gè)實(shí)例,則會(huì)造成內(nèi)存的浪費(fèi)。

          二、餓漢式(靜態(tài)代碼塊)
          /**
          * 餓漢式(靜態(tài)代碼塊)
          */
          class King {
          private static King kingInstance;

          static {
          kingInstance = new King();
          }

          private King() {
          }

          public static King getKingInstance() {
          return kingInstance;
          }
          }
          • 優(yōu)點(diǎn):這種寫法比較簡(jiǎn)單,就是在類裝載的時(shí)候就完成實(shí)例化。避免了線程同步問題。

          • 缺點(diǎn):在類裝載的時(shí)候就完成實(shí)例化,沒有達(dá)到Lazy Loading的效果。如果從始至終從未使用過這個(gè)實(shí)例,則會(huì)造成內(nèi)存的浪費(fèi)。

          三、懶漢式(線程不安全)
          /**
          * 懶漢式(線程不安全)
          */
          public class King {
          private static King kingInstance;

          private King() {
          }

          public static King getKingInstance() {
          if (kingInstance == null) {
          kingInstance = new King();
          }
          return kingInstance;
          }
          }
          • 優(yōu)點(diǎn):懶加載,只有使用的時(shí)候才會(huì)加載。

          • 缺點(diǎn):但是只能在單線程下使用。如果在多線程下,一個(gè)線程進(jìn)入了if (singleton == null)判斷語句塊,還未來得及往下執(zhí)行,另一個(gè)線程也通過了這個(gè)判斷語句,這時(shí)便會(huì)產(chǎn)生多個(gè)實(shí)例。所以在多線程環(huán)境下不可使用這種方式。

          四、懶漢式(線程安全)
          /**
          * 懶漢式(線程安全,同步方法)
          */
          public class King {
          private static King kingInstance;

          private King() {
          }

          public static synchronized King getKingInstance() {
          if (kingInstance == null) {
          kingInstance = new King();
          }
          return kingInstance;
          }
          }
          • 優(yōu)點(diǎn):懶加載,只有使用的時(shí)候才會(huì)加載,獲取單例方法加了同步鎖,保障線程安全。

          • 缺點(diǎn):效率太低了,每個(gè)線程在想獲得類的實(shí)例時(shí)候,執(zhí)行g(shù)etInstance()方法都要進(jìn)行同步。

          五、懶漢式(線程安全,同步代碼塊)
          /**
          * 懶漢式(線程安全,同步代碼塊)
          */
          public class King {
          private static King kingInstance;

          private King() {
          }

          public static King getKingInstance() {
          if (kingInstance == null) {
          synchronized (King.class) {
          kingInstance = new King();
          }
          }
          return kingInstance;
          }
          }
          • 優(yōu)點(diǎn):改進(jìn)了第四種效率低的問題。

          • 缺點(diǎn):不能完全保證單例,假如一個(gè)線程進(jìn)入了if (singleton == null)判斷語句塊,還未來得及往下執(zhí)行,另一個(gè)線程也通過了這個(gè)判斷語句,這時(shí)便會(huì)產(chǎn)生多個(gè)實(shí)例。

          六、雙重檢查(DCL)
          /**
          * 雙重檢查(DCL)
          */
          public class King {

          private static volatile King kingInstance;

          private King() {
          }

          public static King getKingInstance() {
          if (kingInstance == null) {
          synchronized (King.class) {
          if (kingInstance == null){
          kingInstance = new King();
          }
          }
          }
          return kingInstance;
          }
          }
          • 優(yōu)點(diǎn):線程安全;延遲加載;效率較高。

          • 缺點(diǎn):JDK < 1.5 的時(shí)候不可用

          • 不可用原因:由于volatile關(guān)鍵字會(huì)屏蔽Java虛擬機(jī)所做的一些代碼優(yōu)化,可能會(huì)導(dǎo)致系統(tǒng)運(yùn)行效率降低,而JDK 1.5 以及之后的版本都修復(fù)了這個(gè)問題。(面試裝逼用,謹(jǐn)記!?。。?/span>


          七、靜態(tài)內(nèi)部類
          /**
          * 靜態(tài)內(nèi)部類
          */
          public class King {

          private King() {
          }

          private static class KingInstance{
          private static final King KINGINSTANCE = new King();
          }

          public static King getInstance(){
          return KingInstance.KINGINSTANCE;
          }
          }
          • 優(yōu)點(diǎn):避免了線程不安全,延遲加載,效率高。

          • 缺點(diǎn):暫無,最推薦使用。

          • 特點(diǎn):這種方式跟餓漢式方式采用的機(jī)制類似,但又有不同。

          • 兩者都是采用了類裝載的機(jī)制來保證初始化實(shí)例時(shí)只有一個(gè)線程。不同的地方在餓漢式方式是只要Singleton類被裝載就會(huì)實(shí)例化,沒有Lazy-Loading的作用,而靜態(tài)內(nèi)部類方式在Singleton類被裝載時(shí)并不會(huì)立即實(shí)例化,而是在需要實(shí)例化時(shí),調(diào)用getInstance方法,才會(huì)裝載SingletonInstance類,從而完成Singleton的實(shí)例化。類的靜態(tài)屬性只會(huì)在第一次加載類的時(shí)候初始化,所以在這里,JVM幫助我們保證了線程的安全性,在類進(jìn)行初始化時(shí),別的線程是無法進(jìn)入的。

          八、枚舉
          /**
          * 枚舉
          */
          public enum King {
          KINGINSTANCE;
          }
          • 優(yōu)點(diǎn):不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對(duì)象。

          • 缺點(diǎn):JDK 1.5之后才能使用。

          九、容器類管理 
          /**
          * 使用容器實(shí)現(xiàn)單例模式(可以用于管理單例,有興趣的可以嘗試一下)
          * */
          class InstanceManager {
          private static Map<String, Object> objectMap = new HashMap<>();
          private InstanceManager(){}
          public static void registerService(String key,Object instance){
          if (!objectMap.containsKey(key)){
          objectMap.put(key,instance);
          }
          }
          public static Object getService(String key){
          return objectMap.get(key);
          }
          }
          /**
          * 使用方式
          * Dog類就不貼出來了
          * 自己隨便寫個(gè)就行
          * 可以運(yùn)行一下看看 打印的地址是否一致
          */

          class Test {
          public static void main(String[] args) {

          InstanceManager .registerService("dog", new Dog());

          Dog dog = (Dog) InstanceManager .getService("dog");
          Dog dog2 = (Dog) InstanceManager .getService("dog");
          Dog dog3 = (Dog) InstanceManager .getService("dog");
          Dog dog4 = (Dog) InstanceManager .getService("dog");

          System.out.println(dog);
          System.out.println(dog2);
          System.out.println(dog3);
          System.out.println(dog4);
          }
          }
          • 優(yōu)點(diǎn):在程序的初始,將多種單例類型注入到一個(gè)統(tǒng)一的管理類中,在使用時(shí)根據(jù)key獲取對(duì)象對(duì)應(yīng)類型的對(duì)象。這種方式使得我們可以管理多種類型的單例,并且在使用時(shí)可以通過統(tǒng)一的接口進(jìn)行獲取操作, 降低了用戶的使用成本,也對(duì)用戶隱藏了具體實(shí)現(xiàn),降低了耦合度。

          • 缺點(diǎn):不常用,有些麻煩

          九種寫法的優(yōu)劣對(duì)比

          名稱優(yōu)點(diǎn)缺點(diǎn)是否推薦
          餓漢式(靜態(tài)常量)寫法簡(jiǎn)單,類裝載時(shí)完成實(shí)例化。避免了線程同步急切實(shí)例化, 容易內(nèi)存泄漏可用
          餓漢式(靜態(tài)代碼塊)同上同上可用
          懶漢式(線程不安全)懶加載只能在單線程下使用多線程不可用
          懶漢式(線程安全,同步方法)懶加載,方法同步鎖效率低不推薦用
          懶漢式(線程安全,同步代碼塊)同上,同時(shí)改變效率低問題不能完全保證單例不推薦用
          雙重檢查(DCL)①線程安全;延遲加載;效率較高JDK < 1.5 的時(shí)候不可用JDK >1.5 推薦用
          靜態(tài)內(nèi)部類②線程安全,延遲加載,效率高。暫無發(fā)現(xiàn)墻裂推薦
          枚舉寫法簡(jiǎn)單,防止反序列化JDK 1.5之后才能使用JDK >1.5 推薦用
          容器實(shí)現(xiàn)可用管理多個(gè)單例對(duì)象不常用,多創(chuàng)建了一個(gè)Map可用

          ①:不可用原因:由于volatile關(guān)鍵字會(huì)屏蔽Java虛擬機(jī)所做的一些代碼優(yōu)化,可能會(huì)導(dǎo)致系統(tǒng)運(yùn)行效率降低,而JDK 1.5 以及之后的版本都修復(fù)了這個(gè)問題。(面試裝逼用,謹(jǐn)記?。。。?/span>

          ②:這種方式跟餓漢式方式采用的機(jī)制類似,但又有不同。兩者都是采用了類裝載的機(jī)制來保證初始化實(shí)例時(shí)只有一個(gè)線程。不同的地方在餓漢式方式是只要Singleton類被裝載就會(huì)實(shí)例化,沒有Lazy-Loading的作用, 而靜態(tài)內(nèi)部類方式在Singleton類被裝載時(shí)并不會(huì)立即實(shí)例化,而是在需要實(shí)例化時(shí),調(diào)用getInstance方法,才會(huì)裝載SingletonInstance類,從而完成Singleton的實(shí)例化。類的靜態(tài)屬性只會(huì)在第一次加載類的時(shí)候初始化,所以在這里,JVM幫助我們保證了線程的安全性,在類進(jìn)行初始化時(shí),別的線程是無法進(jìn)入的。

          單例模式在Android源碼中的應(yīng)用


          在我們每天接觸的Android源碼中其實(shí)也有很多地方用到了單例模式:

          1、EventBus中獲取實(shí)例:

          private static volatile EventBus defaultInstance;
          public static EventBus getDefault() {
          if (defaultInstance == null) {
          synchronized (EventBus.class) {
          if (defaultInstance == null) {
          defaultInstance = new EventBus();
          }
          }
          }

          return defaultInstance;
          }
          可以看到,EventBus采用的是雙重檢查(DCL)的方式實(shí)現(xiàn)的單例模式。

          2、InputMethodManager獲取實(shí)例

          static InputMethodManager sInstance;
          public static InputMethodManager getInstance() {
          synchronized (InputMethodManager.class) {
          if (sInstance == null) {
          IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
          IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
          sInstance = new InputMethodManager(service, Looper.getMainLooper());
          }
          return sInstance;
          }
          }
          我們看到,其實(shí)這里是懶漢式(同步代碼塊)方式的改寫,去掉了外部判斷為空,放到了里面。然后通過ServiceManger.getService()方法,通過容器的方式獲取了單例。
          在Android很多系統(tǒng)服務(wù)都是通過容器獲取的單例。

          單例模式在日常開發(fā)中的應(yīng)用場(chǎng)景


          日常開發(fā)中我們也有些場(chǎng)景是需要用到單例模式的,例如:
          1、圖片加載
          2、網(wǎng)絡(luò)請(qǐng)求
          3、工具類封裝
          案例有很多,相信大家也都有用到,我就不列舉了。這里我們?nèi)绾魏侠淼脑陧?xiàng)目中使用單例模式。
          合理的辨析一個(gè)設(shè)計(jì)是否應(yīng)該為單例模式前,大家先問問自己幾個(gè)問題,也是檢驗(yàn)標(biāo)準(zhǔn):
          Quote from 《 Use your singletons wisely 》
          Will every application use this class exactly the same way? (keyword: exactly)
          Will every application ever need only one instance of this class? (keyword: ever & one)
          Should the clients of this class be unaware of the application they are part of?
          每一個(gè)應(yīng)用(組件/模塊)是否以完全一致的方式來使用這個(gè)類?
          每一個(gè)應(yīng)用(組件/模塊)是否真的只需要這個(gè)類的一個(gè)實(shí)例呢?
          對(duì)于這個(gè)類的客戶端類來說,對(duì)他們自己是應(yīng)用中的一部分這件事是否應(yīng)該保持毫無察覺的狀態(tài)呢?
          以上3條就是檢驗(yàn)一個(gè)類是否應(yīng)該被設(shè)計(jì)為單例模式的判斷準(zhǔn)則,
          如果我們對(duì)于以上這3條均給出了“是的”的答案,那么這個(gè)類就是可以被設(shè)計(jì)為單例模式了。反之還是不要用的好。

          單例模式的優(yōu)點(diǎn)


          單例模式的優(yōu)點(diǎn)其實(shí)已經(jīng)在定義中提現(xiàn)了:可以減少系統(tǒng)內(nèi)存開支,減少系統(tǒng)性能開銷,避免對(duì)資源的多重占用、同時(shí)操作。

          單例模式的缺點(diǎn)


          任何事物都不是完美的,單例模式也是如此,它也存在以下幾個(gè)缺點(diǎn):
          1、違反了單一責(zé)任鏈原則,測(cè)試?yán)щy
          單例類的職責(zé)過重,在一定程度上違背了“單一職責(zé)原則”。因?yàn)閱卫惣瘸洚?dāng)了工廠角色,提供了工廠方法,同時(shí)又充當(dāng)了產(chǎn)品角色,包含一些業(yè)務(wù)方法,將產(chǎn)品的創(chuàng)建和產(chǎn)品的本身的功能融合到一起。
          2、擴(kuò)展困難
          由于單例模式中沒有抽象層,因此單例類的擴(kuò)展有很大的困難。修改功能必須修改源碼。
          3、共享資源有可能不一致。
          現(xiàn)在很多面向?qū)ο笳Z言(如Java、C#)的運(yùn)行環(huán)境都提供了自動(dòng)垃圾回收的技術(shù),因此,如果實(shí)例化的共享對(duì)象長(zhǎng)時(shí)間不被利用,系統(tǒng)會(huì)認(rèn)為它是垃圾,會(huì)自動(dòng)銷毀并回收資源,下次利用時(shí)又將重新實(shí)例化,這將導(dǎo)致共享的單例對(duì)象狀態(tài)的丟失。

          總結(jié)


          今天我們通過文章學(xué)習(xí)了第一個(gè)設(shè)計(jì)模式,了解了他的設(shè)計(jì)理念,學(xué)會(huì)了他的九種寫法,也認(rèn)識(shí)了他的優(yōu)缺點(diǎn)。相信大家已經(jīng)對(duì)單例模式有了一個(gè)全新的認(rèn)識(shí)。(反正我寫完文章才認(rèn)識(shí)到自己原來根本不了解單例模式)。
          最后還是要給大家說一句話:模式是死的,代碼是活的。不要硬套模式。代碼會(huì)告訴你怎么做,你聽就是了。(也是借鑒前輩們的經(jīng)驗(yàn))


          碼個(gè)蛋專屬活動(dòng):打卡入口


          為什么要做這個(gè)活動(dòng)?

          幫你養(yǎng)成好習(xí)慣(第四彈)




          關(guān)注「碼個(gè)蛋」,一起打卡成長(zhǎng)

          關(guān)注后可獲得碼仔專屬表情包


          瀏覽 40
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色色色热热热 | 河北最大AV网站 | AV狼友| 福利网导航 | 91久久艹这里只有精品 |