杰瑞科技汇

Java内部类如何访问外部类的方法?

这是一个非常核心且重要的 Java 特性。内部类天生就持有外部类的一个引用,因此它可以无障碍地访问外部类的所有成员,无论这些成员是 publicprotecteddefault(包私有)还是 private 的。

Java内部类如何访问外部类的方法?-图1
(图片来源网络,侵删)

核心原理:隐式的外部类引用

当你编译一个包含内部类的 Java 文件时,编译器会做两件事:

  1. 为外部类生成一个 .class 文件,OuterClass.class
  2. 为内部类生成一个 .class 文件,文件名格式为 OuterClass$InnerClass.class

关键在于,编译器在生成的 OuterClass$InnerClass.class 文件中,自动添加了一个指向外部类对象的引用,这个引用通常有一个特殊的名字,this$0

这个隐式引用使得内部类的方法在执行时,总是知道它属于哪个外部类对象,从而可以访问该外部类对象的成员。


访问规则详解

内部类访问外部类成员时,遵循以下规则:

Java内部类如何访问外部类的方法?-图2
(图片来源网络,侵删)

a. 访问外部类的实例成员(非 static 成员)

这是最常见的情况,内部类可以直接访问外部类的实例方法和实例字段。

示例代码:

class OuterClass {
    // 外部类的私有实例字段
    private String outerInstanceField = "这是外部类的实例字段";
    // 外部类的实例方法
    public void outerInstanceMethod() {
        System.out.println("外部类的实例方法被调用了");
    }
    // 定义一个内部类
    class InnerClass {
        public void accessOuterMembers() {
            // 1. 直接访问外部类的私有实例字段
            System.out.println("内部类访问外部类的实例字段: " + outerInstanceField);
            // 2. 直接调用外部类的实例方法
            outerInstanceMethod();
        }
    }
}
// 测试类
public class Main {
    public static void main(String[] args) {
        // 1. 创建外部类对象
        OuterClass outer = new OuterClass();
        // 2. 通过外部类对象创建内部类对象
        // 语法: OuterClass.InnerClass inner = outer.new InnerClass();
        OuterClass.InnerClass inner = outer.new InnerClass();
        // 3. 调用内部类的方法,该方法会访问外部类的成员
        inner.accessOuterMembers();
    }
}

输出结果:

内部类访问外部类的实例字段: 这是外部类的实例字段
外部类的实例方法被调用了

关键点:

Java内部类如何访问外部类的方法?-图3
(图片来源网络,侵删)
  • 创建内部类对象的前提是必须先存在一个外部类对象,语法是 outer.new InnerClass()
  • 内部类对象 inner 持有对外部类对象 outer 的引用,因此可以访问其所有成员。

b. 访问外部类的静态成员(static 成员)

内部类不仅可以访问外部类的实例成员,也可以访问外部类的静态成员(静态字段和静态方法)。

示例代码:

class OuterClass {
    // 外部类的静态字段
    private static String outerStaticField = "这是外部类的静态字段";
    // 外部类的静态方法
    public static void outerStaticMethod() {
        System.out.println("外部类的静态方法被调用了");
    }
    class InnerClass {
        public void accessOuterStaticMembers() {
            // 1. 直接访问外部类的静态字段
            System.out.println("内部类访问外部类的静态字段: " + outerStaticField);
            // 2. 直接调用外部类的静态方法
            outerStaticMethod();
        }
    }
}
// 测试类
public class Main {
    public static void main(String[] args) {
        OuterClass.InnerClass inner = new OuterClass().new InnerClass();
        inner.accessOuterStaticMembers();
    }
}

输出结果:

内部类访问外部类的静态字段: 这是外部类的静态字段
外部类的静态方法被调用了

特殊情况:静态内部类

静态内部类(用 static 修饰的内部类)是一个特例,它本质上是一个外部类的静态成员,而不是一个外部类对象的“内部”部分。

静态内部类不持有外部类的隐式引用,这意味着:

  1. 不能访问外部类的实例成员(非 static 字段和方法)。
  2. 可以访问外部类的静态成员static 字段和方法)。
  3. 创建静态内部类对象时,不需要先创建外部类对象,语法是 new OuterClass.InnerClass()

示例代码:

class OuterClass {
    private String outerInstanceField = "外部类的实例字段"; // 实例成员
    private static String outerStaticField = "外部类的静态字段"; // 静态成员
    // 定义一个静态内部类
    static class StaticInnerClass {
        public void accessMembers() {
            // 下面这行代码会编译出错!
            // System.out.println(outerInstanceField); // Error: 无法从静态上下文中引用非静态 变量 outerInstanceField
            // 下面这行代码是正确的
            System.out.println("静态内部类访问外部类的静态字段: " + outerStaticField);
        }
    }
}
// 测试类
public class Main {
    public static void main(String[] args) {
        // 创建静态内部类对象,不需要外部类对象
        OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();
        staticInner.accessMembers();
    }
}

当内部类和外部类有同名成员时

如果内部类和外部类拥有同名的字段或方法,内部类默认会访问自己的成员,如果想在内部类中明确访问外部类的成员,可以使用 外部类类名.this 的形式。

示例代码:

class OuterClass {
    private String name = "外部类的 name";
    class InnerClass {
        private String name = "内部类的 name";
        public void printNames() {
            // 访问内部类的 name
            System.out.println("内部类的 name: " + this.name);
            // 访问外部类的 name
            System.out.println("外部类的 name: " + OuterClass.this.name);
        }
    }
}
// 测试类
public class Main {
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        OuterClass.InnerClass inner = outer.new InnerClass();
        inner.printNames();
    }
}

输出结果:

内部类的 name: 内部类的 name
外部类的 name: 外部类的 name

局部内部类和匿名内部类

这两种内部类也遵循相同的规则,因为它们本质上也是内部类,只是定义的位置和形式不同。

  • 局部内部类:定义在方法或作用域内部,它可以访问外部类的所有成员,以及方法中 final 或有效 final 的局部变量。
  • 匿名内部类:没有类名,常用于实现接口或继承类,它同样可以访问外部类的所有成员。

匿名内部类示例:

interface Greeting {
    void sayHello();
}
class Outer {
    private String message = "你好,世界!";
    public Greeting getGreeting() {
        // 匿名内部类
        return new Greeting() {
            @Override
            public void sayHello() {
                // 可以访问外部类的私有成员 message
                System.out.println(message);
            }
        };
    }
}
public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Greeting greeting = outer.getGreeting();
        greeting.sayHello(); // 输出: 你好,世界!
    }
}
内部类类型 是否持有外部类引用 能否访问外部类实例成员 能否访问外部类静态成员 创建方式
成员内部类 outer.new InnerClass()
静态内部类 new OuterClass.InnerClass()
局部内部类 在其定义的作用域内 new InnerClass()
匿名内部类 new Interface() { ... }

核心要点记住:非静态的内部类都持有一个对外部类对象的隐式引用,因此可以自由访问外部类的所有成员。 静态内部类是唯一的例外。

分享:
扫描分享到社交APP
上一篇
下一篇