从虚拟机层面看Java 中的常量

编译期常量

我们知道在Java的类中通过static final 修饰的变量在程序运行过程中是不能够改变的,我们称之为常量。常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,从而也不会导致定义常量的类的初始化。我们看下面这个示例:

package com.geekymv.test.jvm;

public class Test02ConstClass {

    public static void main(String[] args) {

        System.out.println(Constant.COUNT);
    }
}

class Constant {

    public static final String COUNT = "hello world";

    static {
        System.out.println("const class static block");
    }
}

运行main方法,输入结果如下:

hello world

Constant 类中的static 代码块并没有执行!Test02ConstClass 类虽然在源码中引用了Constant 类中的常量COUNT,但其实在编译阶段 jvm 通过常量传播优化,已经将此常量的值hello world存储到了调用类(Test02ConstClass)的常量池中了。以后Test02ConstClass 对常量Constant.COUNT 的引用都变成了对自身常量池的引用,也就是说这两个类在编译成Class 之后就不存在任何联系了。我们甚至可以将Constant 类的 Constant.class 文件删除,Test02ConstClass 类依然可以正常执行。

我们可以通过JDK 自带的反编译工具javap,通过它可以查看java编译器为我们生成的字节码。

 javap -c com.geekymv.test.jvm.Test02ConstClass

反编译后的结果

Compiled from "Test02ConstClass.java"
public class com.geekymv.test.jvm.Test02ConstClass {
  public com.geekymv.test.jvm.Test02ConstClass();
    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: ldc           #4                  // String hello world
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

通过这行代码 3: ldc #4 // String hello world
我们可以看出常量Constant.COUNT 的值已经是确定了就是hello world 。
助记符ldc表示将int,float或者String类型的常量值从常量池中推送至栈顶供程序使用。

运行期常量

当一个常量的值非编译期间可以确定,那么其值就不会被放到调用类的常量池中。这时在程序运行时,会导致主动使用这个常量所在类,显然会导致这个类被初始化。

package com.geekymv.test.jvm;
import java.util.Random;

public class Test03ConstClass {
    public static void main(String[] args) {
        System.out.println(ConstClass03.COUNT);
    }
}

class ConstClass03 {
    public static final int COUNT = new Random().nextInt(100);
    static {
        System.out.println("const class static block");
    }
}

运行main方法,输入结果如下:

const class static block
51

运行时常量相对于常量来说,有一个很重要的特征是:动态性。Java语言并不要求常量一定只能在编译期产生,运行期间也可能产生新的常量,这些常量被放在运行时常量池中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值