【问题标题】:How to catch exception which happens at initialization of Java class field?java - 如何捕获Java类字段初始化时发生的异常?
【发布时间】:2021-09-22 11:57:11
【问题描述】:

如果在类字段的初始化过程中发生异常,您将如何捕获它?

例如:

class a{
    int a = 1 / 0;
}

这里的异常发生在字段级别。

我知道我可以做到:

class a {
    a() {
        try {
            this.a = 1 / 0;
        } catch (Throwable a) {}
    }

    int a;
}

但出于好奇,是否可以在初始化字段时这样做?

附加信息:我问这个是因为在我的最新项目中,我有一个字段要初始化为对象的新实例,写a = new Object(); 会很酷,但我不能因为构造函数该特定类型的会引发已检查的异常。

【问题讨论】:

    标签: java class exception


    【解决方案1】:

    在初始化字段的时候可以这样做吗?

    你可以定义一个方法:

    class a {
      int a = aValue();
    
      private int aValue() {
        try
        {
          return 1/0;
        }
        catch (Throwable a){
          // ...but now you need to return something, or (re)throw an exception.
        }
      }
    }
    

    或使用实例初始化器:

    class a {
      int a;
    
      {
        try
        {
          this.a=1/0;
        }
        catch (Throwable a){
          // You don't need to do anything here, unless `a` were `final`.
        }
      }
    }
    

    但请注意,实例初始值设定项被内联到构造函数中(或者,至少,任何显式或隐式调用 super(...) 的构造函数,而不是 this(...)),因此这实际上与将其放入构造函数中一样在问题中。

    【讨论】:

      【解决方案2】:

      真的很难抓住那些。因此,强烈建议确保静态初始化器不会抛出任何人可能想要捕获的东西。 (例如,抛出 OutOfMemoryError 很好,不太可能有人想要编写代码来捕获这个并执行替代路径或尝试解决问题)。

      这通常从用方法调用替换静态初始化程序开始。替换:

      static int a; static {a = 1/0; }
      

      与:

      static int a = calculateA();
      
      private static int calculateA() {
          return 1/0;
      }
      

      当然,这只是沿途的一步。将初始化代码(calculateA 方法)移至单独的类,现在您可以自行测试它,甚至不会遇到捕获类静态初始化期间抛出的异常的问题。

      一旦你解决了这个问题,你就可以使用这个“技巧”来解决问题。想象一下,这个类中的 2 个方法需要 a 的值。然后,“推迟”异常:

      public class Example {
          private static final int a;
          private static final Throwable aProblem;
      
          static {
              int a = 0;
              Throwable aProblem = null;
              try {
                  a = calculateA();
              } catch (RuntimeException e) {
                  aProblem = e;
              }
              Example.a = a;
              Example.aProblem = aProblem;
          }
      
          private static int calculateA() { return 1/0; }
      
          public static void apiMethodUsingA1() {
              if (aProblem != null) throw aProblem;
              return a;
          }
      
          public static void apiMethodUsingA2() {
              if (aProblem != null) throw aProblem;
              return a + 5;
          }
      }
      

      如果这些选项都不可用,例如因为 A 不是由您编写且无法更改,那么您必须将 A 类委托为“糟糕的 API / 糟糕的库”,并且当您执行面对这样一个库:解决它,接受您需要编写难以维护/丑陋的代码,如果它真的很糟糕,请编写一个包装器来隔离问题。甚至可以使用反射。

      这是将异常隔离到代码块中的一种有保证的方法:

      package com.foo;
      
      class Example {
          static int a = 1/0;
      }
      
      class Main {
        public static void main(String[] args) throws Exception {
          try {
            Class<?> c = Class.forName("com.foo.Example");
          } catch (ExceptionInInitializerError e) {
            System.err.println("Hey I caught it");
            Throwable actualException = e.getCause();
            // do something with it here
            actualException.printStackTrace(); // not this - this is for debugging only!
          }
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-23
        • 2013-04-29
        相关资源
        最近更新 更多