Reputation: 2635
I know that in Java, classes are loaded using a lazy manner, such that they aren't loaded before they're used. Are exceptions treated differently for some reason? I just came across a situation where I get a ClassNotFound
exception for an exception class, even though no exceptions have been thrown.
Example:
public class A {
public static void main(String[] args) {
if( args.length == 1 ){
new C();
}
if( args.length > 2 ){
// try {
// B.throwAnException();
// } catch (com.google.protobuf.InvalidProtocolBufferException e) {
// e.printStackTrace();
// }
}
}
}
Class B:
import com.google.protobuf.InvalidProtocolBufferException;
public class B {
static{
System.out.println( "Load Class B" );
}
static void throwAnException() throws InvalidProtocolBufferException{
throw new com.google.protobuf.InvalidProtocolBufferException("jkl");
}
}
Class C:
public class C {
static{
System.out.println( "Load class C" );
}
}
When I run the program like this with one argument, I get:
$java A arg1
Load class C
However, if I uncomment the try/catch in class A, I get:
$ java A arg1
Exception in thread "main" java.lang.NoClassDefFoundError: com/google/protobuf/InvalidProtocolBufferException
Why is Java attempting to load the exception class when no exception has been thrown/the class has not been loaded?
Upvotes: 3
Views: 1142
Reputation: 19682
VM probably needs to prepare exception jump table, which requires all exception types mentioned in catch clauses. This has to be setup before the method is invoked for the 1st time.
If your program is
if( args.length > 2 )
throw new InvalidProtocolBufferException();
or
if( args.length > 2 )
try {
B.throwAnException();
} catch (Exception e) {
e.printStackTrace();
}
it'll be fine since the exception type does not appear in a catch clause.
JLS actually does not enforce how lazy class loading should be - it can be as lazy as possible, on the other hand, if a VM chooses to load all classes up front, and bail if it cannot do so, it is also allowed by JLS. see http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.1.2
The resolution step is optional at the time of initial linkage. An implementation may resolve symbolic references from a class or interface that is being linked very early, even to the point of resolving all symbolic references from the classes and interfaces that are further referenced, recursively. ............... An implementation may instead choose to resolve a symbolic reference only when it is actively used ............. loading and linkage errors could occur before the program is executed if they involved a class or interface mentioned in the class Test or any of the further, recursively referenced, classes and interfaces
However, JLS is very strict on when class initialization can happen. So in your example, the exception class will be loaded early, but it's initialization must not occur until new InvalidProtocolBufferException()
is reached.
Upvotes: 2
Reputation: 4307
Loading class in JVM is recursive. When load a class, all referenced class will be loaded before.
InvalidProtocolBufferException
was referenced by A
. When loading A
, all classes referenced by a will loaded.
Upvotes: 0
Reputation: 14786
You are calling a method that references the missing class. When you run a method (if not sooner), all classes referenced in the method have to be resolved-- in other words, lazy loading isn't as lazy as you had supposed.
Upvotes: 0
Reputation: 272247
The JVM will attempt to load classes referenced directly by the class just loaded. i.e. it'll try and resolve everything visible.
In your example above, I suspect you've compiled against a Google library, but you're not including it in your runtime CLASSPATH
.
Upvotes: 0