ArrayStoreException是Java运行时异常,因数组协变导致向实际类型为String[]的Object[]引用中存入Integer等不兼容类型而抛出;JVM在写入时依据数组真实组件类型校验,不匹配即中断执行。

Java中数组是协变的,这意味着子类型的数组可以赋值给父类型的数组引用,但这种设计会在运行时引入类型安全风险,导致ArrayStoreException。它不是编译期错误,而是在尝试向数组存入不兼容类型元素时抛出的运行时异常。
为什么数组协变会引发 ArrayStoreException
Java为保持与早期版本的兼容性,允许数组协变(如String[]可赋给Object[]),但JVM在运行时会对每次数组写操作做类型检查。一旦发现待存入元素的实际类型与数组声明的**组件类型(component type)**不匹配,就立即抛出ArrayStoreException。
注意:这个检查只发生在写入时,读取不会触发;且仅对引用类型数组生效(基本类型数组无此问题,因不存在协变关系)。
典型触发场景与代码示例
以下情况极易触发该异常:
-
将父类数组引用指向子类数组后,误存入其他子类实例:
Object[] arr = new String[2]; arr[0] = new Integer(1); // ArrayStoreException -
泛型擦除 + 数组混用(常见于反射或工具类):
方法返回Object[]但实际是String[],调用方直接往里塞File对象 -
多态参数传递中隐式向上转型后修改:
方法接收Number[]参数,内部却尝试存入String(即使参数实为Integer[])
如何排查和定位问题
关键看异常堆栈中的array store位置和数组实际类型:
- 检查抛出异常那一行的数组变量,用
array.getClass().getComponentType()确认其真实组件类型 - 回溯该数组的初始化来源——是否通过父类型声明、强制转型或方法返回获得
- 使用IDE调试时,在异常行观察数组运行时类型(如IntelliJ中hover变量可显示
String[]而非声明的Object[]) - 避免在泛型集合转数组时用
new Object[n]再强转——应使用list.toArray(new T[n])保证类型一致
规避方案与最佳实践
根本原则:**优先用泛型集合替代数组,或确保数组操作不破坏类型契约**:
- 声明数组时尽量使用最具体的类型(如用
String[]而非Object[]接收字符串数组) - 若必须接受父类型数组参数,只读不写;如需写入,先用
instanceof校验目标元素是否属于数组组件类型 - 集合转数组统一用
list.toArray(new Type[list.size()]),避免list.toArray()返回Object[]埋雷 - 工具类中避免返回“伪泛型数组”,例如不要写
public static——这在运行时可能崩溃T[] copy(T[] src) { return (T[]) new Object[src.length]; }
不复杂但容易忽略:数组协变是Java语言特性,不是bug;真正的问题在于开发时未意识到它带来的运行时约束。只要写入前确认类型兼容性,或绕过数组改用List,就能彻底避开这类异常。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











