类加载器何时加载 java 中的接口?
When classloader loads interfaces in java?
我正在尝试了解 classloader 的工作原理。我构建了一个简单的测试应用程序,并在 -verbose:class 模式下 运行ning。我有一个接口和一个 class。
界面
public interface Animal {
int h = 8;
}
Class
public class Elephant implements Animal {
static int staticInt;
static void initTest(){
System.out.println("HELLO");
}
}
我 运行 在主应用程序中执行以下命令并获得预期的输出。接口和 class 都加载到内存中,因为我已经访问了大象的静态字段 class
int i = Elephant.staticInt;
[0.795s][info][class,load] classloader.test.Animal source: file:/C:/study/class-loader/out/production/class-loader/
[0.795s][info][class,load] classloader.test.Elephant source: file:/C:/study/class-loader/out/production/class-loader/
当我 运行 下面的代码时,我看到只有 Animal class 被加载了,这也是我所期望的。
Animal.class.getClassLoader();
[0.864s][info][class,load] classloader.test.Animal source: file:/C:/study/class-loader/out/production/class-loader/
问题是当我只访问界面字段时,我在控制台中看不到 class 加载输出。接口加载是否不同?如果未加载整数值,我如何访问它?我找不到关于该主题的任何内容。
//When i run the below code i only get the value of the integer but not the class loading info
System.out.println("The integer is " + Animal.h);
The integer is 8
注意:所有三个案例都是 运行 单独
我记不太清楚了,但我认为这是因为 h 在一个界面中 "public static final"。这意味着常量只是在 Elephant.class 字节码内部重新定义,如果我没记错的话,这意味着此时不需要 Animal。
Java 编译器内联静态最终常量(接口中的 int h
是隐式静态和最终的),因此您的代码有效编译为:
System.out.println("The integer is " + 8);
如您所见,不再有对 Animal
的引用。这就是它没有加载的原因。
Are interfaces loaded differently ?
实际上,没有。 (如果您尝试访问另一个 class 中的 class 的 public static final
字段,您会得到同样的结果。)
How can I access to the integer value if it is not loaded?
您指的是用常量表达式初始化的字段。表达式在编译时求值,值在使用时插入到字节码中。因此,使用this接口字段不会触发class加载。
JLS 的相关部分是:
直接查看字节码就可以看出发生了什么。例如 class:
public class Test {
public void test() {
System.out.println(Animal.h);
}
}
它是字节码(为简洁起见只显示 test()
):
// access flags 0x1
public test()V
L0
LINENUMBER 3 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
BIPUSH 8
INVOKEVIRTUAL java/io/PrintStream.println (I)V
L1
LINENUMBER 4 L1
RETURN
L2
LOCALVARIABLE this LTest; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
可以看到java bytecode instruction listings中定义的BIPUSH 8
指令,其描述为:
push a byte onto the stack as an integer value
值 8
不会从任何地方加载,因为它是一个编译时常量,因此在编译时直接解析,因为它永远不会改变。
因此您的 Animal
class 将永远不会被 class 加载程序加载。
同样在使用反编译器时(我使用 IntelliJ,它使用 Fernflower),结果 Test
class 看起来像这样:
public class Test {
public Test() {
}
public void test() {
System.out.println(8);
}
}
我正在尝试了解 classloader 的工作原理。我构建了一个简单的测试应用程序,并在 -verbose:class 模式下 运行ning。我有一个接口和一个 class。
界面
public interface Animal {
int h = 8;
}
Class
public class Elephant implements Animal {
static int staticInt;
static void initTest(){
System.out.println("HELLO");
}
}
我 运行 在主应用程序中执行以下命令并获得预期的输出。接口和 class 都加载到内存中,因为我已经访问了大象的静态字段 class
int i = Elephant.staticInt;
[0.795s][info][class,load] classloader.test.Animal source: file:/C:/study/class-loader/out/production/class-loader/
[0.795s][info][class,load] classloader.test.Elephant source: file:/C:/study/class-loader/out/production/class-loader/
当我 运行 下面的代码时,我看到只有 Animal class 被加载了,这也是我所期望的。
Animal.class.getClassLoader();
[0.864s][info][class,load] classloader.test.Animal source: file:/C:/study/class-loader/out/production/class-loader/
问题是当我只访问界面字段时,我在控制台中看不到 class 加载输出。接口加载是否不同?如果未加载整数值,我如何访问它?我找不到关于该主题的任何内容。
//When i run the below code i only get the value of the integer but not the class loading info
System.out.println("The integer is " + Animal.h);
The integer is 8
注意:所有三个案例都是 运行 单独
我记不太清楚了,但我认为这是因为 h 在一个界面中 "public static final"。这意味着常量只是在 Elephant.class 字节码内部重新定义,如果我没记错的话,这意味着此时不需要 Animal。
Java 编译器内联静态最终常量(接口中的 int h
是隐式静态和最终的),因此您的代码有效编译为:
System.out.println("The integer is " + 8);
如您所见,不再有对 Animal
的引用。这就是它没有加载的原因。
Are interfaces loaded differently ?
实际上,没有。 (如果您尝试访问另一个 class 中的 class 的 public static final
字段,您会得到同样的结果。)
How can I access to the integer value if it is not loaded?
您指的是用常量表达式初始化的字段。表达式在编译时求值,值在使用时插入到字节码中。因此,使用this接口字段不会触发class加载。
JLS 的相关部分是:
直接查看字节码就可以看出发生了什么。例如 class:
public class Test {
public void test() {
System.out.println(Animal.h);
}
}
它是字节码(为简洁起见只显示 test()
):
// access flags 0x1
public test()V
L0
LINENUMBER 3 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
BIPUSH 8
INVOKEVIRTUAL java/io/PrintStream.println (I)V
L1
LINENUMBER 4 L1
RETURN
L2
LOCALVARIABLE this LTest; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
可以看到java bytecode instruction listings中定义的BIPUSH 8
指令,其描述为:
push a byte onto the stack as an integer value
值 8
不会从任何地方加载,因为它是一个编译时常量,因此在编译时直接解析,因为它永远不会改变。
因此您的 Animal
class 将永远不会被 class 加载程序加载。
同样在使用反编译器时(我使用 IntelliJ,它使用 Fernflower),结果 Test
class 看起来像这样:
public class Test {
public Test() {
}
public void test() {
System.out.println(8);
}
}