美文网首页
编译期常量与运行期常量的区别

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

作者: 大鹏_xzlp | 来源:发表于2018-07-08 16:22 被阅读0次

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

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课程

相关文章

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

    我们先来看一下下面这个程序 运行程序,输出: 很明显访问MyParent2的常量,并不会导致MyParent2的初...

  • 02_JVM学习笔记_类加载机制详解二

    编译期常量与运行期常量的区别 如下代码执行后会输出什么结果? 输出 修改后的代码执行后会输出什么结果? 输出 小结...

  • 编译期常量运行期常量

    所属文集:ClassLoader串烧 编译期常量 在编译阶段,常量就被放到使用这个常量的方法的所在的类的常量池中调...

  • Java知识点总结

    static final 和 static 的区别 static+final 静态常量,编译期常量,编译时就确定值...

  • 常量

    常量  Java中由final修饰的就是常量。如下:  分类:编译期常量和运行时常量 编译期常量:它的值在编译期就...

  • 快速上手Dart语言

    1. 变量与常量 变量声明: 常量声明: 区别:const必须赋值常量值,编译期间必须给一个确定值(编译时)fin...

  • iOS const,static,extern

    const:常量,当有字符串常量的时候,推荐使用const与宏的区别:1 .编译时刻:宏:预编译 const:...

  • 巧用 readonly与 const

    c# 中的常量有两种,分别是编译期常量和运行期常量。通过名字我们就可以看出来它俩在行为上是不同的。在开发中如果这两...

  • ⽅法区(运⾏时常量池)和元空间溢出

    ⽅法区和堆⼀样,是线程共享的区域,包含Class⽂件信息、运⾏时常量池、常量池,运⾏时常量池和常量池的主要区别是具...

  • Java基础-String可以多长

    主要分编译期和运行期。1.编译期 这样定义的字符串在编译期就已经确定了,aaa字符串存放在方法区的常量池。代码编译...

网友评论

      本文标题:编译期常量与运行期常量的区别

      本文链接:https://www.haomeiwen.com/subject/gyuouftx.html