变量可重新赋值,常量用final修饰且不可变引用;static final基本类型/String会被编译器内联,引用类型不会;公共常量须public static final,实例final字段仅为不可变状态而非共享常量。

变量声明后可以重新赋值,常量声明后不能修改
Java 中 变量 使用基本类型或引用类型声明,未加修饰符,默认可变;常量 必须用 final 修饰,且通常配合 static 和大写命名(如 MAX_RETRY)。一旦初始化完成,再对 final 变量赋值会触发编译错误:error: cannot assign a value to final variable。
注意:这里的“不可变”仅指引用地址不可变。若 final List,仍可调用 list.add("x") —— 因为对象内容可变,只是 list 不能再指向另一个 List 实例。
变量适合临时存储计算结果,常量适合定义配置或协议值
实际编码中,变量 多用于流程控制、中间计算、用户输入暂存等场景;常量 则用于避免魔法数字/字符串,提升可读与可维护性。
- 用变量:循环计数器
i、HTTP 响应体responseBody、解析后的 JSON 对象data - 用常量:HTTP 状态码
HTTP_OK = 200、数据库表名TABLE_USER = "user"、超时毫秒数TIMEOUT_MS = 5000
特别提醒:如果常量值在运行时才能确定(比如从配置文件读取),就不能用 static final 编译期常量,而要用普通 final 字段 + 构造器/静态块初始化。
立即学习“Java免费学习笔记(深入)”;
局部变量不加访问修饰符,类级常量通常用 public static final
作用域决定声明方式:
- 方法内声明的变量是局部变量,不能加
public/private,也不需要final(除非你明确禁止重赋值) - 类字段若要作为公共常量,必须是
public static final,否则其他类无法直接访问(如MyConfig.API_URL) -
static是关键:没有它,每个实例都会有一份副本,失去“常量”的共享语义
反例:private final String DB_NAME = "prod_db"; 看似常量,但每个对象都存一份,且外部不可见,实际意义接近配置字段而非常量。
public class NetworkConfig {
// ✅ 正确的公共常量
public static final int CONNECT_TIMEOUT_MS = 3000;
public static final String BASE_URL = "https://api.example.com";
// ⚠️ 不推荐:非 static 的 final 字段不是“类常量”
private final String apiKey; // 需通过构造器注入,属于实例状态
public NetworkConfig(String apiKey) {
this.apiKey = apiKey; // 只能赋值一次,但每个实例不同
}
}
编译器会对 static final 基本类型/字符串做内联优化
当常量是 public static final 且类型为基本类型或 String,Javac 会在编译期把所有引用位置替换成字面值。例如:
源码中写 if (status == HttpStatus.OK),而 HttpStatus.OK 是 public static final int OK = 200;,生成的字节码里直接出现 200,不走字段读取。
这个优化带来两个后果:
- 如果常量定义在外部 jar 中,你更新了它的值但没重新编译依赖方,运行时仍用旧值
-
static final引用类型(如List、Map)不会被内联,因为对象地址无法在编译期确定
所以,涉及跨模块共享的常量,尤其是数字和字符串,务必确认上下游是否同步重建。










