简体   繁体   English

如果从未使用过相应的 class,类加载器是否加载 class 文件?

[英]Does the classloader load a class file if the corresponding class is never used?

To make my question more clear, consider following use-case:为了使我的问题更清楚,请考虑以下用例:

Suppose there is a package that permits a set of operations on a given platform, for example a class to edit the registry on Windows. This package does not exist on other platforms, as there is no equivalent operation on other operating system.假设有一个 package 允许在给定平台上进行一组操作,例如 class 可以在 Windows 上编辑注册表。这个 package 在其他平台上不存在,因为在其他操作系统上没有等效的操作。

For the sake of simplicity, consider为了简单起见,考虑

windows/Registry.java窗户/注册表.java

package windows;

public class Registry {

  static Registry instance = null;
  static{
    System.out.println("print from static block");
  }

  private Registry() {
    System.out.println("Registry instance created!");
  }

  public static synchronized Registry getInstance() {
    if (null == instance) {
      instance = new Registry();
    }
    return instance;
  }

  public void foo() {
    System.out.println("foo called.");
  }
}

and the class where I am going to conditionally use the registry: main/Main.java以及我将有条件地使用注册表的 class:main/Main.java

package main;

import windows.Registry;

public class Main {

  public static void test1(boolean onWindows) {
    if (onWindows) {
      Registry instance = Registry.getInstance();
      System.out.println("We are on Windows: ");
      instance.foo();
    } else {
      System.out.println("We are somewhere else!");
    }
  }

  public static void main(String[] args) {
    System.out.println("Entered main");
    boolean onWindows = args.length > 0 ? Boolean.parseBoolean(args[0]) : false;
    test1(onWindows);
  }
}

The question is, if no function or class is explicitly executed in Main.class , is it guaranteed that no code from Registry.class is executed?问题是,如果Main.class中没有显式执行function或Main.class ,是否保证Registry.class中的代码不执行?

I was able to test this example on multiple Desktop Platforms and with different java versions, but I would like to know if this behavior is documented and one can rely on it, if it thus the expected behavior also on other platforms, like android or embedded version of the JRE.我能够在多个桌面平台和不同的 java 版本上测试这个例子,但我想知道这种行为是否被记录并且可以依赖它,如果它因此在其他平台上也是预期的行为,比如 android 或嵌入式JRE 的版本。

In case there is no such guarantee, as a (maybe custom) classloader might decide to load all .class files in the classpath, can I assume that the code will work without a java.lang.NoClassDefFoundError if onWindows is false and I remove Registry.class from the classpath?如果没有这样的保证,因为(可能是自定义的)类加载器可能决定加载类路径中的所有.class文件,我是否可以假设如果onWindows为 false 并且我删除Registry.class ,代码将在没有java.lang.NoClassDefFoundError的情况下工作Registry.class来自类路径?

The behavior I've observed so far is到目前为止我观察到的行为是

rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main true
Entered main
print from static block
Registry instance created!
We are on Windows: 
foo called.

rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main false
Entered main
We are somewhere else!


rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main false
Entered main
We are somewhere else!

rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main true
Entered main
Exception in thread "main" java.lang.NoClassDefFoundError: windows/Registry
        at main.Main.test1(Main.java:9)
        at main.Main.main(Main.java:20)
Caused by: java.lang.ClassNotFoundException: windows.Registry
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
        ... 2 more

Are those behavior (lazy classloader and removing Registry.class ) defined?这些行为(惰性类加载器和删除Registry.class )是否已定义?

The question is, if no function or class is explicitly executed in Main.class , is it guaranteed that no code from Registry.class is executed?问题是,如果Main.class中没有显式执行function或Main.class ,是否保证Registry.class中的代码不执行?

That doesn't touch directly on class loading , but rather on class initialization , which is the first point at which any code from the class in question gets executed.这并没有直接触及 class loading ,而是触及 class initialization ,这是来自 class 的任何代码被执行的第一个点。 Specifically, static initialization blocks and the initializers of static members are executed during this stage.具体来说,static个初始化块和static个成员的初始化器在这个阶段被执行。 The class in question must already have been loaded and verified at this point, but it might have been loaded an arbitrary amount of time earlier.有问题的 class 此时必须已经加载和验证,但它可能在任意时间之前加载过。

Per JLS 12.4.1 ,根据JLS 12.4.1

A class or interface T will be initialized immediately before the first occurrence of any one of the following: class 或接口 T 将在第一次出现以下任何一项之前立即初始化:

  • T is a class and an instance of T is created. T 是一个 class 并且创建了一个 T 的实例。

  • A static method declared by T is invoked.调用 T 声明的static方法。

  • A static field declared by T is assigned.分配了 T 声明的static字段。

  • A static field declared by T is used and the field is not a constant variable使用了 T 声明的static字段,该字段不是常量变量

Thus, if you never instantiate the class or access access any of its static methods or fields (except to read a static field that is a "constant variable") then no code from the class will ever be executed.因此,如果您从未实例化 class 或访问访问其任何 static 方法或字段(读取 static 字段是“常量变量”除外),则永远不会执行 class 中的任何代码。

But that a class does not get initialized does not mean that no attempt will be made to load it.但是 class 没有被初始化并不意味着不会尝试加载它。 The JLS does not forbid implementations from loading classes prospectively. JLS 不禁止实现前瞻性地加载类。 In fact, JLS 12.2.1 specifically says:事实上, JLS 12.2.1中明确表示:

a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage , or load a group of related classes together . class 加载器可以缓存类和接口的二进制表示,根据预期用途预取它们,或一起加载一组相关类

Thus, no, it is not safe to rely on the application represented by your class main.Main to run without a java.lang.NoClassDefFoundError or other loading error when class windows.Registry cannot be loaded, regardless of whether it can be expected actually to be used.因此,不,依赖 class main.Main代表的应用程序在没有java.lang.NoClassDefFoundError或其他加载错误时运行是不安全的 class windows.Registry无法加载,无论是否可以实际预期要使用的。 However, you can , under the right circumstances, rely on that class not being initialized, and therefore no code from it being executed.但是,在适当的情况下,您可以相信 class 没有被初始化,因此它的代码没有被执行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM