System.arraycopy不会因“指针偏移”出错,而是因srcPos、destPos或length非法导致ArrayIndexOutOfBoundsException;需调用前严格校验五项边界条件,确保不越界。

System.arraycopy 本身不管理“指针偏移”概念——Java 没有指针,只有数组索引。所谓“读取过程中因指针偏移抛错”,实际是指调用 arraycopy 时传入了非法的 srcPos、destPos 或 length,导致 ArrayIndexOutOfBoundsException。
要防止这类异常,核心不是“避开偏移”,而是在调用前主动校验边界条件。JVM 不做宽容处理,它只按你写的数字硬搬,越界就崩。
明确校验这三组关系
调用前必须确保以下全部成立:
-
srcPos >= 0 -
destPos >= 0 -
length >= 0 -
srcPos + length <= src.length destPos + length <= dest.length
例如:
int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[8];
int srcPos = 2;
int length = 4; // ❌ 错:srcPos + length = 6 > src.length(5)
int destPos = 1;
// 正确做法:先校验再调用
if (srcPos >= 0 && destPos >= 0 && length >= 0
&& srcPos + length <= src.length
&& destPos + length <= dest.length) {
System.arraycopy(src, srcPos, dest, destPos, length);
} else {
throw new IllegalArgumentException("arraycopy 参数越界");
}常见易错场景与应对
-
动态读取长度不确定时
比如从网络缓冲区读取一段变长数据,length来自协议头,必须和当前可用字节数比对:int available = buffer.length - readOffset; int wantCopy = header.getLength(); int actualCopy = Math.min(wantCopy, available); // 宁可少拷,不可越界 System.arraycopy(buffer, readOffset, payload, 0, actualCopy);
-
目标数组复用(如环形缓冲区)
destPos可能是计算出来的模值,务必检查是否落在[0, dest.length)内:int destPos = writeIndex % dest.length; if (actualCopy <= dest.length - destPos) { // 可以直写到尾部 System.arraycopy(src, 0, dest, destPos, actualCopy); } else { // 需要拆成两段:写到末尾 + 从头开始 int firstPart = dest.length - destPos; System.arraycopy(src, 0, dest, destPos, firstPart); System.arraycopy(src, firstPart, dest, 0, actualCopy - firstPart); } 源/目标为同一数组(如左移、覆盖)
System.arraycopy按从左到右复制,若重叠且方向不对,会覆盖未读数据。此时srcPos < destPos且重叠时需改用Arrays.copyOfRange或反向逻辑,或直接用System.arraycopy的安全变体(如Collections.rotate底层已处理)。
不推荐的“防错”方式
用
try-catch包裹arraycopy并吞掉ArrayIndexOutOfBoundsException
→ 掩盖真正逻辑错误,调试困难,性能差。依赖
Arrays.copyOf或clone()替代所有场景
→ 它们自动分配新数组,无法复用已有内存,也不支持任意偏移拷贝,在高性能路径(如帧解析、IO 缓冲)中反而引入 GC 和冗余对象。
小结
防止 arraycopy 抛错的关键,是把校验逻辑写进业务流程里,而不是靠运行时兜底。它不是“危险函数”,而是“精准工具”——给对位置和长度,它就零开销完成;给错,立刻报错,帮你早发现问题。

















