应按场景归类→抽象为高阶操作→用工具类或封装类型替代原始调用:数组合并用arrayutils.addall(),循环移位用collections.rotate()或arrayops.rotateleft(),动态扩容用arrays.copyof();推广不可变契约,建立使用守则并静态检查,渐进式迁移。

不能靠“统一收拢”来治理散落的 System.arraycopy 调用点——它本质是底层内存搬运指令,不是业务逻辑,强行集中反而破坏语义、增加耦合、掩盖真实意图。真正有效的重构路径是:**按场景归类 → 抽象为高阶操作 → 用工具类或封装类型替代原始调用**。
识别三类高频 arraycopy 场景并分类替换
散落的 arraycopy 往往集中在以下三类上下文中,每类对应不同抽象层级:
-
数组合并:如日志批量写入、分片结果拼接。直接替换为
ArrayUtils.addAll()(Apache Commons)或自建Arrays2.merge(a, b)工具方法,内部仍用两次 arraycopy,但对外屏蔽细节 -
循环移位:如滑动窗口、队列旋转。改用
Collections.rotate(list, k)(对 List 封装后),或封装ArrayOps.rotateLeft(int[] arr, int k),内部按安全三步拆解(暂存+前移+回填),避免重叠风险 -
动态扩容:如 ArrayList 内部 grow。不应暴露 arraycopy,而应统一走
ResizableArray类或复用Arrays.copyOf()——后者语义更清晰,且已内联优化,性能损失可忽略
用不可变契约替代裸数组传递
多数 arraycopy 源于外部传入数组后需“复制防御”。与其在各处写 System.arraycopy(src, 0, newDst, 0, len),不如从源头约束:
- 入参统一接收
List<t></t>或ImmutableList<t></t>(Guava),消除“是否要拷贝”的判断分支 - 返回值用
Collections.unmodifiableList(new ArrayList(data))或Arrays.asList(...).toArray()封装,不再需要手动拷贝防篡改 - 对必须用数组的场景(如 JNI 交互),定义
ArrayView包装类,内部持引用+范围,只在真正需要物理复制时才触发 arraycopy
建立 arraycopy 使用守则并接入静态检查
禁止零散调用,不等于禁止使用。关键在规范而非禁绝:
- 所有 arraycopy 必须出现在
ArrayUtils、ArrayOps等限定工具类中,禁止在业务类里直接出现 - 新增调用必须带注释说明:复制目的(如“为避免原数组被修改”)、是否涉及重叠(如“右移3位,srcPos=0, destPos=3,安全”)、长度校验逻辑
- 用 SonarQube 自定义规则或 ErrorProne 插件扫描:匹配
System.arraycopy\(.+\)且不在白名单类中的调用,编译期报错
渐进式迁移策略
数百个点无法一次性清理。按影响面分级推进:
- 高危优先:先定位所有涉及对象数组拷贝(可能引发共享状态 bug)和重叠移动(可能数据错乱)的调用,用封装方法替换
- 高频次优先:统计调用频次,将 top 10 的 arraycopy 模式(如“合并两个 String[]”)提炼为工具方法,批量替换
- 新代码强制:CI 流程中加入 checkstyle 规则,禁止新提交代码含
System.arraycopy字样,倒逼使用新工具类










