编译期常量与运行期常量的区别
例1
package com.study.classloader;
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.m);
}
}
class MyParent2{
public static final String content = "parent";
public static final short s = 127;
public static final int i = 128;
public static final int m = 5;
static {
System.out.println("parent static block");
}
}
执行结果:
5
分析:
静态代码块不会被执行,因为在常量m在编译阶段会被保存在调用常量m的方法所在的类MyTest2的常量池中(MyTest2的class文件中),所以执行时没有对MyParent2进行主动使用,不会触发其初始化,而静态代码块又是要在初始化阶段被执行的。所以这里不会被执行
例2
package com.study.classloader;
import java.util.UUID;
public class MyTest3 {
public static void main(String[] args) {
System.out.println(MyParent3.CONTENT);
}
}
class MyParent3 {
public static final String CONTENT = UUID.randomUUID().toString();
static {
System.out.println("MyParent3 static block");
}
}
执行结果:
MyParent3 static block
2d759b96-5ba8-4be2-87fe-9eeb29949d61
分析:
为什么例1不会打印静态代码块,而例2会打印静态代码块?
从本质上来说两个例子都是在打印的常量,但区别在于常量的值能否在编译器确定下来。例1可以,例2不行,只能在运行期确定值
当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中,这时候在程序运行时,会导致主动使用这个常量所在的类,显然会导致这个类被初始化。
数组创建本质分析
例3
package com.study.classloader;
public class MyTest4 {
public static void main(String[] args) {
MyParent4[] myParent4s = new MyParent4[1];
System.out.println(myParent4s.getClass());
MyParent4[][] myParent4s1 = new MyParent4[1][1];
System.out.println(myParent4s1.getClass());
System.out.println(myParent4s.getClass().getSuperclass());
System.out.println(myParent4s1.getClass().getSuperclass());
}
}
class MyParent4 {
static {
System.out.println("MyParent4 static block");
}
}
结果:
class [Lcom.study.classloader.MyParent4;
class [[Lcom.study.classloader.MyParent4;
class java.lang.Object
class java.lang.Object
分析:
没有执行MyParent4的初始化,并不属于主动使用的7种类型的任何一种。
[Lcom.study.classloader.MyParent4为数组的类型,我们并没有在自己的代码中定义过这个类型,这是JVM帮助我们在运行期生成出来数组的类型
例4
基本数据类型数组
package com.study.classloader;
public class MyTest4 {
public static void main(String[] args) {
MyParent4[] myParent4s = new MyParent4[1];
System.out.println(myParent4s.getClass());
MyParent4[][] myParent4s1 = new MyParent4[1][1];
System.out.println(myParent4s1.getClass());
System.out.println(myParent4s.getClass().getSuperclass());
System.out.println(myParent4s1.getClass().getSuperclass());
System.out.println("=====");
int[] ints = new int[1];
System.out.println(ints.getClass());
System.out.println(ints.getClass().getSuperclass());
}
}
class MyParent4 {
static {
System.out.println("MyParent4 static block");
}
}
结果:
class [Lcom.study.classloader.MyParent4;
class [[Lcom.study.classloader.MyParent4;
class java.lang.Object
class java.lang.Object
=====
class [I
class java.lang.Object
对于数组实例来说,其类型是由JVM在运行期动态生成的,表示为[Lcom.study.classloader.MyParent4这种形式。动态生成的类型,其父类型就是Object。
对于数组来说,JavaDoc经常将构成数组的元素为Component,实际上就是将数组降低一个维度后的类型。
反编译分析
➜ jvm javap -c target.classes.com.study.classloader.MyTest4
警告: 文件 ./target/classes/com/study/classloader/MyTest4.class 不包含类 target.classes.com.study.classloader.MyTest4
Compiled from "MyTest4.java"
public class com.study.classloader.MyTest4 {
public com.study.classloader.MyTest4();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: anewarray #2 // class com/study/classloader/MyParent4
4: astore_1
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: aload_1
9: invokevirtual #4 // Method java/lang/Object.getClass:()Ljava/lang/Class;
12: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
15: iconst_1
16: iconst_1
17: multianewarray #6, 2 // class "[[Lcom/study/classloader/MyParent4;"
21: astore_2
22: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
25: aload_2
26: invokevirtual #4 // Method java/lang/Object.getClass:()Ljava/lang/Class;
29: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
32: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
35: aload_1
36: invokevirtual #4 // Method java/lang/Object.getClass:()Ljava/lang/Class;
39: invokevirtual #7 // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
42: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
45: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
48: aload_2
49: invokevirtual #4 // Method java/lang/Object.getClass:()Ljava/lang/Class;
52: invokevirtual #7 // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
55: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
58: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
61: ldc #8 // String =====
63: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
66: iconst_1
67: newarray int
69: astore_3
70: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
73: aload_3
74: invokevirtual #4 // Method java/lang/Object.getClass:()Ljava/lang/Class;
77: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
80: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
83: aload_3
84: invokevirtual #4 // Method java/lang/Object.getClass:()Ljava/lang/Class;
87: invokevirtual #7 // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
90: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
93: return
}
助记符:
anewarray:表示创建一个引用类型的(如类、接口、数组)数组,并将其引用值压入栈顶
newarray:表示创建一个指定的原始类型(如int、float、char等)的数组,并将其引用值压入栈顶