【问题标题】:How to catch exception thrown in static initializer block如何捕获静态初始化程序块中引发的异常
【发布时间】:2012-09-06 18:00:05
【问题描述】:

我写了以下代码:

    static {
        /* Attempts to load JDBC driver */
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new DBConfigurationException("JDBC Driver not found.", e);
        }
        /* Attempts to load configuration */
        conf = loadConfiguration(); //this may throw some subclasses of RuntimeException
    }

因为我希望 JDBC 驱动程序和配置只加载一次。

我想在启动时做这样的事情(我会尽可能简化):

public static void main(String[] args) {
    try {
        // load the class that contains the code above
    } catch (DBConfigurationException e) {
        // display proper error message using JOptionPane, then quit
    } catch (MissingConfigurationException e) {
        // display proper error message using JOptionPane
        // show a JDialog and allow user to input and store a configuration
    } catch (InvalidConfigurationException e) {
        // display proper error message using JOptionPane
        // show a JDialog and allow user to input and store a configuration
    }

    /* if everything it's ok */
    // do some other checks in order to decide which JFrame display first.
}

现在的问题是,如果发生异常,JVM 将抛出 ExceptionInInitializerError 广告并且不会构造该对象。 可能我仍然理解出了什么问题,抓住ExceptionInInitializerError(即使这听起来对我来说是错误的)并检查其原因(我仍然没有尝试这样做,但我认为这是可能的)。

我需要那个对象,因为如果异常是可恢复的(例如 MissingConfigurationException),程序将不会退出并且需要那个对象。

我应该避免使用静态初始化器吗? 我可以这样做:

private static final Configuration conf = null;

Constructor() {
    if (conf == null) {
        /* Attempts to load JDBC driver */
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new DBConfigurationException("JDBC Driver not found.", e);
        }
        /* Attempts to load configuration */
        conf = loadConfiguration();
    }
}

但即使这对我来说听起来也不正确:只有在第一次尝试使用时才会抛出异常(我知道这将在启动时进行,因为我必须进行检查),那是在加载类时。所以理论上第一种方法会更正确。 :\

我该怎么办?哪种方式更正确?

问题在于具有静态初始化器的类需要驱动程序和配置,所以在它们都可用之前不应该使用。 :\

【问题讨论】:

  • 根据定义,static{} 块抛出的异常会生成 ExceptionInInitializer 并将类标记为不可用。这是它应该的方式(加上或减去一个小增量)。

标签: java exception static initialization classloader


【解决方案1】:

为什么不在main() 方法或main() 方法调用的东西中检查这些条件?应用程序的入口点只能输入一次。一个简单的方法比静态初始化器和类加载器技巧要好得多。

public static void main(String[] args) {
    if (!requirementsMet()) {
         System.exit(1);
    }
    //proceed with app...
}

private static boolean requirementsMet() {
     // check if DB driver can be loaded, and other non-recoverable errors
}

【讨论】:

  • +1 表示“诡计”这个词。 ;) 此外,应该或多或少地避免使用静态初始化程序,因为如果它们失败会出现奇怪的错误,例如 NoClassDefFound 错误。
  • 嗯,从不需要欺骗的角度来看,这很好,但它并没有以模块化的方式隔离需求。取决于应用程序的大小和你在写什么,我猜。
  • @TonyK。这取决于应用程序。如果它需要数据库访问才能运行,它应该在main 线程中失败并终止。如果没有,那么它应该在其他地方失败,我同意。
  • 我同意 Brain 的评论。如果它需要数据库访问才能运行,它应该会失败,否则将代码移动到其他地方。
  • @Brian。我的评论是关于将 requirementsMet() 放在哪里......如果数据库是某种可能使用不同驱动程序的可插入模块,那么围绕它的抽象是理想的。从 main() 调用它很好。
【解决方案2】:

您可以使用单例类。例如

DBConfigProvider {
    private Configuration conf = null
    private DBConfigProvider() {
      /* Attempts to load JDBC driver */
      try {
          Class.forName("com.mysql.jdbc.Driver");
      } catch (ClassNotFoundException e) {
          throw new DBConfigurationException("JDBC Driver not found.", e);
      }
      /* Attempts to load configuration */
      conf = loadConfiguration(); //this may throw some subclasses of RuntimeException
    }

    private static DBConfigProvider instance = null;

    public static Configuration getConf() {
         if (instance == null) {
             instance = new DBConfigurationProvider();
         }

         return instance.conf;
    }
}

【讨论】:

  • 这只是将问题转移到getConf(),它仍然可以在第一次调用时抛出异常;顺便说一句,我应该如何使用它?如果我没记错的话,“private static final Configuration conf = DBConfigProvider.getConf()”行仍然会抛出ExceptionInInitializerError
  • 为什么需要静态变量进行配置?我期待您在实际需要时调用 DBConfigProvider.getConf()。使其成为单例的想法是,您保留一个配置实例,同时“延迟”初始化它(在需要时)。
  • 因为作为负责连接数据库的类,它应该包含必要的详细信息(驱动程序和配置)并且能够在不假设其自身使用情况的情况下工作(例如,在调用之前加载驱动程序和设置配置) getConnection() 方法)。其他类应该在不知道这些细节的情况下调用它的方法。 (抱歉英语不好,我今天有阅读障碍:D)
猜你喜欢
  • 1970-01-01
  • 2018-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多