Java-Errors:ClassNotFoundException

From Juneday education
Jump to: navigation, search

Understanding ClassNotFoundException

There are typically two reasons for a ClassNotFoundException to occur when running a Java application. From the documentation of the exception class, we see:

Thrown when an application tries to load in a class through its string name ... but no definition for the class with the specified name could be found.

In order to understand the above, put yourself in the JVM's place. If an object in some class creates an object of some other class, what must the JVM do then? It must load the corresponding class file into memory, in order to know what the class looks like, and what members exist in the class. So, using its class path, it tries to find the corresponding class file and loads it. If it can't find the class file, either the file is missing (e.g. not compiled), or the class path is wrong.

Let's take a small example:

// Main.java
public class Main {
  public static void main(String[] args) {
    Student s = new Student();
  }
}

// Student.java
public class Student {}

If we compile Main.java, then the compiler sees that the main method in the Main class creates a Student object from the class Student. If the compiler can't find the file Student.class, but it can find Student.java, it compiles Student.java into the file Student.class. If that works OK, then it continues to compile Main.java into Main.class. The result is two new files:

  • Main.class - compiled from Main.java
  • Student.class - compiled from Student.java

Let's verify this:

$ rm *.class

$ ls
Main.java  Student.java

$ javac Main.java 
$ ls
Main.class  Main.java  Student.class  Student.java

$ javap Main Student
Compiled from "Main.java"
public class Main {
  public Main();
  public static void main(java.lang.String[]);
}
Compiled from "Student.java"
public class Student {
  public Student();
}

Now, what happens if we remove Student.class? We still have the successfully compiled Main.class, with the following instructions in its byte code:

$ javap -c Main
Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Student
       3: dup
       4: invokespecial #3                  // Method Student."<init>":()V
       7: astore_1
       8: return
}

The above means that the main method of the class inside Main.class, calls the constructor of the class Student.

What do you think will happen if we run the Main class (since it has a main method we can run the Main class as a program, using java Main? Think for yourself before you check the result below. When running a class with a main method, the JVM loads the class and executes the code in the main method (from the class file). It will find the instructions for creating an object of class Student. So, it must load the Student.class byte code from the file Student.class, in order to run its constructor. But we have deleted the Student.class file. What do you think the JVM will do now?

Here's what will happen:

$ ls
Main.class  Main.java  Student.class  Student.java

$ rm Student.class
$ java Main
Exception in thread "main" java.lang.NoClassDefFoundError: Student
	at Main.main(Main.java:3)
Caused by: java.lang.ClassNotFoundException: Student
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 1 more

The other situation which may account for a ClassNotFoundException, is that you do have the class file (compiled from some Java file), but still the JVM can't find and load the class file. That's then because you have the wrong class path (the list of directory which tells the JVM where to look for class files).

Let's put Main in a package and student in another package as well as inside a JAR file. Now, even though we have a compiled Student.class file in a JAR file, the JVM can only find and load it, if it can find the JAR file. And in order to find the JAR file, we must help the JVM by pointing out the JAR file using the class path mechanism (using the flag -cp).

$ tree
.
├── domain
│   ├── Student.class
│   └── Student.java
└── main
    ├── Main.class
    └── Main.java

2 directories, 4 files

$ cat main/Main.java 
package main;

import domain.Student;

public class Main {
  public static void main(String[] args) {
    Student s = new Student();
  }
}

$ cat domain/Student.java 
package domain;

public class Student {}


# create the JAR file with domain/student.class
$ jar cf school.jar domain/

$ rm domain/Student.class 
$ tree
.
├── domain
│   └── Student.java
├── main
│   ├── Main.class
│   └── Main.java
└── school.jar

2 directories, 4 files

$ java main.Main 
Exception in thread "main" java.lang.NoClassDefFoundError: domain/Student
	at main.Main.main(Main.java:7)
Caused by: java.lang.ClassNotFoundException: domain.Student
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 1 more

The JVM couldn't load the domain.Student class from the JAR file, because the JAR file wasn't on the class path. Let's help the JVM to find the JAR file (so that it will find the domain.Student class inside it):

$ java -cp ".:school.jar" main.Main

In the class path, you may use ";" in place of ":" if you are running a Windows version of Java. The class path we gave the JVM had two paths:

  • . (current directory) and
  • school.jar (the jar file)

The current directory was needed, in order for Java to find the directory main where the Main.class file is, so that it can run the main method of that class. The JAR file was needed in order for Java to find the domain/Student.class file, in order for it to run the code in the main method which creates such an object.