本系列可作为JAVA学习系列的笔记,文中提到的一些练习的代码,小编会将代码复制下来,大家复制下来就可以练习了,方便大家学习。
点赞关注不迷路!您的点赞、关注和收藏是对小编最大的支持和鼓励!
本文篇幅较长,建议先收藏再食用!
系列文章目录
JAVA学习 DAY2 java程序运行、注意事项、转义字符
JAVA学习 DAY5 变量&数据类型 [万字长文!一篇搞定!]
JAVA学习 DAY7 程序逻辑控制【万字长文!一篇搞定!】
JAVA学习 DAY11 类和对象_续1【万字长文!一篇搞定!】
JAVA学习 DAY12 继承和多态【万字长文!一篇搞定!】
JAVA学习 DAY13 抽象类和接口【万字长文!一篇搞定!】
目录
[一、静态内部类(Static Nested Class)](#一、静态内部类(Static Nested Class))
[1. 核心定义](#1. 核心定义)
[2. 完整代码示例(含访问规则)](#2. 完整代码示例(含访问规则))
[3. 典型使用场景](#3. 典型使用场景)
[二、实例内部类(成员内部类,Member Inner Class)](#二、实例内部类(成员内部类,Member Inner Class))
[1. 核心定义](#1. 核心定义)
[2. 关键特性](#2. 关键特性)
[3. 完整代码示例](#3. 完整代码示例)
[4. 典型使用场景](#4. 典型使用场景)
[三、局部内部类(Local Inner Class)](#三、局部内部类(Local Inner Class))
[1. 核心定义](#1. 核心定义)
[2. 完整代码示例](#2. 完整代码示例)
[3. 关键注意事项](#3. 关键注意事项)
[4. 典型使用场景](#4. 典型使用场景)
[四、匿名内部类(Anonymous Inner Class)](#四、匿名内部类(Anonymous Inner Class))
[1. 核心定义](#1. 核心定义)
[2. 完整代码示例(两种常见场景)](#2. 完整代码示例(两种常见场景))
[场景 1:实现接口](#场景 1:实现接口)
[场景 2:继承父类](#场景 2:继承父类)
[3. Java 8 + 替代方案:Lambda 表达式](#3. Java 8 + 替代方案:Lambda 表达式)
[4. 典型使用场景](#4. 典型使用场景)
前言
小编作为新晋码农一枚,会定期整理一些写的比较好的代码,作为自己的学习笔记,会试着做一下批注和补充,如转载或者参考他人文献会标明出处,非商用,如有侵权会删改!欢迎大家斧正和讨论!
核心前提:内部类的设计本质
内部类(Inner Class)是定义在另一个类 / 方法 / 作用域内部的类,其核心设计目的是:让内部类成为外部类的 "组成部分",形成一个完整的逻辑对象 ------ 就像 "汽车"(外部类)包含 "发动机"(内部类),发动机无法脱离汽车独立存在(或失去意义),内部类也通过这种嵌套关系,实现代码的高内聚、低耦合,同时可以直接访问外部类的成员(不同内部类访问权限不同)。
Java 将内部类分为四类,核心差异在于定义位置 、是否静态 、生命周期 和访问权限,下面逐一拆解:
一、静态内部类(Static Nested Class)
1. 核心定义
- 定义在外部类的类级别作用域 (与外部类的属性 / 方法同级),并被
static修饰的内部类。 - 本质:属于外部类本身(类级别),而非外部类的实例,因此也被称为 "静态嵌套类"(Java 官方术语)。
- 核心特征:不持有外部类的实例引用,是四类内部类中唯一 "独立度最高" 的类型。
2. 完整代码示例(含访问规则)
java
public class Car {
// 外部类静态成员
public static String brand = "特斯拉";
// 外部类实例成员
public String color = "白色";
// 静态内部类:发动机(属于Car类,而非某个Car实例)
public static class Engine {
// 静态内部类可以有静态成员(四类中唯一支持的)
public static String type = "纯电动";
// 静态内部类实例成员
public int power = 300;
// 静态内部类静态方法
public static void staticEngineMethod() {
// ✅ 可以访问外部类静态成员
System.out.println("外部类静态属性:" + Car.brand);
// ❌ 无法访问外部类实例成员(无外部类引用)
// System.out.println(Car.color);
}
// 静态内部类实例方法
public void instanceEngineMethod() {
// ✅ 访问自身静态/实例成员
System.out.println("内部类静态属性:" + type);
System.out.println("内部类实例属性:" + this.power);
// ✅ 访问外部类静态成员
System.out.println("外部类品牌:" + Car.brand);
// ❌ 直接访问外部类实例成员(无外部类引用)
// System.out.println(color);
// ⚠️ 特殊:若拿到外部类实例,可通过实例访问
Car car = new Car();
System.out.println("外部类实例属性:" + car.color);
}
}
public static void main(String[] args) {
// 1. 实例化静态内部类:无需外部类实例
Car.Engine engine = new Car.Engine();
// 2. 调用方法
engine.instanceEngineMethod();
Car.Engine.staticEngineMethod();
}
}
3. 典型使用场景
- 构建器模式(Builder):封装复杂对象的创建逻辑;
- 懒加载单例:利用类加载机制实现线程安全的单例;
- 工具类封装:将功能相关的工具类嵌套在主类中,避免类名污染;
- 组件化设计:如
Map接口中的Entry静态内部类(Map.Entry)。
二、实例内部类(成员内部类,Member Inner Class)
1. 核心定义
- 定义在外部类的类级别作用域 ,但不被 static 修饰的内部类(也叫非静态内部类)。
- 本质:属于外部类的实例(对象级别),而非外部类本身。
- 核心特征:隐式持有外部类的实例引用 (通过
外部类名.this访问),生命周期与外部类实例绑定。
2. 关键特性
| 特性 | 具体说明 |
|---|---|
| 实例化方式 | 必须先创建外部类实例,再通过外部类实例创建:Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); |
| 访问权限 | 可直接访问外部类的所有成员(静态 + 实例),包括 private 修饰的成员 |
| 自身成员限制 | 不能定义静态成员(除了static final常量) |
| 内存引用 | 持有外部类的隐式引用,可能导致内存泄漏(外部类实例无法被 GC 回收) |
3. 完整代码示例
java
public class House {
// 外部类静态属性
private static String address = "北京市朝阳区";
// 外部类实例属性
private int area = 120;
// 实例内部类:客厅(属于某个House实例)
public class LivingRoom {
// ✅ 仅允许静态常量
public static final int CHAIR_COUNT = 4;
// ❌ 错误:不允许普通静态成员
// public static String lightType = "LED";
// 实例属性
private String sofaBrand = "芝华仕";
// 实例方法
public void showInfo() {
// ✅ 访问外部类静态成员
System.out.println("房屋地址:" + House.address);
// ✅ 访问外部类实例成员
System.out.println("房屋面积:" + area);
// ✅ 显式访问外部类实例(外部类名.this)
System.out.println("外部类实例引用:" + House.this);
// ✅ 访问自身实例成员
System.out.println("沙发品牌:" + sofaBrand);
}
}
// 外部类方法中创建内部类
public void createLivingRoom() {
LivingRoom livingRoom = new LivingRoom();
livingRoom.showInfo();
}
public static void main(String[] args) {
// 1. 第一步:创建外部类实例
House house = new House();
// 2. 第二步:通过外部类实例创建内部类
House.LivingRoom livingRoom = house.new LivingRoom();
// 3. 调用内部类方法
livingRoom.showInfo();
// 也可以简写
House.LivingRoom livingRoom2 = new House().new LivingRoom();
}
}
4. 典型使用场景
- 逻辑紧密绑定的组件:如
ArrayList中的Itr迭代器(实例内部类),迭代器与集合实例强绑定; - 事件处理:GUI 编程中,组件的事件监听器(早期用法,现在更多用 Lambda);
- 封装复杂对象的子组件:如 "订单" 中的 "订单项",订单项无法脱离订单独立存在。
三、局部内部类(Local Inner Class)
1. 核心定义
- 定义在外部类的方法 / 代码块 / 循环 / 条件语句等局部作用域内的内部类。
- 本质:属于局部作用域(仅在定义的方法 / 代码块内可见),生命周期与局部变量一致。
- 核心特征:
- 作用域仅限于定义它的方法 / 代码块;
- 不能被
public/private/protected/static修饰; - 可访问外部类的所有成员,但访问局部变量时,局部变量必须是
final(Java 8 + 自动隐式 final)。
2. 完整代码示例
java
public class Calculator {
// 外部类实例属性
private double taxRate = 0.08;
// 外部类静态属性
private static String version = "v1.0";
// 外部类方法
public double calculateTotal(double price, int count) {
// 局部变量(Java 8+自动隐式final,无需显式声明)
double discount = 0.9;
// 局部内部类:仅在calculateTotal方法内可见
class PriceCalculator {
// 局部内部类的实例属性
private double basePrice;
public PriceCalculator(double basePrice) {
this.basePrice = basePrice;
}
// 计算最终价格
public double getFinalPrice() {
// ✅ 访问外部类静态成员
System.out.println("计算器版本:" + Calculator.version);
// ✅ 访问外部类实例成员
System.out.println("税率:" + taxRate);
// ✅ 访问方法的局部变量(隐式final)
System.out.println("折扣:" + discount);
// 计算逻辑
double total = basePrice * count * discount;
return total * (1 + taxRate);
}
}
// 在方法内创建局部内部类实例并使用
PriceCalculator calculator = new PriceCalculator(price);
return calculator.getFinalPrice();
}
public static void main(String[] args) {
Calculator calc = new Calculator();
double total = calc.calculateTotal(100, 5);
System.out.println("最终总价:" + total); // 输出:最终总价:486.0
// ❌ 错误:局部内部类作用域仅在方法内,外部无法访问
// Calculator.PriceCalculator pc = new Calculator.PriceCalculator(100);
}
}
3. 关键注意事项
- 局部内部类的字节码文件名格式:
外部类名$数字$局部内部类名.class(如Calculator$1PriceCalculator.class); - 访问局部变量的限制:因为局部变量的生命周期短于内部类实例,Java 要求局部变量必须是 final(确保值不被修改);
- 作用域隔离:不同方法中可以定义同名的局部内部类,互不影响。
4. 典型使用场景
- 方法内的复杂逻辑拆分:将方法内的复杂逻辑封装为局部内部类,提升代码可读性;
- 临时工具类:仅在当前方法内使用的工具类,避免全局类污染;
- 框架适配:在特定方法内适配第三方接口,仅在该方法内生效。
四、匿名内部类(Anonymous Inner Class)
1. 核心定义
- 没有类名的局部内部类,是局部内部类的简化形式。
- 本质:在创建类的同时直接实例化,一次性使用,无法重复创建实例。
- 核心特征:
- 必须继承一个父类或实现一个接口;
- 没有构造方法(因为没有类名);
- 作用域与局部内部类一致,访问局部变量同样要求 final;
- 语法紧凑,适合快速创建 "一次性" 对象。
2. 完整代码示例(两种常见场景)
场景 1:实现接口
java
// 定义接口
interface Greeting {
void sayHello(String name);
}
public class AnonymousDemo {
private String prefix = "Hello, ";
public void greet(String name) {
// 匿名内部类:实现Greeting接口
Greeting greeting = new Greeting() {
// 实现接口方法
@Override
public void sayHello(String name) {
// ✅ 访问外部类实例成员
System.out.println(prefix + name);
}
};
// 调用方法
greeting.sayHello(name);
}
public static void main(String[] args) {
AnonymousDemo demo = new AnonymousDemo();
demo.greet("Java开发者"); // 输出:Hello, Java开发者
}
}
场景 2:继承父类
java
// 定义父类
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
public class AnonymousAnimalDemo {
public static void main(String[] args) {
// 匿名内部类:继承Animal类并覆盖方法
Animal cat = new Animal() {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
};
Animal dog = new Animal() {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
};
cat.makeSound(); // 输出:喵喵喵
dog.makeSound(); // 输出:汪汪汪
}
}

3. Java 8 + 替代方案:Lambda 表达式
匿名内部类在 Java 8 后,对于函数式接口(只有一个抽象方法的接口),可以用 Lambda 简化:
java
// 原匿名内部类写法
Greeting greeting1 = new Greeting() {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
};
// Lambda简化写法
Greeting greeting2 = name -> System.out.println("Hello, " + name);
4. 典型使用场景
- 事件监听器:如 Swing/AWT 中的按钮点击事件(
button.addActionListener(new ActionListener() {...})); - 线程创建:
new Thread(new Runnable() {...}).start()(Java 8 + 已被 Lambda 替代); - 临时接口实现:快速实现一个接口的单次使用实例,无需定义独立类。
五、四类内部类核心对比表(博客核心素材)
| 特性 | 静态内部类 | 实例内部类 | 局部内部类 | 匿名内部类 |
|---|---|---|---|---|
| 定义位置 | 外部类类级别 | 外部类类级别 | 方法 / 代码块内 | 方法 / 代码块内 |
| static 修饰 | 是 | 否 | 不允许 | 不允许 |
| 实例化方式 | 无需外部类实例 | 必须外部类实例 | 仅在作用域内实例化 | 定义时直接实例化 |
| 持有外部类引用 | 否 | 是(隐式) | 是(隐式) | 是(隐式) |
| 访问外部类成员 | 仅静态成员 | 所有成员 | 所有成员 | 所有成员 |
| 访问局部变量 | 无 | 无 | 需 final(隐式) | 需 final(隐式) |
| 自身静态成员 | 允许 | 仅 static final | 仅 static final | 不允许 |
| 类名 | 有 | 有 | 有 | 无 |
| 构造方法 | 有 | 有 | 有 | 无 |
| 字节码文件名 | Outer$Inner.class | Outer$Inner.class | Outer$1Inner.class | Outer$1.class |
| 核心使用场景 | 构建器、单例、工具类 | 迭代器、组件绑定 | 方法内逻辑拆分 | 临时接口实现、事件处理 |
六、内部类的核心设计价值(博客升华部分)
- 封装性:将辅助类隐藏在外部类内部,避免暴露给外部,减少全局类的数量,降低命名冲突风险;
- 访问权限:内部类可直接访问外部类的私有成员,无需通过 get/set 方法,简化代码;
- 逻辑内聚:将 "属于外部类的组件" 嵌套在外部类中,形成完整的逻辑对象(如汽车 + 发动机);
- 回调简化:匿名内部类是早期实现回调的核心方式(如事件处理),Lambda 是其简化形式。
七、内部类的坑点(博客避坑指南)
- 内存泄漏:实例 / 匿名 / 局部内部类持有外部类引用,若内部类实例生命周期长于外部类,会导致外部类无法被 GC 回收;
- 序列化问题:实例内部类序列化时会携带外部类引用,容易导致序列化失败;
- 可读性降低:过度使用内部类(尤其是多层嵌套)会让代码难以阅读和维护;
- 局部变量限制:局部 / 匿名内部类访问局部变量时的 final 限制,容易引发编译错误。
总结
- 分类核心 :Java 内部类分为四类,核心差异在定义位置 和是否静态,静态内部类属于类级别,其余三类属于实例 / 局部级别;
- 访问规则:静态内部类仅能访问外部类静态成员,其余三类可访问外部类所有成员,但局部 / 匿名内部类访问局部变量需 final;
- 使用原则:优先使用静态内部类(无内存泄漏风险),实例内部类仅用于强绑定场景,局部 / 匿名内部类仅用于临时简化逻辑,Java 8 + 优先用 Lambda 替代匿名内部类。
总结
以上就是今天要讲的内容,本文简单记录了JAVA学习笔记,大家根据注释理解,您的点赞关注收藏就是对小编最大的鼓励!