class文件中的ConstantValue属性

先看下面的例子

public class Test {
    public static void main(String[] args) {
        System.out.println(A.static_final_STR);
    }
}
class A{
    public static final String static_final_STR = "static_final";
    public static String static_STR = "static";
    public final String final_STR = "final";
    static {
        System.out.println("---------A---------");
    }
}

会输出什么,简单啊~~~

static_final
---------A---------

然而不是这样的,正确的输出应该是

static_final

tell me why? 怎么回事呢
这里就得说说变量的赋值方式啦

  • 实例变量
  • 类变量

在这里插入图片描述

我们先来看看 Test.class 文件的字节码吧

Classfile /D:/IDEA2018/question/questions/target/classes/com/tq/questions/Test.class
  Last modified 2022-6-26; size 574 bytes
  MD5 checksum 5bb163207d9349accabeb68d18722613
  Compiled from "Test.java"
public class com.tq.questions.Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#21         // java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Class              #24            // com/tq/questions/A
   #4 = String             #25            // static_final,static_final经编译后已经存在于Test.class文件的常量池中
   #5 = Methodref          #26.#27        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #28            // com/tq/questions/Test
   #7 = Class              #29            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               LocalVariableTable
  #13 = Utf8               this
  #14 = Utf8               Lcom/tq/questions/Test;
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               args
  #18 = Utf8               [Ljava/lang/String;
  #19 = Utf8               SourceFile
  #20 = Utf8               Test.java
  #21 = NameAndType        #8:#9          // "<init>":()V
  #22 = Class              #30            // java/lang/System
  #23 = NameAndType        #31:#32        // out:Ljava/io/PrintStream;
  #24 = Utf8               com/tq/questions/A
  #25 = Utf8               static_final
  #26 = Class              #33            // java/io/PrintStream
  #27 = NameAndType        #34:#35        // println:(Ljava/lang/String;)V
  #28 = Utf8               com/tq/questions/Test
  #29 = Utf8               java/lang/Object
  #30 = Utf8               java/lang/System
  #31 = Utf8               out
  #32 = Utf8               Ljava/io/PrintStream;
  #33 = Utf8               java/io/PrintStream
  #34 = Utf8               println
  #35 = Utf8               (Ljava/lang/String;)V
{
  public com.tq.questions.Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/tq/questions/Test;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #4                  // String static_final
         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 10: 0
        line 11: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  args   [Ljava/lang/String;
}
SourceFile: "Test.java"

分析class文件,发现 static_final 经编译后已经存在于Test.class文件的常量池中,这也就是说不需要加载A.class就可以输出被static final 修饰的基本类型或者String

查看A.class 文件的字节码

Classfile /D:/IDEA2018/question/questions/target/classes/com/tq/questions/A.class
  Last modified 2022-6-26; size 684 bytes
  MD5 checksum 49d0c30ab769fae048a60263e228e6ad
  Compiled from "Test.java"
class com.tq.questions.A
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Methodref          #10.#27        // java/lang/Object."<init>":()V
   #2 = String             #28            // final
   #3 = Fieldref           #9.#29         // com/tq/questions/A.final_STR:Ljava/lang/String;
   #4 = String             #30            // static
   #5 = Fieldref           #9.#31         // com/tq/questions/A.static_STR:Ljava/lang/String;
   #6 = Fieldref           #32.#33        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = String             #34            // ---------A---------
   #8 = Methodref          #35.#36        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #9 = Class              #37            // com/tq/questions/A
  #10 = Class              #38            // java/lang/Object
  #11 = Utf8               static_final_STR
  #12 = Utf8               Ljava/lang/String;
  #13 = Utf8               ConstantValue
  #14 = String             #39            // static_final
  #15 = Utf8               static_STR
  #16 = Utf8               final_STR
  #17 = Utf8               <init>
  #18 = Utf8               ()V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               LocalVariableTable
  #22 = Utf8               this
  #23 = Utf8               Lcom/tq/questions/A;
  #24 = Utf8               <clinit>
  #25 = Utf8               SourceFile
  #26 = Utf8               Test.java
  #27 = NameAndType        #17:#18        // "<init>":()V
  #28 = Utf8               final
  #29 = NameAndType        #16:#12        // final_STR:Ljava/lang/String;
  #30 = Utf8               static
  #31 = NameAndType        #15:#12        // static_STR:Ljava/lang/String;
  #32 = Class              #40            // java/lang/System
  #33 = NameAndType        #41:#42        // out:Ljava/io/PrintStream;
  #34 = Utf8               ---------A---------
  #35 = Class              #43            // java/io/PrintStream
  #36 = NameAndType        #44:#45        // println:(Ljava/lang/String;)V
  #37 = Utf8               com/tq/questions/A
  #38 = Utf8               java/lang/Object
  #39 = Utf8               static_final
  #40 = Utf8               java/lang/System
  #41 = Utf8               out
  #42 = Utf8               Ljava/io/PrintStream;
  #43 = Utf8               java/io/PrintStream
  #44 = Utf8               println
  #45 = Utf8               (Ljava/lang/String;)V
{
  public static final java.lang.String static_final_STR;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: String static_final     //多了一个ConstantValue属性

  public static java.lang.String static_STR;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC

  public final java.lang.String final_STR;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_FINAL
    ConstantValue: String final      //多了一个ConstantValue属性

  com.tq.questions.A();
    descriptor: ()V
    flags:
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String final
         7: putfield      #3                  // Field final_STR:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 13: 0
        line 16: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/tq/questions/A;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: ldc           #4                  // String static
         2: putstatic     #5                  // Field static_STR:Ljava/lang/String;
         5: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #7                  // String ---------A---------
        10: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: return
      LineNumberTable:
        line 15: 0
        line 18: 5
        line 19: 13
}
SourceFile: "Test.java"

目前由Oracle公司实现的Javac编译器的选择是,只有同时被final和static修饰的字段才有ConstantValue属性,并且这个变量的数据类型是基本类型或者String,在编译时Javac将会为该变量生成ConstantValue属性,在类加载的准备阶段,会用ConstantValue中的值来初始化,而不是使用数据类型的零值来进行初始化。相反,如果类变量不是基本数据类型或者string,或者final修饰,就会在<clinit>()中进行初始化。

为什么仅限于基本类型和字符串
因为常量池中只能引用到基本类型和string类型的字面量

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗罗的1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值