java数组是类型安全的容器,编译期强制元素为声明类型或其子类型,运行时通过arraystoreexception兜底校验;协变性允许string[]赋值给object[],但存入非string对象会抛出该异常。

Java 数组是类型安全的容器,它在编译期就强制要求所有元素必须是声明时指定的同一类型(或其子类型),这是由 Java 的数组协变性(covariance)和运行时类型检查共同决定的。
数组的类型声明决定了元素的合法类型
当你写 String[] arr = new String[3];,JVM 在堆中创建了一个专门存放 String 引用的数组对象。此时:
- 你可以存
arr[0] = "hello"; - 但不能存
arr[1] = 123;(编译报错:类型不匹配) - 也不能存
arr[2] = new Object();(编译报错)
因为编译器会检查赋值表达式的类型是否与数组声明类型兼容。
协变性带来的“看似能存不同类”,实则受 runtime 检查约束
Java 数组是协变的:String[] 是 Object[] 的子类型。所以以下代码能通过编译:
Object[] objArr = new String[2]; objArr[0] = "ok"; // ✅ 合法:String 是 Object 子类 objArr[1] = new Integer(1); // ❌ 运行时报 ArrayStoreException
注意:这行 objArr[1] = new Integer(1); 编译通过,但运行时抛出 ArrayStoreException。
原因:JVM 在运行时会检查实际数组对象的真实类型(这里是 String[]),发现要存入的是 Integer,违反了底层数组的实际元素类型约束。
为什么设计成这样?
-
编译期类型安全:防止明显错误(如直接往
int[]存String)。 -
运行时兜底保护:协变场景下靠
ArrayStoreException避免破坏类型一致性。 -
与泛型对比:泛型是类型擦除的,不支持运行时类型检查;而数组保留完整类型信息(
getClass().getComponentType()可查),所以能做 runtime 校验。
基本类型数组更严格
int[]、double[] 等原始类型数组完全不接受任何引用类型,也不接受其他基本类型:
int[] nums = new int[2]; nums[0] = 42; // ✅ nums[1] = 'a'; // ✅ 自动提升为 int(字符转 ASCII 值) nums[1] = "123"; // ❌ 编译失败:String 无法转 int nums[1] = 3.14; // ❌ 编译失败:double 不能隐式转 int(需显式强转)
总结关键点
- 数组类型在声明时锁定元素类型(包括基本类型或引用类型)
- 编译器阻止明显类型不匹配的赋值
- 协变引用数组(如
Object[]接收String[])允许向上转型,但 runtime 会校验实际存储内容 - 运行时异常
ArrayStoreException是类型安全的最后一道防线 - 这种机制确保了数组读取时无需额外类型判断——取出来的一定是声明类型的实例(或其子类)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











