《Java 100 天进阶之路》第37篇:Java内部类及其作用

第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+),因为匿名内部类会复制该变量的副本,为了保持一致性要求不可变。

六、练习题

  1. 设计 :定义一个 LinkedList 类,包含内部类 Node(表示链表节点),并实现添加和遍历方法。
  2. 代码填空 :使用匿名内部类创建一个 Comparator<String>,按字符串长度降序排序。
  3. 分析:为什么在 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!

👉 点击关注我,更新后第一时间收到推送!

相关推荐
离陌在学C#3 分钟前
C# 事件(Event)详解:从概念到实战
开发语言·c#
0x5318 分钟前
网站通信(一)
java
To_OC23 分钟前
Next.js + Redis 构建笔记系统:Redis Hash 实战与性能优化
redis·后端·next.js
Terra.K26 分钟前
Java异常学习[特殊字符]
java·开发语言·学习
葡萄城技术团队2 小时前
InfluxDB 2\.x 深度解析:核心架构、Flux 函数与制造业落地指南(三)
java·开发语言·架构
Super 含2 小时前
Android 启动优化(五):线程、GC 与 IO 为什么会拖慢启动?
java·服务器·数据库
counting money2 小时前
Java IO流详解:从InputStream到文件操作实战
java·开发语言·python
wuyk5552 小时前
98.C语言易混难点:字符数组与字符串指针的底层差异
c语言·开发语言·c++·stm32·嵌入式硬件·算法
用户47949283569152 小时前
字节Agent开发技术分享—旧会话越聊越笨,新会话又得重讲?我给 Matt Pocock Skill 炼了套《影分身之术》:本体想清楚,分身写代码,完事回来汇报
面试·agent
峥无3 小时前
从0到1手撕红黑树:封装实现 my_map 与 my_set(SGI-STL 源码级深度解析)
开发语言·c++·笔记·算法·stl