1. 概述
本文简要介绍校验和(Checksum)的基本概念,并演示如何使用 Java 内置的工具来计算校验和。✅
校验和在数据完整性校验中非常实用,尤其在网络传输和文件比对场景下,属于“轻量级防篡改”手段。虽然不能替代加密哈希(如 SHA-256),但在性能敏感的场景下是个简单粗暴的好选择。
2. 校验和与常见算法
校验和本质上是对一段二进制数据的压缩表示,通常是一个固定长度的数值(比如 long 类型)。
它的主要用途包括:
- ✅ 网络通信中验证数据完整性:接收方重新计算校验和,与发送方附带的值对比,判断是否丢包或出错。
- ✅ 文件一致性检测:比如判断两个文件内容是否相同,或监控文件是否被修改。
常见的校验和算法有:
- CRC32:速度快,适合非安全场景
- Adler32:比 CRC32 更快,但可靠性略低
⚠️ 注意:这些算法都不是加密安全的!别拿 CRC32 去校验密码或者做防伪签名,那属于踩坑行为。需要安全哈希请用 MessageDigest
配合 SHA 系列算法。
我们以 CRC32 为例,看看 Java 如何支持这类计算。
3. 从字符串或字节数组计算校验和
要计算校验和,首先得有输入数据。如果你的数据是字符串,先转成字节数组:
String test = "test";
byte[] bytes = test.getBytes();
⚠️ 注意编码问题!getBytes()
使用平台默认编码,生产环境建议显式指定,例如 getBytes(StandardCharsets.UTF_8)
。
接下来,使用 Java 内置的 CRC32
类计算校验和:
public static long getCRC32Checksum(byte[] bytes) {
Checksum crc32 = new CRC32();
crc32.update(bytes, 0, bytes.length);
return crc32.getValue();
}
关键点说明:
Checksum
是一个接口,CRC32
是其实现类update()
方法用于喂数据,支持多次调用(适用于分块处理)getValue()
返回最终的 long 类型校验值
✅ 小技巧:update()
支持重置状态,所以同一个 CRC32
实例可以复用,避免频繁创建对象,提升性能。
4. 从 InputStream 计算校验和
上面的方法适用于小数据量。但如果处理大文件或网络流,把整个数据读进内存显然不现实 ❌。
此时应该使用 CheckedInputStream
,它能边读流边计算校验和,内存占用恒定。
示例代码如下:
public static long getChecksumCRC32(InputStream stream, int bufferSize)
throws IOException {
CheckedInputStream checkedInputStream = new CheckedInputStream(stream, new CRC32());
byte[] buffer = new byte[bufferSize];
while (checkedInputStream.read(buffer, 0, buffer.length) >= 0) {}
return checkedInputStream.getChecksum().getValue();
}
要点解析:
CheckedInputStream
包装原始流,并绑定一个Checksum
实现(这里是 CRC32)- 每次调用
read()
方法时,内部自动更新校验和 - 循环读取直到流结束(返回 -1)
- 最后通过
getChecksum().getValue()
获取结果
✅ 推荐 bufferSize 设置为 8192(8KB)或 16384(16KB),这是 I/O 性能较优的常见值。
这个方式特别适合处理大文件、HTTP 响应体、Socket 数据流等场景。
5. 总结
本文介绍了如何在 Java 中使用内置 API 计算 CRC32 校验和:
- ✅ 小数据用
CRC32.update()
直接处理 byte[] - ✅ 大数据用
CheckedInputStream
流式处理,避免内存溢出 - ⚠️ 校验和 ≠ 加密哈希,别在安全场景滥用
所有示例代码已托管至 GitHub:
👉 https://github.com/baeldung/core-java-modules/tree/master/core-java-security-2
实际项目中,合理使用校验和能帮你快速发现数据传输问题,属于那种“不起眼但关键时刻能救命”的工具。