簡體   English   中英

當構造函數可能引發檢查異常時,如何實現Singleton

[英]How to implement Singleton when the constructor may throw a checked exception

我有一個名為Class1Exception的檢查異常

public class Class1Exception extends Exception {
   ...
}

我有一個名為Class1的類,其構造函數可能會拋出Class1Exception

public class Class1 {
   public Class1() throws Class1Exception {
      ...
   }
   ...
}

現在,我想為類Class1實現線程安全的Singleton模式。 通常,我通過使構造函數私有化來實現Singleton,創建與類相同類型的靜態最終變量,該變量也調用構造函數,然后具有getInstance方法來訪問它:

public class Class1 {
   private static final Class1 INSTANCE = new Class1();

   private Class1() throws Class1Exception {
      ...
   }

   public static Class1 getInstance() throws Class1Exception {
      return INSTANCE;
   }
}

問題是此解決方案在這種情況下不起作用,因為構造函數將拋出一個已檢查的異常。 不能將已檢查的異常更改為未檢查。 有什么解決方案?

需要澄清的是:類Class1不能Class1Exception處理Class1Exception 該異常應由調用Class1任何代碼處理。

謝謝

您可以使用靜態初始化部分:

private static Class1 INSTANCE; 
static {
   try { 
      INSTANCE = new Class1();
   } catch (Exception e) {
      // handle exception catching
   }
}

UPD或您可以將初始化包裝到新方法中:

private static final Class1 INSTANCE = createInstance();

private static Class1 createInstance() {
    try {
       return new Class1();
    } catch (Exception e) {
        // handle exception catching
    }
    return null;
}

您有兩種選擇:

  1. 在靜態塊中初始化您的實例,並將構造函數調用包裝在try..catch中。

    靜態{//在此處初始化實例}

  2. 僅在調用getInstance()時懶惰地調用構造函數,並在每次調用時處理異常。

靜態初始化程序塊的一種替代方法是私有靜態方法:

private static final Class1 INSTANCE = initSingleton(); 

private static Class1 initSingleton() {
   Class1 result;
   try { 
      result = new Class1();
   } catch (Exception e) {
      // handle exception catching
   }
   return result;
}

只需使用靜態初始化程序,同時使用ExceptionInInitializerError拋出異常即可。

你可以在這里查找: 點擊我

包含以下信息:

表示在靜態初始化程序中發生了意外的異常。 拋出ExceptionInInitializerError表示在評估靜態初始化程序或靜態變量的初始化程序期間發生了異常。

延遲初始化呢?

public class Class1 {
private static Class1 INSTANCE;

private Class1() throws Class1Exception {
    ...
}

public static Class1 getInstance() {
    if(INSTANCE == null) {
        try {
            INSTANCE = new Class1();

        } catch(Exception1 exc) {
            ...
        }
    }
    return INSTANCE;
}

我認為這是一種反模式。 考慮使用資源的惰性初始化,以便在嘗試調用單例方法之一時引發異常,而不是在構造函數中不清楚何時何處引發異常。 從Java語言規范中的靜態初始化開始:

對於靜態初始化(第12.4節):

類或接口類型T將在以下任何一種首次出現之前立即初始化:

  • T是一個類,並創建T的實例。
  • T是一個類,並調用T聲明的靜態方法。
  • 分配由T聲明的靜態字段。
  • 使用由T聲明的靜態字段,並且該字段不是常量變量(第4.12.4節)。
  • T是頂級類(第7.6節),並執行詞法嵌套在T中的斷言(第14.10節)(第8.1.3節)。

我認為這樣會更易於管理,不是嗎?

一種解決方案是采用兩階段構建:

  1. 創建實例
  2. 繼續進行可能會引發的施工

調用者在第二次調用Instance()之前處理任何異常(例如在finally塊中)。 因為類對象是在階段1之前成功創建的,所以此后續調用現在可以返回有效實例。 當然,第二階段的構建還沒有完成,但是調用者確實通過異常機制知道了這一點。

示例C#(soz :)代碼:

private static volatile Class1 _instance = null;
private static readonly object _sync = new object();

public static Class1 Instance()
{
    if (_instance == null)
    {
        lock (_sync)
        {
            if (_instance == null)
            {
                _instance = new Class1();

                _instance.Init(); // can throw
            }
        }
    }

    return _instance;
}

private class Caller
{
    Class1 _class1;

    private void LoadClass1()
    {
        try
        {
            _class1 = Class1.Instance();
        }
        catch (Exception ex)
        {
            // handle exception
        }
        finally
        {
            _class1 = Class1.Instance();
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM