应使用 char[] 而非 String 存储密码,因 String 不可变且驻留内存时间长,易被内存dump窃取;char[] 可主动清零,确保敏感信息不留痕迹。
![java 数据类型怎么理解字符数组(char[])在密码存储安全性上优于 string 的原因](https://img.php.cn/upload/article/001/242/473/178902445766246.jpg)
因为 char[] 可主动擦除,而 String 一旦创建就无法修改内容,会以明文形式长期滞留内存。
不可变性带来内存残留风险
String 在 Java 中是不可变对象。每次对字符串的操作(比如拼接、截取)都会生成新对象,原字符串仍保留在内存中,直到垃圾回收器清理。而 JVM 的字符串池机制会让常用字符串(包括密码)被缓存复用,进一步延长其驻留时间。攻击者若能获取内存快照(如通过堆转储、core dump 或某些漏洞利用),极易从中直接提取明文密码。
- String 密码一旦进入字符串池,可能在内存中存在数分钟甚至更久
- 你无法通过代码强制让某个 String 立即“消失”,只能被动等待 GC
- 即使调用
password = null,也只是断开引用,原字符串内容仍在堆中
char[] 支持显式清除内容
字符数组是可变的,验证通过后可立即用 Arrays.fill(password, '<p>字符数组是可变的,验证通过后可立即用 <code>Arrays.fill(password, '\0') 或循环赋零覆盖原始数据。这能确保敏感信息在内存中不留痕迹——哪怕 GC 尚未触发,dump 出来的也只会是全零或乱码,而非真实密码。
- 推荐方式:
Arrays.fill(charPassword, '\0')或for (int i = 0; i - 清除操作应在密码校验完成、且确认不再需要原始值后的第一时间执行
- 注意:不要在清除前将 char[] 转为 String(如
new String(charPassword)),否则又引入不可变副本
日志与调试输出更安全
打印 String 会输出明文内容;而直接打印 char[] 仅显示类似 [C@1a2b3c4d 的哈希地址,不会泄露密码本身。这降低了因误打日志、调试语句或异常堆栈导致密码意外暴露的风险。
立即学习“Java免费学习笔记(深入)”;
-
System.out.println("pwd: " + passwordStr);→ 输出pwd: 123456 -
System.out.println("pwd: " + passwordArray);→ 输出pwd: [C@7a81197d - 即便开发人员疏忽写了日志,char[] 也能提供一层“天然防护”
官方实践与标准推荐
Java 标准库从设计上就体现这一安全意识:Swing 的 JPasswordField 提供 getPassword()(返回 char[]),而早已弃用 getText()(返回 String)。《Java 加密体系结构指南》和《Oracle 安全编码准则》均明确建议:敏感字符数据应使用可清除的 char[],而非不可变的 String。
- 不是“Java 偏好”,而是基于内存模型和攻击面分析形成的工程共识
- Spring Security、Apache Shiro 等主流框架内部处理凭证时也遵循该模式
- 它不替代哈希或加密,而是与之配合——先用 char[] 安全接收,再哈希存储,最后立即清空数组


















