
ArrayStoreException 是运行时类型检查失败
它不是编译错误,而是在 array[i] = obj 这一步 JVM 主动抛出的异常,说明你试图把一个不兼容类型的对象存进数组——哪怕这个数组声明是父类类型,实际运行时也只认具体元素类型。
常见错误现象:java.lang.ArrayStoreException: java.lang.String(往 Number[] 里塞了 "123");或更隐蔽的:用泛型擦除后传入的数组被误用。
- 触发前提是:目标数组是**具体类型数组**(如
Integer[]、String[]),不是Object[] - 赋值时右侧对象的实际类型(
obj.getClass())必须是数组元素声明类型的子类或自身,否则立即失败 - 注意:编译器不会报错,因为类型检查发生在运行时;比如
Object[] arr = new Integer[1]; arr[0] = "hello";合法编译,但运行时报错
为什么 Object[] 能存一切,而 Number[] 不行
Java 数组是协变(covariant)的,Integer[] 是 Object[] 的子类型,所以可以向上转型;但协变带来代价:JVM 必须在每次写入时做运行时类型检查,防止破坏类型安全。
而 Object[] 没有这种限制,它的元素类型就是 Object,任何引用类型都能赋值。
立即学习“Java免费学习笔记(深入)”;
-
Integer[] ints = new Integer[1]; ints[0] = new Double(1.0);→ 报错:Double 不是 Integer 的子类 -
Object[] objs = new Integer[1]; objs[0] = new Double(1.0);→ 同样报错:数组底层仍是Integer[],JVM 看的是实际运行时类型 -
Object[] objs = new Object[1]; objs[0] = new Double(1.0);→ 安全:真正是Object[]
和泛型集合混用时最容易踩坑
泛型擦除后,很多工具方法(比如 Arrays.asList().toArray())返回的是 Object[],但如果强制转型成具体类型数组(如 (String[])list.toArray()),一旦 list 里混入非 String 元素,后续往该数组写值就会爆 ArrayStoreException。
- 错误写法:
String[] arr = (String[]) list.toArray(); arr[0] = "ok";—— 如果list实际含Integer,转型虽成功(因为Object[]可转),但arr底层仍是Object[],写入时仍会检查(此时检查失败) - 正确写法:
String[] arr = list.toArray(new String[0]);—— 让 JVM 创建真正的String[],类型安全由构造保证 - 更稳妥:改用
ArrayList<string></string>替代数组,避免裸数组的运行时检查负担
替代方案:什么时候该用 List 而不是数组
当你需要动态增删、泛型安全、或频繁跟集合 API 交互时,ArrayList 几乎总是比数组更省心。数组的唯一优势是确定大小 + 零开销访问,但代价是运行时类型检查不可绕过。
- 如果函数参数必须是数组(如反射调用、JNI、某些老 API),优先用
new T[0]而非new T[n]配合toArray(),避免预分配错误类型 - 调试时遇到
ArrayStoreException,直接查堆栈里哪行=赋值,再确认左侧数组的实际运行时类型(可用arr.getClass().getComponentType()打印) - 不要依赖「反正编译过了」——数组的类型安全是 JVM 在写入那一刻才拍板的,晚得离谱,也严得彻底








