静态变量存于方法区(JDK 8+元空间),类加载时初始化且全局唯一;非静态变量存于堆,每次new独立分配。静态变量通过类名访问,非静态必须通过实例访问,静态方法中不可直接访问非静态成员。

静态变量和非静态变量的内存位置不同
静态变量(static 修饰)存在方法区(JDK 8+ 是元空间),类加载时初始化,整个 JVM 生命周期内只有一份;非静态变量(实例变量)存在堆中,每次 new 对象时才分配,每个对象独有一份。
这意味着:修改一个对象的非静态变量,不影响其他对象;但修改静态变量,所有对象看到的都是同一个值——哪怕它们属于不同类实例,甚至来自不同线程(除非加同步)。
访问方式差异直接暴露设计意图
静态变量可通过类名直接访问:MyClass.count;非静态变量必须通过实例访问:obj.name。编译器会严格检查:在静态方法(如 main)里直接引用 this.name 或未加实例前缀的 name,会报错 non-static variable cannot be referenced from a static context。
常见误操作:
立即学习“Java免费学习笔记(深入)”;
- 在
@Test方法(默认静态)里试图调用非静态字段或方法,忘了加new MyClass() - 把工具类中的配置项(如
DEFAULT_TIMEOUT)误声明为非静态,导致每次 new 都重复加载 - 把本该共享的缓存(如
Map)声明为非静态,内存占用翻倍且无法跨实例复用
初始化时机和顺序影响程序行为
静态变量在类第一次主动使用时触发类初始化,按代码顺序执行静态块和静态变量赋值;非静态变量在构造器执行前、按声明顺序初始化,再进入构造器体。
示例陷阱:
class Demo {
static int a = initA(); // 类加载时执行
int b = initB(); // 每次 new 时执行
static { System.out.println("static block"); }
{ System.out.println("instance block"); } // 每次 new 前执行
}
若 initA() 依赖尚未加载的类,或 initB() 里调用了未初始化的 this 引用(比如在父类构造器中调子类重写方法),就可能引发 NullPointerException 或 NoClassDefFoundError。
序列化时静态变量被跳过
Java 序列化机制明确忽略静态变量和 transient 字段。即使你把静态变量设为 public static final String VERSION = "1.2",反序列化后它的值仍是当前 JVM 中的值,不是保存时的快照。
所以别指望靠序列化持久化静态计数器或全局开关——它不会随对象一起存/取。真要跨进程保持状态,得用数据库、Redis 或文件等外部存储。
容易被忽略的一点:如果静态变量是可变对象(比如 static List),虽然引用本身不序列化,但该对象可能被多个反序列化实例间接持有,造成意料外的共享或并发问题。








