运行时常量和编译期常量
2016年6月13日, 发表于 长春
如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)
问题由来
最近在学习Spring MVC,看的是Spring in action这本书,在书的P155页有下面一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 | @RequestMapping(method = RequestMethod.GET) public String spittles( @RequestParam(value = "max", defaultValue = MAX_LONG_AS_STRING) long max, @RequestParam(value = "count", defaultValue = "20") int count, Model model) { model.addAttribute("spittleList",spittleRepository.findSpittles(max, count)); return "spittles"; } private static final String MAX_LONG_AS_STRING = Long.toString(Long.MAX_VALUE); |
然而编译的时候出现了’Attribute value must be constant’错误,查阅资料后发现是MAX_LONG_AS_STRING是运行时常量而不是编译期常量。
什么是编译期常量呢
- 原始类型字面量,或者String字面量
- 能转型为原始类型字面量,或String字面量的常量
- 一元运算符(+,-,~,!,但不包含++, –) 和1,2组成的表达式
- 多元运算符(*,/和%)和1,2组成的表达式
- 附加运算符( additive operators) (+ 或 -)与之前几条组成的表达式
- 位移运算符(«,», »>)和之前几条组成的表达式
- 关系运算符(<,<=,>,>= ,不包括 instanceof)与之前几条组成的表达式
- 关系运算符(==,!=)与之前几条组成的表达式
位运算符(&, ^, )与之前几条组成的表达式
条件与和条件或运算符(&&, ) 与之前几条组成的表达式 - 三元运算符 (?:)和之前几条组成的表达式
- 带括号的表达式,括号内也是常量表达式
- 引用常量变量的简单变量 §6.5.6.1
- 类中的常量变量引用,使用类的全限定名或类名进行引用(String.class)
很明显Long.toString(Long.MAX_VALUE)表达式不是编译期常量。
解决方案
1 | static final String MAX_LONG_AS_STRING = ""+Long.MAX_VALUE; |
符合第一条规则(原始类型字面量)和第四条规则(多元运算)。
引申:运行时常量、编译期常量、静态块初始化执行顺序
在JAVA中,只有对类的首次主动使用时才会初始化类(这样说法不准确,但暂且可以探究运行时常量和编译期常量的区别),对类中的编译期常量进行访问时不会初始化类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Test{ static { System.out.println("Class Was Loaded !"); } static final String COMPILE_TIME_MAX_LONG = "" + Long.MAX_VALUE; static final String RUN_TIME_MAX_LONG = Long.toString(Long.MAX_VALUE); } public class Main { public static void main(String[] a) { System.out.println(Test.COMPILE_TIME_MAX_LONG); System.out.println(Test.RUN_TIME_MAX_LONG); } } |
运行结果是:
1 2 3 | 9223372036854775807 Class Was Loaded ! 9223372036854775807 |
静态块执行在访问运行时常量和编译期常量之间。
结论
1.编译期常量,并不依赖于类,运行时常量依赖于类,所以对编译时常量的访问不会引发类的初始化,但访问运行时常量会。 2.编译时常量在编译时就可以确定值,运行时常量不能。