
用 Random 生成固定长度随机字符串的可靠写法
别直接拼接 Math.random() 或循环调用 nextInt() 然后强转 char——容易越界或生成控制字符。Random 本身不负责字符映射,得自己限定合法字符集范围。
推荐做法:预定义一个合法字符数组(比如大小写字母 + 数字),用 random.nextInt(charArray.length) 当下标取值。这样既避免非法 Unicode、又保证分布均匀。
-
Random实例建议复用,不要每次生成都 new 一个(影响性能,还可能因快速重复创建导致种子相近) - 字符数组长度最好选质数(如 62),减少模运算周期性偏差(虽对一般业务影响极小,但知道有这回事)
- 如果需要线程安全,改用
ThreadLocalRandom.current(),它比共享Random实例更轻量
char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
Random rnd = new Random();
StringBuilder sb = new StringBuilder(10);
for (int i = 0; i < 10; i++) {
sb.append(chars[rnd.nextInt(chars.length)]);
}
String randomStr = sb.toString();
为什么不用 UUID.randomUUID().toString() 替代
它确实“随机”,但输出是 32 位十六进制 + 4 个短横线(共 36 字符),格式固定、长度不可控、含非字母数字字符。如果你要的是 8 位纯字母数字验证码,UUID 就是杀鸡用牛刀,还多出一堆连字符和大小写混杂的冗余信息。
- 生成开销略大(涉及 SecureRandom、MAC 地址/时间戳等组合)
- 不能指定字符集(比如只要大写字母,或排除易混淆字符 O/0、l/1)
- 去横线再截断容易误切——
uuid.toString().replace("-", "").substring(0, 8)看似简单,实则破坏了随机性(前缀截断偏向某些哈希段)
避免 Random.nextInt(int bound) 越界或死循环
常见错误是把字符集长度硬编码成 magic number,比如 rnd.nextInt(62),但实际字符数组只有 61 个元素——运行时抛 IllegalArgumentException: bound must be positive 或静默出错(如果 bound=0)。
立即学习“Java免费学习笔记(深入)”;
- 永远用
chars.length作为nextInt()的参数,而不是手敲数字 - 确保
chars非空,否则length == 0会触发异常;可加一行if (chars.length == 0) throw new IllegalArgumentException("char array must not be empty"); - 不要用
(char) rnd.nextInt(26 + 26 + 10)这类强制类型转换——ASCII 码不连续,'A'到'Z'是 26 个,但'Z'+1是'[',不是'a'
需要密码级安全性时,必须换 SecureRandom
普通 Random 是伪随机,基于线性同余,可被预测。如果是生成重置令牌、API 密钥这类敏感字符串,Random 不够用。
- 替换构造方式:
new SecureRandom(),其余逻辑完全一致(它也支持nextInt(bound)) - 注意首次调用可能稍慢(要采集系统熵),但后续很快;若在高并发场景下频繁 new,考虑复用实例
- 别试图用
SecureRandom去“增强”Random(比如拿它生成 seed 再喂给 Random)——没意义,反而绕过安全机制
真正难的不是写几行代码,而是想清楚这个字符串用在哪:是前端展示的临时 ID,还是后端签发的访问凭证?前者用 Random 足够,后者漏掉 SecureRandom 就等于把钥匙挂门把手上。


















