← thecodex.expert · The Codex Family of Knowledge
Tier 1 · Beginner · Java Project

Password Generator

Generate a random password from a chosen mix of character sets. Teaches an enum for the character-set options, EnumSet, and SecureRandom for genuinely unpredictable output.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want a tool that creates a strong password: a random mix of letters, numbers, and symbols, of a length the user chooses. It teaches building a string from random choices — and an important lesson about which randomness is safe for security.

Where this shows up: password managers, generating API keys, session tokens, temporary access codes, unique IDs. Any time software needs something unpredictable that an attacker cannot guess.

2 How to Think About It

Two decisions drive the whole program: which character sets to include, and how many characters to pick from the combined pool.

The plan — in plain English
1. Choose which character sets to include (lowercase, uppercase, digits, symbols). → 2. Build one combined pool string from the chosen sets. → 3. Pick a random character from the pool, length times. → 4. Join the picks into the final password.

Build character pool

Ask for length

Pick that many random chars

Join into a password

Show the password

3 The Build — explained part by part

Here is the complete generator. Each character set is a case of an enum, from the course’s Enums lesson — a real class, not just a named integer, which is exactly what lets each constant carry its own character string.

JavaPasswordGenerator.java
import java.security.SecureRandom;
import java.util.EnumSet;
import java.util.Set;

/**
 * Password Generator: builds a random password from a chosen mix of
 * character sets, using SecureRandom for genuinely unpredictable output.
 */
public class PasswordGenerator {

    enum CharSet {
        LOWER("abcdefghijklmnopqrstuvwxyz"),
        UPPER("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
        DIGITS("0123456789"),
        SYMBOLS("!@#$%^&*()-_=+");

        final String chars;
        CharSet(String chars) { this.chars = chars; }
    }

    /** Returns null (an empty pool) if no character sets are selected. */
    static String buildPool(Set<CharSet> sets) {
        StringBuilder pool = new StringBuilder();
        for (CharSet set : sets) pool.append(set.chars);
        return pool.isEmpty() ? null : pool.toString();
    }

    static String generate(int length, Set<CharSet> sets, SecureRandom random) {
        String pool = buildPool(sets);
        if (pool == null) {
            throw new IllegalArgumentException("Select at least one character set.");
        }
        StringBuilder password = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            password.append(pool.charAt(random.nextInt(pool.length())));
        }
        return password.toString();
    }

    public static void main(String[] args) {
        Set<CharSet> sets = EnumSet.of(
                CharSet.LOWER, CharSet.UPPER, CharSet.DIGITS, CharSet.SYMBOLS);
        String password = generate(16, sets, new SecureRandom());
        System.out.println(password);
    }
}
⚠ No in-browser playground here
Java compiles to JVM bytecode and needs a real JDK to run, so unlike the Python version of this project there is no editor above you can run in the browser. Copy the code below and run it on your own machine — it takes seconds once a JDK is installed.
What each part does — in plain words
enum CharSet { LOWER("..."), UPPER("..."), ... } — each constant has a constructor argument, its actual character string, stored in a field. This is the “enum as a real class” idea from the course made concrete: you could not attach data like this to a plain C-style named-integer enum.

EnumSet<CharSet> — a specialized, highly efficient Set implementation that only works with enum types, used here to represent “which character sets are selected” instead of four separate boolean parameters.

buildPool returns null for an empty selection, instead of an empty string — this is a deliberate signal the caller cannot ignore: generate checks for null explicitly and throws a clear IllegalArgumentException, rather than silently producing a password from an empty pool (which would either loop forever or throw an unrelated StringIndexOutOfBoundsException deep inside charAt).

new SecureRandom(), not new Random() — java.util.Random is a fast, predictable pseudo-random generator, fine for a guessing game but genuinely unsuitable for anything security-sensitive; SecureRandom is cryptographically strong and is what a real password generator must use.
Common mistakes — and how to avoid them
✗ Using java.util.Random for a password generator — it is statistically predictable, which defeats the entire purpose of a password.
✓ Always use java.security.SecureRandom for anything security-related, as the code above does.
✗ Letting an empty selection silently produce an empty-pool password — calling pool.charAt(...) on an empty string throws a confusing, unrelated exception instead of a clear error.
✓ Check for an empty pool explicitly and throw a clear IllegalArgumentException, as generate does — the same bug the Rust version of this project caught for real during testing.

4 Test & Prove Each Part

We test the pool-building logic, the output length and character membership, and the empty-selection error — the same “empty pool” case that turned into a real bug in this project's Rust version.

Selecting no character sets is rejected (buildPool returns null)
The generated password has exactly the requested length
The password only contains characters from the sets you actually selected
Generating with no sets throws a clear exception instead of crashing obscurely
JavaPasswordGeneratorTest.java
import org.junit.Test;
import java.security.SecureRandom;
import java.util.EnumSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class PasswordGeneratorTest {

    private static final EnumSet<PasswordGenerator.CharSet> NONE =
            EnumSet.noneOf(PasswordGenerator.CharSet.class);

    @Test
    public void emptySelectionIsRejected() {
        assertNull(PasswordGenerator.buildPool(NONE));
    }

    @Test
    public void generatedPasswordHasTheRequestedLength() {
        Set<PasswordGenerator.CharSet> sets = EnumSet.of(PasswordGenerator.CharSet.LOWER);
        String password = PasswordGenerator.generate(20, sets, new SecureRandom());
        assertEquals(20, password.length());
    }

    @Test
    public void onlyUsesCharactersFromTheSelectedSets() {
        Set<PasswordGenerator.CharSet> sets = EnumSet.of(PasswordGenerator.CharSet.DIGITS);
        String password = PasswordGenerator.generate(50, sets, new SecureRandom());
        assertTrue(password.chars().allMatch(Character::isDigit));
    }

    @Test(expected = IllegalArgumentException.class)
    public void generateWithNoSetsThrowsInsteadOfCrashingObscurely() {
        PasswordGenerator.generate(10, NONE, new SecureRandom());
    }
}

Compile and run with javac -cp junit-4.13.2.jar and hamcrest-core-1.3.jar PasswordGenerator.java PasswordGeneratorTest.java then java -cp .:junit-4.13.2.jar:hamcrest-core-1.3.jar org.junit.runner.JUnitCore PasswordGeneratorTest. The “only uses selected characters” test generates a long, 50-character password specifically to make it statistically very unlikely a bug would slip through by chance.

5 The Interface

INPUTINPUTcharacter-set selection (hardcoded in main; see Try This Next)
What it expects
EnumSet.of(LOWER, UPPER, DIGITS, SYMBOLS), length 16
OUTPUTOUTPUTone random password
What it returns
uyL8S$6lKngwiad)

6 Run It & Automate It

Save the code as PasswordGenerator.java and compile it with javac — that turns your source into .class bytecode files, which java then runs on the JVM. No separate install step: any real JDK ships both tools.

Run it locally
javac PasswordGenerator.java && java PasswordGenerator
Run it again and you will get a different password — that unpredictability is the entire point.

A CI tool like Jenkins runs the same compile-then-test steps automatically whenever the code changes — every line below has a plain explanation.

What you should see when it works
Terminala real run
$ java PasswordGenerator
uyL8S$6lKngwiad)
$ java PasswordGenerator
mdG3z1MtFa1RGOD7
If it breaks — how to fix it
🚨 java.lang.IllegalArgumentException: Select at least one character set.
This is generate working as designed — the EnumSet you passed in was empty. Add at least one CharSet constant to it.
🚨 The password looks weaker than expected, or always starts with the same character.
Double check you constructed a fresh SecureRandom() and are not accidentally reusing a Random seeded with a fixed value from testing code.
GroovyJenkinsfile
// Jenkinsfile — runs the tests automatically every time the code changes.
pipeline {
    agent any                          // run on any available machine
    environment {
        CP = 'junit-4.13.2.jar:hamcrest-core-1.3.jar'   // JUnit + its one dependency
    }

    stages {
        stage('Get the code') {
            steps { checkout scm }     // download the latest code
        }
        stage('Set up JDK') {
            steps {
                sh 'java -version'           // confirm a JDK is installed
                sh 'javac -cp "$CP" *.java'   // compile the program and its tests together
            }
        }
        stage('Run the tests') {
            steps {
                sh 'java -cp ".:$CP" org.junit.runner.JUnitCore PasswordGeneratorTest'
            }
        }
    }

    post {
        success { echo 'All tests passed.' }
        failure { echo 'A test failed — look above.' }
    }
}
🎯 Try this next — make it yours
  1. Read the selection from args. Let the command line choose which character sets and length to use, instead of hardcoding them in main. (Teaches: parsing String[] args into an EnumSet.)
  2. Guarantee at least one of each selected type. A 16-character password with symbols selected could randomly contain zero symbols. (Teaches: post-generation validation and regeneration.)
  3. Estimate entropy. Print how many possible passwords exist for the chosen pool and length. (Teaches: basic combinatorics with Math.pow.)
What you learned
You learned enums as real classes carrying their own data, EnumSet for a type-safe set of options, why SecureRandom replaces Random for anything security-sensitive, and returning null as a deliberate, checked signal rather than letting a downstream call fail confusingly. Related: Enums, Standard Library.