We are often required to generate random numbers while developing applications in Java or any other programming language. Java provides multiple ways to generate random numbers through different built-in methods and classes like `java.util.Random` and `java.lang.Math`.

In this article, we'll look at three different ways to generate random numbers in Java. You'll also learn how to restrict the random number generation in a specific range.

## Java 8 Random Number Generation

Java 8 introduced a new method, `ints()`, as a part of the `java.util.Random` class. This method returns an unlimited stream of pseudorandom integer values. You can restrict the random numbers between a certain range by providing the minimum and maximum values as arguments.

The following example demonstrates how you can use the `Random.ints()` method to generate a random integer value:

``````// create an instance of `Random`
Random random = new Random();

// generate randome integer value
int num = random.ints().findFirst().getAsInt();

// print random number
System.out.println("Random Number: " + num);
``````

The above code will output the following:

``````Random Number: -276754611
``````

Since we didn't set the random number bounds, `Random.ints()` will generate a random integer within `Integer.MIN_VALUE` and `Integer.MAX_VALUE`. However, you can limit the random number generation within a specific range as shown below:

``````// create an instance of `Random`
Random random = new Random();

// generate randome integer value
// within 100 inclusive and 500 exclusive
int num = random.ints(100, 500).findFirst().getAsInt();

// print random number
System.out.println("Random Number [range 100-500]: " + num);
``````

Here is the output of the above code snippet:

``````Random Number [range 100-500]: 455
``````

By default, the `ints()` method generates a stream of unlimited pseudorandom numbers up to `Long.MAX_VALUE` size. To limit the size of the stream, you can do the following:

``````// create an instance of `Random`
Random random = new Random();

// generate 5 randome integers
random.ints(5).forEach(System.out::println);
``````

The above code generates 5 random integer values and prints them on the console. Here is how the output should look like:

``````261853113
-937163633
-2004940360
1260887368
193523502
``````

As you can see above the random numbers are generated within the default range. To specify the range and size of the stream, you can do the following:

``````// create an instance of `Random`
Random random = new Random();

// generate 5 randome integers
// within 10 inclusive and 100 exclusive
random.ints(5, 10, 100).forEach(System.out::println);
``````

The above generates random integers in a range between 10 (inclusive) and 100 (exclusive), with a stream size of 5. Finally, it prints out values using forEach() method. Here is how the output looks like now:

``````32
78
63
67
33
``````

In addition to `Random.ints()`, Java 8 also introduced `Random.doubles()` and `Random.longs()` methods for pseudorandom double and long values generation. These methods accept the same parameters as of `ints()`.

Here is an example that shows how you can generate a stream of random double and long values:

``````// create an instance of `Random`
Random random = new Random();

// generate 5 randome long values
// within 100 inclusive and 1000 exclusive
System.out.println("Random Long Values");
System.out.println("------------------");
random.longs(5, 100, 1000).forEach(System.out::println);

// print an empty line
System.out.println();

// generate 5 randome double values
// within 7.99 inclusive and 49.58 exclusive
System.out.println("Random Double Values");
System.out.println("--------------------");
random.doubles(5, 7.99, 49.58).forEach(System.out::println);
``````

The above code snippet will print the following on the console:

``````Random Long Values
------------------
773
472
749
149
422

Random Double Values
--------------------
43.9940252755332
18.36065220679901
47.97256897484375
33.21094500155469
28.668240009603416
``````

## Using `Random` Class

In Java 7 and below, you can use the `java.util.Random` class to generate random numbers of different types, such as integer, double, floats, longs, and booleans.

To generate random numbers, all you need to do is create an instance of the `Random` class and then call one of the random value generator methods, such as `nextInt()`, `nextLong()`, or `nextDouble()`.

You can also pass an optional argument to the above methods to set an upper bound on the range of the numbers to be generated. Here is an example that shows how you can use the `Random` class to generate random integer, long, and double values:

``````// create an instance of `Random`
Random random = new Random();

// generate random integers
System.out.println("Random Integer: " + random.nextInt());
System.out.println("Random Integer [range 0-100]: " + random.nextInt(100));

// generate random longs
System.out.println("Random Long: " + random.nextLong());

// generate random doubles
System.out.println("Random Double: " + random.nextDouble());

// generate random floats
System.out.println("Random Float: " + random.nextFloat());
``````

The above code will output something like below:

``````Random Integer: -798093346
Random Integer [range 0-100]: 16
Random Long: -6661017962932808427
Random Double: 0.9737058318011228
Random Float: 0.9319658
``````

As you can see above, the `nextInt()` method allows us to specify an upper bound of the random number being generated. Another important thing to notice is both `nextFloat()` and `nextDouble()` methods only generate random float and double values between 0.0 and 1.0.

By default, `nextInt()` generates a random integer between 0 (inclusive) and 2 to the power of 32. If you want to use a custom range, you have to write the additional code. Here is an example that generates a random integer between 10 (inclusive) and 50 (exclusive):

``````// create an instance of `Random`
Random random = new Random();

// generate random integer
// range 10 (inclusive) and 50 (exclusive)
int num = random.nextInt(50) + 10;

// print random integer
System.out.println("Random Integer [range 10-50]: " + num);
``````

Here is how the output looks like:

``````Random Integer [range 10-50]: 48
``````

## Using `Math` Class

The `java.lang.Math` class provides various static methods for performing different kinds of numeric operations such as calculating exponentiation, logarithms, and more. One of such methods is `Math.random()` that generates a double value with a positive sign, greater than or equal to 0.0 and less than 1.0. The returned values are chosen pseudorandomly with (approximately) uniform distribution from that range.

Here is an example:

``````// create a random double
double num = Math.random();

// print random value
System.out.println("Random Double [range 0-1]: " + num);
``````

You should see an output like below for the above code snippet:

``````Random Double [range 0-1]: 0.10691196575437478
``````

To generate a random integer within a custom range using the `Math.random()` static method, you need to perform some manual calculations:

``````// create an random integer
// range between 20 (inclusive) and 50 (inclusive)
int num = Double.valueOf(Math.random() * ((50 - 20) + 1)).intValue() + 20;

// print random value
System.out.println("Random Integer [range 20-50]: " + num);
``````

The above code will output the following:

``````Random Integer [range 20-50]: 25
``````