静态内部类与非静态内部类
面试官问:"静态内部类和非静态内部类有什么区别?"
候选人小雷答:"静态内部类用 static 修饰,不依赖于外部类实例;非静态内部类需要外部类对象。"
面试官点点头,继续追问:"为什么非静态内部类不能定义 static 字段?"
小雷愣住了。
【面试官心理】 追问 static 字段的限制,是在测候选人对内部类编译实现的理解程度。能说出"非静态内部类在实例层面,而非静态字段属于类层面"的候选人,是真正理解了 Java 内存模型的。
一、核心区别对比表 🔴
二、编译后的差异 🔴
2.1 非静态内部类的编译器处理
2.2 静态内部类的编译器处理
三、为什么非静态内部类不能有 static 字段?🔴
3.1 原因分析
为什么不能有 static 非 final 字段?
static字段属于类(Class 对象),而不是实例- 非静态内部类属于实例(持有外部类引用)
- 如果允许定义 static 字段,语义混乱:应该通过哪个外部类实例来访问这个 static 字段?
3.2 为什么常量可以?
四、非静态内部类的内存泄漏风险 🔴
4.1 典型泄漏场景
⚠️
非静态内部类导致的内存泄漏:
- Handler 持有 Activity 引用 → Activity 无法销毁
- 回调对象持有内部类 → 外部类无法 GC
- 集合持有内部类迭代器/回调
解决方案:将非静态内部类改为静态内部类 + WeakReference。
4.2 正确的做法
五、静态内部类的典型应用 🔴
5.1 单例模式(双重检查锁)
原理:Holder 是静态内部类,只有在 getInstance() 被调用时才会加载,而类加载是线程安全的。
5.2 建造者模式
5.3 Map.Entry
HashMap 用静态内部类 Node 来表示键值对,因为 Node 不需要访问 HashMap 的实例字段(HashMap 的实例字段是 table、size 等,Node 不需要),用静态内部类避免了持有外部类引用。
六、追问升级
面试官:"HashMap 的 Entry 为什么用静态内部类而不是非静态?"
答案:节省内存 + 避免无意义的引用持有。Node 是纯数据结构,不需要访问 HashMap 实例。
【面试官心理】 能说出 HashMap Node 用静态内部类原因的候选人,说明对 JDK 源码有过深入阅读,并且理解了"静态内部类节省内存"的设计意图。这是 P6+ 的加分项。