【问题标题】:ClassLoader does not load inner class [closed]ClassLoader 不加载内部类 [关闭]
【发布时间】:2021-11-01 10:07:50
【问题描述】:

ClassLoader clMain -> URLClassLoader clA(A 类,$B)。 $B 是类 A 的内部类 B。

  1. 从创建具有指定 URL 的新 clA 的 clMain 到包含 A 类(和 $B 类)的 jar 文件。然后通过 clA 创建新对象 A。
  2. 另一个类,从 X ClassLoader 加载,通过接口执行对象 A 的方法没有问题。
  3. 然后,在该方法中,应该创建新对象 $B,但它会抛出 NoClassDefFoundError
  4. 在类A的构造函数中加载类$B解决问题:this.getClass().getClassLoader().loadClass("A$B");

所以,提示是:“为什么在第 3 步找不到类 $B 的定义,可以用另一种方式解决这个问题吗?”

检查方法中使用的ClassLoader (this.getClass().getClassLoader()) 并将其与构造函数中使用的ClassLoader 进行比较 - 是相同的,并且包含必要的 url。 Java 8 和 14 尝试运行,jdk 8 构建。

编辑:

public class A {
 public A(){
  this.getClass().getClassLoader().loadClass("A$B"); // success
 }
 public void foo(){
  this.getClass().getClassLoader().loadClass("A$B"); // success
  new B(); // success
 }
 public static final class B {}
}
public class A {
 public A(){
 }
 public void foo(){
  this.getClass().getClassLoader().loadClass("A$B"); // throw
  new B(); // Edit: added this line to demostrate what is not the main diff between two examples
 }
 public static final class B {}
}

编辑:此行为与内部类无关,而是与 jar 中的每个类相关。简而言之,我需要遍历 jar 中的每个类并在使用它们之前手动加载它们,否则 jvm 会抛出 NoClassDefFoundError

【问题讨论】:

  • 这似乎是jvm的一个奇怪特性。在创建对象以解决问题后,我只是使用 guava 库从我的包中加载所有类。 baeldung.com/java-find-all-classes-in-package
  • 第二个示例中缺少new B() 是否可能相关?当字节码包含对另一个类的静态引用时,我可以想象不同的结果,而当它不包含时。为了绝对确定不是这个,你能测试一下吗?如果没有其他差异,我们只能确定是您的构造函数造成的。
  • @kaya3,不是真正的代码,而是一个示例。我将尝试创建一个重复此行为的测试项目。 |我真正的项目(正在开发中)我遇到的问题是github.com/p1k-Server/Server-env-test,分支dev。要重复这种情况,请参阅包含我的修复的提交“510a864c”(/Server)。
  • 这能回答你的问题吗? Does the Java ClassLoader load inner classes?

标签: java classloader


【解决方案1】:

NoClassDefFoundError 总是意味着 $inner 类文件(在这种特定情况下)本身无法找到。打包或复制问题或 url 引用问题(?JNDI 命名问题),但无论如何,该文件不在指定的位置。 (有时文件名在某处是拼写错误,并且位置正确)。

不幸的是两点,1子类是静态的,所以使用反射调用是一个非常奇怪的选择,2你只是导致一个没有被使用或分配的语句(更奇怪) 重新

this.getClass().getClassLoader().loadClass("A$B"); // throw

虽然它可能只需要在其封闭类中被称为“B”

例如

B subcab = (Class<B>).....

你的问题看起来很像“嵌套内部类shadowing” 它是很久以前的事了,但我认为这在其封闭类中尽可能地是 A.B。

我会觉得尝试反射类似... ?

A.B subcab = (Class<A.B>)((this.getClass()).getClassLoader().loadClass("A$B")); // A should have , its package name if any, also - see "binary name"

几年前的另一个模糊说明,我认为当您使用类加载器加载时,您需要在检查中执行此操作

Class<?> aclss = this.getClass();
if( (Class<?>.isAssignableFrom(aclss)) && (Class<?>.isInstance(aclss)) ){
//... load here
}

【讨论】:

  • 不是文件位置或类路径丢失的问题。 Jvm 只是不想加载类。在对象创建之后或之前手动加载类( .loadCLass(...) )正在解决问题。但是不幸的是,在对象方法中加载缺少的类会抛出 NoClassDefFoundError。
  • 对于“内部类”的实例化,您必须使用封装类实例,因此您必须先加载封装类,然后通过反射爬入该实例,或者如果正常代码 Encapulator encap = new Encapulator( ) Encapulator.Inner inot = new encap.Inner() 来自加载的封装类。但是,如果它是静态的,您只需调用 Encapsulator.Innerstatic() 而不使用 new。注意 - loadClass 应该有一个布尔值来决定是加载实例还是使用已经实例化的实例。
  • “你只是导致了一个没有被使用或分配的语句” 是的,调用该方法是因为它的副作用,而不是它的返回值。看到list.add(23); 而不是boolean r = list.add(23);,你不会感到惊讶吧?
  • 好吧...在钓鱼中,通常点适用于静态类,它必须是嵌套的内部子类,如果加载了一个,则没有其他副本(除非您将其显式复制到带有强制转换运算符的实例),但单个原始实例由 jvm 作为新实例加载。我在引用内部嵌套类时遇到了麻烦,有时就像上面的 B 一样,但是当您比较普通的静态类“调用”时,您需要 A.B() 我担心如果没有方法/变量调用也没用?看看如果你创建另一个单独的类来像你一样在外面调用它会发生什么。
猜你喜欢
  • 1970-01-01
  • 2014-08-23
  • 2020-12-21
  • 1970-01-01
  • 1970-01-01
  • 2019-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多