java中只有编译期能确定值的static final基本类型或string变量才是常量表达式,如static final int a = 1 + 2;或"abc" + "def";非static final、含方法调用或new对象的均不符合。

Java中哪些变量算常量表达式?编译器怎么判断
只有满足特定条件的 final 变量才被当作常量表达式,不是所有 final 都行。关键看值是否能在编译期完全确定。
常见错误现象:声明了 final int x = getValue();,但 getValue() 是普通方法,结果 x 不能用在 case 或注解里,报错 constant expression required。
-
static final基本类型或String,且初始化是字面量或其它常量表达式(如static final int A = 1 + 2;)→ ✅ 是常量表达式 -
final int x = 42;(非 static)→ ❌ 不是常量表达式(仅限局部变量时可作 switch case,但不算“编译期常量”) -
static final String s = new String("abc");→ ❌ 不是(new触发运行期对象创建) -
static final String s = "a" + "b";→ ✅ 是(字符串拼接在编译期折叠)
为什么 switch 里的 case 必须是常量表达式
因为 switch 编译后可能转成跳转表(tableswitch)或二分查找(lookupswitch),这些指令要求 case 值在 class 文件里固定、不可变。
使用场景:写枚举替代方案、配置驱动的分支逻辑时,如果误用运行期变量,会直接编译失败,而不是运行时报错。
立即学习“Java免费学习笔记(深入)”;
- 允许:
case MyConstants.STATUS_OK:(前提是STATUS_OK是static final int字面量初始化) - 不允许:
case config.getTimeout():(哪怕getTimeout()返回final字段,仍是方法调用) - 注意:从 Java 14 开始支持
switch表达式,但 case 值约束没变,依然只认常量表达式
常量表达式和普通 final 变量的性能/兼容性差异
常量表达式会被内联(inlined):所有引用处直接替换成字面值。普通 final 字段只是不可再赋值,但访问仍走字段读取。
这影响字节码大小、JIT 优化机会,也影响 API 兼容性——改一个常量表达式的值,所有依赖它的类必须重新编译,否则用的是旧值。
- 示例:
public static final int MAX_RETRY = 3;→ 调用方字节码里直接写3,不是读字段 - 而
public final int MAX_RETRY = 3;(非 static)→ 每次都执行getfield指令 - 如果把
MAX_RETRY从3改成5,但不重编译调用方,它仍用3(内联导致的“假静态”)
注解参数为什么只能用常量表达式
注解信息要写进 class 文件的 RuntimeVisibleAnnotations 属性,这部分结构在编译期就固化,无法存运行期计算结果。
容易踩的坑:想用配置类里的 final 字段传给注解,比如 @Value("${port}") 不能直接塞进自定义注解的 int port() 参数里。
- 合法:
@MyAnno(value = 8080)、@MyAnno(value = Level.DEBUG.ordinal())(ordinal()是编译期常量) - 非法:
@MyAnno(value = SystemProperties.get("port"))、@MyAnno(value = Constants.PORT)(若PORT初始化含方法调用或非字面量) - 字符串拼接要注意:
"prefix" + SOME_CONST合法的前提是SOME_CONST本身是常量表达式
final 写法,只要带一点点运行期痕迹,就会掉出常量表达式范围。









