3

ClassLoader clMain -> URLClassLoader clA (class A, $B). $B is a inner class B of the class A.

  1. From clMain creating new clA with specified url to the jar file which contains class A (and class $B). Then creating new object A via clA.
  2. Another class, loaded from X ClassLoader, execute a method of the object A via interface without problems.
  3. Then, in that method, should be created new object $B, but it throws the NoClassDefFoundError.
  4. Loading the class $B in the constructor of the class A is solving the problem: this.getClass().getClassLoader().loadClass("A$B");

So, the cuestion is: "Why the definition of the class $B cannot be found for step 3 and Can be solved this porblem in another way?"

Checking the ClassLoader used in the method (this.getClass().getClassLoader()) and comparing it with the ClassLoader used in the constructor - is the same and contains the necessary url. Java 8 and 14 tried to run, jdk 8 to build.

Edit:

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 {}
}

Edit: this behavior is not related specifically to inner class, but to every class located in the jar. In simple words, i need to iterate every class located in the jar and load them manually before they are used, or else jvm will throw the NoClassDefFoundError.

  • It seems like is a strange feature of the jvm. I just used guava library to load all classes from my package after the object was created to solve the problem. https://www.baeldung.com/java-find-all-classes-in-package – pro100kryto Oct 24 '21 at 08:15
  • Is the absence of `new B()` in the second example possibly relevant? I can imagine different results when the bytecode contains a static reference to the other class, vs. when it doesn't. To be absolutely sure it's not this, can you test? We can only be sure it's your constructor causing it if there are no other differences. – kaya3 Oct 29 '21 at 15:33
  • @kaya3, is not real code but a demostrative example. I will try to create a test project repeating this behavior. | My real project (is in development) where the problem i had is https://github.com/p1k-Server/Server-env-test, branch *dev*. To repeat the case, see the commit "510a864c" (/Server) which contains my fix. – pro100kryto Nov 01 '21 at 01:49
  • Does this answer your question? [Does the Java ClassLoader load inner classes?](https://stackoverflow.com/questions/24538509/does-the-java-classloader-load-inner-classes) – Roman Vottner Nov 01 '21 at 06:28

1 Answers1

-2

NoClassDefFoundError invariably means the $inner class file (in this specific case) itself could not be found. packaging or copying problem or a url referencing problem (? JNDI naming problem) but whatever, the file was not located where stated. (sometimes the filename is a typo somewhere and the location to it is correct).

unfortunately two points, 1 the sub class is static , so an extremely weird choice to invoke using reflect, 2 You simply cause a statement that does not get used or assigned (weirder) re

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

although it may only be required inside its enclosing class to be known as "B"

e.g.

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

Your problem seems very alike "nested inner class shadowing" Its a long time back but i think that's as possible as A.B inside its enclosing class.

I would see more sense in attempting with reflect something like either... ?

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

One more vague note from years back, i think when you load using classloader you require to do that inside a check

Class<?> aclss = this.getClass();
if( (Class<?>.isAssignableFrom(aclss)) && (Class<?>.isInstance(aclss)) ){
//... load here
}
Samuel Marchant
  • 331
  • 2
  • 6
  • Is not problem of the file location or classpath missing. Jvm just does not want to load class. And loading class manually ( .loadCLass(...) ) after or before object creation is solving the problem. But loading that missing classes inside of the object methods unfortunately will throw NoClassDefFoundError. – pro100kryto Oct 24 '21 at 08:31
  • For instantiation of an "inner class" you must use the encapsulating class instance, so you must load the encapsulating class first and then climb into that instance with reflection or if normal code Encapulator encap = new Encapulator() Encapulator.Inner inot = new encap.Inner() from the loaded encapsulating class. However if its static you simply call Encapsulator.Innerstatic() without new. Note - loadClass should have a boolean for whether to load an instance or use an already instantiated instance. – Samuel Marchant Oct 25 '21 at 03:27
  • 1
    *"You simply cause a statement that does not get used or assigned"* Well yes, the method is called for its side-effect, not its return value. You wouldn't be surprised to see `list.add(23);` instead of `boolean r = list.add(23);`, would you? – kaya3 Oct 29 '21 at 15:27
  • well ok... In fishing around the usual points apply to a static class, it must be a nested inner subclass, if one is loaded there are no other copies (unless you explicitly copy it to an instance with cast operator), but the single original is loaded as new instance by the jvm. I have had trouble with referencing inner nested classes, sometimes as above like just B but as you are doing compared normal static class "call" you need A.B() that i fear is useless without a method/variable call too? See what happens if you make another separate class to call it outside as that you had. – Samuel Marchant Nov 01 '21 at 04:07