Java延迟加载异常

Java延迟加载异常,java,exception,lazy-loading,Java,Exception,Lazy Loading,我知道在Java中,类是以惰性的方式加载的,因此在使用之前不会加载它们。例外情况是否因某种原因而被区别对待?我刚刚遇到了这样一种情况:对于一个异常类,我得到了一个ClassNotFound异常,即使没有抛出异常 例如: public class A { public static void main(String[] args) { if( args.length == 1 ){ new C(); } if( args.l

我知道在Java中,类是以惰性的方式加载的,因此在使用之前不会加载它们。例外情况是否因某种原因而被区别对待?我刚刚遇到了这样一种情况:对于一个异常类,我得到了一个
ClassNotFound
异常,即使没有抛出异常

例如:

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();
//          }
     }
  }

}
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");
  }

}
C类:

public class C {

  static{
    System.out.println( "Load class C" );
  }

}
当我用一个参数运行这样的程序时,我得到:

$java A arg1
Load class C
但是,如果我取消注释类A中的try/catch,我会得到:

$ java A arg1
Exception in thread "main" java.lang.NoClassDefFoundError: com/google/protobuf/InvalidProtocolBufferException

为什么Java在没有抛出异常/未加载该类的情况下尝试加载异常类?

JVM将尝试加载刚加载的类直接引用的类。i、 它将尝试解决所有可见的问题


在上面的示例中,我怀疑您是针对Google库进行编译的,但您没有将其包含在运行时
类路径中

您正在调用引用缺少的类的方法。当您运行一个方法时(如果不是更早的话),必须解析该方法中引用的所有类——换句话说,延迟加载并不像您想象的那么延迟

在JVM中加载类是递归的。加载类时,所有引用的类都将在加载之前加载


A
引用了
InvalidProtocolBufferException
。加载
A
时,将加载A引用的所有类。

VM可能需要准备异常跳转表,该表需要catch子句中提到的所有异常类型。必须在第一次调用该方法之前进行设置

如果你的程序是

    if( args.length > 2 )
        throw new InvalidProtocolBufferException();

这很好,因为异常类型不会出现在catch子句中


JLS实际上并不强制执行类加载的延迟程度-它可以尽可能地延迟,另一方面,如果VM选择提前加载所有类,如果它不能这样做,JLS也允许这样做。看

在初始链接时,解析步骤是可选的。一个实现可以解析早期链接的类或接口的符号引用,甚至解析进一步递归引用的类和接口的所有符号引用。。。。。。。。。。。。。。。 实现可能会选择仅在符号引用被积极使用时解析符号引用。。。。。。。。。。。。。如果加载和链接错误涉及类测试中提到的类或接口或任何其他递归引用的类和接口,则在执行程序之前可能会发生加载和链接错误


但是,JLS对何时可以进行类初始化非常严格。因此,在您的示例中,将提前加载exception类,但它的初始化必须在到达新的InvalidProtocolBufferException()之前进行。

我认为要加载一个类,所有的导入也需要可加载。我在没有任何导入和使用完整类名的情况下尝试过它,但它仍然会抛出异常。但此时,您正在引用一个完整的类-为了让JVM能够运行该代码,它至少需要知道您正在引用的类是否可以加载。那么,异常跳转表一定是问题所在,这就解释了为什么它在到达
main
之前就试图加载该类。
    if( args.length > 2 )
        try {
            B.throwAnException();
        } catch (Exception e) {
            e.printStackTrace();
        }