java多态数组本质是父类类型数组引用子类对象,依赖向上转型和运行时动态绑定;它协变但不类型安全,需谨慎转型,而泛型集合更安全但非协变。

Java 中的多态数组不是一种特殊数组类型,而是普通数组(如 Animal[])在声明时使用父类/接口类型,实际存入不同子类对象的写法——它靠的是引用多态性,不是数组本身“支持多态”。
为什么 Animal[] 能存 Dog 和 Cat 对象
因为数组存储的是对象引用,而 Java 允许子类引用赋值给父类引用(向上转型)。数组类型只约束引用类型,不约束堆中真实对象类型。
常见错误现象:Object[] arr = new String[2]; arr[0] = new Integer(1); 会抛 ArrayStoreException —— 这说明数组在运行时仍做类型检查,但检查的是「声明类型」与「存入对象的运行时类型是否兼容」,不是编译期擦除后的类型。
- 必须确保所有存入对象是数组声明类型的子类或实现类,否则运行时报错
- 不能用
new Object[2]存Dog再当作Animal[]用——类型不匹配,编译失败 - 泛型数组(如
List<animal>[]</animal>)禁止创建,因泛型擦除后无法保证运行时安全
Animal[] 和 List<animal></animal> 的关键区别
数组是协变(covariant)的:Dog[] 是 Animal[] 的子类型;而 List<dog></dog> 不是 List<animal></animal> 的子类型(不变,invariant),这是为避免类型污染。
立即学习“Java免费学习笔记(深入)”;
使用场景:需要固定长度、快速随机访问、且明确由统一父类协调行为时,Animal[] 简洁直接;若需动态增删、类型安全更强,优先选 List extends Animal> 或 List<animal></animal>。
-
Animal[]支持arr[i].makeSound()直接调用重写方法,依赖运行时绑定 -
List<animal></animal>在 add 时就校验类型,比数组更早暴露问题 - 性能上,数组访问略快;但
ArrayList封装开销极小,通常可忽略
容易踩的坑:向下转型前没校验 instanceof
从多态数组取元素后,如果想调用子类特有方法(如 Dog.bark()),必须先判断类型再强转。否则 ClassCastException 很常见。
示例:
Animal[] animals = {new Dog(), new Cat()};
for (Animal a : animals) {
if (a instanceof Dog) {
((Dog) a).bark(); // 安全
}
}
- 别省略
instanceof检查,哪怕“确定只有 Dog”——维护时容易出错 - 避免连续强转:
((Dog)((Animal)obj)).bark(),冗余且易崩 - 如果大量分支判断类型,说明设计可能该用访问者模式或策略模式了
多态数组的“多态”不在数组结构里,而在引用变量的静态类型和对象实际类型的分离——这点一旦混淆,后面转型、泛型、集合混用时问题会层层放大。








