在java中,static final修饰的是常量。根据编译器的不同行为,常量又可分为编译时常量和运行时常量。
举例说明吧
- public static final int a = 10就是一个编译时常量,在编译后的符号中找不到a,所有对a的引用都被替换成了20;它是不依赖于类的,在编译时就可以确定值。
- public static final int b = “hello”.length()就是一个运行时常量;它是依赖于类的,它的赋值会引起类的初始化。
- 归根结底还是javac的编译机制导致了这样的结果。当涉及到方法的调用时,必须在运行的时候才能确定结果。
示例代码:
public class TestConstant {
public static void main(String[] args) {
System.out.println(Test.a);
System.out.println(Test.s);
// System.out.println(Test.b);
// System.out.println(Test.a2);
// System.out.println(Test.a3);
// System.out.println(Test.e);
}
}
class Test{
static{
System.out.println("Test正在被初始化");
}
public static final int a = 10;
public static final int b = "test".length();
public static final String s = "world";
public static Integer a2 = 20;
public static final A a3 = new A();
public static final E e = E.A;
}
class A{
}
enum E{
A,B,C,D,E,F,G
}
挨个运行main函数中的打印语句,我们会发现编译时常量不会引起类的初始化,而运行时常量会引起类的初始化。
通过运行我们还可以得出如下结论,编译时常量必须定义为基本类型或者String,而不可能是引用类型或者包装类、枚举。
一道面试题:编译时常量存在什么样的风险?
编译时常量,在使用时会被直接写成值,而不会再从原来的类中读取。这样就会导致问题的产生:如果A类定义了常量,B类使用了常量,并且都进行了编译。当A类的源码被改动了,常量的值发生了变化。我们对A类进行了重新编译,但是没有对B类进行重新编译;那么B类中用到的是原来A类中的常量值,即旧值。这就导致了风险的发生。