成员内部类是定义在外部类的成员位置上的内部类,其层级与外部类的字段和方法相同。作为外部类的非静态成员,它必须依赖于外部类实例才能存在,并且可以直接访问外部类的所有成员,包括私有成员。
private
此外,成员内部类本身可以使用不同的访问修饰符进行修饰,例如 public、private、protected 或默认包访问权限,从而控制其对外可见性。
public
private
default
protected
一、主要特性
1. 对外部类实例的依赖性
成员内部类的对象创建必须基于已存在的外部类对象。无法在没有外部类实例的情况下直接构造内部类实例。
new 内部类()
2. 访问权限机制
- 内部类访问外部类:可直接访问外部类的所有成员,无论是否为私有成员。
private
外部类访问内部类:可通过直接实例化内部类来访问;若内部类被声明为 private,则仅外部类自身可创建其实例。
private
其他类访问内部类:需通过外部类的实例间接创建内部类对象,同时受内部类自身访问修饰符限制。例如,private 内部类无法从外部类之外访问。
private
3. 关键字 this 的使用
在成员内部类中,this 指向的是内部类自身的实例。
this
this
若要引用外部类的实例,应使用 外部类名.this 的形式,如:
外部类名.this
Outer.this
二、语法结构
1. 定义格式
class 外部类名 {
// 外部类成员(字段、方法)
访问修饰符 class 内部类名 {
// 内部类成员(字段、方法)
}
}
2. 创建内部类对象的三种典型情况
(1)在外部类内部创建
此时无需显式创建外部类实例,因为在外部类的作用域内已隐含持有实例。可以直接通过 new 操作符构建内部类对象,方式最为简洁。
new 内部类名()
class Outer {
private String outerField = "外部类私有字段";
// 成员内部类
public class Inner {
public void accessOuter() {
// 直接访问外部类私有成员
System.out.println(outerField);
}
}
// 外部类方法中创建内部类对象
public void createInner() {
Inner inner = new Inner();
inner.accessOuter(); // 输出:外部类私有字段
}
}
// 测试
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
outer.createInner(); // 间接通过外部类方法创建内部类
}
}
(2)在外部类外部、同一包下创建(适用于非 private 成员内部类)
需要先获得外部类实例,然后通过该实例创建内部类对象,语法如下:
外部类实例.new 内部类名()
public class Test {
public static void main(String[] args) {
// 1. 先创建外部类实例
Outer outer = new Outer();
// 2. 通过外部类实例创建内部类对象
Outer.Inner inner = outer.new Inner();
// 3. 调用内部类方法
inner.accessOuter(); // 输出:外部类私有字段
}
}
(3)在静态方法中创建
由于静态方法不隶属于任何实例,因此不存在隐式的外部类上下文。必须首先手动创建外部类对象,再基于此对象创建内部类实例。
new 外部类()
public class Test {
public static void main(String[] args) {
// 静态方法中创建:外部类实例.new 内部类()
Outer.Inner inner = new Outer().new Inner();
inner.accessOuter(); // 输出:外部类私有字段
}
}
三、关键细节说明
1. 直接访问外部类成员(含私有成员)
成员内部类会自动持有一个指向外部类实例的引用,因此能够无障碍地访问外部类中的所有成员,无需额外传参或引用获取。
class Outer {
private int num = 10;
private void outerMethod() {
System.out.println("外部类私有方法");
}
class Inner {
public void show() {
System.out.println(num); // 直接访问外部类私有字段
outerMethod(); // 直接访问外部类私有方法
}
}
}
2. 处理同名成员时的区分策略
当内部类与外部类存在同名成员变量或方法时,可通过以下方式明确指定访问目标:
this
外部类名.this.成员:用于访问外部类中对应的成员。
外部类名.this
示例如下:
this.成员
外部类名.this.成员
class Outer {
String name = "外部类";
class Inner {
String name = "内部类";
public void showName() {
System.out.println(name); // 内部类成员:内部类
System.out.println(this.name); // 内部类成员:内部类
System.out.println(Outer.this.name); // 外部类成员:外部类
}
}
}
// 测试
public class Test {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.showName();
}
}
输出结果为:
内部类
内部类
外部类
3. 不同访问修饰符的影响
成员内部类可被多种访问控制符修饰,影响其可访问范围:
- public:所有类均可访问,但必须通过外部类实例进行创建。
public
private:仅外部类自身可访问,外部无法创建该内部类对象。
private
默认(无修饰符):仅限同一包内的类可以访问。
default
protected:同一包内或子类可访问。
protected
示例:将内部类设为 private 的情形
class Outer {
// 私有成员内部类:仅 Outer 类可访问
private class Inner {
public void method() {
System.out.println("私有内部类方法");
}
}
// 外部类可创建并使用私有内部类
public void useInner() {
Inner inner = new Inner();
inner.method(); // 合法
}
}
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
outer.useInner(); // 合法(通过外部类方法间接使用)
// 错误:外部类外无法访问 private 内部类
// Outer.Inner inner = outer.new Inner();
}
}
4. 静态成员的限制
由于成员内部类与外部类实例绑定,不能定义普通的静态字段或静态方法(因为静态成员属于类而非实例)。唯一的例外是使用 static final 修饰的常量,其值在编译期即可确定,不依赖运行时实例。
static final
class Outer {
class Inner {
// 错误:成员内部类不能有静态字段
// public static int num = 10;
// 正确:static final 常量允许
public static final int MAX = 100;
// 错误:成员内部类不能有静态方法
// public static void staticMethod() {}
}
}
四、成员内部类与静态内部类的核心差异对比
| 特性 |
成员内部类(Member Inner Class) |
静态内部类(Static Nested Class) |
| 依赖外部类实例 |
是,必须先创建外部类对象 |
否,可通过 Outer.Inner 直接创建 |
new Outer.Inner()
| 访问外部类成员能力 |
可访问所有成员,包括私有非静态成员 |
只能访问外部类的静态成员 |
| 能否定义静态成员 |
仅允许 static final 常量 |
static final
允许任意静态和非静态成员 |
| 创建对象语法 |
new Outer().new Inner() |
外部类实例.new 内部类()
new Outer.Inner() |
new 外部类名.内部类名()
五、典型应用场景
成员内部类适用于与外部类具有强关联逻辑的场景,常见于:
- 内部类需要频繁调用外部类的非静态成员,充当辅助工具角色。
- 封装一个只为外部类服务的组件,提升封装性,可通过 private 修饰实现信息隐藏。
示例:体现外部类与内部类紧密协作的关系
// 外部类:汽车
class Car {
private String brand; // 品牌
private int speed; // 速度
public Car(String brand) {
this.brand = brand;
this.speed = 0;
}
// 成员内部类:发动机(与汽车强关联,依赖汽车实例)
public class Engine {
// 发动机启动,修改汽车速度
public void start() {
speed = 60;
System.out.println(brand + " 的发动机启动,当前速度:" + speed + "km/h");
}
// 发动机停止
public void stop() {
speed = 0;
System.out.println(brand + " 的发动机停止,当前速度:" + speed + "km/h");
}
}
}
// 测试
public class TestCar {
public static void main(String[] args) {
// 1. 创建汽车(外部类实例)
Car bmw = new Car("宝马");
// 2. 创建发动机(内部类实例,依赖汽车)
Car.Engine engine = bmw.new Engine();
// 3. 调用内部类方法(间接修改外部类状态)
engine.start(); // 输出:宝马 的发动机启动,当前速度:60km/h
engine.stop(); // 输出:宝马 的发动机停止,当前速度:0km/h
}
}
总结
- 成员内部类属于外部类的非静态组成部分,必须依托外部类实例而存在。
- 创建语法为:
new Outer().new Inner()(在外部类外部),或直接 new Inner()(在外部类内部)。
外部类实例.new 内部类名()
new 内部类名()
当内外类成员同名时,优先级规则为:this 指向内部类成员,外部类名.this 明确指向外部类成员。
this
外部类名.this
通过设置访问修饰符(如 private)可实现高内聚封装;public 则允许外部通过外部类实例访问。
private
public
核心适用场景:作为与外部类强耦合的辅助模块,需频繁操作外部类实例数据。