Java随机数生成方法详解
从现在开始,我们要努力学习啦!今天我给大家带来《如何在Java中使用Random生成随机数》,感兴趣的朋友请继续看下去吧!下文中的内容我们主要会涉及到等等知识点,如果在阅读本文过程中有遇到不清楚的地方,欢迎留言呀!我们一起讨论,一起学习!
Java中生成随机数的核心是java.util.Random类,它通过算法生成伪随机数,支持整数、浮点数和布尔值等类型;可通过指定种子实现序列复现,适用于测试场景;与Math.random()相比,Random提供更丰富的类型支持和种子控制;生成指定范围随机数需结合公式调整;在高并发下建议使用ThreadLocalRandom以避免性能瓶颈,安全敏感场景则应选用SecureRandom。
Java中生成随机数的核心在于使用java.util.Random
类。它提供了一系列方法来生成不同数据类型(如整数、浮点数、布尔值)的伪随机数。简单来说,就是先创建一个Random
实例,然后调用它提供的方法来获取你想要的随机数。
解决方案
在Java里,我们想搞点随机性,java.util.Random
这个类就是我们的得力助手。它能生成各种类型的伪随机数,从整数到浮点数,甚至布尔值都能搞定。
最基础的用法是这样的:
import java.util.Random; public class RandomExample { public static void main(String[] args) { Random random = new Random(); // 创建一个Random实例 // 生成一个任意范围的int整数(正负都可能) int randomInt = random.nextInt(); System.out.println("任意int随机数: " + randomInt); // 生成一个[0, bound)范围的int整数,不包含bound // 比如生成[0, 100)的整数,也就是0到99 int randomIntBounded = random.nextInt(100); System.out.println("0到99的随机数: " + randomIntBounded); // 生成一个任意范围的long整数 long randomLong = random.nextLong(); System.out.println("任意long随机数: " + randomLong); // 生成一个[0.0, 1.0)范围的float浮点数 float randomFloat = random.nextFloat(); System.out.println("0.0到1.0的随机浮点数: " + randomFloat); // 生成一个[0.0, 1.0)范围的double浮点数 double randomDouble = random.nextDouble(); System.out.println("0.0到1.0的随机双精度浮点数: " + randomDouble); // 生成一个随机的布尔值 boolean randomBoolean = random.nextBoolean(); System.out.println("随机布尔值: " + randomBoolean); // 有时候,我们希望随机数序列是可重复的,这在测试或模拟时很有用。 // 这时可以给Random传入一个种子(seed)。 Random seededRandom = new Random(12345L); // 使用固定的种子 System.out.println("\n使用固定种子12345L生成的随机数序列:"); System.out.println("第一个随机数: " + seededRandom.nextInt(100)); System.out.println("第二个随机数: " + seededRandom.nextInt(100)); // 如果再次用12345L作为种子创建Random实例,会得到相同的序列 } }
值得一提的是,Random
类生成的是“伪随机数”,这意味着它们并不是真正意义上的随机,而是通过一个确定性的算法从一个初始种子(seed)计算出来的。如果你不指定种子,Random
会使用当前时间作为默认种子,这样每次运行程序得到的序列就不同了。但如果你提供了相同的种子,每次运行都会得到相同的随机数序列,这在调试或需要可复现结果的场景下非常有用。
Random
类与Math.random()
有什么区别?我应该选择哪个?
说实话,这可能是初学者最常问的问题之一。Random
类和Math.random()
都能生成随机数,但它们之间确实存在一些关键区别,决定了你在不同场景下的选择。
首先,Math.random()
是一个静态方法,它直接返回一个double
类型的伪随机数,范围是[0.0, 1.0)
。它的实现其实是内部调用了一个Random
实例。所以,你可以把它看作是new Random().nextDouble()
的一个简化版本,但你无法控制它的种子,也无法直接生成其他类型的随机数(比如int
或long
),除非你手动进行类型转换和范围计算。
而Random
类,正如我们前面看到的,是一个实实在在的类,你需要先实例化它。它提供了更丰富的方法,不仅仅是double
,还有nextInt()
、nextLong()
、nextBoolean()
、nextFloat()
等。更重要的是,你可以通过构造函数传入一个long
类型的种子,从而控制随机数序列的可复现性。
那么,到底选哪个呢?
- 如果你只是需要一个快速的、介于0.0和1.0之间的
double
类型随机数,并且不关心种子、不涉及高并发,那么Math.random()
用起来确实很方便。 比如,我有时候写个小测试,需要随机决定某个操作是否执行,if (Math.random() < 0.5)
这种写法就挺顺手。 - 但如果你需要更灵活的控制,比如生成特定范围的整数、生成不同数据类型的随机数、或者需要一个可复现的随机数序列(比如为了单元测试),那么毫无疑问,
Random
类是你的首选。 我个人觉得,养成使用Random
类的习惯会更好,因为它提供了更多的可能性和控制力,让你能更精确地表达你的“随机”意图。尤其是在生产环境中,你可能需要对随机数的行为有更细致的掌控。
如何生成指定范围内的随机整数或浮点数?
生成指定范围内的随机数,这是我们日常开发中非常普遍的需求。Random
类虽然提供了nextInt(int bound)
,但它只能生成[0, bound)
范围的数。如果我们要自定义范围,比如[min, max]
,就需要一点小技巧了。
1. 生成指定范围 [min, max]
的随机整数(包含min和max):
这个公式是random.nextInt(max - min + 1) + min
。
我们来拆解一下:
max - min + 1
:这计算的是这个范围里总共有多少个整数。比如[5, 10]
,就是10 - 5 + 1 = 6
个数字(5, 6, 7, 8, 9, 10)。random.nextInt(max - min + 1)
:这会生成一个[0, max - min + 1)
范围的随机数。+ min
:最后加上min
,就把这个[0, count)
的范围平移到了[min, max]
。
import java.util.Random; public class RandomRangeInt { public static void main(String[] args) { Random random = new Random(); // 生成一个[1, 100]之间的随机整数 (包含1和100) int min = 1; int max = 100; int randomNumber = random.nextInt(max - min + 1) + min; System.out.println("在[" + min + ", " + max + "]范围内的随机整数: " + randomNumber); // 另一个例子:生成一个[-50, 50]之间的随机整数 min = -50; max = 50; randomNumber = random.nextInt(max - min + 1) + min; System.out.println("在[" + min + ", " + max + "]范围内的随机整数: " + randomNumber); } }
2. 生成指定范围 [min, max)
的随机浮点数(包含min,不包含max):
对于浮点数,nextDouble()
和nextFloat()
默认生成的是[0.0, 1.0)
。要把它映射到[min, max)
,公式是random.nextDouble() * (max - min) + min
。
random.nextDouble()
:得到一个[0.0, 1.0)
的数。* (max - min)
:将这个数缩放到[0.0, max - min)
的范围。+ min
:最后加上min
,就平移到了[min, max)
。
import java.util.Random; public class RandomRangeDouble { public static void main(String[] args) { Random random = new Random(); // 生成一个[10.0, 20.0)之间的随机双精度浮点数 (包含10.0,不包含20.0) double min = 10.0; double max = 20.0; double randomDouble = random.nextDouble() * (max - min) + min; System.out.println("在[" + min + ", " + max + ")范围内的随机浮点数: " + randomDouble); // 另一个例子:生成一个[-5.0, 5.0)之间的随机浮点数 min = -5.0; max = 5.0; randomDouble = random.nextDouble() * (max - min) + min; System.out.println("在[" + min + ", " + max + ")范围内的随机浮点数: " + randomDouble); } }
这些小公式看起来简单,但却是随机数生成里最常用、也最容易出错的地方。特别是整数范围的+1
,如果忘了,就会导致max
值永远无法生成。
Random
类的线程安全性如何?在高并发场景下有什么替代方案?
这是一个非常实际的问题,尤其是在构建高性能、多线程应用时,我们必须考虑Random
类的线程安全性及其潜在的性能瓶颈。
java.util.Random
类本身是线程安全的。它的所有next
方法(比如nextInt()
, nextDouble()
等)都使用了synchronized
关键字进行同步。这意味着,如果你在多个线程中共享同一个Random
实例,每次只有一个线程能够调用它的next
方法,其他线程必须等待。
问题出在哪呢?
虽然线程安全,但这种同步机制在高并发场景下可能会成为性能瓶颈。多个线程频繁地去争抢同一个Random
实例的锁,会导致线程上下文切换的开销,降低程序的吞吐量。想象一下,一堆人排队去一个窗口办业务,即使窗口能保证每个人都办好,但队伍太长,效率自然就低了。
那么,在高并发场景下,我们有什么更好的替代方案呢?
java.util.concurrent.ThreadLocalRandom
(Java 7及更高版本) 这是在高并发场景下生成随机数的首选方案。ThreadLocalRandom
是Random
的子类,它利用了ThreadLocal
的原理。简单来说,每个线程都会拥有自己独立的Random
实例。这样一来,线程之间就不会存在竞争,也就没有了锁的开销,性能自然就上去了。import java.util.concurrent.ThreadLocalRandom; public class ThreadLocalRandomExample { public static void main(String[] args) { // 获取当前线程的ThreadLocalRandom实例 ThreadLocalRandom current = ThreadLocalRandom.current(); // 用法与Random类似,但无需实例化 int randomNumber = current.nextInt(1, 101); // 生成[1, 100]的随机整数 double randomDouble = current.nextDouble(0.0, 1.0); // 生成[0.0, 1.0)的随机双精度浮点数 System.out.println("ThreadLocalRandom生成的整数: " + randomNumber); System.out.println("ThreadLocalRandom生成的浮点数: " + randomDouble); // 模拟多线程使用 Runnable task = () -> { ThreadLocalRandom tlr = ThreadLocalRandom.current(); System.out.println(Thread.currentThread().getName() + " 生成随机数: " + tlr.nextInt(1000)); }; for (int i = 0; i < 5; i++) { new Thread(task, "Thread-" + i).start(); } } }
ThreadLocalRandom
的优点是显而易见的:高性能,因为它消除了多线程之间的竞争。缺点嘛,可能就是你不能像Random
那样手动设置一个全局的种子来复现整个随机序列,因为每个线程都有自己的种子。但对于大多数并发随机数生成的需求,这都不是问题。java.security.SecureRandom
这个类主要用于加密安全的随机数生成。它与Random
的主要区别在于,SecureRandom
生成的随机数具有更高的不可预测性,更难以被猜测,因此更适合用于生成密钥、安全令牌等对安全性要求极高的场景。import java.security.SecureRandom; public class SecureRandomExample { public static void main(String[] args) { SecureRandom secureRandom = new SecureRandom(); byte[] bytes = new byte[16]; secureRandom.nextBytes(bytes); // 生成随机字节数组 System.out.println("SecureRandom生成的随机字节: " + bytes[0]); int randomNumber = secureRandom.nextInt(100); System.out.println("SecureRandom生成的整数: " + randomNumber); } }
SecureRandom
的性能通常比Random
和ThreadLocalRandom
要低,因为它需要执行更复杂的算法来确保随机数的质量。所以,除非你真的需要加密级别的随机性,否则不建议在普通的业务逻辑中使用SecureRandom
,以免造成不必要的性能开销。
总结一下我的建议:
- 单线程或低并发:
java.util.Random
足够好用,简单直接。 - 高并发场景:毫无疑问选择
java.util.concurrent.ThreadLocalRandom
,它能提供最佳的性能。 - 安全敏感场景(如密码学):必须使用
java.security.SecureRandom
,牺牲一点性能换取更高的安全性。
选择合适的随机数生成器,是编写健壮、高效Java应用的一个小细节,但往往这些小细节决定了整体的质量。
今天带大家了解了的相关知识,希望对你有所帮助;关于文章的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~

- 上一篇
- JS负载均衡配置全攻略

- 下一篇
- Python提取数字的实用技巧
-
- 文章 · java教程 | 6分钟前 |
- Java反射修改final字段技巧
- 108浏览 收藏
-
- 文章 · java教程 | 12分钟前 |
- Spring中Bean的常用作用域有哪些?
- 395浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Spring Boot整合Kafka消息消费教程
- 434浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java动态加载对象教程:从文件实例化详解
- 114浏览 收藏
-
- 文章 · java教程 | 2小时前 |
- Java线程管理与重启技巧详解
- 274浏览 收藏
-
- 文章 · java教程 | 2小时前 |
- Java开发日记管理软件教程
- 434浏览 收藏
-
- 文章 · java教程 | 3小时前 |
- Mapbox获取LatLng方法全解析
- 162浏览 收藏
-
- 文章 · java教程 | 3小时前 |
- Eclipse更新失败?解决依赖冲突技巧
- 159浏览 收藏
-
- 文章 · java教程 | 4小时前 |
- Stripe支付手动捕获详解教程
- 357浏览 收藏
-
- 文章 · java教程 | 5小时前 |
- Java中emptyList与emptyMap使用全解析
- 371浏览 收藏
-
- 文章 · java教程 | 5小时前 |
- Java文件复制方法与API对比详解
- 267浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 499次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- PandaWiki开源知识库
- PandaWiki是一款AI大模型驱动的开源知识库搭建系统,助您快速构建产品/技术文档、FAQ、博客。提供AI创作、问答、搜索能力,支持富文本编辑、多格式导出,并可轻松集成与多来源内容导入。
- 263次使用
-
- AI Mermaid流程图
- SEO AI Mermaid 流程图工具:基于 Mermaid 语法,AI 辅助,自然语言生成流程图,提升可视化创作效率,适用于开发者、产品经理、教育工作者。
- 1051次使用
-
- 搜获客【笔记生成器】
- 搜获客笔记生成器,国内首个聚焦小红书医美垂类的AI文案工具。1500万爆款文案库,行业专属算法,助您高效创作合规、引流的医美笔记,提升运营效率,引爆小红书流量!
- 1080次使用
-
- iTerms
- iTerms是一款专业的一站式法律AI工作台,提供AI合同审查、AI合同起草及AI法律问答服务。通过智能问答、深度思考与联网检索,助您高效检索法律法规与司法判例,告别传统模板,实现合同一键起草与在线编辑,大幅提升法律事务处理效率。
- 1084次使用
-
- TokenPony
- TokenPony是讯盟科技旗下的AI大模型聚合API平台。通过统一接口接入DeepSeek、Kimi、Qwen等主流模型,支持1024K超长上下文,实现零配置、免部署、极速响应与高性价比的AI应用开发,助力专业用户轻松构建智能服务。
- 1153次使用
-
- 提升Java功能开发效率的有力工具:微服务架构
- 2023-10-06 501浏览
-
- 掌握Java海康SDK二次开发的必备技巧
- 2023-10-01 501浏览
-
- 如何使用java实现桶排序算法
- 2023-10-03 501浏览
-
- Java开发实战经验:如何优化开发逻辑
- 2023-10-31 501浏览
-
- 如何使用Java中的Math.max()方法比较两个数的大小?
- 2023-11-18 501浏览