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

Java向上转型子类对象为何变量用父类、方法用子类?附构造器疑问

Java继承常见问题解答

1. 向上转型后方法调用与变量访问的差异原因

这是因为Java中方法调用是动态绑定,而变量访问是静态绑定:

  • 方法speak()被Dog类重写了,当通过向上转型的引用beagle调用时,JVM会在运行时根据实际对象的类型(Dog)选择执行子类的重写方法。
  • 变量legs在Dog类中是重新定义的(属于变量隐藏,Java不支持变量重写),编译期会根据引用的编译类型(Animal)确定访问哪个变量,所以beagle.legs访问的是Animal类中未初始化的legs(默认值0)。

2. 构造器执行两次的原因

每次创建子类对象时,Java会隐式调用父类的无参构造器:

  • 子类构造器的第一行默认会执行super()(编译器自动添加,无需手动编写),所以每次new Dog()都会先执行Animal的构造器,再执行Dog的构造器。
  • 你创建了2个Dog对象,因此Animal和Dog的构造器各被调用2次,对应输出里的四行构造器日志。

3. Animal dog = new Dog(); 中各部分的含义

  • Animal是引用变量dog的编译类型:它决定了编译阶段这个引用能调用哪些方法(比如只能调用Animal类中定义或继承的方法)。
  • new Dog()是实际创建的对象的运行类型:这个对象本质是Dog实例,运行时的方法执行、对象属性的实际存储都由这个类型决定。

示例代码

// Online Java Compiler
// Use this editor to write, compile and run your Java code online

class Animal {
    int legs;

    Animal(){
        //this.legs = 5;
        System.out.println("-- Animal Constructor called --");
    }

    void speak(){
        System.out.println("Speak!");
    }
}

class Dog extends Animal {

    int legs;

    Dog(){
        this.legs = 4;
        System.out.println("-- Dog Constructor called --");
    }
    void speak(){
        System.out.println("Bow wow!");
    }
}

class Main {
    public static void main(String[] args) {

        Animal beagle = new Dog();
        Dog chew_wawa = new Dog();
//        Animal animal = new Animal();


        System.out.println(beagle.legs);
        System.out.println(chew_wawa.legs);
//        System.out.println(animal.legs);


        beagle.speak();
        chew_wawa.speak();
//        animal.speak();
    }

}

代码输出

-- Animal Constructor called --
-- Dog Constructor called --
-- Animal Constructor called --
-- Dog Constructor called --
0
4
Bow wow!
Bow wow!

Process finished with exit code 0

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:01:05