常量在编译阶段会存入调用这个常量的方法所在类的常量池中,本质上调用类并没有直接引用定义常量的类,因此并不会出发定义常量类的初始化。如果该常量值时动态生成的,则不会放在常量池中,会导致定义常量的类主动使用并初始化。 下面验证该结论。两个简单类的如下:
1package com.test.jvm.learn01;
2
3public class Main {
4
5 public static void main(String[] args) {
6 System.out.println(Consts.s);
7 }
8}
9
10
11package com.test.jvm.learn01;
12
13public class Consts {
14 public static final String s = "Hello world";
15 static {
16 System.out.println("This is Consts");
17 }
18}
然后执行main方法结果如下:
1......
2[Loaded java.io.FilePermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
3[Loaded java.io.FilePermission$1 from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
4[Loaded java.io.FilePermissionCollection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
5[Loaded java.security.AllPermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
6[Loaded java.security.UnresolvedPermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
7[Loaded java.security.BasicPermissionCollection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
8[Loaded com.test.jvm.learn01.Main from file:/E:/develop/workspace/workspace-eclipse-jee-neon/JVMLearning/bin/]
9[Loaded sun.launcher.LauncherHelper$FXHelper from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
10[Loaded java.lang.Class$MethodArray from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
11[Loaded java.lang.Void from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
12Hello world
13[Loaded java.lang.Shutdown from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
14[Loaded java.lang.Shutdown$Lock from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
如果把Consts类中final关键字去掉,打印结果如下:
1......
2[Loaded java.nio.MappedByteBuffer from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
3[Loaded java.nio.DirectByteBuffer from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
4[Loaded java.nio.LongBuffer from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
5[Loaded java.nio.DirectLongBufferU from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
6[Loaded java.security.PermissionCollection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
7[Loaded java.security.Permissions from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
8[Loaded java.net.URLConnection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
9[Loaded sun.net.www.URLConnection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
10[Loaded sun.net.www.protocol.file.FileURLConnection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
11[Loaded sun.net.www.MessageHeader from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
12[Loaded java.io.FilePermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
13[Loaded java.io.FilePermission$1 from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
14[Loaded java.io.FilePermissionCollection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
15[Loaded java.security.AllPermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
16[Loaded java.security.UnresolvedPermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
17[Loaded java.security.BasicPermissionCollection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
18[Loaded com.test.jvm.learn01.Main from file:/E:/develop/workspace/workspace-eclipse-jee-neon/JVMLearning/bin/]
19[Loaded sun.launcher.LauncherHelper$FXHelper from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
20[Loaded java.lang.Class$MethodArray from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
21[Loaded java.lang.Void from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
22[Loaded com.test.jvm.learn01.Consts from file:/E:/develop/workspace/workspace-eclipse-jee-neon/JVMLearning/bin/]
23This is Consts
24Hello world
25[Loaded java.lang.Shutdown from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
26[Loaded java.lang.Shutdown$Lock from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
可以看到第22中加载的类com.test.jvm.learn01.Consts。在第一种情况中,定义常量的类并没有被加载,常量s的值"hello world"存放在了类类Main的常量池中,类Main和Consts没有任何关系,甚至编译之后,可以把Consts.class文件删除掉,执行运行程序都能打印出正确结果。 特别地,需要注意的是,如果常量值是在程序运行时生成的,则定义常量的类依然会被引用,如: String s = UUID.randomUUID().toString();
接着反编译Main.class文件,结果:
1$ javap -c Main.class
2Compiled from "Main.java"
3public class com.test.jvm.learn01.Main {
4 public com.test.jvm.learn01.Main();
5 Code:
6 0: aload_0
7 1: invokespecial #8 // Method java/lang/Object."<init>":()V
8 4: return
9
10 public static void main(java.lang.String[]);
11 Code:
12 0: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
13 3: ldc #22 // String Hello world
14 5: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
15 8: return
16}
第三行的注释String Hello World表明已经Hello World常量异常存储到本类的常量池中。
如果在接口中放置常量会怎么样?接口中的变量默认修饰符是public static final,所以推断接口中定义了常量不会导致该接口被主动使用。同样地,如果该常量值是动态生成的,接口会被主动使用。代码和运行结果如下:
1package com.test.jvm.learn01;
2
3public interface MyInterface {
4 int t = 20;
5}
1[Loaded sun.misc.Perf from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
2[Loaded sun.misc.PerfCounter$CoreCounters from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
3[Loaded sun.nio.ch.DirectBuffer from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
4[Loaded java.nio.MappedByteBuffer from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
5[Loaded java.nio.DirectByteBuffer from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
6[Loaded java.nio.LongBuffer from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
7[Loaded java.nio.DirectLongBufferU from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
8[Loaded java.security.PermissionCollection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
9[Loaded java.security.Permissions from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
10[Loaded java.net.URLConnection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
11[Loaded sun.net.www.URLConnection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
12[Loaded sun.net.www.protocol.file.FileURLConnection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
13[Loaded sun.net.www.MessageHeader from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
14[Loaded java.io.FilePermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
15[Loaded java.io.FilePermission$1 from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
16[Loaded java.io.FilePermissionCollection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
17[Loaded java.security.AllPermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
18[Loaded java.security.UnresolvedPermission from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
19[Loaded java.security.BasicPermissionCollection from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
20[Loaded com.test.jvm.learn01.Main from file:/E:/develop/workspace/workspace-eclipse-jee-neon/JVMLearning/bin/]
21[Loaded sun.launcher.LauncherHelper$FXHelper from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
22[Loaded java.lang.Class$MethodArray from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
23[Loaded java.lang.Void from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
2420
25[Loaded java.lang.Shutdown from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
26[Loaded java.lang.Shutdown$Lock from C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]
这里有一些助记符:
- ldc:将int,float或者String类型的常量值从常量池中推送到栈顶,即将被使用。
- bipush:将单字节的常量值推送到栈顶
- sipush:将-32768~32767推送到栈顶
- iconst_1:将常量值1推送到栈顶(iconst_1~iconst_5)
另外,VM选项设置:
-
**-XX:
-
**-XX:
-
**-XX:
-
-XX:+TraceClassLoading表示开启类的追踪