内部类(Inner Class)是定义在另一个类内部的类。它允许将逻辑上相关的类组织在一起,并可以控制内部类的可见性。
分类
- 成员内部类
- 局部内部类
- 匿名内部类
- 静态内部类(定义在类中,任何方法外,用static修饰)
1、成员内部类
这是最普通的内部类形式,定义为另一个类的成员,与类的字段、方法、构造器处于同一级别。
- 语法位置: 定义在外部类的内部,与方法、属性平级。
- 访问权限: 可以是 public, protected, (默认), private。这决定了它在外部类之外的可见性。
- 核心特性:
它隐式持有一个指向外部类对象的引用(OuterClass.this)。因此,它可以无限制地访问外部类的所有成员(包括私有成员)。
反之,外部类要访问成员内部类的成员,必须先创建一个成员内部类的对象。 - 创建对象:
在外部类内部:直接 new InnerClass()。
在外部类之外:必须先有一个外部类的实例,然后通过这个实例来创建。
OuterClass outerObj = new OuterClass();
OuterClass.InnerClass innerObj = outerObj.new InnerClass(); // 注意 new 关键字前的实例
示例代码:
public class Outer {
private String outerField = "Outer Field";
// 成员内部类
public class Inner {
public void print() {
// 可以直接访问外部类的私有字段
System.out.println("Accessing from Inner: " + outerField);
// 等价于 System.out.println(Outer.this.outerField);
}
}
public void useInner() {
Inner inner = new Inner(); // 在外部类内部创建内部类对象
inner.print();
}
}
// 在另一个类中使用
class Test {
public static void main(String[] args) {
Outer outer = new Outer();
outer.useInner(); // 方式一:通过外部类方法间接使用
// 方式二:直接创建内部类对象
Outer.Inner inner = outer.new Inner(); // 关键语法
inner.print();
}
}
2、局部内部类
定义在方法、代码块或构造器内部的类,其作用域仅限于所在的代码块内。
- 语法位置: 定义在方法内部。
- 访问权限: 不能使用任何访问修饰符(如 public, private),因为它本身就是方法的一部分,其作用域已被方法限定。
- 核心特性:
和成员内部类一样,它可以访问外部类的所有成员。
它可以访问方法中的 final 或 有效最终(effectively final)的局部变量(即初始化后值永不改变的变量)。在 Java 8 之前,必须显式声明为 final。 - 创建对象: 只能在定义它的方法或作用域内部创建和使用。
示例代码:
public class Outer {
private String outerField = "Outer Field";
public void someMethod() {
final String localVar = "Local Variable"; // 有效最终变量
// 局部内部类
class LocalInner {
public void print() {
System.out.println("Outer field: " + outerField);
System.out.println("Local variable: " + localVar); // 只能访问 final 或 effectively final 的局部变量
}
}
// 只能在方法内部使用局部内部类
LocalInner localInner = new LocalInner();
localInner.print();
}
}
3、 匿名内部类
一种没有名字的局部内部类,它同时完成了类的定义和实例化。通常用于只需使用一次的场景,例如实现接口或继承类,并重写方法。
-
语法位置: 通常作为 new 语句的一部分。
-
语法: new InterfaceName() { … } 或 new ClassName() { … }。
-
核心特性:
没有显式的 class 关键字和类名。
只能继承一个类或实现一个接口。
同样可以访问外部类的所有成员以及作用域内的 final/有效最终变量。 -
常见用途: 快速创建线程(Runnable)、事件监听器(ActionListener)等。
示例代码:
public interface Greeting {
void sayHello();
}
public class Outer {
public void greet() {
// 匿名内部类:实现 Greeting 接口
Greeting greeting = new Greeting() { // Greeting 是一个接口
@Override
public void sayHello() {
System.out.println("Hello from Anonymous Inner Class!");
}
}; // 注意这里有分号,因为这是一个赋值语句
greeting.sayHello();
// 更常见的用法:直接在方法参数中使用
new Thread(new Runnable() { // Runnable 是一个接口
@Override
public void run() {
System.out.println("A new thread is running.");
}
}).start();
}
}
**注意:**在 Java 8 之后,很多匿名内部类的场景可以被 Lambda 表达式 替代,使代码更简洁。
4、静态内部类
使用 static 关键字修饰的内部类。它不依赖于外部类的实例。
- 语法位置: 定义在外部类的内部,用 static 修饰。
- 访问权限: 可以是 public, protected, (默认), private。
- 核心特性:
它没有指向外部类实例的隐式引用(即没有 OuterClass.this)。因此:
它不能直接访问外部类的非静态成员(包括非静态字段和方法)。
它的创建不需要外部类的实例。
它就像是外部类的一个静态成员,只是这个成员恰好是一个类。 - 创建对象:
在外部类之外:直接通过外部类名创建,无需外部类实例。
OuterClass.StaticNestedClass nestedObj = new OuterClass.StaticNestedClass();
示例代码:
public class Outer {
private String nonStaticField = "Non-Static";
private static String staticField = "Static";
// 静态内部类
public static class StaticNested {
public void print() {
// System.out.println(nonStaticField); // 错误!不能访问外部类的非静态成员
System.out.println("Accessing static field: " + staticField); // 可以访问静态成员
System.out.println("I am a static nested class.");
}
}
}
class Test {
public static void main(String[] args) {
// 创建静态内部类对象,无需外部类实例
Outer.StaticNested nested = new Outer.StaticNested();
nested.print();
}
}
总结对比
| 特性 | 成员内部类 | 局部内部类 | 匿名内部类 | 静态内部类 |
|---|---|---|---|---|
| 位置 | 类成员,与方法平级 | 方法或代码块内部 | 方法内,通常是new语句 | 类成员,用static修饰 |
| 类名 | 有明确类名 | 有明确类名 | 无类名 | 有明确类名 |
| 访问修饰符 | 可以使用 | 不能使用 | 不能使用 | 可以使用 |
| 持有外部类引用 | 是 (Outer.this) | 是 | 是 | 是 |
| 访问外部类成员 | 可访问所有 | 可访问所有 | 可访问所有 | 只能访问静态成员 |
静态内部类和非静态内部类的区别
- 非静态内部类不可以声明静态成员变量和静态成员方法。
- 一般非静态内部类可以随意访问其外部类的成员变量以及方法(包括声明为private的方法);静态内部类只能访问外部类的静态成员变量和静态成员方法,不能访问非静态成员变量和方法。
- 在一个类中创建非静态内部类,内部类的实例一定要绑定在外部类的实例中,在一个外部类中定义一个静态的内部类,不需要利用关键字new来创建内部类的实例,也就是说创建静态内部类对象时,不需要其外部类的对象。
本文详细介绍了Java中的内部类概念,包括成员内部类、局部内部类、匿名内部类和静态内部类,并重点阐述了静态内部类的特点及其与非静态内部类的区别。

被折叠的 条评论
为什么被折叠?



