为了账号安全,请及时绑定邮箱和手机立即绑定

java final 关键字 -- 常量部分

标签:
Java

java中final 定义常量有两种方式,一种是静态常量,一种是实例常量,下面分别介绍

静态常量的定义又可以分两种情况:一种是定义时赋值,一种是静态方法块中赋值

定义时赋值,如下代码:

/**

* Created by Jokul on 2018/1/17.

*/

public class FinalTest {

   private static final String A = "av";

   

   public static void main(String[] args) {

       System.out.println(A);

   }

}

此种情况,由编译器在编译时就将 “av” 常量值放入了常量区,不存在A这个变量。

静态方法块中赋值,如下代码:

/**

* Created by Jokul on 2018/1/17.

*/

public class FinalTest {

   private static final String A;

   static {

       A = "av";

   }

   public static void main(String[] args) {

       System.out.println(A);

   }

}

看一下class文件的指令,如下所示:

Classfile /D:/ideaProjects/final-test/target/classes/com/test/java/FinalTest.class

 Last modified 2018-1-17; size 652 bytes

 MD5 checksum b2a05ed23493ecfc5d34dd7a7a64f41a

 Compiled from "FinalTest.java"

public class com.test.java.FinalTest

 SourceFile: "FinalTest.java"

 minor version: 0

 major version: 49

 flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

  #1 = Methodref          #7.#24         //  java/lang/Object."<init>":()V

  #2 = Fieldref           #25.#26        //  java/lang/System.out:Ljava/io/PrintStream;

  #3 = Fieldref           #6.#27         //  com/test/java/FinalTest.A:Ljava/lang/String;

  #4 = Methodref          #28.#29        //  java/io/PrintStream.println:(Ljava/lang/String;)V

  #5 = String             #30            //  av

  #6 = Class              #31            //  com/test/java/FinalTest

  #7 = Class              #32            //  java/lang/Object

  #8 = Utf8               A

  #9 = Utf8               Ljava/lang/String;

 #10 = Utf8               <init>

 #11 = Utf8               ()V

 #12 = Utf8               Code

 #13 = Utf8               LineNumberTable

 #14 = Utf8               LocalVariableTable

 #15 = Utf8               this

 #16 = Utf8               Lcom/test/java/FinalTest;

 #17 = Utf8               main

 #18 = Utf8               ([Ljava/lang/String;)V

 #19 = Utf8               args

 #20 = Utf8               [Ljava/lang/String;

 #21 = Utf8               <clinit>

 #22 = Utf8               SourceFile

 #23 = Utf8               FinalTest.java

 #24 = NameAndType        #10:#11        //  "<init>":()V

 #25 = Class              #33            //  java/lang/System

 #26 = NameAndType        #34:#35        //  out:Ljava/io/PrintStream;

 #27 = NameAndType        #8:#9          //  A:Ljava/lang/String;

 #28 = Class              #36            //  java/io/PrintStream

 #29 = NameAndType        #37:#38        //  println:(Ljava/lang/String;)V

 #30 = Utf8               av

 #31 = Utf8               com/test/java/FinalTest

 #32 = Utf8               java/lang/Object

 #33 = Utf8               java/lang/System

 #34 = Utf8               out

 #35 = Utf8               Ljava/io/PrintStream;

 #36 = Utf8               java/io/PrintStream

 #37 = Utf8               println

 #38 = Utf8               (Ljava/lang/String;)V

{

 public com.test.java.FinalTest();

   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 6: 0

     LocalVariableTable:

       Start  Length  Slot  Name   Signature

              0       5     0  this   Lcom/test/java/FinalTest;

 public static void main(java.lang.String[]);

   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: getstatic     #3                  // Field A:Ljava/lang/String;

        6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

        9: return        

     LineNumberTable:

       line 23: 0

       line 24: 9

     LocalVariableTable:

       Start  Length  Slot  Name   Signature

              0      10     0  args   [Ljava/lang/String;

 static {};

   flags: ACC_STATIC

   Code:

     stack=1, locals=0, args_size=0

        0: ldc           #5                  // String av

        2: putstatic     #3                  // Field A:Ljava/lang/String;

        5: return        

     LineNumberTable:

       line 12: 0

       line 13: 5

}

从class文件的指令中可以看出(看黄底标示的指令),在类被加载时 执行static{} 代码块时会将 "av" 字面值赋值给 静态常量A,然后在main() 方法中获取了常量A的值,所以静态方法块对静态常量赋值是在类加载阶段完成的

静态常量相关内容介绍完了,下面介绍实例常量,实例常量跟静态常量一样也有两种赋值方式,一种是定义时赋值,一种是构造函数中赋值,但最终编译后都是构造函数中赋值,我们下面一起看一下

先看定义时赋值,代码如下:

/**

* Created by Jokul on 2018/1/17.

*/

public class FinalTest {

   private final String b = "bv";

   public static void main(String[] args) {

       FinalTest test = new FinalTest();

       System.out.println(test.b);

   }

}

class指令如下:

Classfile /D:/ideaProjects/final-test/target/classes/com/test/java/FinalTest.class

Last modified 2018-1-17; size 708 bytes

MD5 checksum a28964e208e3fd7b3e8368a8688be37d

Compiled from "FinalTest.java"

public class com.test.java.FinalTest

SourceFile: "FinalTest.java"

minor version: 0

major version: 49

flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

#1 = Methodref          #9.#27         //  java/lang/Object."<init>":()V

#2 = String             #28            //  bv

#3 = Fieldref           #4.#29         //  com/test/java/FinalTest.b:Ljava/lang/String;

#4 = Class              #30            //  com/test/java/FinalTest

#5 = Methodref          #4.#27         //  com/test/java/FinalTest."<init>":()V

#6 = Fieldref           #31.#32        //  java/lang/System.out:Ljava/io/PrintStream;

#7 = Methodref          #9.#33         //  java/lang/Object.getClass:()Ljava/lang/Class;

#8 = Methodref          #34.#35        //  java/io/PrintStream.println:(Ljava/lang/String;)V

#9 = Class              #36            //  java/lang/Object

#10 = Utf8               b

#11 = Utf8               Ljava/lang/String;

#12 = Utf8               ConstantValue

#13 = Utf8               <init>

#14 = Utf8               ()V

#15 = Utf8               Code

#16 = Utf8               LineNumberTable

#17 = Utf8               LocalVariableTable

#18 = Utf8               this

#19 = Utf8               Lcom/test/java/FinalTest;

#20 = Utf8               main

#21 = Utf8               ([Ljava/lang/String;)V

#22 = Utf8               args

#23 = Utf8               [Ljava/lang/String;

#24 = Utf8               test

#25 = Utf8               SourceFile

#26 = Utf8               FinalTest.java

#27 = NameAndType        #13:#14        //  "<init>":()V

#28 = Utf8               bv

#29 = NameAndType        #10:#11        //  b:Ljava/lang/String;

#30 = Utf8               com/test/java/FinalTest

#31 = Class              #37            //  java/lang/System

#32 = NameAndType        #38:#39        //  out:Ljava/io/PrintStream;

#33 = NameAndType        #40:#41        //  getClass:()Ljava/lang/Class;

#34 = Class              #42            //  java/io/PrintStream

#35 = NameAndType        #43:#44        //  println:(Ljava/lang/String;)V

#36 = Utf8               java/lang/Object

#37 = Utf8               java/lang/System

#38 = Utf8               out

#39 = Utf8               Ljava/io/PrintStream;

#40 = Utf8               getClass

#41 = Utf8               ()Ljava/lang/Class;

#42 = Utf8               java/io/PrintStream

#43 = Utf8               println

#44 = Utf8               (Ljava/lang/String;)V

{

 public com.test.java.FinalTest();

flags: ACC_PUBLIC

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 bv

        7: putfield      #3                  // Field b:Ljava/lang/String;

10: return

LineNumberTable:

line 6: 0

line 9: 4

LocalVariableTable:

Start  Length  Slot  Name   Signature

0      11     0  this   Lcom/test/java/FinalTest;

 public static void main(java.lang.String[]);

flags: ACC_PUBLIC, ACC_STATIC

Code:

stack=2, locals=2, args_size=1

0: new           #4                  // class com/test/java/FinalTest

3: dup

4: invokespecial #5                  // Method "<init>":()V

7: astore_1

8: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;

11: aload_1

12: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;

15: pop

       16: ldc           #2                  // String bv

18: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

21: return

LineNumberTable:

line 21: 0

line 23: 8

line 24: 21

LocalVariableTable:

Start  Length  Slot  Name   Signature

0      22     0  args   [Ljava/lang/String;

8      14     1  test   Lcom/test/java/FinalTest;

}

请看标黄的指令行,我们在代码中并未编写构造函数,更没有在构造函数中给实例常量b 赋值,编译器自动都给加上了,同时编译器在使用常量时做优化,直接使用了常量区的常量值,请看main方法中标黄的指令,接下来再来看我们主动在构造函数中给实例常量b赋值,代码如下:

/**

* Created by Jokul on 2018/1/17.

*/

public class FinalTest {

   private final String b;

   public FinalTest(){

       b = "bv";

   }

   public static void main(String[] args) {

       FinalTest test = new FinalTest();

       System.out.println(test.b);

   }

}

class指令如下:

Classfile /D:/ideaProjects/final-test/target/classes/com/test/java/FinalTest.class

Last modified 2018-1-17; size 642 bytes

MD5 checksum 3254d93a7430c59105e2a9532bd3d95b

Compiled from "FinalTest.java"

public class com.test.java.FinalTest

SourceFile: "FinalTest.java"

minor version: 0

major version: 49

flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

#1 = Methodref          #8.#25         //  java/lang/Object."<init>":()V

#2 = String             #26            //  bv

#3 = Fieldref           #4.#27         //  com/test/java/FinalTest.b:Ljava/lang/String;

#4 = Class              #28            //  com/test/java/FinalTest

#5 = Methodref          #4.#25         //  com/test/java/FinalTest."<init>":()V

#6 = Fieldref           #29.#30        //  java/lang/System.out:Ljava/io/PrintStream;

#7 = Methodref          #31.#32        //  java/io/PrintStream.println:(Ljava/lang/String;)V

#8 = Class              #33            //  java/lang/Object

#9 = Utf8               b

#10 = Utf8               Ljava/lang/String;

#11 = Utf8               <init>

#12 = Utf8               ()V

#13 = Utf8               Code

#14 = Utf8               LineNumberTable

#15 = Utf8               LocalVariableTable

#16 = Utf8               this

#17 = Utf8               Lcom/test/java/FinalTest;

#18 = Utf8               main

#19 = Utf8               ([Ljava/lang/String;)V

#20 = Utf8               args

#21 = Utf8               [Ljava/lang/String;

#22 = Utf8               test

#23 = Utf8               SourceFile

#24 = Utf8               FinalTest.java

#25 = NameAndType        #11:#12        //  "<init>":()V

#26 = Utf8               bv

#27 = NameAndType        #9:#10         //  b:Ljava/lang/String;

#28 = Utf8               com/test/java/FinalTest

#29 = Class              #34            //  java/lang/System

#30 = NameAndType        #35:#36        //  out:Ljava/io/PrintStream;

#31 = Class              #37            //  java/io/PrintStream

#32 = NameAndType        #38:#39        //  println:(Ljava/lang/String;)V

#33 = Utf8               java/lang/Object

#34 = Utf8               java/lang/System

#35 = Utf8               out

#36 = Utf8               Ljava/io/PrintStream;

#37 = Utf8               java/io/PrintStream

#38 = Utf8               println

#39 = Utf8               (Ljava/lang/String;)V

{

 public com.test.java.FinalTest();

flags: ACC_PUBLIC

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 bv

        7: putfield      #3                  // Field b:Ljava/lang/String;

10: return

LineNumberTable:

line 15: 0

line 16: 4

line 17: 10

LocalVariableTable:

Start  Length  Slot  Name   Signature

0      11     0  this   Lcom/test/java/FinalTest;

 public static void main(java.lang.String[]);

flags: ACC_PUBLIC, ACC_STATIC

Code:

stack=2, locals=2, args_size=1

0: new           #4                  // class com/test/java/FinalTest

3: dup

4: invokespecial #5                  // Method "<init>":()V

7: astore_1

8: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;

11: aload_1

       12: getfield      #3                  // Field b:Ljava/lang/String;

15: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

18: return

LineNumberTable:

line 21: 0

line 23: 8

line 24: 18

LocalVariableTable:

Start  Length  Slot  Name   Signature

0      19     0  args   [Ljava/lang/String;

8      11     1  test   Lcom/test/java/FinalTest;

}

从以上标黄指令可以看出,给实例常量b赋值仍然是在构造函数中进行的,这次编译器并未对使用实例常量的地方进行编译优化,而是采用 getfield 来获取实例常量的值。

综上所述,可以得出如下表所示的结论:

常量类型

定义赋值

构造函数赋值

静态常量

编译时存储到常量池

直接引用常量池中的值

构造函数中赋值给常量字段

从常量字段中获取

实例常量

编译时存储到常量池,同时构造函数进行初始化(如果没有构造函数会放在默认构造函数中)

直接引用常量池中的值

构造函数中赋值给常量字段

从常量字段中获取

发生的阶段如下表所示:

阶段

静态常量

实例常量

编译阶段

字面值

字面值

类加载阶段

静态构造函数(静态块)赋值

类初始化阶段

定义赋值和构造函数赋值

自己原创,希望跟大家交流讨论,有不当的地方请指正

注:本文中使用到的查看class文件的工具,使用的是 javap –verbose 命令


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消