Appearance
12.4 内部类(入门)
内部类的概念
内部类是定义在另一个类内部的类。内部类可以访问外部类的成员,包括私有成员。
内部类的类型
在 Java 中,内部类主要分为四种类型:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
成员内部类
成员内部类是定义在外部类的成员位置的内部类,没有 static 修饰。
语法:
java
class 外部类 {
// 外部类成员
class 内部类 {
// 内部类成员
}
}示例:
java
public class OuterClass {
private int outerVar = 100;
private static int staticOuterVar = 200;
// 成员内部类
class InnerClass {
private int innerVar = 300;
public void display() {
// 可以访问外部类的实例变量
System.out.println("outerVar: " + outerVar);
// 可以访问外部类的静态变量
System.out.println("staticOuterVar: " + staticOuterVar);
// 可以访问内部类的实例变量
System.out.println("innerVar: " + innerVar);
}
}
public void createInner() {
// 在外部类中创建内部类的对象
InnerClass inner = new InnerClass();
inner.display();
}
public static void main(String[] args) {
// 创建外部类的对象
OuterClass outer = new OuterClass();
// 通过外部类对象创建内部类的对象
OuterClass.InnerClass inner = outer.new InnerClass();
inner.display();
// 调用外部类的方法创建内部类对象
outer.createInner();
}
}静态内部类
静态内部类是定义在外部类的成员位置,使用 static 修饰的内部类。
语法:
java
class 外部类 {
// 外部类成员
static class 内部类 {
// 内部类成员
}
}示例:
java
public class OuterClass {
private int outerVar = 100;
private static int staticOuterVar = 200;
// 静态内部类
static class StaticInnerClass {
private int innerVar = 300;
private static int staticInnerVar = 400;
public void display() {
// 不能访问外部类的实例变量
// System.out.println("outerVar: " + outerVar);
// 可以访问外部类的静态变量
System.out.println("staticOuterVar: " + staticOuterVar);
// 可以访问内部类的实例变量
System.out.println("innerVar: " + innerVar);
// 可以访问内部类的静态变量
System.out.println("staticInnerVar: " + staticInnerVar);
}
public static void staticDisplay() {
// 不能访问外部类的实例变量
// System.out.println("outerVar: " + outerVar);
// 可以访问外部类的静态变量
System.out.println("staticOuterVar: " + staticOuterVar);
// 不能访问内部类的实例变量
// System.out.println("innerVar: " + innerVar);
// 可以访问内部类的静态变量
System.out.println("staticInnerVar: " + staticInnerVar);
}
}
public static void main(String[] args) {
// 直接创建静态内部类的对象,不需要外部类对象
OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
inner.display();
// 调用静态内部类的静态方法
OuterClass.StaticInnerClass.staticDisplay();
}
}局部内部类
局部内部类是定义在方法或代码块中的内部类。
语法:
java
class 外部类 {
// 外部类成员
方法返回类型 方法名() {
// 方法体
class 内部类 {
// 内部类成员
}
// 使用内部类
}
}示例:
java
public class OuterClass {
private int outerVar = 100;
private static int staticOuterVar = 200;
public void method() {
final int localVar = 300; // 局部变量,必须是 final 或 effectively final
// 局部内部类
class LocalInnerClass {
private int innerVar = 400;
public void display() {
// 可以访问外部类的实例变量
System.out.println("outerVar: " + outerVar);
// 可以访问外部类的静态变量
System.out.println("staticOuterVar: " + staticOuterVar);
// 可以访问方法中的局部变量(必须是 final 或 effectively final)
System.out.println("localVar: " + localVar);
// 可以访问内部类的实例变量
System.out.println("innerVar: " + innerVar);
}
}
// 创建局部内部类的对象
LocalInnerClass inner = new LocalInnerClass();
inner.display();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.method();
}
}匿名内部类
匿名内部类是没有名称的内部类,通常用于创建接口或抽象类的实例。
语法:
java
new 接口或抽象类() {
// 实现方法
};示例:
java
// 接口
interface Greeting {
void sayHello();
}
// 抽象类
abstract class Animal {
abstract void makeSound();
}
public class AnonymousInnerClassExample {
public static void main(String[] args) {
// 使用匿名内部类实现接口
Greeting greeting = new Greeting() {
@Override
public void sayHello() {
System.out.println("Hello from anonymous inner class!");
}
};
greeting.sayHello();
// 使用匿名内部类继承抽象类
Animal animal = new Animal() {
@Override
void makeSound() {
System.out.println("Woof! Woof!");
}
};
animal.makeSound();
// 使用匿名内部类作为方法参数
printMessage(new Greeting() {
@Override
public void sayHello() {
System.out.println("Hello as method parameter!");
}
});
}
public static void printMessage(Greeting greeting) {
greeting.sayHello();
}
}内部类的特点
访问权限:
- 成员内部类:可以访问外部类的所有成员(包括私有成员)
- 静态内部类:只能访问外部类的静态成员
- 局部内部类:可以访问外部类的所有成员,以及方法中的 final 或 effectively final 局部变量
- 匿名内部类:可以访问外部类的所有成员,以及方法中的 final 或 effectively final 局部变量
创建方式:
- 成员内部类:需要通过外部类对象创建
- 静态内部类:可以直接通过外部类名创建,不需要外部类对象
- 局部内部类:只能在定义它的方法或代码块中创建
- 匿名内部类:在创建时直接实现接口或继承抽象类
作用域:
- 成员内部类:与外部类的成员具有相同的作用域
- 静态内部类:与外部类的静态成员具有相同的作用域
- 局部内部类:只在定义它的方法或代码块中可见
- 匿名内部类:只在创建它的地方可见
内部类的应用场景
1. 封装
内部类可以隐藏在外部类中,不被外部代码访问,提高封装性。
示例:
java
public class DataStructure {
private int[] array;
public DataStructure(int size) {
array = new int[size];
for (int i = 0; i < size; i++) {
array[i] = i;
}
}
// 内部类实现迭代器
private class Iterator {
private int index = 0;
public boolean hasNext() {
return index < array.length;
}
public int next() {
return array[index++];
}
}
// 提供获取迭代器的方法
public Iterator getIterator() {
return new Iterator();
}
public static void main(String[] args) {
DataStructure data = new DataStructure(5);
DataStructure.Iterator iterator = data.getIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}2. 事件处理
匿名内部类常用于事件处理,特别是在 GUI 编程中。
示例:
java
import java.awt.*;
import java.awt.event.*;
public class ButtonClickExample {
public static void main(String[] args) {
Frame frame = new Frame("Button Click Example");
Button button = new Button("Click Me");
// 使用匿名内部类处理按钮点击事件
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked!");
}
});
frame.add(button);
frame.setSize(300, 200);
frame.setVisible(true);
}
}3. 适配器模式
匿名内部类常用于实现适配器模式,只重写需要的方法。
示例:
java
// 适配器接口
interface MouseAdapter {
void mousePressed();
void mouseReleased();
void mouseMoved();
void mouseClicked();
}
// 抽象适配器类
abstract class AbstractMouseAdapter implements MouseAdapter {
@Override
public void mousePressed() {}
@Override
public void mouseReleased() {}
@Override
public void mouseMoved() {}
@Override
public void mouseClicked() {}
}
public class AdapterExample {
public static void main(String[] args) {
// 使用匿名内部类实现适配器,只重写需要的方法
MouseAdapter adapter = new AbstractMouseAdapter() {
@Override
public void mouseClicked() {
System.out.println("Mouse clicked!");
}
};
adapter.mouseClicked();
}
}4. 工厂方法模式
内部类可以用于实现工厂方法模式,创建不同类型的对象。
示例:
java
interface Shape {
void draw();
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public class ShapeFactory {
// 静态内部类作为工厂
public static class Factory {
public static Shape createShape(String type) {
if (type.equals("circle")) {
return new Circle();
} else if (type.equals("rectangle")) {
return new Rectangle();
} else {
throw new IllegalArgumentException("Invalid shape type");
}
}
}
public static void main(String[] args) {
Shape circle = ShapeFactory.Factory.createShape("circle");
circle.draw();
Shape rectangle = ShapeFactory.Factory.createShape("rectangle");
rectangle.draw();
}
}常见问题
1. 内部类访问外部类成员
症状:内部类无法访问外部类的成员
解决方案:
- 成员内部类可以直接访问外部类的所有成员
- 静态内部类只能访问外部类的静态成员
- 局部内部类和匿名内部类可以访问外部类的所有成员,以及方法中的 final 或 effectively final 局部变量
示例:
java
public class OuterClass {
private int outerVar = 100;
private static int staticOuterVar = 200;
// 成员内部类
class InnerClass {
public void accessOuter() {
System.out.println("outerVar: " + outerVar); // 正确
System.out.println("staticOuterVar: " + staticOuterVar); // 正确
}
}
// 静态内部类
static class StaticInnerClass {
public void accessOuter() {
// System.out.println("outerVar: " + outerVar); // 错误
System.out.println("staticOuterVar: " + staticOuterVar); // 正确
}
}
public void method() {
final int localVar = 300;
// 局部内部类
class LocalInnerClass {
public void accessVariables() {
System.out.println("outerVar: " + outerVar); // 正确
System.out.println("staticOuterVar: " + staticOuterVar); // 正确
System.out.println("localVar: " + localVar); // 正确
}
}
}
}2. 创建内部类对象
症状:无法创建内部类的对象
解决方案:
- 成员内部类:需要通过外部类对象创建:
outer.new InnerClass() - 静态内部类:可以直接通过外部类名创建:
OuterClass.StaticInnerClass() - 局部内部类:只能在定义它的方法或代码块中创建
- 匿名内部类:在创建时直接实现接口或继承抽象类
示例:
java
public class OuterClass {
// 成员内部类
class InnerClass {}
// 静态内部类
static class StaticInnerClass {}
public void method() {
// 局部内部类
class LocalInnerClass {}
// 创建局部内部类的对象
LocalInnerClass localInner = new LocalInnerClass();
}
public static void main(String[] args) {
// 创建外部类对象
OuterClass outer = new OuterClass();
// 创建成员内部类的对象
OuterClass.InnerClass inner = outer.new InnerClass();
// 创建静态内部类的对象
OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();
// 创建匿名内部类的对象
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous inner class");
}
};
}
}3. 内部类的名称冲突
症状:内部类与外部类或其他类的名称冲突
解决方案:使用完全限定名来区分
示例:
java
class OuterClass {
private int x = 10;
class InnerClass {
private int x = 20;
public void display() {
int x = 30;
System.out.println("局部变量 x: " + x); // 30
System.out.println("内部类成员变量 x: " + this.x); // 20
System.out.println("外部类成员变量 x: " + OuterClass.this.x); // 10
}
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.display();
}
}最佳实践
成员内部类:当内部类需要访问外部类的实例成员时使用
静态内部类:当内部类不需要访问外部类的实例成员时使用,提高性能
局部内部类:当内部类只在一个方法中使用时使用
匿名内部类:当只需要创建一个接口或抽象类的实例,且只使用一次时使用
命名规范:内部类的命名应该清晰,表明其用途
避免过度使用:内部类会增加代码的复杂性,应该只在必要时使用
总结
内部类是 Java 中一种重要的语法结构,主要包括四种类型:
成员内部类:定义在外部类的成员位置,没有
static修饰,可以访问外部类的所有成员静态内部类:定义在外部类的成员位置,使用
static修饰,只能访问外部类的静态成员局部内部类:定义在方法或代码块中,只能在定义它的方法或代码块中使用
匿名内部类:没有名称的内部类,通常用于创建接口或抽象类的实例
内部类的特点:
- 可以访问外部类的成员,包括私有成员
- 可以实现更好的封装
- 可以更方便地实现某些设计模式
- 可以使代码更加简洁
通过合理使用内部类,可以提高代码的可读性、可维护性和灵活性。
