final常量 java_java final 关键字 -- 常量部分

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."":()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              

#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              

#22 = Utf8               SourceFile

#23 = Utf8               FinalTest.java

#24 = NameAndType        #10:#11        //  "":()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."":()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."":()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."":()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              

#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        //  "":()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."":()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 "":()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."":()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."":()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              

#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        //  "":()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."":()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 "":()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 命令

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值