Why does the following code cause ClassNotFoundException?
public class App02 {
public static class A {
}
public static void main(String[] args) throws ClassNotFoundException {
try {
System.out.println("A.class.getCanonicalName() = " + A.class.getCanonicalName());
Class c = Class.forName("tests.App02.A"); //error on this line
System.out.println(c.getName());
}
catch(Exception e) {
e.printStackTrace();
}
}
}
Output:
A.class.getCanonicalName() = tests.App02.A
java.lang.ClassNotFoundException: tests.App02.A
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:190)
at tests.App02.main(App02.java:15)
Try Class c = Class.forName("tests.App02$A"). It's not a top-level class, so use $ to locate it.
You need to use $ to access the nested class:
Class c = Class.forName("tests.App02$A");
When you compile your class, you will notice that the nested class is named as: App02$A.class, under package tests. It would make more sense then.
Because you are using a canonical name, but you should use name (A.class.getName()).
In your case you should use Class c = Class.forName("tests.App02$A");
There is a helpful util in commons-lang which support these classes:
org.apache.commons.lang3.ClassUtils.get("tests.App02.A")
Related
This question already has answers here:
Does the JVM throw if an unused class is absent?
(3 answers)
How to avoid NoClassDefFoundError thrown by unused code in Java
(3 answers)
Closed 1 year ago.
We use some optional libraries. This libraries will only access if available. But the JIT produce already NoClassDefFoundError in the constructor of the class with optional access.
public Configuration {
public boolean libraryAvailable() {
return false; // some configuration that signal that the library is not available
}
}
public class Foo {
public Foo() {
... do some things
}
public void callLater() {
...
if( libraryAvailable() ) {
xyz();
}
...
}
private void xyz() {
new OptionalClass(); // available at compile time but not at runtime
}
}
How can I prevent that the JIT will load all possible dependencies of my call before calling the constructor?
java.lang.NoClassDefFoundError: com/inet/OptionalClass
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.newInstance(Class.java:412)
...
Caused by: java.lang.ClassNotFoundException: com.inet.OptionalClass
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at com.inet.plugin.DependencyClassLoader.loadClass(DependencyClassLoader.java:104)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.inet.plugin.DependencyClassLoader.loadClass(DependencyClassLoader.java:138)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 9 more
Instead of importing your optional class, use Class.forName
try {
Class<?> act = Class.forName("com.bla.TestActivity");
MyInterface driver = act.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
You could write your own custom method as shown in below code to check if a class is available in classpath.
boolean result=false;
try {
Class.forName( "your.test.class" );
result=true;
} catch( ClassNotFoundException e ) {
//Class or Library is not available
}
return result;
The decoupling of class loading for unused code with a separate method only work if the constructor empty. If there is a constructor then you need to move the unused code in a separate class. This can also be an anonymous class.
For example:
new Object () { {
new OptionalClass();
} };
The compiler for a JVM-based programming language currently that I am working on uses this code to run a specified main method after compilation:
URL url = DyvilCompiler.config.outputDir.toURI().toURL();
Class c = Class.forName(mainType, false, new URLClassLoader(new URL[] { url }, ClassLoader.getSystemClassLoader()));
Method m = c.getMethod("main", String[].class);
m.invoke(null, new Object[] { args });
However, when compiling this code:
package dyvil.test
// some uninteresting import stuff
public class Main
{
#ArrayConvertible
case class IntVector([int] ints = [ 1, 2 ])
public static void main([String] args)
{
println(IntVector())
println([ 1, 2, 3 ] as IntVector)
}
}
The ClassLoader fails with the inner class (that is actually a nested static class):
java.lang.ClassFormatError: Illegal class name "Ldyvil/test/Main$IntVector;" in class file dyvil/test/Main$IntVector
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:367)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at dyvil.test.Main.main(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at dyvil.tools.compiler.util.TestThread.run(TestThread.java:32)
Note that the class files generated for Main dyvil/test/Main and IntVector dyvil/test/Main$IntVector do define the inner class relation:
Inner classes:
[inner class info: #2 dyvil/test/Main$IntVector, outer class info: #7 dyvil/test/Main
inner name: #9 IntVector, accessflags: 8 static]
Enclosing Method: #7 #0 dyvil/test/Main
What is the problem with the Ldyvil/test/Main$IntVector; signature?
Also, when adding the output directory to the classpath and changing the code to
Class c = Class.forName(mainType)
everything works well without any errors.
EDIT: Not only does this cause problems with the JVM, but when using a class like this in Eclipse by importing it, Code Completions stop working completely.
I have a maven project in the following structure.
TestRun
|
|--src/main/java/com/main/CallAddNumbers.java (Package - Com.main)
|
|--src/test/java/com/test/RunTest.java (package com.test)
and here is the sample program
package com.main;
public class CallAddNumbers {
public static void main(String[] args) {
com.test.AddNumbers.addNumbers(5, 4);
}
}
package com.test;
public class AddNumbers {
public static void addNumbers (int a, int b){
System.out.println(a+b);
}
}
When I am calling addNumbers method from main, I am ending up with the following error. May be something simple, but can't figure it out.
Exception in thread "main" java.lang.NoClassDefFoundError: com/test/RunTest
at com.main.CallAddNumbers.main(CallAddNumbers.java:6)
Caused by: java.lang.ClassNotFoundException: com.test.RunTest
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 1 more
you said it might be something simple, just wanted to check that this is your actual file structure before looking any further!
|
|--src/main/java/com/main/CallAddNumbers.java (Package - com.main)
|
|--src/test/java/com/test/addNumbers.java (package com.test)
Add this import statement to the CallAddNumbers class
import test.java.com.test.AddNumbers;
It will work fine. Btw better dont write production codes in test package
I would like to call the following Scala code in Java:
Scala code
package calculate
import java.io._
class CalculationScala
object CalculationScala {
def main(args: Array[String]) {
def operate(a: Double, b: Double, op: (Double, Double) => Double): Double = (op(a, b))
println(operate(5, 15, _-_))
}
}
Java code
package calculate;
public class Calculation {
public static void main(String[] args) {
CalculationScala calculationScala = new CalculationScala();
calculationScala.main(args);
}
}
but the following error occurs.
Error
Exception in thread "main" java.lang.NoClassDefFoundError: scala/Function2
at calculate.CalculationScala.main(CalculationScala.scala)
at calculate.Calculation.main(Calculation.java:79)
Caused by: java.lang.ClassNotFoundException: scala.Function2
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 2 more
How to solve this issue?
It looks like you are missing scala-library.jar on your runtime classpath.
I am new to reflection and to practice, I downloaded a random Java project from a website. I decided to find out which class has the main method so I wrote the following code:
package reflection;
import java.io.IOException;
import java.lang.reflect.*;
import java.nio.file.*;
public class FindMethods {
public static void main(String[] args) throws IOException{
if(args.length==0){
System.out.println("Exiting");
System.exit(1);
}else{
Path p = Paths.get(args[0]);
DirectoryStream<Path> allClassFiles = Files.newDirectoryStream(p, "*.class");
for(Path each : allClassFiles){
// System.out.println(each.getFileName());
try {
findMethods(each.getFileName().toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
public static void findMethods(String file) throws ClassNotFoundException{
System.out.println(file);
Class c = Class.forName(file);
Method[] m = c.getDeclaredMethods();
for(Method each : m){
System.out.println(each.toString());
}
}
}
System.out.println(each.getFileName()); properly returns the .class files in the folder however, it is interspersed with stack trace of ClassNotFoundException
The classes are as follows:
Addwindow$1.class
Addwindow$2.class
Addwindow.class
Authorwindow.class
clsConnection.class
clsSettings$1.class
clsSettings.class
Deletewindow$1.class
Deletewindow$2.class
Deletewindow.class
Editwindow$1.class
Editwindow$2.class
Editwindow.class
Emprptwindow$PrintCommand.class
Emprptwindow.class
Helpwindow.class
LoginFrame$1.class
LoginFrame.class
MainMenu$1.class
MainMenu$2.class
MainMenu.class
Payrptwindow.class
printwindow$1.class
printwindow.class
Settingswindow$1.class
Settingswindow.class
What changes do I need to make to the code to get the methods from each class ?
Stack trace:
java.lang.ClassNotFoundException: Settingswindow
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at reflection.FindMethods.findMethods(FindMethods.java:33)
at reflection.FindMethods.main(FindMethods.java:22)
Random project being talked about:
http://projectseminar.org/java-projects/payroll-accounting-system/98/
.class is part of the filename, but it isn't part of the class name. You need to strip it before passing it to Class.forName.
Another issue is that forName expects packages to be separated using periods, rather than than slashes or whatever directory separator your filesystem uses. If everything is in the default package, this shouldn't be an issue though.
If it's still not working, you should double check the classpath.
Class names that contain a $ are anonymous classes within the outer class (determined by the name to the left of the $). You can safely ignore those in your search for main. Just test for the presence of a $ in the class names in your main loop and skip the processing.
Without knowing more about what app you are looking at, I can't say why your code can't find some of the other classes (like clsConnection).
There is a problem in this approach - you load all project's classes. It is better to analize classes without loading them. There are tools for that. Here's what we can do with http://www.jboss.org/javassist
public static void findMethods(String file) throws Exception {
ClassPool cp = ClassPool.getDefault();
try (InputStream is = new FileInputStream(file)) {
CtClass cc = cp.makeClass(is);
for (CtMethod m : cc.getMethods()) {
System.out.println(m);
}
}
}