Java生成随机数的5种方式,你知道几种?(建议收藏)

1. Math.random() 静态方法

产生的随机数是 0 – 1 之间的一个 double ,即 0 <= random <= 1

使用:结果:实现原理:

当第一次调用 Math.random() 方法时,自动创建了一个伪随机数生成器 ,实际上用的是 new java.util.Random() 。当接下来继续调用 Math.random() 方法时,就会使用这个新的伪随机数生成器 。

源码如下:

initRNG() 方法是 synchronized 的,因此在多线程情况下,只有一个线程会负责创建伪随机数生成器 (使用当前时间作为种子),其他线程则利用该伪随机数生成器 产生随机数。

因此 Math.random() 方法是线程安全的。什么情况下随机数的生成线程不安全:

  • 线程1在第一次调用 random() 时产生一个生成器 generator1 ,使用当前时间作为种子。
  • 线程2在第一次调用 random() 时产生一个生成器 generator2 ,使用当前时间作为种子。
  • 碰巧 generator1generator2 使用相同的种子,导致 generator1 以后产生的随机数每次都和 generator2 以后产生的随机数相同。

什么情况下随机数的生成线程安全: Math.random() 静态方法使用

  • 线程1在第一次调用 random() 时产生一个生成器 generator1 ,使用当前时间作为种子。
  • random() generator1 generator1

结果:2. java.util.Random 工具类

基本算法:linear congruential pseudorandom number generator (LGC) 线性同余法伪随机数生成器 缺点:可预测

An attacker will simply compute the seed from the output values observed. This takes significantly less time than 2^48 in the case of java.util.Random. 从输出中可以很容易计算出种子值。It is shown that you can predict future Random outputs observing only two(!) output values in time roughly 2^16. 因此可以预测出下一个输出的随机数。 You should never use an LCG for security-critical purposes. 在注重信息安全的应用中,不要使用 LCG 算法生成随机数,请使用 SecureRandom。

使用:结果:Random类默认使用当前系统时钟作为种子:

Random类提供的方法:API

  • nextBoolean() true false
  • nextBytes(byte[] bytes)
  • nextDouble() – 返回 0.0 到 1.0 之间的均匀分布的 double
  • nextFloat() – 返回 0.0 到 1.0 之间的均匀分布的 float
  • nextGaussian() – 返回 0.0 到 1.0 之间的高斯分布(即正态分布)的 double
  • nextInt() – 返回均匀分布的 int
  • nextInt(int n) – 返回 0 到 n 之间的均匀分布的 int (包括 0,不包括 n)
  • nextLong() – 返回均匀分布的 long
  • setSeed(long seed) – 设置种子

只要种子一样,产生的随机数也一样:因为种子确定,随机数算法也确定,因此输出是确定的!

结果:3. java.util.concurrent.ThreadLocalRandom 工具类

ThreadLocalRandom 是 JDK 7 之后提供, 也是继承至 java.util.Random。

每一个线程有一个独立的 随机数生成器 ,用于并发产生随机数,能够解决多个线程发生的竞争争夺。 效率更高!

ThreadLocalRandom 不是直接用 new 实例化,而是第一次使用其静态方法 current() 得到 ThreadLocal<ThreadLocalRandom> 实例,然后调用 java.util.Random 类提供的方法获得各种随机数。

使用:结果:4. java.Security.SecureRandom也是继承至 java.util.Random。

Instances of java.util.Random are not cryptographically secure.Consider instead using SecureRandom to get a cryptographically secure pseudo-random number generator for use by security-sensitive applications. SecureRandom takes Random Data from your os (they can be interval between keystrokes etc – most os collect these data store them in files – /dev/random and /dev/urandom in case of linux/solaris) and uses that as the seed. 操作系统收集了一些随机事件,比如鼠标点击,键盘点击等等,SecureRandom 使用这些随机事件作为种子。

SecureRandom 提供加密的强随机数生成器 (RNG),要求种子必须是不可预知 的,产生非确定性 输出。 SecureRandom 也提供了与实现无关的算法,因此,调用方(应用程序代码)会请求特定的 RNG 算法并将它传回到该算法的 SecureRandom 对象中。

  • 如果仅指定算法名称,如下所示: SecureRandom random = SecureRandom.getInstance(\”SHA1PRNG\”);
  • 如果既指定了算法名称又指定了包提供程序,如下所示: SecureRandom random = SecureRandom.getInstance(\”SHA1PRNG\”, \”SUN\”);

使用:结果:5. 随机字符串

可以使用 Apache Commons-Lang 包中的 RandomStringUtils 类。Maven 依赖如下:

API 参考:https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/RandomStringUtils.html

示例:

RandomStringUtils 类的实现上也是依赖了 java.util.Random 工具类:

RandomStringUtils 类的定义

Java随机数之System/Random/SecureRandom详解

本系列为:从零开始学Java,为千锋资深Java教学老师独家创作

致力于为大家讲解清晰Java学习相关知识点,含有丰富的代码案例及讲解。如果感觉对大家有帮助的话,可以【关注】持续追更~

文末有本文重点总结!关于技术类问题,也欢迎大家和我们沟通交流!

我们在解决实际问题时,除了经常需要对数字、日期、时间进行操作之外,有时候还需要对系统进行设置,另外还需要生成一些随机数字。

那么我们又该如何实现这些需求呢?接下来我们会带着大家来学习一下Java中的另几个常用类,包括System、Random、SecureRandom等。

全文大约 【4000】字 不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考…

System类位于java.lang包中,代表着当前Java程序的运行平台,系统级的很多属性和控制方法都放在该类中。

由于该类的构造方法是private的,所以我们无法直接通过new的方式来创建该类的对象。System类提供了一些静态变量和静态方法,允许我们直接通过System类来调用这些类变量和类方法。在System类中,虽然有挺多的静态变量和方法,但对我们来说,只需记住一些常用的即可。

System类中常用的静态变量有如下几个:

● PrintStream out:标准输出流;

● InputStream in:标准输入流;

● PrintStream err:标准错误输出流;

1.2 常用静态方法

System类中常用的静态方法有如下几个:

● currentTimeMillis():返回当前的计算机时间;

● exit():终止当前正在运行的 Java 虚拟机;

● gc():请求系统进行垃圾回收,完成内存中的垃圾清除;

● getProperty():获得系统中属性名为 key 的属性对应的值;

● arraycopy():进行数组复制,即从指定源数组中复制一个数组。

接下来我们就把

以上这些静态变量和静态方法的基本使用,给大家简要介绍一下。

首先我们来看看System类中几个常用静态变量该如何使用。

2.1 out

out静态变量属于PrintStream类型,是System类中的标准输出流,用于接收要输出的数据。out中的数据内容通常会输出到显示器,或用户指定的某个输出目标。其实对我们来说,out并不陌生,可以说在我们之前的案例中经常使用,尤其是out中的print方法,最近我们一直在使用。但我们要搞清楚,print属于PrintStream流的方法,并不是 System类的方法。

in静态变量属于InputStream类型,是System类中的标准输入流,用于接收输入的数据。in通常是对应着键盘的输入,或是用户指定的另一个输入源。我们在之前的案例中,也简单使用过in常量,但它没有out用的那么频繁。

上面的这个案例,System.in.read()语句可以读入一个字符,read()方法是InputStream类拥有的方法。变量c必须用 int 类型,而不能用char类型,否则可能会丢失精度而导致编译失败。另外上面的程序,如果输入的是汉字将不能正常输出。如果我们想正常输出汉字,需要把 System.in声明为 InputStreamReader类型的实例。比如

InputStreamReader in=new InputStreamReader(System.in,\”GB2312\”),此时就可以读入完整的Unicode码,才能显示正常的汉字。

err静态变量属于PrintStream类型,是System类中的标准错误输出流,用于接收要输出的数据。err中的数据内容通常会输出到显示器,或用户指定的某个输出目标。其用法与System.out一样,只是不需要我们提供参数就可以输出错误信息,也可以用来输出用户指定的其他信息,包括一些变量的值。

以上这几个静态变量都很简单,大家记住其用法即可。

接下来再跟大家说说System类中的几个常用静态方法的用法

currentTimeMillis()方法用于返回当前计算机的时间戳,时间格式是当前计算机的时间与GMT时间(格林尼治时间),自1970年 1月 1日 0时 0分 0秒以来所经历的毫秒数,我们一般用它来测试程序的执行时间。通过调用currentTimeMillis()方法,我们可以获得一个长整型的数字,该数字是以差值表达的当前时间。其实currentTimeMillis()方法我们在之前的文章中已经详细讲解过,这里就不再细说了。

exit()方法用于终止当前正在运行的Java虚拟机,也就是可以用于退出程序。该方法需要一个整型的status参数,0表示正常退出,非零表示异常退出。我们使用该方法,可以在图形界面编程中实现程序的退出功能。该方法的用法如下:

gc()方法用于请求对系统主动进行垃圾回收,完成内存中的垃圾清除。但系统是否会立刻回收这些垃圾,却取决于系统中垃圾回收算法的具体实现,以及系统执行时的具体情况。一般我们在开发时不会主动调用该方法,有时候调用了也未必有效果。

getProperty()方法可以根据指定的key,获得系统中对应的某些属性值,系统中常见的属性名及其属性如下表所示:

arraycopy()方法用于数组复制,可以从指定的源数组中复制出一个数组,复制会从指定的位置开始,到目标数组的指定位置结束。arraycopy()方法一般有5个参数,其中,src表示源数组,srcPos表示从源数组中复制的起始位置,dest表示目标数组,destPos表示要复制到的目标数组的起始位置,length表示复制的个数。

我们在开发时,除了操作一些固定的数字之外,有时候还要操作一些不确定的随机数。Java中给我们提供了两种生成指定范围内随机数的方法:

● 使用Random类:伪随机数类,用来创建伪随机数。所谓伪随机数,就是指我们只要给定一个初始的种子,产生的随机数序列是完全一样的;

● 调用Math类的random()方法:Math.random()内部其实是在调用Random类,它也是伪随机数,但我们无法指定种子。

Random类为我们提供了比较丰富的随机数生成方法,比如nextInt()、nextLong()、nextFloat()、nextDouble()等方法。这些方法可以产生boolean、int、long、float、byte数组以及double类型的随机数,这是它比random()方法更好的地方,random()方法只能产生0~1之间的double类型随机数。

而且Random类提供的所有方法,生成的随机数字都是均匀分布的,也就是说区间内部的数字生成的概率是均等的。Random类位于java.util包中,该类有如下两个常用的构造方法:

● Random():默认利用当前系统的时间戳作为种子数,使用该种子数构造出Random对象。

● Random(long seed):使用单个的long类型参数,创建一个新的随机数生成器。

在Random类中,有如下一些常用的API方法供我们操作随机数:

接下来我们通过一个案例,来给大家讲解一下上述方法该如何使用。

但是我们从上面的案例中,会发现每次生成的随机数可能都是不同的,并没有体现出伪随机数的特性,这是为什么呢?其实这是因为我们创建Random实例时,如果没有给定种子,默认是使用系统的当前时间戳作为种子。因此每次运行时,种子都不同,所以得到的伪随机数序列就不同。如果我们在创建Random实例时指定一个固定的种子,就会得到完全确定的随机数序列。

之前给大家说过,Random是一种伪随机数类。这时候就有小伙伴问了,那有没有真随机数类呢?

当然是有的!

SecureRandom就是一种真随机数!

从原理来看,SecureRandom内部使用了RNG (Random Number Generator,随机数生成)算法,来生成一个不可预测的安全随机数。但在JDK的底层,实际上SecureRandom也有多种不同的具体实现。有的是使用安全随机种子加上伪随机数算法来生成安全的随机数,有的是使用真正的随机数生成器来生成随机数。实际使用时,我们可以优先获取高强度的安全随机数生成器;如果没有提供,再使用普通等级的安全随机数生成器。但不管哪种情况,我们都无法指定种子。

因为这个种子是通过CPU的热噪声、读写磁盘的字节、网络流量等各种随机事件产生的“熵”,所以这个种子理论上是不可能会重复的。这也就保证了SecureRandom的安全性,所以最终生成的随机数就是安全的真随机数。

尤其是在密码学中,安全的随机数非常重要。如果我们使用不安全的伪随机数,所有加密体系都将被攻破。因此,为了保证系统的安全,我们尽量使用SecureRandom来产生安全的随机数。

SecureRandom 给我们提供了 nextBoolean()、nextBytes()、nextDouble()、nextFloat()、nextInt() 等随机数生成方法,如下图所示:

接下来我们就通过一个案例,来看看到底该如何生成一个安全的随机数。

至此,就把与系统类、伪随机数、真随机数等相关的类给大家介绍完了,这样我们就把开发时的一些常见类介绍完毕了。今天的重点内容是:

● System:代表着当前Java程序的运行平台,系统级的很多属性和控制方法都放在该类中;

● Random:生成伪随机数;

● SecureRandom:生成安全的真随机数。

往期精彩推荐:

更多技术类干货/IT资讯、关注

Java随机数怎么生成

在Java中,我们可以使用多种方法来生成随机数。以下是其中几种常见的方法:

1.使用Random类生成随机数Random类是Java中用于生成随机数的标准类。可以使用它的nextInt()和nextDouble()等方法来生成随机整数和随机浮点数。例如:

2.使用Math.random()生成随机数Math.random()方法可以生成一个0到1之间的随机浮点数。例如

3.使用ThreadLocalRandom类生成随机数ThreadLocalRandom类是Java中用于生成随机数的线程安全类。可以使用它的nextInt()和nextDouble()等方法来生成随机整数和随机浮点数。例如:

这些方法都可以用来生成随机数,具体使用哪种方法取决于具体情况和需要。需要注意的是,生成的随机数并不是真正的随机数,而是伪随机数,它们是通过一定的算法生成的,因此在某些情况下可能会出现重复的情况。

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。