首页 > 编程笔记 > Java笔记

Java生成随机数(Random和SecureRandom)

随机数在统计分析、概率论、现代计算机模拟和数字密码学等领域具有广泛的应用。开发 Java 程序时,经常会遇到需要使用随机数的场景,如随机验证码、随机测试数据、随机移动路径和密码加密中使用的辅助值等。

Java 提供的 Random 和 SecureRandom 可以作为生成随机数的工具类。

1、Random类

Random 是 java.util 包中的一个类,可以在指定的取值范围内随机产生数字。

Random 类内部采用伪随机数算法,简单来说就是基于一个种子(long 型值),经过特定的迭代计算得到一个结果,作为产生的随机数。当这个种子是随机数时,产生的结果也是随机数;当这个种子是固定值时,产生的结果也是固定值。

Random 类的构造方法如下表所示。

表 1 Random类的构造方法
方法声明 功能描述
Random() 创建一个随机数生成器,基于当前系统时间生成种子。
Random(long seed) 创建一个随机数生成器,使用 long 型的 seed 值作为种子,使用相同的 seed 值创建的多个随机数生成器产生的随机数序列相同。

Random 类中常用的方法如下:
【实例 1】nextInt() 方法的应用。
public class Demo {
    public static void main(String[] args) {
        // 创建一个随机数生成器
        Random ran = new Random();
        // 生成一个int型取值范围内的随机数
        System.out.println(ran.nextInt());
        // 生成一个10以内的随机数
        System.out.println(ran.nextInt(10));
    }
}
运行结果是:

1205200642
9


【实例 2】下面创建 3 个随机数生成器,其中的两个使用相同的种子值 50 创建,最后一个使用种子值 100 创建。
import java.util.*;

public class Demo {
    public static void main(String[] args) {
        // 创建一个种子值为50的Random对象
        Random rl = new Random(50);
        System.out.println("第一个种子值为50的Random对象");
        System.out.println("rl.nextBoolean():\t" + rl.nextBoolean());
        System.out.println("rl.nextInt():\t\t" + rl.nextInt());
        System.out.println("rl.nextDouble():\t" + rl.nextDouble());
        System.out.println("rl.nextGaussian():\t" + rl.nextGaussian());

        Random r2 = new Random(50);
        System.out.println("第二个种子值为50的Random对象");
        System.out.println("r2.nextBoolean():\t" + r2.nextBoolean());
        System.out.println("r2.nextInt():\t\t" + r2.nextInt());
        System.out.println("r2.nextDouble():\t" + r2.nextDouble());
        System.out.println("r2.nextGaussian():\t" + r2.nextGaussian());

        Random r3 = new Random(100);
        System.out.println("种子值为100的Random对象");
        System.out.println("r3.nextBoolean():\t" + r3.nextBoolean());
        System.out.println("r3.nextInt():\t\t" + r3.nextInt());
        System.out.println("r3.nextDouble():\t " + r3.nextDouble());
        System.out.println("r3.nextGaussian():\t" + r3.nextGaussian());
    }
}
运行结果为:
第一个种子值为50的Random对象
rl.nextBoolean():   true
rl.nextInt():       -1727040520
rl.nextDouble():    0.6141579720626675
rl.nextGaussian():  2.377650302287946
第二个种子值为50的Random对象
r2.nextBoolean():   true
r2.nextInt():       -1727040520
r2.nextDouble():    0.6141579720626675
r2.nextGaussian():  2.377650302287946
种子值为100的Random对象
r3.nextBoolean():   true
r3.nextInt():       -1139614796
r3.nextDouble():     0.19497605734770518
r3.nextGaussian():  0.6762208162903859
由运行结果可知,使用相同的种子值创建的随机数生成器生成的随机数序列也是相同的。

2、SecureRandom类

Random 类提供的基于种子的随机数生成器,在种子值已知的情况下生成的随机数序列是可知的,即不再随机,这在安全相关的应用中是非常危险的。例如,如果网站基于该机制生成手机验证码,那么攻击者可以推测出验证码,进而冒充用户登录网站。

为了保障安全性,Java 提供了 SecureRandom 类。

SecureRandom 类提供了一个加密的强随机数生成器。SecureRandom 类的底层有多种实现,其中一些实现采用伪随机数生成器(Pseudo Random Number Generator,PRNG)的形式,这意味着它们使用确定性算法根据真正的随机种子生成伪随机序列。其他实现可能会产生真正的随机数,有的实现则使用这两种技术的组合形式。当实际使用时,可以优先获取高强度的安全随机数生成器,如果没有提供,就使用普通等级的安全随机数生成器。

SecureRandom 类提供的静态方法 getInstance() 可以用来获取该类的一个对象。常用的 getInstance() 方法有两种:一是仅指定算法名称,二是既指定算法名称又指定提供者名称。

仅指定算法名称:
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
系统将确定环境中是否有所请求的算法实现,是否有多个,以及是否有首选实现。

既指定算法名称又指定提供者名称:
SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
系统将确定在所请求的包中是否有算法实现,若没有则抛出异常。

SecureRandom 类中常用的方法如下:
【实例 3】SecureRandom类的基础用法。
import java.security.SecureRandom;

public class Demo {
    public static void main(String[] args) {
        // 创建一个SecureRandom实例
        SecureRandom secureRandom = new SecureRandom();

        // 生成指定数量的随机字节,并打印出来
        byte[] randomBytes = new byte[16]; // 假设我们想要16个字节的随机数据
        secureRandom.nextBytes(randomBytes);
        System.out.println("生成的随机字节: " + bytesToHex(randomBytes));

        // 使用generateSeed生成种子,并设置到SecureRandom实例
        byte[] seed = secureRandom.generateSeed(8); // 生成8个字节的种子
        secureRandom.setSeed(seed); // 设置种子,这将补充而非替代原有的种子

        // 再次生成随机字节,这次使用了新的种子
        secureRandom.nextBytes(randomBytes);
        System.out.println("使用新种子生成的随机字节: " + bytesToHex(randomBytes));
    }

    // 辅助方法,将字节转换为十六进制字符串
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}
运行结果为:

生成的随机字节: 6ef49573b9eb0a98d635057d28461bef
使用新种子生成的随机字节: d22fc62f5f63a87318bc2baf80d45e1d


声明:《Java系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。