Java Random Numbers Explained: Math.random(), Random, SecureRandom, and Range Patterns

目次

1. What You’ll Learn in This Article

When you try to work with “random numbers” in Java, you’ll quickly run into multiple options such as Math.random(), Random, and SecureRandom.
Many people end up thinking, “Which one am I supposed to use?”

In this section, we’ll start with the bottom line and clarify what you will be able to do by reading this article to the end. By understanding the big picture before diving into detailed mechanisms and code, the later sections will become much easier to follow.

1.1 You’ll Understand the Major Ways to Generate Random Numbers in Java

This article explains the major ways to generate random numbers in Java step by step.

Specifically, you’ll learn:

  • A simple method you can use immediately
  • A method that allows more programmatic control
  • A method for situations where security matters

We’ll organize everything by use case so it’s easy to choose the right approach.

That means this article works for both:

  • Beginners who want to try sample code
  • People who want to move beyond “it runs, so it’s fine”

The structure is designed to support both types of readers.

1.2 You’ll Understand Range Rules and Common Misconceptions

One of the biggest stumbling blocks when working with random numbers is range selection.

For example:

  • You want a random number from 0 to 9
  • You want to simulate a die roll from 1 to 6
  • You want random numbers that include negative values

In these cases, questions like the following come up constantly:

  • Is the upper bound included?
  • Is the lower bound always included?
  • Why am I not getting the values I expected?

This article explains it in a beginner-friendly flow:
“Why does it happen?” → “How do you write it correctly?”

1.3 You’ll Learn the Difference Between Reproducibility and Risk

Random numbers can have opposite requirements depending on the situation:

  • You want different results every time
  • You want to reproduce the same results repeatedly

For example:

  • For testing and debugging, you want to reproduce “the same random values”
  • For passwords and tokens, you need “unpredictable random values”

If you use them without understanding this difference, you can run into issues such as:

  • Unstable tests
  • Security-dangerous implementations

This article clearly separates:
“Randomness that should be reproducible” vs. “Randomness that must not be reproducible”

1.4 You’ll Be Able to Choose “Safe Randomness” vs. “Dangerous Randomness”

Java provides multiple random-number options that may look similar, but their purposes are completely different.

  • Randomness suitable for games and sample code
  • Randomness that is fine for business applications
  • Randomness that is absolutely required for security use cases

If you use them without distinguishing the purpose, you’re likely to end up with:

  • “It seems to work, but it’s actually dangerous”
  • “Code that becomes a problem later”

This article explains the decision criteria with reasons, in the form:
“For this use case, use this.”

1.5 Even Beginners Will Be Able to Explain “Why This Is the Right Way”

Instead of just listing code examples, we focus on the reasoning behind them, such as:

  • Why this method is used
  • Why this specific style is correct
  • Why other approaches are not suitable

We emphasize the background and way of thinking.

So this is useful for people who want to:

  • Understand and use it (not just memorize)
  • Reach a level where they can explain it to others

as well.

2. What “Random Numbers” Mean in Java

Before generating random numbers in Java, this section organizes the core fundamentals you must know.
If you skip this and only copy code, you will almost certainly get confused later.

2.1 “Random” Does Not Mean “Perfectly Random”

A common beginner misconception is this:
Random numbers generated in Java are not “perfectly random.”

Most random values used in Java are more accurately described as:

  • Numbers computed according to fixed rules (an algorithm)
  • They look random, but internally they follow a pattern

This kind of randomness is called a pseudorandom number (PRNG output).

2.2 What Is a Pseudorandom Number Generator (PRNG)?

A pseudorandom number generator is a mechanism that:

  • Starts from an initial value (a seed)
  • Repeats mathematical computations
  • Produces a sequence that appears random

Key benefits include:

  • Fast generation
  • The same seed reproduces the same random sequence
  • Easy to handle on computers

On the other hand, drawbacks include:

  • Predictable if the algorithm is known
  • May be unsuitable for security use cases

2.3 When “Reproducible Randomness” Is Useful

At first glance, you might think:

If the same random values appear, isn’t that not random and therefore bad?

But in practice, reproducible randomness is extremely important.

For example:

  • You want to verify the same results every time in test code
  • You want to reproduce a bug report
  • You want to compare simulation results

In these situations, it’s far more practical to have:

  • The same random values every run
  • rather than results changing each time

Many Java random-number classes are designed with this reproducibility in mind.

2.4 When Randomness Must NOT Be Reproducible

On the other hand, some random values must never be reproducible.

Typical examples include:

  • Password generation
  • Authentication tokens
  • Session IDs
  • One-time keys

If these values are:

  • Predictable
  • Reproducible

that alone can lead to a serious security incident.

That’s why Java provides
security-focused random generators
separate from ordinary pseudorandom generators.

If you implement without understanding this difference, you can easily end up with:

  • Code that “works” but is dangerous
  • Code that becomes an issue in reviews or audits

So make sure you understand this point.

2.5 What Is a “Uniform Distribution”? (The Basics of Bias)

A term that often appears in random-number discussions is uniform distribution.

A uniform distribution means:

  • Every value appears with the same probability

For example:

  • A die where 1–6 are equally likely
  • Digits 0–9 appear evenly

That state is a uniform distribution.

Java’s random APIs are generally designed assuming a uniform distribution.

2.6 Common Beginner Examples of “Biased Randomness”

When you manually “adjust” random numbers, you can accidentally introduce bias.

Common examples include:

  • Forcing a range using % (the remainder operator)
  • Casting double to int at the wrong point
  • Misunderstanding whether the bounds are inclusive

These are tricky because the code still runs, which makes the mistake
hard to notice.

Later sections will explain, with concrete examples:

  • Why the bias happens
  • How to write it correctly

so you can avoid these pitfalls.

3. Getting Started Quickly with Math.random()

From here, we’ll look at concrete ways to generate random numbers in Java.
The first method is Math.random(), which is the simplest and most commonly encountered by beginners.

3.1 What Is Math.random()?

Math.random() is a static method provided by Java that returns a double random value greater than or equal to 0.0 and less than 1.0.

double value = Math.random();

When you run this code, the returned value is:

  • greater than or equal to 0.0
  • less than 1.0

In other words, 1.0 is never included.

3.2 Why Math.random() Is So Easy to Use

The biggest advantage of Math.random() is that
it requires absolutely no setup.

  • No class instantiation
  • No import statements
  • Usable in a single line

Because of this, it’s very convenient for:

  • Learning examples
  • Simple demos
  • Quickly checking program flow

in those kinds of situations.

3.3 Generating Integer Random Numbers with Math.random()

In real programs, you’ll often want integer random numbers
rather than double values.

3.3.1 Generating a Random Number from 0 to 9

int value = (int)(Math.random() * 10);

The values produced by this code are:

  • 0 or greater
  • 9 or less

Here’s why:

  • Math.random() returns values from 0.0 to 0.999…
  • Multiplying by 10 gives 0.0 to 9.999…
  • Casting to int truncates the decimal part

3.4 A Common Pitfall When Generating 1 to 10

A very common beginner mistake is where to shift the starting value.

int value = (int)(Math.random() * 10) + 1;

This produces random numbers that are:

  • greater than or equal to 1
  • less than or equal to 10

If you get the order wrong and write this instead:

// Common mistake
int value = (int)Math.random() * 10 + 1;

This code results in:

  • (int)Math.random() always being 0
  • The result always becoming 1

Always wrap the cast in parentheses—this is a critical point.

3.5 Advantages and Limitations of Math.random()

Advantages

  • Extremely simple
  • Low learning cost
  • Enough for small, simple use cases

Limitations

  • No reproducibility (no seed control)
  • No internal control
  • Not suitable for security use cases
  • Lacks flexibility for complex scenarios

In particular, if you need:

  • The same random values in tests
  • Fine-grained control over behavior

then Math.random() will not be sufficient.

3.6 When You Should Use Math.random()

Math.random() is best suited for:

  • Early-stage Java learning
  • Algorithm explanation code
  • Simple verification samples

On the other hand, for:

  • Business applications
  • Test code
  • Security-related logic

you should choose a more appropriate random-number generator.

4. Understanding the Core Class java.util.Random

Now let’s move one step beyond Math.random() and look at
the java.util.Random class.

Random is a fundamental class that has been used for many years in Java. It appears when you want
“proper control over random numbers.”

4.1 What Is the Random Class?

Random is a class for generating pseudorandom numbers.
You use it by creating an instance like this:

import java.util.Random;

Random random = new Random();
int value = random.nextInt();

The biggest difference from Math.random() is that
the random-number generator is treated as an object.

This allows you to:

  • Reuse the same generator
  • Keep behavior consistent
  • Explicitly manage randomness in your design

which is not possible with Math.random().

4.2 Types of Random Values You Can Generate with Random

The Random class provides methods tailored to different use cases.

Common examples include:

  • nextInt(): an int random value
  • nextInt(bound): an int from 0 (inclusive) to bound (exclusive)
  • nextLong(): a long random value
  • nextDouble(): a double from 0.0 (inclusive) to 1.0 (exclusive)
  • nextBoolean(): true or false

By choosing the right method, you can generate random values
naturally suited to each data type.

5. Controlling Range and Reproducibility with Random

One of the biggest advantages of using java.util.Random is
explicit control over ranges and reproducibility.

5.1 Generating Values Within a Specific Range

The most commonly used method is nextInt(bound).

Random random = new Random();
int value = random.nextInt(10);

This code produces values that are:

  • greater than or equal to 0
  • less than 10

So the result range is 0 to 9.

5.2 Shifting the Range (e.g., 1 to 10)

To shift the range, simply add an offset:

int value = random.nextInt(10) + 1;

This produces values from:

  • 1 (inclusive)
  • 10 (inclusive)

This pattern:

random.nextInt(range) + start

is the standard way to generate integer random values within a range in Java.

5.3 Generating Random Numbers with Negative Ranges

You can also generate ranges that include negative values.

For example, to generate values from -5 to 5:

int value = random.nextInt(11) - 5;

Explanation:

  • nextInt(11) → 0 to 10
  • Subtract 5 → -5 to 5

This approach works consistently regardless of the sign of the range.

5.4 Reproducibility with Seeds

Another key feature of Random is seed control.

If you specify a seed, the random sequence becomes reproducible:

Random random = new Random(12345);
int value1 = random.nextInt();
int value2 = random.nextInt();

As long as the same seed value is used:

  • The same sequence of random values is generated

This is extremely useful for:

  • Unit tests
  • Simulation comparisons
  • Debugging difficult bugs

In contrast, Math.random() does not allow explicit seed control.

6. Why Random Is Not Suitable for Security

At this point, you might think:

Random can generate unpredictable values, so isn’t it fine for security?

This is a very common misunderstanding.

6.1 Predictability Is the Core Problem

java.util.Random uses a deterministic algorithm.

This means:

  • If the algorithm is known
  • If enough output values are observed

then future values can be predicted.

For games or simulations, this is not a problem.
For security, this is a critical flaw.

6.2 Concrete Examples of Dangerous Usage

Using Random for the following is dangerous:

  • Password generation
  • API keys
  • Session identifiers
  • Authentication tokens

Even if the values “look random,” they are not cryptographically secure.

6.3 The Key Difference Between “Random-Looking” and “Secure”

The critical distinction is this:

  • Random: fast, reproducible, predictable in theory
  • Secure random: unpredictable, resistant to analysis

Security-focused randomness must be designed under the assumption that:

  • The attacker knows the algorithm
  • The attacker can observe outputs

Random does not meet this requirement.

That is why Java provides a separate class specifically for security use cases.

7. Using SecureRandom for Security-Critical Randomness

When randomness must be unpredictable and resistant to attacks,
Java provides java.security.SecureRandom.

This class is designed specifically for security-sensitive use cases.

7.1 What Makes SecureRandom Different?

SecureRandom differs from Random in its design goals.

  • Uses cryptographically strong algorithms
  • Draws entropy from multiple system sources
  • Designed to be unpredictable even if outputs are observed

Unlike Random, the internal state of SecureRandom is not practically reversible.

7.2 Basic Usage of SecureRandom

The usage is similar to Random, but the intent is very different.

import java.security.SecureRandom;

SecureRandom secureRandom = new SecureRandom();
int value = secureRandom.nextInt(10);

This produces values from:

  • 0 (inclusive)
  • 10 (exclusive)

The API is intentionally similar so it can replace Random when needed.

7.3 When You Must Use SecureRandom

You should use SecureRandom for:

  • Password generation
  • Session IDs
  • Authentication tokens
  • Cryptographic keys

In these scenarios, using Random is not “slightly risky” — it is incorrect.

The performance cost of SecureRandom is intentional and acceptable for security.

8. Modern Random APIs: ThreadLocalRandom and RandomGenerator

Recent Java versions provide more advanced random APIs to address performance and design issues.

8.1 ThreadLocalRandom: Randomness for Multithreading

ThreadLocalRandom is optimized for multi-threaded environments.

Instead of sharing a single Random instance, each thread uses its own generator.

int value = java.util.concurrent.ThreadLocalRandom.current().nextInt(1, 11);

This generates values from 1 (inclusive) to 11 (exclusive).

Advantages include:

  • No contention between threads
  • Better performance under concurrency
  • Clean range-based APIs

For parallel processing, this is usually preferable to Random.

8.2 RandomGenerator: A Unified Interface (Java 17+)

RandomGenerator is an interface introduced to unify random-number generation.

It allows:

  • Switching algorithms easily
  • Writing implementation-agnostic code
  • Better future-proof design
import java.util.random.RandomGenerator;

RandomGenerator generator = RandomGenerator.getDefault();
int value = generator.nextInt(10);

This approach is recommended for modern Java codebases.

9. Summary: Choosing the Right Random API in Java

Java provides multiple random-number APIs because
“randomness” has different meanings depending on context.

9.1 Quick Decision Guide

  • Math.random(): learning, simple demos
  • Random: tests, simulations, reproducible behavior
  • ThreadLocalRandom: multi-threaded applications
  • RandomGenerator: modern, flexible design
  • SecureRandom: passwords, tokens, security

Choosing the wrong one may not cause immediate errors,
but it can create serious problems later.

9.2 The Key Principle to Remember

The most important takeaway is this:

Randomness is a design decision, not just a function call.

By understanding the intent behind each API, you can write Java code that is:

  • Correct
  • Maintainable
  • Safe

and suitable for real-world applications.