我們先來看一下下面這個程序
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.i);
}
}
class MyParent2 {
public static final int i = 3;
static{
System.out.println("Myparent2 static block");
}
}
運行程序,輸出:
3
很明顯訪問MyParent2的常量雁芙,并不會導(dǎo)致MyParent2的初始化,我們?nèi)タ匆幌翸yTest2的class文件
運行javap -c com.shengsiyuan.jvm.classloader.MyTest2
命令查看詳情
public class com.shengsiyuan.jvm.classloader.MyTest2 {
public com.shengsiyuan.jvm.classloader.MyTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_3
4: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
7: return
}
這里面有很多助記符,我們看到其中一個是iconst_3
汁掠,對應(yīng)的就是我們的System.out.println(MyParent2.i);
這一句輸出的3史隆,iconst_3
表示將int類型推送至棧頂魂务,這里面并未看到MyParent2的身影,然后我們將MyParent2中的i修改成變量泌射,再運行粘姜,查看class文件,比較下區(qū)別
Compiled from "MyTest2.java"
public class com.shengsiyuan.jvm.classloader.MyTest2 {
public com.shengsiyuan.jvm.classloader.MyTest2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #3 // Field com/shengsiyuan/jvm/classloader/MyParent2.i:I
6: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
9: return
}
可以看到輸出位置變成了
3: getstatic #3 // Field com/shengsiyuan/jvm/classloader/MyParent2.i:I
這里我們就可以發(fā)現(xiàn)以下結(jié)論:
- 常量在編譯階段會存到調(diào)用這個常量的方法所在的類的常量池中
- 本質(zhì)上熔酷,調(diào)用類并沒有直接引用到定義常量的類孤紧,因此并不會觸發(fā)定義常量的類的初始化
注意:這里指的是將常量存放到MyTest2的常量池中,之后MyTest2與MyParent2就沒有任何關(guān)系了拒秘,甚至我們可以將MyParent2的class文件刪除
運行期常量
我們再來看一下下面這個程序
public class MyTest3 {
public static void main(String[] args) {
System.out.println(MyParent3.str);
}
}
class MyParent3{
public static final String str = UUID.randomUUID().toString();
static {
System.out.println("Myparent3 static code");
}
}
這個程序也是對常量的調(diào)用号显,運行程序,輸出:
Myparent3 static code
72a9293a-5c77-468d-8aa7-a6dd0e02a429
可以看到MyParent3被初始化了躺酒,所以和編譯期常量不同
- 當(dāng)一個常量的值并非編譯期間可以確定的押蚤,那么其值就不會被放到調(diào)用類的常量池中,
- 這時在程序運行時阴颖,會導(dǎo)致主動使用這個常量所在的類活喊,顯然會導(dǎo)致這個類被初始化。
常用助記符
ldc:表示將int,float或是String類型的常量值從常量池中推送至棧頂
bipush:表示將單字節(jié)(-128 ~ 127)的常量值推送至棧頂 (比如short類型)
sipush:表示將短zheng'xing整形常量值(-32768 ~ 32767)推送至棧頂
iconst_1:表示將int類型推送至棧頂,(iconst_m1 ~ iconst_5)
參考資料:
圣思園JVM課程