static变量存于方法区(JDK 8+为元空间),属类而非实例,所有对象共享;static方法不能访问非static成员,无this/super;static块仅执行一次且线程安全;static内部类不持外部类引用。

static变量在JVM里存在哪?不是堆也不是栈
static变量不归实例管,它属于类本身,加载时就进方法区(JDK 8+ 是元空间)。你new一百个Person对象,count还是那一份,所有实例共享。
- 常见错误现象:
NullPointerException不是因为static变量为空,而是你试图用null引用去调静态方法——静态方法压根不依赖实例,obj.staticMethod()这种写法编译能过,但语义误导人,实际等价于Person.staticMethod() - 使用场景:计数器、缓存单例、配置常量(配合
final)、工具类状态标记 - 参数差异:static方法不能直接访问非static字段或方法,编译报错
non-static variable xxx cannot be referenced from a static context;反过来,非static方法可以安全调用static成员
static方法为什么不能用this和super
因为static方法绑定的是类,不是对象。this指向当前实例,super指向父类实例——而static方法运行时可能根本没创建任何实例。
- 常见错误现象:在static方法里写
this.name或super.toString(),IDE立刻标红,javac报错 - 使用场景:工具函数(如
StringUtils.isEmpty())、工厂入口(如LocalDateTime.now())、main入口 - 性能影响:static方法调用是静态绑定,比虚方法调用少一次运行时查表,但现代JIT优化后差距极小,别为这点微优化牺牲可读性
static代码块什么时候执行?只一次,且早于构造器
类第一次被主动使用时触发(比如首次new、首次调用static方法、首次访问static字段),且仅执行一次。它比任何构造器都早,连父类static块都先于子类执行。
- 常见错误现象:把初始化逻辑错写在static块里,结果发现多线程下重复执行——不会,JVM保证其线程安全;但若static块里抛异常,类加载失败,后续所有对该类的引用都会抛
NoClassDefFoundError - 使用场景:加载驱动(
Class.forName("com.mysql.cj.jdbc.Driver"))、预热缓存、读取配置文件并解析成static final常量 - 兼容性注意:Android Dalvik/ART对static块执行时机有细微差异,尤其涉及反射或资源路径时,建议用
Application.onCreate()兜底
内部类加static有什么实质变化
普通内部类隐式持外部类引用,static内部类不持——这意味着它不依赖外部实例,能独立存在,内存更轻,也能在外部类还没实例化时就用。
立即学习“Java免费学习笔记(深入)”;
- 常见错误现象:
Serializable时忘了static内部类默认不可序列化(因含外部类引用),加了static反而要手动处理序列化ID;或者误以为static内部类能直接访问外部类的非static成员,其实不能 - 使用场景:封装工具类(如
Map.Entry)、避免内存泄漏(Handler+Activity场景中,用static+WeakReference替代非static内部类) - 参数差异:static内部类无法访问外部类的非static字段,只能访问static字段或通过传参获得实例引用
最容易被忽略的是:static变量的初始化顺序依赖类加载顺序,而类加载顺序受代码调用链影响,跨模块时可能出乎意料;如果两个类互相static引用,还可能触发死锁或ExceptionInInitializerError。










