<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # Java 單例模式介紹 > 原文: [https://howtodoinjava.com/design-patterns/creational/singleton-design-pattern-in-java/](https://howtodoinjava.com/design-patterns/creational/singleton-design-pattern-in-java/) **單例模式**是一種設計解決方案,其中應用希望在所有可能的情況下都具有一個且只有一個任何類的實例,而沒有任何特殊情況。 在 Java 社區中,關于使任何類單例化的可能方法已經爭論了很長時間。 不過,您會發現人們對您提供的任何解決方案都不滿意。 它們也不能被否決。 在這篇文章中,我們將討論一些好的方法,并將盡我們最大的努力。 ```java Table of Contents: 1\. Singleton with eager initialization 2\. Singleton with lazy initialization 3\. Singleton with static block initialization 4\. Singleton with bill pugh solution 5\. Singleton using Enum 6\. Add readResolve() to singleton objects 7\. Add serialVersionUID to singleton objects 8\. Conclusion ``` > 單例術語是從其[數學對應術語](https://en.wikipedia.org/wiki/Singleton_%28mathematics%29 "Singleton in mathematics")中得出的。 如上所述,它希望我們每個上下文只有一個實例。 在 Java 中,每個 JVM 一個實例。 讓我們看一下在 Java 中創建單例對象的可能解決方案。 ## 1.立即初始化的單例 這是一種設計模式,其中的類實例實際上是在實際需要之前創建的。 通常,它是在系統啟動時完成的。 在熱切的初始化單例模式中,無論是否有其他類實際請求其實例,都將創建該單例實例。 ```java public class EagerSingleton { private static volatile EagerSingleton instance = new EagerSingleton(); // private constructor private EagerSingleton() { } public static EagerSingleton getInstance() { return instance; } } ``` 上面的方法很好用,但是有一個缺點。 不論是否在運行時都需要創建實例。 如果此實例不是大對象,并且您可以在不使用它的情況下生存下去,那么這是最好的方法。 讓我們用下一種方法解決上述問題。 ## 2.延遲初始化的單例 在計算機編程中,[延遲初始化](https://en.wikipedia.org/wiki/Lazy_initialization "lazy initilization")是將對象的創建,值的計算或其他昂貴的過程推遲到第一次使用時的策略。 在單例模式中,它將限制實例的創建,直到首次請求該實例為止。 讓我們在代碼中看到這一點: ```java public final class LazySingleton { private static volatile LazySingleton instance = null; // private constructor private LazySingleton() { } public static LazySingleton getInstance() { if (instance == null) { synchronized (LazySingleton.class) { instance = new LazySingleton(); } } return instance; } } ``` 第一次調用時,上述方法將檢查是否已使用`instance`變量創建了實例。 如果沒有實例,即實例為`null`,它將創建一個實例并返回其引用。 如果實例已經創建,它將僅返回實例的引用。 但是,這種方法也有其自身的缺點。 讓我們看看如何。 假設有兩個線程 T1 和 T2。 兩者都來創建實例并檢查是否`“instance==null”`。 現在,兩個線程都將實例變量標識為 null,因此它們都假定必須創建一個實例。 他們依次進入同步塊并創建實例。 最后,我們的應用中有兩個實例。 可以使用[雙檢鎖](https://en.wikipedia.org/wiki/Double_checked_locking_pattern#Usage_in_Java "double check locking")解決此錯誤。 該原理告訴我們在同步塊中再次重新檢查實例變量,如下所示: ```java public class LazySingleton { private static volatile LazySingleton instance = null; // private constructor private LazySingleton() { } public static LazySingleton getInstance() { if (instance == null) { synchronized (LazySingleton.class) { // Double check if (instance == null) { instance = new LazySingleton(); } } } return instance; } } ``` 上面的代碼是單例模式的正確實現。 請確保對實例變量使用[**`volatile`**](https://en.wikipedia.org/wiki/Volatile_variable#In_Java)關鍵字,否則您可能會遇到亂碼的錯誤情況,在實例實際構造對象之前返回實例的引用,即 JVM 僅分配了內存,而構造器代碼仍未執行。 在這種情況下,引用未初始化對象的其他線程可能會引發空指針異常,甚至可能使整個應用崩潰。 ## 3.使用靜態塊初始化的單例 如果您對類的加載順序有所了解,則可以使用以下事實:即使在調用構造器之前,也要在類的加載期間執行靜態塊。 我們可以在單例模式中使用此功能,如下所示: ```java public class StaticBlockSingleton { private static final StaticBlockSingleton INSTANCE; static { try { INSTANCE = new StaticBlockSingleton(); } catch (Exception e) { throw new RuntimeException("Uffff, i was not expecting this!", e); } } public static StaticBlockSingleton getInstance() { return INSTANCE; } private StaticBlockSingleton() { // ... } } ``` 上面的代碼有一個缺點。 假設一個類中有 5 個靜態字段,并且應用代碼只需要訪問 2 或 3,那么根本不需要創建實例。 因此,如果我們使用此靜態初始化,盡管沒有要求,我們將創建一個實例。 下一節將克服此問題。 ## 4\. 單例與 Bill Pugh 解決方案 Bill Pugh 是 [java 內存模型](https://en.wikipedia.org/wiki/Java_Memory_Model "java memory model")更改背后的主要力量。 他的原則“[按需初始化持有人慣例](https://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom "bill pugh principle")”也使用了靜態塊的想法,但方式有所不同。 建議使用靜態內部類。 ```java public class BillPughSingleton { private BillPughSingleton() { } private static class LazyHolder { private static final BillPughSingleton INSTANCE = new BillPughSingleton(); } public static BillPughSingleton getInstance() { return LazyHolder.INSTANCE; } } ``` 如您所見,在需要實例之前,`LazyHolder`類直到需要時才會初始化,您仍然可以使用`BillPughSingleton`類的其他靜態成員。 ***這是解決方案,我建議使用。 我在所有項目中都使用了它。*** ## 5.使用枚舉的單例 這種類型的實現使用枚舉。 [枚舉](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html "enum in java")(如 java 文檔中所寫)為線程安全提供了隱式支持,并且僅保證了一個實例。 **Java 枚舉單例**也是使單例花費最少的好方法。 ```java public enum EnumSingleton { INSTANCE; public void someMethod(String param) { // some class member } } ``` ## 6.將`readResolve()`添加到單例對象 到目前為止,您必須已經決定如何實現單例。 現在,讓我們看看即使在面試中也可能出現的其他問題。 假設您的應用是分布式的,并且經常將對象序列化到文件系統中,直到以后需要時才讀取它們。 請注意,反序列化總是創建一個新實例。 我們來看一個例子: 我們的單例類是: ```java public class DemoSingleton implements Serializable { private volatile static DemoSingleton instance = null; public static DemoSingleton getInstance() { if (instance == null) { instance = new DemoSingleton(); } return instance; } private int i = 10; public int getI() { return i; } public void setI(int i) { this.i = i; } } ``` 讓我們對該類進行序列化,并在進行一些更改后對其進行反序列化: ```java public class SerializationTest { static DemoSingleton instanceOne = DemoSingleton.getInstance(); public static void main(String[] args) { try { // Serialize to a file ObjectOutput out = new ObjectOutputStream(new FileOutputStream( "filename.ser")); out.writeObject(instanceOne); out.close(); instanceOne.setI(20); // Serialize to a file ObjectInput in = new ObjectInputStream(new FileInputStream( "filename.ser")); DemoSingleton instanceTwo = (DemoSingleton) in.readObject(); in.close(); System.out.println(instanceOne.getI()); System.out.println(instanceTwo.getI()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } Output: 20 10 ``` 不幸的是,兩個變量的變量“`i`”的值不同。 顯然,該類有兩個實例。 因此,我們再次遇到應用中多個實例的相同問題。 要解決此問題,我們需要在`DemoSingleton`類中包含`readResolve()`方法。 當您反序列化對象時,將調用此方法。 在此方法的內部,必須返回現有實例以確保整個實例應用范圍。 ```java public class DemoSingleton implements Serializable { private volatile static DemoSingleton instance = null; public static DemoSingleton getInstance() { if (instance == null) { instance = new DemoSingleton(); } return instance; } protected Object readResolve() { return instance; } private int i = 10; public int getI() { return i; } public void setI(int i) { this.i = i; } } ``` 現在,當您執行類`SerializationTest`時,它將為您提供正確的輸出。 ```java 20 20 ``` ## 7.將`serialVersionUId`添加到單例對象 到目前為止,一切都很好。 到目前為止,我們已經解決了同步和序列化這兩個問題。 現在,我們距正確而完整的實現僅一步之遙。 唯一缺少的部分是序列號。 在您的類結構在序列化和反序列化之間更改的情況下,這是必需的。 更改的類結構將導致 JVM 在反序列化過程中給出異常。 ```java java.io.InvalidClassException: singleton.DemoSingleton; local class incompatible: stream classdesc serialVersionUID = 5026910492258526905, local class serialVersionUID = 3597984220566440782 at java.io.ObjectStreamClass.initNonProxy(Unknown Source) at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source) at java.io.ObjectInputStream.readClassDesc(Unknown Source) at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) at java.io.ObjectInputStream.readObject0(Unknown Source) at java.io.ObjectInputStream.readObject(Unknown Source) at singleton.SerializationTest.main(SerializationTest.java:24) ``` 僅通過向類添加唯一的串行版本 ID 即可解決此問題。 通過告訴兩個類相同,這將防止編譯器引發異常,并且僅加載可用的實例變量。 ## 8.結論 在討論了許多可能的方法和其他可能的錯誤情況之后,我將向您推薦以下代碼模板,以設計您的單例類,該類應確保在上述所有情況下,整個應用中僅一個類的實例。 ```java public class DemoSingleton implements Serializable { private static final long serialVersionUID = 1L; private DemoSingleton() { // private constructor } private static class DemoSingletonHolder { public static final DemoSingleton INSTANCE = new DemoSingleton(); } public static DemoSingleton getInstance() { return DemoSingletonHolder.INSTANCE; } protected Object readResolve() { return getInstance(); } } ``` 我希望這篇文章有足夠的信息來幫助您了解**單例模式**和**單例最佳實踐**的最常用方法。 讓我知道你的想法。 學習愉快! **實時單例示例** – 我只是想添加一些示例,以供進一步研究和在面試中提及: * [`java.awt.Desktop#getDesktop()`](https://docs.oracle.com/javase/6/docs/api/java/awt/Desktop.html#getDesktop%28%29 "Desktop singelton") * [`java.lang.Runtime#getRuntime()`](https://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#getRuntime%28%29 "runtime")
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看