前言
final
是Java中常用的关键字,可以修饰类、方法和变量,类被修饰后,不能被继承;方法被修饰后,不能被重写;变量被修饰后,则值/引用不会改变;
工程中定义一些常量的时候,可以通过接口常量、枚举、类静态常量等提供给外部业务使用,在一些场景下,如果定义的常量发生变更,但外部没有升级依赖的版本号,则会存在外部依旧使用旧常量的场景。
测试
按照接口常量、枚举和类静态常量三种模式,分别查看引用处的字节码:
1. 接口常量
public interface IFruit {
String APPLE = "apple";
String BANANA = "banana";
String ORANGE = "orange";
String PEAR = "pear";
}
public class Test1 {
public void test() {
String a = IFruit.BANANA;
System.out.println(a);
}
}
对应test方法的字节码如下:
public test()V
L0
LINENUMBER 3 L0
LDC "banana" //这里
ASTORE 1
L1
LINENUMBER 4 L1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L2
LINENUMBER 5 L2
RETURN
L3
LOCALVARIABLE this LTest1; L0 L3 0
LOCALVARIABLE a Ljava/lang/String; L1 L3 1
MAXSTACK = 2
MAXLOCALS = 2
可以看到IFruit.BANANA
被直接替换为字符串 banana
;
2. 枚举
public enum Fruit {
APPLE("apple"), BANANA("banana"), ORANGE("orange"), PEAR("pear");
Fruit(String name) {
this.name = name;
}
private final String name;
public String getName() {
return name;
}
}
public class Test2 {
public void test() {
Fruit banana = Fruit.BANANA;
System.out.println(banana.getName());
}
}
对应test方法的字节码如下:
public test()V
L0
LINENUMBER 3 L0
GETSTATIC Fruit.BANANA : LFruit; //这里
ASTORE 1
L1
LINENUMBER 4 L1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL Fruit.getName ()Ljava/lang/String;
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L2
LINENUMBER 5 L2
RETURN
L3
LOCALVARIABLE this LTest2; L0 L3 0
LOCALVARIABLE banana LFruit; L1 L3 1
MAXSTACK = 2
MAXLOCALS = 2
可以看到Fruit.BANANA
的引用依旧存在;
3. 类静态常量
public class Fruit {
public final static String APPLE = "apple";
public final static String BANANA = "banana";
public final static String ORANGE = "orange";
public final static String PEAR = "pear";
}
public class Test3 {
public void test() {
String a = Fruit.BANANA;
System.out.println(a);
}
}
对应test方法的字节码如下:
public test()V
L0
LINENUMBER 3 L0
LDC "banana" //这里
ASTORE 1
L1
LINENUMBER 4 L1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L2
LINENUMBER 5 L2
RETURN
L3
LOCALVARIABLE this LTest3; L0 L3 0
LOCALVARIABLE a Ljava/lang/String; L1 L3 1
MAXSTACK = 2
MAXLOCALS = 2
}
可以看到Fruit.BANANA
被直接替换为字符串 banana
,和接口是一样的,实际上接口中的常量都会被添加上public final static
的修复符;
小结
final
修饰的常量在编译的时候,编译器将会直接将对应的值替换到引用的位置,如果常量提供给外部使用,通过接口常量、类静态常量的模式,尽量不要进行二次修改,如果必须修改,请务必确保使用方升级版本,可以通过枚举方式替换;
如有任何知识产权、版权问题或理论错误,还请指正。
转载请注明原作者及以上信息。