<commit_before>﻿#运行时常量和编译时常量


最近在学习Spring MVC，看的是Spring in action这本书，在书的P155页有下面一段代码:
```java
@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](https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.5.6.1)
14. 类中的常量变量引用，使用类的全限定名或类名进行引用（String.class)


很明显Long.toString(Long.MAX_VALUE)表达式不是编译期常量。

###解决方案

```java
static final String MAX_LONG_AS_STRING = ""+Long.MAX_VALUE;
```
符合第一条规则（原始类型字面量）和第四条规则（多元运算）。

##引申：运行时常量、编译期常量、静态块初始化执行顺序
在JAVA中，只有对类的首次主动使用时才会初始化类（这样说法不准确，但暂且可以探究运行时常量和编译期常量的区别），对类中的编译期常量进行访问时不会初始化类：
```java
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);
    }
}
```

运行结果是：
```
9223372036854775807
Class Was Loaded !
9223372036854775807
```
静态块执行在访问运行时常量和编译期常量之间。
##结论
1.编译期常量，并不依赖于类，运行时常量依赖于类，所以对编译时常量的访问不会引发类的初始化，但访问运行时常量会。
2.编译时常量在编译时就可以确定值，运行时常量不能。




<commit_msg>update post<commit_after>---
layout: post_layout
title: 运行时常量和编译期常量
time: 2016年6月13日
location: 长春
pulished: true
excerpt_separator: "##"
---

最近在学习Spring MVC，看的是Spring in action这本书，在书的P155页有下面一段代码:
```java
@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](https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.5.6.1)
14. 类中的常量变量引用，使用类的全限定名或类名进行引用（String.class)


很明显Long.toString(Long.MAX_VALUE)表达式不是编译期常量。

###解决方案

```java
static final String MAX_LONG_AS_STRING = ""+Long.MAX_VALUE;
```
符合第一条规则（原始类型字面量）和第四条规则（多元运算）。

##引申：运行时常量、编译期常量、静态块初始化执行顺序
在JAVA中，只有对类的首次主动使用时才会初始化类（这样说法不准确，但暂且可以探究运行时常量和编译期常量的区别），对类中的编译期常量进行访问时不会初始化类：
```java
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);
    }
}
```

运行结果是：
```
9223372036854775807
Class Was Loaded !
9223372036854775807
```
静态块执行在访问运行时常量和编译期常量之间。
##结论
1.编译期常量，并不依赖于类，运行时常量依赖于类，所以对编译时常量的访问不会引发类的初始化，但访问运行时常量会。
2.编译时常量在编译时就可以确定值，运行时常量不能。

{'fraction_non_alphanumeric': 0.14430082256169213, 'fraction_numerical': 0.03830787309048179, 'mean_word_length': 5.045454545454546, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 19, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3397442', 'n_tokens_mistral': 2463, 'n_tokens_neox': 2632, 'n_words': 270}