1. 简介

本文将快速介绍 Java 语言中的嵌套类概念。

简单来说,Java 允许我们在一个类内部定义另一个类。嵌套类能帮助我们实现以下目标:

  • ✅ 逻辑上组织仅在某处使用的类
  • ✅ 提升代码可读性和可维护性
  • ✅ 增强封装性

在深入之前,先了解 Java 中嵌套类的四种类型:

  1. 静态嵌套类
  2. 非静态嵌套类(内部类)
  3. 局部类
  4. 匿名类

接下来我们将逐一详细解析。

2. 静态嵌套类

关于静态嵌套类,记住这几个关键点:

  • ✅ 属于外部类本身,而非外部类实例
  • ✅ 声明时可使用任意访问修饰符
  • ✅ 只能访问外部类的静态成员
  • ✅ 可定义静态和非静态成员

声明方式示例:

public class Enclosing {
    
    private static int x = 1;
    
    public static class StaticNested {

        private void run() {
            // 方法实现
        }
    }
    
    @Test
    public void test() {
        Enclosing.StaticNested nested = new Enclosing.StaticNested();
        nested.run();
    }
}

3. 非静态嵌套类

关于非静态嵌套类(内部类),记住这些要点:

  • ✅ 也称为内部类
  • ✅ 声明时可使用任意访问修饰符
  • ✅ 与外部类实例绑定(类似实例变量/方法)
  • ✅ 可访问外部类的所有成员(静态/非静态)
  • ✅ 只能定义非静态成员

声明方式示例:

public class Outer {
    
    public class Inner {
        // ...
    }
}

⚠️ 关键区别:声明时是否带 static 关键字。虽然语法上仅一字之差,但语义差异巨大——内部类实例与外部类实例绑定,因此能访问其成员。选择嵌套类型时需特别注意这点。

创建内部类实例必须先创建外部类实例

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

3.1. 局部类

局部类是内部类的一种特殊形式——在方法或作用域块内定义的类

记住这些特点:

  • ✅ 声明时不能有访问修饰符
  • ✅ 可访问外部上下文的静态/非静态成员
  • ✅ 只能定义实例成员

示例代码:

public class NewEnclosing {
    
    void run() {
        class Local {

            void run() {
                // 方法实现
            }
        }
        Local local = new Local();
        local.run();
    }
    
    @Test
    public void test() {
        NewEnclosing newEnclosing = new NewEnclosing();
        newEnclosing.run();
    }
}

3.2. 匿名类

匿名类用于快速实现接口或抽象类,无需创建可重用实现类。

记住这些要点:

  • ✅ 声明时不能有访问修饰符
  • ✅ 可访问外部上下文的静态/非静态成员
  • ✅ 只能定义实例成员
  • 唯一不能定义构造器的嵌套类
  • ❌ 不能继承/实现其他类或接口

先定义一个抽象类:

abstract class SimpleAbstractClass {
    abstract void run();
}

匿名类实现示例:

public class AnonymousInnerUnitTest {
    
    @Test
    public void whenRunAnonymousClass_thenCorrect() {
        SimpleAbstractClass simpleAbstractClass = new SimpleAbstractClass() {
            void run() {
                // 方法实现
            }
        };
        simpleAbstractClass.run();
    }
}

想深入了解?可参考我们的 Java 匿名类教程

4. 成员遮蔽

当内部类成员与外部类成员同名时,内部类成员会遮蔽外部类成员

此时:

  • this 关键字指向嵌套类实例
  • 通过 外部类名.this 访问外部类成员

示例代码:

public class NewOuter {

    int a = 1;
    static int b = 2;

    public class InnerClass {
        int a = 3;
        static final int b = 4;

        public void run() {
            System.out.println("a = " + a);  // 输出内部类 a
            System.out.println("b = " + b);  // 输出内部类 b
            System.out.println("NewOuterTest.this.a = " + NewOuter.this.a);  // 访问外部类 a
            System.out.println("NewOuterTest.b = " + NewOuter.b);  // 访问外部类静态 b
            System.out.println("NewOuterTest.this.b = " + NewOuter.this.b);  // 访问外部类 b
        }
    }

    @Test
    public void test() {
        NewOuter outer = new NewOuter();
        NewOuter.InnerClass inner = outer.new InnerClass();
        inner.run();
    }
}

5. 序列化踩坑

尝试序列化嵌套类时,为避免 java.io.NotSerializableException,必须:

  1. ✅ 将嵌套类声明为 static
  2. ✅ 让嵌套类和外部类都实现 Serializable 接口

6. 总结

本文系统介绍了 Java 嵌套类的概念及四种类型,并分析了不同类型中字段可见性和访问修饰符的差异。掌握这些知识能帮你写出更优雅的 Java 代码。

完整示例代码见 GitHub 项目