3

我知道在 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 会尝试加载异常类?

4

4 回答 4

2

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

如果你的程序是

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

或者

    if( args.length > 2 )
        try {
            B.throwAnException();
        } catch (Exception e) {
            e.printStackTrace();
        }

没关系,因为异常类型没有出现在 catch 子句中。


JLS 实际上并没有强制类加载应该有多懒惰——它可以尽可能地懒惰,另一方面,如果 VM 选择预先加载所有类,如果不能这样做则保释,JLS 也允许. 见http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.1.2

在初始链接时,解析步骤是可选的。实现可以从很早就被链接的类或接口解析符号引用,甚至可以递归地解析来自被进一步引用的类和接口的所有符号引用。...... 一个实现可能会选择仅在积极使用符号引用时才解析它...... 可能会发生加载和链接错误在程序执行之前,如果它们涉及到类 Test 中提到的类或接口或任何进一步的递归引用的类和接口

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

于 2013-04-18T16:22:43.037 回答
0

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.

于 2013-04-18T16:17:14.200 回答
0

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

于 2013-04-18T16:16:36.367 回答
0

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

在您上面的示例中,我怀疑您已经针对 Google 库进行了编译,但您没有将它包含在您的 runtimeCLASSPATH中。

于 2013-04-18T16:09:46.357 回答