gpt4 book ai didi

java - JVM 类加载中的意外行为(真正需要类之前的 ClassNotFoundException)

转载 作者:搜寻专家 更新时间:2023-11-01 02:01:59 27 4
gpt4 key购买 nike

我需要帮助来理解为什么这会发生在我身上:

使用 Java 1.8.0_131,我有一个这样的类:

public class DynamicClassLoadingAppKO {

/*
* THIS VERSION DOES NOT WORK, A ClassNotFoundException IS THROWN BEFORE EVEN EXECUTING main()
*/


// If this method received ChildClassFromLibTwo, everything would work OK!
private static void showMessage(final ParentClassFromLibOne obj) {
System.out.println(obj.message());
}


public static void main(final String[] args) throws Throwable {

try {

final ChildClassFromLibTwo obj = new ChildClassFromLibTwo();
showMessage(obj);

} catch (final Throwable ignored) {
// ignored, we just wanted to use it if it was present
}

System.out.println("This should be displayed, but no :(");

}

}

其他两个类正在那里被使用:ParentClassFromLibOneChildClassFromLibTwo。后者是前者的延伸。

涉及两个外部库:

  • 一个名为libone 的库包含ParentClassFromLibOne 类。应用程序将此库包含在类路径中以用于编译和执行。
  • 第二个库称为 libtwo,包含 ChildClassFromLibTwo 类。应用程序在类路径中包含此库用于编译,但不用于执行

据我所知,Java 运行时应尝试在这一行加载 ChildClassFromLibTwo(运行时在类路径中):

final ChildClassFromLibTwo obj = new ChildClassFromLibTwo();

鉴于此类不在类路径中,应该抛出 ClassNotFoundException,并且鉴于此行位于 try...catch (Throwable) 中,System.out.println 末尾的行无论如何都应该执行。

但是,我得到的是 ClassNotFoundException 在加载 DynamicClassLoadingAppKO 本身时抛出,显然 main() 方法之前完全执行,因此不会被 try...catch 捕获。

对我来说更奇怪的是,如果我更改 showMessage() 方法的签名,而不是接收 parent 类,它直接属于child 类:

/*
* THIS VERSION WORKS OK, BECAUSE showMessage RECEIVES THE CHILD CLASS AS A PARAMETER
*/
private static void showMessage(final ChildClassFromLibTwo obj) {
System.out.println(obj.message());
}

这怎么可能?我在类加载的工作方式中缺少什么?

为了方便测试,我创建了一个复制此行为的 GitHub 存储库 [1]。

[1] https://github.com/danielfernandez/test-dynamic-class-loading/tree/20170504

最佳答案

好的,这个 Spring Boot ticket [1] 中解释了为什么会发生这种情况的详细信息,我很幸运能够被 Andy Wilkinson 及时指出。这绝对是一个困难的 IMO。

显然,在这种情况下发生的事情是,当调用类本身被加载时, validator 启动并看到 showMessage()方法接收类型为 ParentClassFromLibOne 的参数.到目前为止一切顺利,这不会引发 ClassNotFoundException在这个阶段即使ParentClassFromLibOne在运行时不在类路径中。

但显然 validator 也扫描方法代码 并注意到在main() 中有一个调用那个showMessage()方法。不作为参数传递的调用 ParentClassFromLibOne ,而是不同类的对象:ChildClassFromLibTwo .

所以在这种情况下,验证者确实会尝试加载 ChildClassFromLibTwo为了能够检查它是否真的从ParentClassFromLibOne延伸.

有趣的是如果ParentClassFromLibOne 就不会发生这种情况是一个接口(interface),因为接口(interface)被视为Object分配。

此外,如果 showMessage(...) 则不会发生这种情况直接要求 ChildClassFromLibTwo作为参数,因为在这种情况下, validator 不需要加载子类来检查它是否兼容……与自身。

Daniel,我赞成你的回答,但我不会将其标记为已接受,因为我认为它无法解释在验证时发生这种情况的真正原因(这不是导致 ClassNotFoundException 的方法的签名)。

[1] https://github.com/spring-projects/spring-boot/issues/8181

关于java - JVM 类加载中的意外行为(真正需要类之前的 ClassNotFoundException),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43792289/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com