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

标签 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

Thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.

The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found.

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

更新:错误报告 ID:9052375

从命令行执行并打印出预期的错误: 问题是,catch 中使用的异常。

enter image description here

最佳答案

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

关于java - 为什么我得到的是 NoClassDefFoundError 异常而不是 StackOverflow 错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48392860/

相关文章:

java - 如何使用多个 yaml 文件生成 openAPI 代码

Mysql 继续处理程序语法错误

Excel VBA 比较两个单元格的日期时出错

java - 使用正则表达式获取字符串的首字母,同时保留标点符号和空格

java - 如何在 ANTLR 中分离出 block 的开始和结束标记

javascript - 如何在 Sublime text 上使用 Flask 后端编写 javascript/p5.js 代码时调试并获取错误消息?

android - AutoDispose:如何在生命周期到期后处理来自可观察对象的错误

function - Racket : expected: procedure?

c++ - 运行时错误和逻辑错误的区别

java - IntelliJ 中为 "java: cannot find symbol"但 gradle 构建