必须捕获NoSuchAlgorithmException并显式指定UTF-8编码,MessageDigest实例不可复用,digest()后需手动转十六进制字符串;错误包括算法名拼写、编码不一致、复用实例和直接String转换。

MessageDigest.getInstance("SHA-256") 必须捕获 NoSuchAlgorithmException
Java 的 MessageDigest 不会自动支持所有算法名称,传入非法字符串(比如拼错成 "SHA256" 或 "SHA-256" 在某些旧 JDK 版本中)会直接抛 NoSuchAlgorithmException。JDK 8+ 支持 "SHA-256"(带短横线),但必须显式处理异常——不能忽略或用 throws 往上推,否则调用方极易崩溃。
常见错误现象:Exception in thread "main" java.security.NoSuchAlgorithmException: SHA-256 MessageDigest not available,多因 JCE 策略限制或算法名大小写/符号不匹配导致。
- 始终用
try-catch包裹MessageDigest.getInstance("SHA-256") - 不要用
"SHA256"、"SHA_256"等变体,JDK 规范只认"SHA-256" - 若部署在受限环境(如某些 Android API 级别或老版 JRE),可先用
Security.getAlgorithms("MessageDigest")检查是否注册
输入字符串必须转为字节数组,且编码需显式指定
MessageDigest 只处理 byte[],不接受 String。直接调用 string.getBytes() 依赖平台默认编码(如 Windows 是 GBK,Linux/macOS 是 UTF-8),会导致同一字符串在不同机器上生成不同摘要——这是线上环境最隐蔽的 bug 来源之一。
使用场景:用户密码哈希、API 签名、文件内容校验等,都要求确定性输出。
- 必须显式指定编码,推荐
StandardCharsets.UTF_8(JDK 7+)或"UTF-8"字符串 - 避免
string.getBytes(Charset.defaultCharset()),它不可移植 - 如果原始数据是 Base64 或十六进制字符串,先解码再摘要,不要对编码后的文本直接摘要
digest() 调用后实例不可重用,重复调用会返回空数组
MessageDigest 实例是状态化的:调用 digest() 会重置内部状态并返回结果;再次调用 digest() 会返回长度正确但全零的 byte[](因为缓冲区已清空)。这不是 bug,是设计使然——类似流读取后无法倒带。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
性能影响:反复创建新实例开销极小(MessageDigest 是轻量对象),比复用出错更安全。
- 每次摘要操作应新建实例:
MessageDigest md = MessageDigest.getInstance("SHA-256") - 不要缓存
MessageDigest实例做单例或池化(除非你明确 reset() 并重新 update) - 若需连续计算多个输入的摘要(如分块哈希),用
update(byte[])多次,最后只调一次digest()
输出 byte[] 需转为十六进制字符串才便于存储或传输
digest() 返回的是原始 byte[32](SHA-256 固定长度),直接 new String(byte[]) 会乱码,Arrays.toString() 生成的是 JVM 内部格式(如 [1, -2, 127, ...]),无法用于签名比对或数据库存储。
兼容性注意:Android 和部分嵌入式 JDK 可能不支持 Hex.encodeHexString()(Apache Commons Codec),应优先用标准库方案。
- 推荐用
String.format或StringBuilder手动转十六进制,避免第三方依赖 - 示例:
byte[] hash = md.digest(); StringBuilder hex = new StringBuilder(); for (byte b : hash) { hex.append(String.format("%02x", b & 0xFF)); } String hexStr = hex.toString(); // 长度恒为 64 - 不要用
BigInteger(hash).toString(16),负字节会被截断,导致前导零丢失
实际用起来最易踩的坑不是算法本身,而是字节编码不一致和 digest() 后误复用实例——这两点一旦出问题,测试时可能通过,上线后跨环境就失效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










