Java中抽象类作方法参数及父类引用指向子类对象的原理疑问
抽象类作为方法参数与多态的原理(Java vs C++)
示例代码
抽象类与子类定义
public abstract class Animal { public abstract void talk(); } class Dog extends Animal { @Override public void talk() { System.out.println("Woof"); } } class Cat extends Animal { @Override public void talk() { System.out.println("Meow"); } }
测试类代码
public class Test { public static void main(String[] args) { Dog dog = new Dog(); Cat cat = new Cat(); Test t = new Test(); t.makeAnimalTalk(dog); t.makeAnimalTalk(cat); } public void makeAnimalTalk(Animal animal) { animal.talk(); } }
疑问解答
1. makeAnimalTalk方法中的Animal类型参数animal是什么?
这里的animal并不是抽象类Animal的实例,而是一个引用变量——它的作用是指向Animal子类(比如Dog、Cat)的实际对象。抽象类不能实例化指的是你无法直接通过new Animal()创建对象,但完全可以用抽象类类型的引用来指向它的子类实例。
2. 为什么这种写法可行?
这是Java多态特性的核心体现:
- 当你把
Dog或Cat的实例传入makeAnimalTalk方法时,Java会自动将子类对象的引用向上转型为Animal类型的引用。 - 由于
talk()是抽象方法,所有子类必须重写它;调用animal.talk()时,JVM会根据引用实际指向的对象类型,动态调用对应的子类重写方法(即Dog.talk()或Cat.talk()),这就是动态绑定。
3. Animal dog = new Dog();为何能运行?和C++的区别?
首先纠正误解:这种写法在C里是可以运行的——C中抽象类(含纯虚函数)同样不能直接实例化,但可以用基类指针/引用指向子类对象,逻辑和Java一致。
Java里Animal dog = new Dog();的本质是向上转型:
new Dog()创建的是Dog类的实际对象,存储在堆内存中。Animal类型的引用dog只是指向这个Dog对象的"指针"(Java隐藏了指针概念,统一用引用)。- 因为
Dog是Animal的子类,符合"is-a"关系,所以这种向上转型是安全的,Java编译器允许该赋值操作。
和C++的细微区别:
- Java里所有对象都是引用类型(基本数据类型除外),
Animal dog本身就是引用;而C++需要显式用指针Animal* dog = new Dog();或引用Animal& dog = *new Dog();。 - Java的动态绑定是默认行为,只要方法被
override(且不是final、static、private)就会自动触发;C++则需要基类方法声明为virtual才能实现动态绑定,纯虚函数对应Java的抽象方法。
内容的提问来源于stack exchange,提问作者montag
相关产品推荐
相关产品推荐

