编译期常量是在编译阶段就能确定值并嵌入字节码的final基本类型或String变量,需满足声明即初始化、类型合法、表达式为常量表达式四条件,影响赋值、switch、字符串优化等行为。

编译期常量是什么
编译期常量(Compile-time Constant)是指在代码编译阶段就能完全确定其值、且该值被直接嵌入字节码的常量。它不是运行时才计算出来的,而是由编译器在编译时“固化”下来的值。这类常量能参与编译优化,比如方法内联、条件剪枝、类型窄化赋值等。
成为编译期常量的四个必要条件
一个变量要被Java编译器认定为编译期常量,必须同时满足以下四点:
-
声明为 final:必须用
final修饰,不可重新赋值 -
是基本类型或 String 类型:仅限
byte/short/int/long/char/float/double/boolean和String;不能是其他引用类型(如Integer、ArrayList等) -
必须在声明时直接初始化:不能是“空白 final”(即不能先声明
final int x;,再在构造器中赋值) -
初始化表达式必须是常量表达式:即只包含字面量、强制类型转换、简单运算符(
+ - * / % & ^ | > >= == != && || ?:)、括号以及对其他编译期常量的引用
常见正确与错误示例对比
下面这些写法能被识别为编译期常量:
-
final int MAX = 100;✅ 字面量直接初始化 -
final String NAME = "Java";✅ String 字面量 -
final long SIZE = 1024L * 1024;✅ 常量表达式(乘法+字面量) -
final char FLAG = 'A' + 1;✅ 字符字面量参与常量运算,结果仍是常量 -
final byte B = (byte)(127 + 1);✅ 强制类型转换 + 常量表达式(但注意:若超出byte范围会编译失败)
而这些写法不是编译期常量:
立即学习“Java免费学习笔记(深入)”;
-
final int x; x = 5;❌ 空白 final,未在声明处初始化 -
final Integer Y = 10;❌ 是包装类,非基本类型或 String -
final double PI = Math.PI;❌Math.PI是运行时静态字段,非字面量或常量表达式 -
final int Z = someMethod();❌ 方法调用无法在编译期求值 -
final int A = a + b;(其中a、b是普通变量)❌ 含非常量变量,表达式不满足常量表达式定义
编译期常量的实际影响
它不只是语义概念,会直接影响编译行为和运行表现:
-
允许窄类型赋值:如
final int x = 100;→byte b = x;合法(因为编译器确认 100 在byte范围内);但换成普通变量就会报错 -
参与 switch 表达式:JDK 14+ 的
switch支持String和枚举,但分支 case 值仍需是编译期常量 -
字符串拼接优化:如
"Hello" + "World"在编译期合并为"HelloWorld";若其中任一操作数是非编译期常量(如final String s = getStr();),则拼接推迟到运行时 - 类加载时就确定值:编译期常量的值会写入常量池,被其他类直接引用时,甚至不触发定义类的初始化
基本上就这些。关键不在“是不是 final”,而在于“能不能在编译那一刻算出唯一确定的值”。










