Java 中的随机数 | Java Debug 笔记

简介: Java 中的随机数 | Java Debug 笔记

从伪随机数生成器 & 真随机数生成器的角度看 java.util.Random & java.security.SecureRandom 。



伪随机数生成器 & 真随机数生成器



伪随机数生成器:又被称为确定性随机比特生成器,是一个生成数字序列的算法,其特性近似于随机数序列的特性。完全由一个初始值决定,这个初始值被称为 PRNG 的随机种子。同样的初始值总是生成同样的序列。


真随机数生成器:又称为硬件随机数生成器,是一种通过物理过程而不是计算机程序来生成随机数的设备。这样的设备通常是基于一些能生成低等级、统计学随机的“噪声”信号的微观现象,如热力学噪声、光电效应和量子现象。这些物理过程在理论上是完全不可预测的。



java.util.Random



平时用的最多的类就是 java.util.Random 了,通过操作符 new 就可以获取一个实例,调用诸多方法获取随机数,十分方便。


Random r = new Random();
  System.out.println(r.nextInt());
  System.out.println(r.nextFloat());
  System.out.println(r.nextBoolean()); 
复制代码


每次运行获取的数值都不一样,视乎满足了我们想要的“随机”的要求。但其实通过 java.util.Random 获取到的都是伪随机数,只是我们通常不指定随机种子直接使用。


由相关的构造方法实现可知,当不指定随机种子创建 java.util.Random 实例的时候会自动生成一个与当前时间相关的数值作为随机种子。


public Random() {
      this(seedUniquifier() ^ System.nanoTime());
  }
  public Random(long seed) {
      if (getClass() == Random.class)
        this.seed = new AtomicLong(initialScramble(seed));
      else {
        // subclass might have overridden setSeed
        this.seed = new AtomicLong();
        setSeed(seed);
      }
    }
复制代码


这就是为什么我们使用 java.util.Random 获取随机数还能得到“不确定”随机序列的原因,一旦我们指定随机数创建 java.util.Random 实例时,那么产生的随机序列就是完全确定的:


Random r = new Random(123);
  System.out.println(r.nextInt());
  System.out.println(r.nextFloat());
  System.out.println(r.nextBoolean());
复制代码


因而 java.util.Random 产生随机序列并不可靠。这时候可能会有读者有疑问,不指定随机种子使用不就可以了,让 java.util.Random 始终使用与时间相关的数值作为随机种子,这样产生的随机序列也是不可预测。其实不然,因为一旦你使用的随机种子是有规律可复现的,那么产生的随机序列也就是有规律可复现的,也就是不能做到真正的“随机”。


举个🌰,假如你使用 java.util.Random 产生的随机序列(不指定随机种子)作为加密的密钥原料,那么我完全可以通过时钟回拨+暴力枚举的方式获取到当时用作生成密钥所用到的随机序列。最终整个加密流程会因为使用了伪随机算法生成密钥导致可攻破。



java.security.SecureRandom



我们知道真随机算法依靠的是“噪声”来产生随机数。通常有热力学噪声、光电效应和量子现象产生。那么计算机如果要产生真正意义上的随机数只能是依靠量子力学,通过量子比特的量子叠加来产生,但是目前量子计算机还没面世,那么我们如何通过计算机产生一个较为可靠的“随机数”呢?java 提供了 java.security.SecureRandom 。


其实 java.security.SecureRandom 也是继承 java.util.Random ,只不过不能再指定真正用于产生序列的随机种子。 java.security.SecureRandom 能够通过 new 操作符获取普通的随机数生成器,也能通过静态方法 SecureRandom.getInstanceStrong(); 获取健壮的随机数生成器。


通过 java.security.SecureRandom 即使再指定相同的 seed 也无法产生可复现的确定随机序列:


SecureRandom r = new SecureRandom(new byte[]{123});
//        SecureRandom r = SecureRandom.getInstanceStrong();
  System.out.println(r.nextInt());
  System.out.println(r.nextFloat());
  System.out.println(r.nextBoolean());
复制代码


使用 java.security.SecureRandom 虽然不是真正意义上的“真随机”,但相对于 java.util.Random 确实更为安全。所以在涉及加密流程需要用到随机数,建议使用 java.security.SecureRandom 。

相关文章
|
12月前
|
Java 开发工具 Android开发
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
118 2
|
12月前
|
Java 开发工具 Android开发
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
本系列教程笔记详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。若需快速学习Kotlin,建议查看“简洁”系列教程。本期重点介绍了Kotlin与Java的共存方式,包括属性、单例对象、默认参数方法、包方法、扩展方法以及内部类和成员的互操作性。通过这些内容,帮助你在项目中更好地结合使用这两种语言。
140 1
|
9月前
|
存储 Java 开发者
【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。
本文详细介绍了 Java 中 `toString()` 方法的重写技巧及其重要
418 10
【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。
|
9月前
|
前端开发 JavaScript Java
Java构建工具-maven的复习笔记【适用于复习】
这篇文档由「潜意识Java」创作,主要介绍Maven的相关知识。内容涵盖Maven的基本概念、作用、项目导入步骤、依赖管理(包括依赖配置、代码示例、总结)、依赖传递、依赖范围以及依赖的生命周期等七个方面。作者擅长前端开发,秉持“得之坦然,失之淡然”的座右铭。期待您的点赞、关注和收藏,这将是作者持续创作的动力! [个人主页](https://blog.csdn.net/weixin_73355603?spm=1000.2115.3001.5343)
119 3
|
10月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
10月前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
11月前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
166 2
|
11月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)

热门文章

最新文章