You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java匿名类重定义核心技术疑问解析

Java匿名类继承父类的核心问题解析

这是一个基于普通类(而非接口)创建匿名子类的Java程序:

class Anonymous {
    int i = 10;
    void main() {
        System.out.println("Anonymous Main " + i);
    }

    void setI(int i) {
        System.out.println("under set");
        this.i = i;
    }

    void main2() {
        setI(30);
        System.out.println("Main 2's " + i);
    }

}

public class Runner {

    public static void main(String[] args) {
        Anonymous a = new Anonymous() {
            int i = 20;

            @Override
            void setI(int i) {
                System.out.println("Over set");
                this.i = i;
            }

            @Override
            public void main() {
                setI(0);
                System.out.println("Redefined Anonymous Main " + i);
            }
        };
        a.main();
        a.main2();
    }
}

程序输出:

Over set
Redefined Anonymous Main 0
Over set
Main 2's 10

核心疑问解答

1. 在main2方法中,为何访问到的i值为10?

main2是定义在父类Anonymous里的方法,Java规则里,成员方法访问成员变量时,默认认的是当前方法所属类的变量。main2属于父类,它里面的i直接指向父类的int i = 10,不会去子类找同名变量——哪怕子类有一样名字的i,父类方法的代码也只认自己类里的变量。

2. 为什么对象a存在两个i的成员变量副本?

这个匿名类是Anonymous的子类,子类里声明了和父类同名的int i = 20,这属于成员变量隐藏,不是方法那种重写。父类的成员变量和子类的同名变量会各自占一块内存:父类的i在对象的父类实例区域,子类的i在子类扩展的实例区域,所以整个a对象里就有两个i的副本。

3. 既然存在两个i的副本,为何调用setI时不存在两个setI方法副本?

方法和成员变量的规则完全不一样:子类重写父类的setI后,父类的该方法会被覆盖。Java的动态绑定机制下,通过父类引用调用setI时,会自动执行子类重写后的版本。父类的setI方法虽然还在字节码里,但对于这个子类对象来说,调用时只会触发子类的版本——除非你用super.setI()显式调用父类的方法,否则看不到父类的版本。

4. 为什么main2方法能访问重定义后的setI方法,却无法访问重定义后的i?

  • 关于setI:Java方法调用是动态绑定,不管方法定义在父类还是子类,调用时会根据实际对象的类型(这里是匿名子类)执行对应的重写方法,所以main2里调用setI(30)会触发子类的版本。
  • 关于i:成员变量的访问是静态绑定,编译期就确定了要访问的变量所属的类。main2是父类的方法,编译时就定了它访问的是父类的i,和实际对象的子类类型无关,所以看不到子类的i。

5. Java在底层针对该场景具体执行了什么操作?

从底层实现看:

  1. 编译器会把这个匿名类编译成独立的字节码文件(比如Runner$1.class),明确标记它是Anonymous的子类。
  2. 子类字节码的内存布局里,会同时包含父类的i和自己的i——父类的实例字段在前,子类的在后,各自占独立内存位置。
  3. 方法调用时:JVM会通过对象的方法表(Method Table)找到子类重写后的setI方法地址执行;而main2里访问i时,编译期就计算好了父类i在对象内存中的偏移量,直接读取该位置的值。
  4. 当用父类引用Anonymous a指向子类对象时,JVM会维护对象的类型信息,确保动态方法调用能找到正确的子类方法,但变量访问还是按编译时的父类类型来定位。

内容的提问来源于stack exchange,提问作者Razer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 00:27:06