第37篇:Java内部类及其作用
📌 系列导航 :《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第36篇:选择最适合自己的NIO,一探流技术 |
➡️ 下一篇:第38篇:Java是动态语言吗?
一、核心知识点
- 成员内部类(非静态内部类)
- 局部内部类(方法内定义)
- 匿名内部类(最常用)
- 静态内部类(嵌套类)
- 内部类的访问规则、生命周期、应用场景
二、通俗讲解(1分钟开心学)
1. 什么是内部类?
定义在另一个类内部的类。内部类可以访问外部类的所有成员(包括私有),实现更好的封装和组织。
2. 四种内部类
- 成员内部类 :定义在外部类内部,像成员变量一样。创建时需要先有外部类实例(
outer.new Inner())。 - 静态内部类 :用
static修饰,独立于外部类实例,不能访问外部类非静态成员。 - 局部内部类:定义在方法内部,作用域仅限于该方法。
- 匿名内部类:没有类名,通常用于简化代码,比如实现接口或继承类的临时实例。
3. 内部类的作用
- 逻辑分组:把只服务于外部类的类放在内部,提高封装性。
- 增加封装:内部类可以访问外部类私有成员。
- 简洁代码:匿名内部类常用于事件监听、回调等。
生活类比 :
内部类就像手机里的"电池模块"。手机(外部类)的电池(内部类)可以访问手机的电量管理(私有成员)。如果电池做成静态内部类,就像可拆卸电池,不需要手机本身就能存在。
三、实操代码案例 + 场景说明
场景:设计一个迭代器(Iterator)作为外部类的内部类,用于遍历集合。
java
import java.util.Iterator;
// 外部类:简单列表
public class MyList<T> implements Iterable<T> {
private Object[] elements;
private int size;
public MyList(int capacity) {
elements = new Object[capacity];
}
public void add(T item) {
elements[size++] = item;
}
// 成员内部类:迭代器
private class MyIterator implements Iterator<T> {
private int cursor = 0;
@Override
public boolean hasNext() {
return cursor < size;
}
@Override
@SuppressWarnings("unchecked")
public T next() {
return (T) elements[cursor++];
}
}
@Override
public Iterator<T> iterator() {
return new MyIterator(); // 创建内部类对象
}
public static void main(String[] args) {
MyList<String> list = new MyList<>(3);
list.add("A"); list.add("B"); list.add("C");
for (String s : list) {
System.out.println(s);
}
}
}
// === 其他内部类示例 ===
class Outer {
private int data = 100;
// 1. 成员内部类
class Inner {
void print() { System.out.println(data); } // 可访问外部私有
}
// 2. 静态内部类
static class StaticInner {
void show() { System.out.println("静态内部类"); }
// void printData() { System.out.println(data); } // 错误,不能访问非静态成员
}
// 3. 局部内部类(方法内)
public void method() {
class Local {
void run() { System.out.println("局部内部类"); }
}
Local l = new Local();
l.run();
}
// 4. 匿名内部类(常见于线程、监听器)
public void startThread() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类线程");
}
}).start();
// Java 8+ lambda 简化(当接口是函数式时)
new Thread(() -> System.out.println("lambda")).start();
}
}
四、避坑要点
| 错误/误区 | 后果 | 正确做法 |
|---|---|---|
| 成员内部类中定义静态成员 | 编译错误(除了静态常量) | 内部类中尽量避免静态成员 |
匿名内部类中访问局部变量,但变量不是 final(或 effectively final) |
编译错误 | 确保局部变量不修改 |
| 静态内部类试图访问外部类非静态成员 | 编译错误 | 只能访问外部类静态成员 |
| 内部类持有外部类引用导致内存泄漏(如 Android Handler) | 外部类无法被 GC | 使用静态内部类 + 弱引用 |
五、面试高频考点
Q1:为什么内部类可以访问外部类的私有成员?
编译器会在编译时生成一个隐式的构造方法,传入外部类的引用,并生成桥接方法访问私有成员(通过 access$0 等合成方法)。
Q2:静态内部类和非静态内部类的区别?
静态内部类不持有外部类的引用,可以独立创建;非静态内部类需要外部类实例才能创建,可以访问外部类所有成员(包括私有)。
Q3:匿名内部类如何访问外部方法的局部变量?
该局部变量必须是
final或 effectively final(Java 8+),因为匿名内部类会复制该变量的副本,为了保持一致性要求不可变。
六、练习题
- 设计 :定义一个
LinkedList类,包含内部类Node(表示链表节点),并实现添加和遍历方法。 - 代码填空 :使用匿名内部类创建一个
Comparator<String>,按字符串长度降序排序。 - 分析:为什么在 Android 开发中,Handler 使用静态内部类 + 弱引用来避免内存泄漏?
📊 你的学习进度
- 当前:第37篇 / 共44篇 · 第六阶段:NIO、泛型、JVM内幕、字节码(第36~44篇)
- ✅ 已完成:第1~36篇
- 📖 正在学:第37篇
- ⏳ 待学习:第38~44篇
👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇
💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!
👉 下一篇文章预告
《第38篇:Java是动态语言吗?》
内容简介:动态语言判定标准、Java静态特性、半动态特性(反射/动态代理/字节码生成)、与Python对比。
💡 学完这篇,你将理解Java的静态本质和动态能力,面试再问轻松回答。
📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注 ,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!