【问题标题】:class newInstance propagates checked and unchecked exceptions - is it true?类 newInstance 传播已检查和未检查的异常 - 是真的吗?
【发布时间】:2015-05-02 19:03:30
【问题描述】:

据此:https://docs.oracle.com/javase/tutorial/reflect/member/ctorTrouble.html

Class.newInstance() 抛出意外异常 ConstructorTroubleToo 示例显示了 Class.newInstance() 中无法解决的问题。也就是说,它会传播构造函数抛出的任何异常(已检查或未检查)。

这种情况是反射所独有的。通常,不可能编写忽略已检查异常的代码,因为它不会编译。可以使用 Constructor.newInstance() 而不是 Class.newInstance() 来包装构造函数抛出的任何异常。

下面我有一个代码,它没有捕获任何未经检查的 (RuntimeEcxeption/Error) 异常,我对它们进行了注释并编译。那么这种传播在哪里呢?我编写的代码忽略了被告知不可能的未经检查的异常。请解释一下关于上述引用的 Class.newInstance() 有什么问题?

try {
        Class<?> c = Class.forName("ConstructorTroubleToo");
        // Method propagetes any exception thrown by the constructor
        // (including checked exceptions).

        Object o = c.newInstance();

        // production code should handle these exceptions more gracefully
    } catch (ClassNotFoundException | 
            InstantiationException | 
            IllegalAccessException  x
           /*IllegalArgumentException | 
             SecurityException x*/ ) {
        x.printStackTrace();
    }

【问题讨论】:

    标签: java


    【解决方案1】:

    Class.newInstance() 确实传播了它调用的构造函数抛出的任何异常,检查或未检查。通过调用sun.misc.Unsafe.throwException() 方法,传播是以一种相当不正当的方式完成的。这个方法,它只是抛出你给它的异常,是sun.misc.Unsafe 类的一部分,它包含Java 中低级“不安全”操作的各种方法。 This article 总结了其中一些方法。

    在 JDK 中有一个 src.zip 文件,其中包含一些标准 Java 平台类的源代码。特别是,您将在此 zip 中的文件 java/lang/Class.java 中找到 java.lang.Class 的源代码。打开这个文件并查看其中的newInstance() 方法。你会发现Class.newInstance() 实际上使用Constructor.newInstance() 来创建对象。

    如果构造函数抛出异常,通过Constructor.newInstance() 调用构造函数会将“真实”异常包装在 InvocationTargetException 中并抛出该异常。但是,Class.newInstance() 并未声明为抛出 InvocationTargetException,因此它会捕获 Constructor.newInstance() 抛出的 InvocationTargetException,并使用 Unsafe.throwException() 抛出“真正的”异常。

    Class.newInstance() 没有声明为抛出 InvocationTargetException 的原因是因为这个方法可以追溯到 Java 1.0。 InvocationTargetException 和反射 API 的其余部分在 1.1 版中被引入 Java。在使用 Java 1.1 编译 Java 1.0 代码时,将 throws InvocationTargetException 添加到 Class.newInstance() 会破坏向后兼容性。

    【讨论】:

      【解决方案2】:

      传播意味着异常只会消失,不像反射方法通常行为那样包装到 InvocationTargetException 中。

      实际上,编写忽略已检查异常的代码并非不可能。考虑here的第一个案例:

      public class Test {
          // No throws clause here
          public static void main(String[] args) {
              doThrow(new SQLException());
          }
      
          static void doThrow(Exception e) {
              Test.<RuntimeException> doThrow0(e);
          }
      
          @SuppressWarnings("unchecked")
          static <E extends Exception> 
          void doThrow0(Exception e) throws E {
              throw (E) e;
          }
      }
      

      JVM 不区分已检查和未检查的异常,并且有一些方法可以欺骗 Java 编译器。

      【讨论】:

      • 不错的技巧,但提到引号,为什么他们说 class.newInstance() “传播构造函数抛出的任何异常 - 已检查或未检查 - ”是“一个无法解决的问题”?这正是其他方法的工作原理,如果它们抛出 RuntimeException 我们不捕获它,如果它是 checkedException 我们捕获它,那有什么问题?他们希望它如何工作?
      • 我想我现在看到了,在一些构造函数中我抛出了新的异常();通常我不会在没有 try+catch 的情况下用这样的构造函数编写新类,class.newInstance 隐藏了这个异常,我什至不能抓住它,constructor.newInstance 包裹在 InvocationTargetException 中,所以它比 class.newInstace 更好,因为我可以控制这种情况。是这样还是有更多?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多