Java原生支持用父类或接口类型声明数组来存储不同子类对象,体现多态性;通过动态方法调度调用重写方法,需instanceof判断后转型才能访问子类特有成员,推荐优先使用ArrayList实现灵活增删。

用父类或接口类型声明数组,就能存下多个不同子类的对象——这不是“模拟”,而是Java原生支持的多态行为。关键不在怎么存,而在怎么安全、清晰地用。
用父类类型声明数组,直接存子类实例
数组声明时只看类型兼容性,不关心具体实现。只要子类继承自父类(或实现同一接口),就可以直接赋值:
- 写法示例:Animal[] animals = new Animal[4];,然后 animals[0] = new Dog(); animals[1] = new Cat();
- 编译能过,运行安全——因为每个子类对象都“是”Animal,满足里氏替换原则
- 数组里存的不是对象本身,而是指向堆中真实子类实例的引用,内存布局没额外开销
统一调用重写方法,靠JVM动态绑定
遍历时调用公共方法,JVM在运行期自动匹配实际对象所属类的版本,不用if-else硬判断:
- 比如所有子类都重写了 makeSound(),写 animals[i].makeSound() 就会分别打出“汪汪”或“喵喵”
- 这个过程叫动态方法调度,依赖的是对象的实际类型(runtime type),不是声明类型(compile-time type)
- 这也是策略模式、事件处理器等常见架构落地的底层支撑
需要调用子类特有方法?先判断再转型
父类引用无法直接访问子类独有的字段或方法,强行调用会编译失败。必须显式判断+向下转型:
- 写法:if (animals[i] instanceof Dog) { ((Dog) animals[i]).fetchBall(); }
- 注意:instanceof 判断是安全前提,避免 ClassCastException
- 如果逻辑复杂或频繁出现,建议重构——比如把 fetchBall() 提升为接口方法,或改用 Visitor 模式
数组长度固定?优先考虑 ArrayList<T>
多态数组适合已知数量、读多写少的场景。若需增删、扩容,ArrayList 是更自然的选择:
- 声明:List<Animal> animals = new ArrayList<>();,add 任意子类实例都合法
- 泛型擦除后底层仍是 Object[],但封装了扩容、索引校验等细节,代码更健壮
- Spring 等主流框架注入多个 Bean 时,也常按 List<PaymentStrategy> 方式处理


















