Java Academy · 课时

内部类与访问外部类

使用持有外部类实例引用的非静态内部类

第 2 / 4 课13 个步骤

内部类与访问外部类 是 CoddyKit 上的免费 Java Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

内部类

非静态内部类会隐式持有对外部类实例的引用。它可以访问外部类的所有成员,包括私有成员。

声明和使用内部类

只能通过外部类实例创建内部类实例,使用方式为 outer.new InnerClass()。

class BankAccount {
    private double balance;
    private String owner;

    BankAccount(String owner, double initial) {
        this.owner = owner; this.balance = initial;
    }

    class Transaction {
        void deposit(double amount) {
            balance += amount; // accesses outer field directly
            System.out.println(owner + " deposited " + amount);
        }
    }
}

BankAccount acct = new BankAccount("Alice", 500);
BankAccount.Transaction tx = acct.new Transaction();
tx.deposit(100); // Alice deposited 100.0

访问外部成员

内部类可以访问外部类的所有成员,包括私有字段和方法。

class Encryption {
    private final String secretKey = "s3cr3t"; // private!

    class Encryptor {
        String encrypt(String data) {
            // Inner class accesses private outer field
            return data + "|" + secretKey.hashCode();
        }
    }

    class Decryptor {
        String decrypt(String encrypted) {
            int idx = encrypted.lastIndexOf('|');
            return encrypted.substring(0, idx);
        }
    }
}

使用 this 处理名称遮蔽

当内部类拥有与外部类同名的字段时,请使用 OuterClass.this.field 引用外部类中的字段。

class Outer {
    int value = 10;

    class Inner {
        int value = 20; // shadows outer.value

        void printBoth() {
            System.out.println("Inner value: " + value);         // 20
            System.out.println("Outer value: " + Outer.this.value); // 10
        }
    }
}

new Outer().new Inner().printBoth();
// Inner value: 20
// Outer value: 10

使用内部类实现迭代器模式

一个经典用例是:将迭代器实现为内部类,使其能够访问外部集合的私有数据。

class NumberRange implements Iterable<Integer> {
    private final int start;
    private final int end;

    NumberRange(int start, int end) {
        this.start = start; this.end = end;
    }

    @Override
    public java.util.Iterator<Integer> iterator() {
        return new RangeIterator(); // inner class instance
    }

    private class RangeIterator implements java.util.Iterator<Integer> {
        private int current = start; // accesses outer start

        public boolean hasNext() { return current <= end; }
        public Integer next()    { return current++; }
    }
}

for (int n : new NumberRange(1, 5)) System.out.print(n + " ");
// 1 2 3 4 5

作为内部类的事件监听器

GUI 和事件驱动编程通常使用内部类实现事件监听器,从而访问外部用户界面组件的状态。

// Swing-style example (conceptual)
class LoginPanel {
    private String username = "";

    class LoginButtonListener {
        void onButtonClicked() {
            if (username.isEmpty()) {
                System.out.println("Please enter username"); // accesses outer field
            } else {
                System.out.println("Logging in as: " + username);
            }
        }
    }

    void setUsername(String name) { this.username = name; }
}
LoginPanel panel = new LoginPanel();
panel.setUsername("alice");
panel.new LoginButtonListener().onButtonClicked();
// Logging in as: alice

集合中的内部类

某些集合实现会使用内部类保存游标或视图,这些游标或视图由外部集合提供支持。

class SimpleStack<T> {
    private Object[] elements;
    private int size = 0;

    SimpleStack(int capacity) {
        elements = new Object[capacity];
    }

    void push(T item) {
        if (size >= elements.length) throw new StackOverflowError();
        elements[size++] = item;
    }

    T pop() {
        if (size == 0) throw new java.util.EmptyStackException();
        T item = (T) elements[--size];
        elements[size] = null;
        return item;
    }

    int size() { return size; }
}

内存泄漏警告

内部类实例会持有对外部实例的引用。如果内部类脱离外部类实例的作用域(例如被存储在长期存在的集合中),外部实例就无法被垃圾回收。

// Memory leak: anonymous inner class (implicit outer ref) stored statically
class LeakExample {
    byte[] data = new byte[1024 * 1024]; // 1MB

    // BAD: this Runnable holds a reference to LeakExample.this
    Runnable leak = new Runnable() {
        public void run() { System.out.println(data.length); }
    };
}
// If 'leak' is stored somewhere, LeakExample (and its 1MB) is never GC'd

// FIX: use a static nested class or a lambda that captures only needed values
static Runnable noLeak(byte[] data) {
    return () -> System.out.println(data.length); // captures array, not outer
}

内部类与 Lambda 的比较

现代 Java 中的 Lambda 表达式通常会替代单方法接口的匿名内部类。Lambda 表达式更加简洁,但 NOT 拥有自己的 this。

// Old way: anonymous inner class
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Running (inner class) — this is the Runnable");
    }
};

// Modern: lambda
Runnable r2 = () -> System.out.println("Running (lambda)");

r1.run();
r2.run();

// Lambda this = enclosing class (not the Runnable)
// Inner class this = the Runnable instance itself

何时使用内部类

在以下情况下,请使用非静态内部类:

  • 该类需要访问外部实例的私有状态
  • 每个内部实例在逻辑上都与特定的外部实例关联
  • 使用迭代器模式封装遍历逻辑

实践:Paginator

一个实用的 Paginator,其中内部类保存页面状态并访问外部类的数据列表。

import java.util.*;

class Paginator<T> {
    private final List<T> items;
    private final int pageSize;

    Paginator(List<T> items, int pageSize) {
        this.items = items; this.pageSize = pageSize;
    }

    class Page {
        private int pageIndex = 0;

        List<T> current() {
            int start = pageIndex * pageSize;
            int end = Math.min(start + pageSize, items.size()); // outer
            return start < items.size() ? items.subList(start, end) : List.of();
        }
        boolean hasNext() { return (pageIndex + 1) * pageSize < items.size(); }
        void next() { if (hasNext()) pageIndex++; }
    }
}
Paginator<String> p = new Paginator<>(List.of("a","b","c","d","e"), 2);
Paginator.Page page = p.new Page();
System.out.println(page.current()); // [a, b]
page.next();
System.out.println(page.current()); // [c, d]

快速检查

内部类(非静态)持有什么,而静态嵌套类不持有?

回顾:内部类与外部类访问

要点:

  • 非静态内部类会隐式持有对外围外部实例的引用
  • 只能通过外部实例创建内部类实例:outer.new Inner()
  • 内部类可以访问外部类的所有成员,包括私有字段
  • 使用 Outer.this.field 消除字段遮蔽造成的歧义
  • 最适合的场景:Iterator、事件监听器和内部数据结构
  • 风险:内部类实例会阻止外部实例被垃圾回收
免费开始

用 AI 导师学习 Java — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
104
课程
374

常见问题解答

「内部类与访问外部类」课时是免费的吗?

是的 — 「内部类与访问外部类」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「内部类与访问外部类」这节课中我会学到什么?

使用持有外部类实例引用的非静态内部类 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「内部类与访问外部类」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 静态嵌套类
  2. 内部类与访问外部类
  3. 局部类与匿名类
  4. 选择合适的嵌套策略
← 返回 Java Academy