在玩Java(特别是v9)时,我发现了这种情况:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

interface A {
    static A staticMethod() {
        try {
            Method method = A.class.getDeclaredMethods()[0];
            return (A) method.invoke(null);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

public class Test {
    public static void main(String[] args) {
        A.staticMethod();
    }
}
该程序流应导致StackOverflow错误,但是,我得到了NoClassDefFoundError
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 880
Exception in thread "main"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "main"
(根据Javadoc)

这是一个奇怪的错误消息,是错误吗?
更新:错误报告ID:9052375
从命令行执行并显示预期的错误:
问题是catch中使用的异常。
java - 为什么会出现NoClassDefFoundError异常而不是StackOverflow错误?-LMLPHP

最佳答案

这不是错误,并且也与接口(interface)中的静态方法无关。
java.lang.instrument ASSERTION FAILED消息也不相关,只是从IDE运行代码的产物。从命令行运行相同的类只会导致Exception in thread "main"

让我们简化您的示例

public class Test {
    public static void main( String[] args ) throws Exception {
        recursive();
    }

    public static void recursive() throws Exception {
        try {
            Test.class
                    .getDeclaredMethod( "recursive" )
                    .invoke( null );
        } catch ( InvocationTargetException e ) {
            e.printStackTrace();
        }
    }
}

到底是怎么回事:
  • 递归方法会导致StackOverflowError,如预期的那样。
  • StackOverflowError包装在InvocationTargetException中,从最深的嵌套调用method.invoke()抛出。
  • 会立即捕获InvocationTargetException,并且JVM尝试执行printStackTrace(),但为此必须加载一些类。但是请记住,此时堆栈已耗尽,所有非平凡的方法都会再次命中StackOverflowError,这恰恰是在类加载器尝试加载打印堆栈跟踪所需的某些类时在类加载器内部发生的情况。类加载器确实找到了该类,但是未能加载和初始化它,并且将其报告为NoClassDefFoundError

  • 以下代码将证明InvocationTargetException确实包装了StackOverflowError:
    public class Test {
        public static void main( String[] args ) throws Exception {
            recursive();
        }
    
        public static void recursive() throws Exception {
            try {
                Test.class
                        .getDeclaredMethod( "recursive" )
                        .invoke( null );
            } catch ( InvocationTargetException e ) {
                System.out.println(e);
                System.out.println(e.getTargetException());
            }
        }
    }
    

    并且下面的代码将证明,如果已经加载了执行printStackTrace()所需的类,则该代码将按预期方式工作(打印由InvocationTargetException引起的StackOverflowError的堆栈跟踪:
    public class Test {
        public static void main( String[] args ) throws Exception {
            new Exception().printStackTrace(); // initialize all required classes
            recursive();
        }
    
        public static void recursive() throws Exception {
            try {
                Test.class
                        .getDeclaredMethod( "recursive" )
                        .invoke( null );
            } catch ( InvocationTargetException e ) {
                e.printStackTrace();
            }
        }
    }
    

    开放的问题是,为什么反射API完全处理StackOverflowError,而不是简单地终止整个错误调用链。

    07-24 09:50
    查看更多