
Java 的 static 变量属于类而非实例,在同一 JVM 进程内所有类共享同一份内存副本;但每次独立运行一个 main 方法,都会启动全新的 JVM 实例,导致静态变量彼此隔离——这正是 Chocolate1 未感知 brandname 修改的根本原因。
java 的 `static` 变量属于类而非实例,在**同一 jvm 进程内所有类共享同一份内存副本**;但每次独立运行一个 `main` 方法,都会启动全新的 jvm 实例,导致静态变量彼此隔离——这正是 chocolate1 未感知 brandname 修改的根本原因。
你提供的代码看似在“同一个项目”中,实则包含了 三个独立可执行类(Dairy、Chocolateee、Chocolate1),每个都定义了自己的 public static void main(String[] args) 方法。当你在 Eclipse 中分别右键 → “Run As → Java Application” 执行它们时,Eclipse 会为每一次运行启动一个全新的 JVM 进程。而 static 变量的作用域严格限定在单个 JVM 实例 + 单个类加载器内——它并非操作系统级或跨进程的“全局变量”。
因此,执行顺序(Dairy → Chocolateee → Chocolate1)并不构成连续上下文,而是:
- 第一次运行
Dairy.main():JVM 启动 → 加载Dairy.class→ 初始化brandname = "oompaa..lumpaa..Drinks"→ 输出后进程退出 → 静态变量销毁; - 第二次运行
Chocolateee.main():全新 JVM 启动 → 再次加载Dairy.class→brandname重新初始化为原始值 → 然后被修改为"..Yum Drinks"→ 输出后进程退出; - 第三次运行
Chocolate1.main():又一个全新 JVM 启动 →Dairy.class再次加载 →brandname再次从初始值开始 → 此时bnameString在类加载阶段(static String bnameString = Dairy.brandname;)就完成了赋值,取到的是刚初始化的"oompaa..lumpaa..Drinks"→ 输出该值。
✅ 关键结论:
static共享 ≠ 跨 JVM 共享。它只在单次 JVM 生命周期内有效。
✅ 正确验证 static 共享性的写法(单 JVM 内)
要真正观察 static 变量被修改后对其他类的可见性,所有逻辑必须在同一个 JVM 进程中执行:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
package drinks;
public class Dairy {
public static String brandname = " oompaa..lumpaa..Drinks";
// 私有构造器 + 静态内部类实现线程安全懒汉单例(可选增强)
private Dairy() {}
private static class Holder {
static final Dairy INSTANCE = new Dairy();
}
public static Dairy getInstance() { return Holder.INSTANCE; }
}
// 合并在同一主类中,确保单 JVM 执行
public class StaticDemo {
public static void main(String[] args) {
// Step 1: 初始值
System.out.println("Initial: " + Dairy.brandname); // oompaa..lumpaa..Drinks
// Step 2: 修改静态变量(同一 JVM 内)
Dairy.brandname = "..Yum Drinks";
// Step 3: 验证修改生效 —— 直接访问
System.out.println("After change: " + Dairy.brandname); // ..Yum Drinks
// Step 4: 模拟 Chocolate1 的逻辑(在运行时读取,非编译期绑定)
String bnameSnapshot = Dairy.brandname; // 动态获取当前值
System.out.println("Chocolate1-like read: " + bnameSnapshot); // ..Yum Drinks
}
}
⚠️ 特别注意:static 字段的初始化时机陷阱
在你的 Chocolate1 类中:
class Chocolate1 {
static String bnameString = Dairy.brandname; // ❌ 危险!编译期/类加载期快照
public static void main(String[] args) {
System.out.println("The brand name now is " + bnameString);
}
}
此处 bnameString 的赋值发生在 Chocolate1 类的静态初始化阶段(即 Chocolate1 第一次被主动使用时)。此时 JVM 会触发 Dairy 的类初始化(因首次访问其静态字段),但该过程是独立于之前 Chocolateee 运行的另一个 JVM。更重要的是:这个赋值是一次性快照,后续 Dairy.brandname 的任何修改都不会自动同步给 bnameString —— 它已是一个普通字符串变量,与 Dairy.brandname 再无关联。
✅ 正确做法:始终通过 Dairy.brandname 实时访问,而非缓存其副本。
? 最佳实践总结
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 跨对象共享状态 |
private static AtomicInteger counter + public static 访问方法 |
封装+线程安全+可控 |
| 全局配置 |
private static final Properties config + public static String get(String key)
|
防止误改+支持热更新扩展 |
| 单例管理 | 静态内部类模式(如 Holder.INSTANCE) |
懒加载+线程安全+无同步开销 |
| 避免陷阱 | 不在 static 中持有 Context/Connection/大对象引用 |
防止内存泄漏(尤其 Web/Android) |
| 单元测试 | 使用 @AfterEach 清理静态状态(如 reset()) |
避免测试间污染 |
? 记住一句口诀:“Static is per-JVM, not per-project.”
你的 Eclipse 项目里可以有 100 个main方法,但只要不是在同一个 JVM 进程里调用,它们就互不相识——static变量亦如此。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










