My Avatar

胡湘铭的博客

Coding and thinking!

运行时常量和编译期常量

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是运行时常量而不是编译期常量。

什么是编译期常量呢

  1. 原始类型字面量,或者String字面量
  2. 能转型为原始类型字面量,或String字面量的常量
  3. 一元运算符(+,-,~,!,但不包含++, –) 和1,2组成的表达式
  4. 多元运算符(*,/和%)和1,2组成的表达式
  5. 附加运算符( additive operators) (+ 或 -)与之前几条组成的表达式
  6. 位移运算符(«,», »>)和之前几条组成的表达式
  7. 关系运算符(<,<=,>,>= ,不包括 instanceof)与之前几条组成的表达式
  8. 关系运算符(==,!=)与之前几条组成的表达式
  9. 位运算符(&, ^, )与之前几条组成的表达式
  10. 条件与和条件或运算符(&&,   ) 与之前几条组成的表达式
  11. 三元运算符 (?:)和之前几条组成的表达式
  12. 带括号的表达式,括号内也是常量表达式
  13. 引用常量变量的简单变量 §6.5.6.1
  14. 类中的常量变量引用,使用类的全限定名或类名进行引用(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.编译时常量在编译时就可以确定值,运行时常量不能。