Work with Java reflection and invoke getSimpleName() met java.lang.NoClassDefFoundError
Use getName() instead, the logic seems work well.
Before compare these two methods’ difference go through the code quickly.
Class::getName()
for getName()
1 | public String getName() { |
and the getName0() is native method
1 | private native String getName0(); |
Class::getSimpleName()
for getSimpleName()
1 | public String getSimpleName() { |
The main part is
1 | String simpleName = getSimpleBinaryName(); |
And then get enclosingClass:
1 | private String getSimpleBinaryName() { |
what is enclosingClass:
1 | // There are five kinds of classes (or interfaces): |
in my case it tend to be a) Top level classes, so next part of code goes to getDeclaringClass()
1 | // JVM Spec 4.8.6: A class must have an EnclosingMethod |
and then getDeclaringClass0() will be used.
1 | @CallerSensitive |
which is a native method:
1 | private native Class<?> getDeclaringClass0(); |
Comparision
From the code before this section, aboveously the getSimpleName() always call native method but getName() may use Class’s local variable directly.
So see the local variable before we come to conclusion.
1 | // cache the name to reduce the number of calls into the VM |
the name used by getName() use a transient String as cache to reduce the number of calls into VM.
And combine to getName()’s implement, the first time getName0() invoked, this name is set.
And go for Java doc:
1 | public String getSimpleName() |
Returns the simple name of the underlying class as given in the source code. Returns an empty string if the underlying class is anonymous.
The simple name of an array is the simple name of the component type with “[]” appended. In particular the simple name of an array whose component type is anonymous is “[]”.
Returns:
the simple name of the underlying class
Since:
1.5
1 | public String getName() |
Returns the name of the entity (class, interface, array class, primitive type, or void) represented by this Class
object, as a String
.
If this class object represents a reference type that is not an array type then the binary name of the class is returned, as specified by The Java™ Language Specification.
If this class object represents a primitive type or void, then the name returned is a String
equal to the Java language keyword corresponding to the primitive type or void.
If this class object represents a class of arrays, then the internal form of the name consists of the name of the element type preceded by one or more ‘[
‘ characters representing the depth of the array nesting. The encoding of element type names is as follows:
Element Type Encoding boolean Z byte B char C class or interface Lclassname; double D float F int I long J short S
The class or interface name classname is the binary name of the class specified above.
Examples:
1
2
3
4
5
6
7
8
9 String.class.getName()
returns "java.lang.String"
byte.class.getName()
returns "byte"
(new Object[3]).getClass().getName()
returns "[Ljava.lang.Object;"
(new int[3][4][5][6][7][8][9]).getClass().getName()
returns "[[[[[[[I"
Returns:
the name of the class or interface represented by this object.
and more details for the error:
1 | public 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.
So that means class found at compile time but not available at runtime Refer this link
Check for our configuration:
1 | <dependency> |
use a system scope.
According to maven doc:
There are 6 scopes:
- compile
This is the default scope, used if none is specified. Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects. - provided
This is much likecompile
, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scopeprovided
because the web container provides those classes. A dependency with this scope is added to the classpath used for compilation and test, but not the runtime classpath. It is not transitive. - runtime
This scope indicates that the dependency is not required for compilation, but is for execution. Maven includes a dependency with this scope in the runtime and test classpaths, but not the compile classpath. - test
This scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases. This scope is not transitive. Typically this scope is used for test libraries such as JUnit and Mockito. It is also used for non-test libraries such as Apache Commons IO if those libraries are used in unit tests (src/test/java) but not in the model code (src/main/java). - system
This scope is similar toprovided
except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository. - import
This scope is only supported on a dependency of typepom
in the<dependencyManagement>
section. It indicates the dependency is to be replaced with the effective list of dependencies in the specified POM’s<dependencyManagement>
section. Since they are replaced, dependencies with a scope ofimport
do not actually participate in limiting the transitivity of a dependency.
system most like provided but a dependency with this scope is added to the classpath used for compilation and test, but not the runtime classpath
so runtime getSimpleName() will met exception.
Class loader
get back to the error call trace again:
1 | Caused by: java.lang.NoClassDefFoundError: xxxxx |
and another class not found exception:
1 | Caused by: java.lang.ClassNotFoundException: xxxxx |
when try to get simple name of class A extends B
find A and try to define A but need to define B first, but B is not available at runtime so a exception raised.