Java 当构造函数可能抛出已检查异常时,如何实现单例

Java 当构造函数可能抛出已检查异常时,如何实现单例,java,exception,constructor,singleton,Java,Exception,Constructor,Singleton,我有一个名为classexception public class Class1Exception extends Exception { ... } 我有一个名为Class1的类,其构造函数可能会抛出classexception public class Class1 { public Class1() throws Class1Exception { ... } ... } 现在我想为类Class1实现线程安全的单例模式。通常,我通过将构造函数设为私有

我有一个名为
classexception

public class Class1Exception extends Exception {
   ...
}
我有一个名为
Class1
的类,其构造函数可能会抛出
classexception

public class Class1 {
   public Class1() throws Class1Exception {
      ...
   }
   ...
}
现在我想为类
Class1
实现线程安全的单例模式。通常,我通过将构造函数设为私有来实现Singleton,创建一个与class类型相同的静态最终变量,该变量也会调用构造函数,然后使用getInstance方法来访问它:

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

   private Class1() throws Class1Exception {
      ...
   }

   public static Class1 getInstance() throws Class1Exception {
      return INSTANCE;
   }
}
问题是这个解决方案在这种情况下不起作用,因为构造函数正在抛出一个已检查的异常。将选中的异常更改为未选中不是一个选项。解决办法是什么

只是澄清一下:类
Class1
无法自行处理
classexception
。此异常将由调用
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;
}

您有两个选择:

  • 在静态块中初始化实例,并在try..catch中包装构造函数调用

    静止的{ //在这里初始化您的实例 }

  • 仅在调用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(§8.1.3)中的assert语句(§14.10)

    我认为这样更容易管理,不?

    一个解决方案是采用两阶段施工:

  • 创建实例
  • 继续进行可能会造成严重后果的施工
  • 调用方在第二次调用Instance()之前(比如在
    finally
    块中)处理任何异常。由于类对象是在阶段1的早期成功创建的,因此此后续调用现在能够返回有效实例。当然,第二阶段的构造还没有完成,但是调用方通过异常机制知道这一点

    示例C#(soz:)代码:


    谢谢但是我希望
    Class1
    将其构造函数中抛出的任何异常作为
    classexception
    传播给
    Class1
    的调用方。
    Class1
    的调用者将捕获
    classexception
    。谢谢。但是我希望
    Class1
    将其构造函数中抛出的任何异常作为
    classexception
    传播给
    Class1
    的调用方。
    Class1
    的调用者将捕获
    classexception
    。谢谢。但是在我的例子中,
    Class1
    必须将其构造函数或任何方法中抛出的任何异常作为
    classexception
    传播给
    Class1
    的调用方。
    Class1
    的调用者将捕获
    classexception
    。谢谢。示例中的
    getInstance()
    方法不应捕获任何异常;相反,它应该抛出
    classexception
    。而且它必须是线程安全的,这在你的例子中是不存在的。正如@Radek给出的答案,它基本上是正确的(只需要从
    getInstance()
    方法中取出try-catch块,它应该抛出
    classexception
    )。但问题是他的解决方案不是线程安全的。我可以在
    getInstance()
    方法的方法定义中添加
    synchronized
    ,但这会影响性能。让它线程安全的更好方法是什么?谢谢。但是在我的例子中,
    Class1
    必须将其构造函数或任何方法中抛出的任何异常作为
    classexception
    传播给
    Class1
    的调用方。
    Class1
    的调用者将捕获
    classexception
    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();
            }
        }
    }