编译期常量与运行期常量的区别

2018-07-08  本文已影响0人  大鹏_xzlp

我们先来看一下下面这个程序

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的常量,并不会导致MyParent2的初始化,我们去看一下MyTest2的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,对应的就是我们的System.out.println(MyParent2.i);这一句输出的3,iconst_3表示将int类型推送至栈顶,这里面并未看到MyParent2的身影,然后我们将MyParent2中的i修改成变量,再运行,查看class文件,比较下区别

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

这里我们就可以发现以下结论:

注意:这里指的是将常量存放到MyTest2的常量池中,之后MyTest2与MyParent2就没有任何关系了,甚至我们可以将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");

    }
}

这个程序也是对常量的调用,运行程序,输出:

Myparent3 static code
72a9293a-5c77-468d-8aa7-a6dd0e02a429

可以看到MyParent3被初始化了,所以和编译期常量不同

常用助记符

ldc:表示将int,float或是String类型的常量值从常量池中推送至栈顶
bipush:表示将单字节(-128 ~ 127)的常量值推送至栈顶 (比如short类型)
sipush:表示将短zheng'xing整形常量值(-32768 ~ 32767)推送至栈顶
iconst_1:表示将int类型推送至栈顶,(iconst_m1 ~ iconst_5)


参考资料:
圣思园JVM课程

上一篇 下一篇

猜你喜欢

热点阅读