为什么我得到NoClassDefFoundError异常而不是StackOverflow错误?

时间:2018-01-23 01:32:00

标签: java error-handling runtime-error

使用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

  

Class NoClassDefFoundError

     

如果Java虚拟机或ClassLoader实例尝试加载类的定义(作为普通方法调用的一部分或作为使用新表达式创建新实例的一部分)并且没有定义,则抛出可以找到班级

     

在编译当前正在执行的类时存在搜索的类定义,但无法再找到定义

这是一个奇怪的错误消息,它是一个错误吗?

更新: 错误报告ID:9052375

从命令行执行并打印预期的错误: 问题是catch 中使用的例外情况。

enter image description here

1 个答案:

答案 0 :(得分:6)

这不是一个错误,它也与接口中的静态方法无关。

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的堆栈跟踪:< / p>

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,而不是简单地用错误终止整个调用链。