Java 嵌套类(Nested Classes)

更新于 2025-12-25

Jakob Jenkov 2015-03-09

在 Java 中,嵌套类(Nested Classes)是指定义在另一个类内部的类。

嵌套类的主要目的是将嵌套类与其外部类清晰地组织在一起,表明这两个类应协同使用;或者表明该嵌套类仅在其封闭(拥有)类内部使用。

Java 开发者通常将嵌套类称为内部类(Inner Classes),但实际上“内部类”只是 Java 中多种嵌套类类型之一。

在 Java 中,嵌套类被视为其外部类的成员。因此,嵌套类可以使用 publicpackage(无访问修饰符)、protectedprivate 等访问修饰符(详见 访问修饰符)。此外,如 Java 继承 教程所述,嵌套类也可以被子类继承。

Java 支持以下几种不同类型的嵌套类:

  • 静态嵌套类(Static Nested Classes)
  • 非静态嵌套类(Non-static Nested Classes / Inner Classes)
  • 局部类(Local Classes)
  • 匿名类(Anonymous Classes)

下面将逐一介绍这些嵌套类类型。


静态嵌套类(Static Nested Classes)

静态嵌套类在 Java 中声明如下:

public class Outer {
    public static class Nested {
    }
}

要创建 Nested 类的实例,必须通过外部类名进行引用:

Outer.Nested instance = new Outer.Nested();

在 Java 中,静态嵌套类本质上就是一个普通的类,只是被嵌套在另一个类中。由于它是 static 的,因此只能通过外部类的实例引用来访问其非静态成员。


非静态嵌套类(内部类,Inner Classes)

Java 中的非静态嵌套类也被称为内部类(Inner Classes)。内部类与外部类的一个实例相关联,因此必须先创建外部类的实例,才能创建内部类的实例。

内部类的定义示例如下:

public class Outer {
    public class Inner {
    }
}

创建 Inner 类实例的方式如下:

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

注意:在外部类实例后使用 new 关键字来创建内部类实例。

非静态嵌套类(内部类)可以访问其外部类的所有字段,即使这些字段被声明为 private。例如:

public class Outer {
    private String text = "I am private!";

    public class Inner {
        public void printText() {
            System.out.println(text);
        }
    }
}

注意:Inner 类的 printText() 方法直接引用了 Outer 类的私有字段 text,这是完全合法的。

调用方式如下:

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

内部类的变量遮蔽(Inner Class Shadowing)

如果 Java 内部类中声明了与外部类同名的字段或方法,则内部类的成员会遮蔽(shadow)外部类的成员。

示例:

public class Outer {
    private String text = "I am Outer private!";

    public class Inner {
        private String text = "I am Inner private";

        public void printText() {
            System.out.println(text); // 输出 Inner 的 text
        }
    }
}

在上述例子中,OuterInner 都包含名为 text 的字段。当 Inner 引用 text 时,它指的是自己的字段。

不过,Java 允许内部类通过 Outer.this.text 的方式显式访问外部类的字段:

public class Outer {
    private String text = "I am Outer private!";

    public class Inner {
        private String text = "I am Inner private";

        public void printText() {
            System.out.println(text);           // Inner 的 text
            System.out.println(Outer.this.text); // Outer 的 text
        }
    }
}

局部类(Local Classes)

局部类是定义在方法内部或方法内的作用域块({ ... })中的类,类似于内部类。

示例:

class Outer {
    public void printText() {
        class Local {
        }
        Local local = new Local();
    }
}

局部类只能在其定义的方法或作用域块内部访问。

与内部类一样,局部类可以访问其外部类的成员(字段和方法)。

此外,局部类还可以访问所在方法中的局部变量,但这些变量必须被声明为 final

Java 8 开始,局部类还可以访问实际上为 final(effectively final)的局部变量或参数——即初始化后从未被修改的变量。

如果局部类定义在静态方法中,则它只能访问外部类的静态成员。由于局部类本质上是非静态的,因此即使在静态方法中定义,也不能包含大多数静态声明(但允许 static final 常量)。

局部类同样遵循与内部类相同的遮蔽规则


匿名类(Anonymous Classes)

匿名类是没有类名的嵌套类,通常用于以下两种场景:

  • 作为现有类的子类(继承并重写方法)
  • 实现某个接口

匿名类在实例化时同时定义。例如,以下代码定义了一个 SuperClass 的匿名子类:

public class SuperClass {
    public void doIt() {
        System.out.println("SuperClass doIt()");
    }
}

SuperClass instance = new SuperClass() {
    public void doIt() {
        System.out.println("Anonymous class doIt()");
    }
};

instance.doIt(); // 输出: Anonymous class doIt()

匿名类也可以实现接口:

public interface MyInterface {
    public void doIt();
}

MyInterface instance = new MyInterface() {
    public void doIt() {
        System.out.println("Anonymous class doIt()");
    }
};

instance.doIt();

匿名类可以访问其外部类的成员,也可以访问所在方法中 finaleffectively final 的局部变量(Java 8 起)。

你可以在匿名类中声明字段和方法,但不能声明构造函数。不过,你可以使用静态初始化块(static initializer)来初始化字段:

final String textToPrint = "Text...";

MyInterface instance = new MyInterface() {
    private String text;

    // 静态初始化块(实际是非静态初始化块,因匿名类非静态)
    {
        this.text = textToPrint;
    }

    public void doIt() {
        System.out.println(this.text);
    }
};

instance.doIt();

匿名类同样适用与内部类相同的遮蔽规则


嵌套类的优势(Nested Class Benefits)

使用 Java 嵌套类的主要优势在于:将逻辑上紧密相关的类组合在一起

虽然你也可以通过将类放在同一个包中实现分组,但将一个类嵌套在另一个类内部表达了更强的关联性。

嵌套类通常只被其外部类使用或与之配合使用。有时,嵌套类对外部完全不可见(如 private 嵌套类),仅用于内部实现;有时则对外暴露,但必须与外部类一起使用。

示例:缓存类(Cache)

假设你有一个 Cache 类,内部需要一个 CacheEntry 类来存储缓存项的元数据(如缓存值、插入时间、访问次数等)。

用户可能只需要获取缓存值,而无需知道 CacheEntry 的存在;但有时你也希望暴露 CacheEntry,以便用户获取更多元信息(如最后刷新时间)。

示例 1:隐藏嵌套类

public class Cache {
    private Map<String, CacheEntry> cacheMap = new HashMap<>();

    private class CacheEntry {
        public long timeInserted = 0;
        public Object value = null;
    }

    public void store(String key, Object value) {
        CacheEntry entry = new CacheEntry();
        entry.value = value;
        entry.timeInserted = System.currentTimeMillis();
        this.cacheMap.put(key, entry);
    }

    public Object get(String key) {
        CacheEntry entry = this.cacheMap.get(key);
        if (entry == null) return null;
        return entry.value;
    }
}

在此版本中,CacheEntryprivate 的,对外不可见。

示例 2:暴露嵌套类

public class Cache {
    private Map<String, CacheEntry> cacheMap = new HashMap<>();

    public class CacheEntry {
        public long timeInserted = 0;
        public Object value = null;
    }

    public void store(String key, Object value) {
        CacheEntry entry = new CacheEntry();
        entry.value = value;
        entry.timeInserted = System.currentTimeMillis();
        this.cacheMap.put(key, entry);
    }

    public Object get(String key) {
        CacheEntry entry = this.cacheMap.get(key);
        if (entry == null) return null;
        return entry.value;
    }

    public CacheEntry getCacheEntry(String key) {
        return this.cacheMap.get(key);
    }
}

此版本将 CacheEntry 设为 public,允许外部代码直接访问缓存条目对象。