☕ Java

Command Line Arguments

Command line arguments are values passed to a Java program when it is launched from the terminal. They are received as the String[] args parameter of the main method. Every argument is a String regardless of the intended type — numeric and boolean arguments must be parsed manually. Command line arguments allow the same program to behave differently based on inputs provided at launch time without recompilation.

How Command Line Arguments Work

When the JVM launches a Java program, it collects all tokens after the class name on the command line and passes them as elements of the String array named args. The array is zero-indexed — args[0] is the first argument, args[1] the second, and so on. args.length gives the total count. If no arguments are passed, args is an empty array (not null).
Java
// ── Program: ShowArgs.java ────────────────────────────────────────────
public class ShowArgs {
    public static void main(String[] args) {

        System.out.println("Argument count: " + args.length);

        for (int i = 0; i < args.length; i++) {
            System.out.println("args[" + i + "] = "" + args[i] + """);
        }
    }
}

// ── Compile and run: ──────────────────────────────────────────────────
// javac ShowArgs.java
// java ShowArgs Alice 30 Engineer

// Output:
// Argument count: 3
// args[0] = "Alice"
// args[1] = "30"
// args[2] = "Engineer"

// ── Arguments with spaces — use quotes in the terminal: ───────────────
// java ShowArgs "Alice Smith" 30 "Senior Engineer"
// Output:
// Argument count: 3
// args[0] = "Alice Smith"      ← treated as one argument
// args[1] = "30"
// args[2] = "Senior Engineer"  ← treated as one argument

// ── No arguments: ────────────────────────────────────────────────────
// java ShowArgs
// Output:
// Argument count: 0
// (no args[] lines printed)

// ── Key facts: ───────────────────────────────────────────────────────
// args is NEVER null — it is an empty array when no args are given.
// Every element is a String — types must be parsed manually.
// The program name itself is NOT included in args.
// Arguments are split by whitespace in the shell.
// Quoted strings with spaces become a single argument.

Accessing and Validating Arguments

Accessing args without checking args.length causes an ArrayIndexOutOfBoundsException when fewer arguments are provided than expected. Always validate the argument count before accessing specific indices, and provide a usage message that tells the user what arguments the program expects.
Java
// ── Basic validation — check count before accessing: ─────────────────
public class Greeter {
    public static void main(String[] args) {

        // Validate argument count:
        if (args.length == 0) {
            System.out.println("Usage: java Greeter <name>");
            System.exit(1);     // non-zero exit code signals error
        }

        String name = args[0];
        System.out.println("Hello, " + name + "!");
    }
}
// java Greeter Alice   → Hello, Alice!
// java Greeter         → Usage: java Greeter <name>

// ── Multiple required arguments with detailed usage: ──────────────────
public class Calculator {
    public static void main(String[] args) {

        if (args.length != 3) {
            System.err.println("Usage: java Calculator <num1> <operator> <num2>");
            System.err.println("Example: java Calculator 10 + 5");
            System.err.println("Operators: + - * /");
            System.exit(1);
        }

        double num1;
        double num2;

        // Validate that num1 and num2 are valid numbers:
        try {
            num1 = Double.parseDouble(args[0]);
        } catch (NumberFormatException e) {
            System.err.println("Error: '" + args[0] + "' is not a valid number.");
            System.exit(1);
            return;     // keeps compiler happy — exit() stops execution
        }

        try {
            num2 = Double.parseDouble(args[2]);
        } catch (NumberFormatException e) {
            System.err.println("Error: '" + args[2] + "' is not a valid number.");
            System.exit(1);
            return;
        }

        String operator = args[1];
        double result;

        switch (operator) {
            case "+"  -> result = num1 + num2;
            case "-"  -> result = num1 - num2;
            case "*"  -> result = num1 * num2;
            case "/" -> {
                if (num2 == 0) {
                    System.err.println("Error: division by zero.");
                    System.exit(1);
                    return;
                }
                result = num1 / num2;
            }
            default -> {
                System.err.println("Error: unknown operator '" + operator + "'.");
                System.err.println("Supported: + - * /");
                System.exit(1);
                return;
            }
        }

        System.out.printf("%.2f %s %.2f = %.2f%n",
            num1, operator, num2, result);
    }
}
// java Calculator 10 + 510.00 + 5.00 = 15.00
// java Calculator 9 / 39.00 / 3.00 = 3.00
// java Calculator 10 / 0Error: division by zero.

Parsing Argument Types

All command line arguments arrive as Strings. Numbers, booleans, and other types must be explicitly parsed. Each parse method throws a NumberFormatException if the string does not represent a valid value of that type — always handle this exception to give the user a meaningful error message rather than a stack trace.
Java
public class TypeParsing {
    public static void main(String[] args) {

        if (args.length < 5) {
            System.err.println(
                "Usage: java TypeParsing <int> <double> <long> <boolean> <char>");
            System.exit(1);
        }

        // ── Parse int: ────────────────────────────────────────────────
        int count;
        try {
            count = Integer.parseInt(args[0]);
        } catch (NumberFormatException e) {
            System.err.println("Expected integer for arg 1, got: " + args[0]);
            System.exit(1); return;
        }

        // ── Parse double: ─────────────────────────────────────────────
        double price;
        try {
            price = Double.parseDouble(args[1]);
        } catch (NumberFormatException e) {
            System.err.println("Expected decimal for arg 2, got: " + args[1]);
            System.exit(1); return;
        }

        // ── Parse long: ───────────────────────────────────────────────
        long timestamp;
        try {
            timestamp = Long.parseLong(args[2]);
        } catch (NumberFormatException e) {
            System.err.println("Expected long for arg 3, got: " + args[2]);
            System.exit(1); return;
        }

        // ── Parse boolean: ────────────────────────────────────────────
        // Boolean.parseBoolean does NOT throw — returns false for anything
        // other than "true" (case-insensitive). Validate explicitly:
        String boolStr = args[3].toLowerCase();
        if (!boolStr.equals("true") && !boolStr.equals("false")) {
            System.err.println("Expected true/false for arg 4, got: " + args[3]);
            System.exit(1); return;
        }
        boolean verbose = Boolean.parseBoolean(boolStr);

        // ── Parse single char: ────────────────────────────────────────
        if (args[4].length() != 1) {
            System.err.println("Expected single char for arg 5, got: " + args[4]);
            System.exit(1); return;
        }
        char separator = args[4].charAt(0);

        // ── Use parsed values: ────────────────────────────────────────
        System.out.printf("Count:     %d%n",   count);
        System.out.printf("Price:     %.2f%n", price);
        System.out.printf("Timestamp: %d%n",   timestamp);
        System.out.printf("Verbose:   %b%n",   verbose);
        System.out.printf("Separator: '%c'%n", separator);
    }
}
// java TypeParsing 5 19.99 1717012345 true ,
// Output:
// Count:     5
// Price:     19.99
// Timestamp: 1717012345
// Verbose:   true
// Separator: ','

Flags and Named Arguments

Programs that accept many optional arguments benefit from named flags rather than positional arguments. Flags are typically prefixed with -- (long form) or - (short form). Parsing them manually with a loop is straightforward for simple programs. For complex CLIs with many options, libraries such as Apache Commons CLI or picocli provide annotation-driven argument parsing.
Java
// ── Manual flag parsing: ─────────────────────────────────────────────
// Usage: java AppConfig --host localhost --port 8080 --debug --env prod

public class AppConfig {
    public static void main(String[] args) {

        // Defaults:
        String  host    = "localhost";
        int     port    = 8080;
        boolean debug   = false;
        String  env     = "dev";

        // ── Parse flags in a loop: ────────────────────────────────────
        for (int i = 0; i < args.length; i++) {
            switch (args[i]) {
                case "--host" -> {
                    if (i + 1 >= args.length) {
                        System.err.println("--host requires a value");
                        System.exit(1);
                    }
                    host = args[++i];
                }
                case "--port" -> {
                    if (i + 1 >= args.length) {
                        System.err.println("--port requires a value");
                        System.exit(1);
                    }
                    try {
                        port = Integer.parseInt(args[++i]);
                        if (port < 1 || port > 65535) {
                            throw new NumberFormatException("out of range");
                        }
                    } catch (NumberFormatException e) {
                        System.err.println("--port must be 165535");
                        System.exit(1);
                    }
                }
                case "--debug" -> debug = true;   // boolean flag — no value
                case "--env"   -> {
                    if (i + 1 >= args.length) {
                        System.err.println("--env requires a value");
                        System.exit(1);
                    }
                    env = args[++i];
                }
                default -> {
                    System.err.println("Unknown argument: " + args[i]);
                    printUsage();
                    System.exit(1);
                }
            }
        }

        // ── Use parsed config: ────────────────────────────────────────
        System.out.printf("Host:  %s%n",  host);
        System.out.printf("Port:  %d%n",  port);
        System.out.printf("Debug: %b%n",  debug);
        System.out.printf("Env:   %s%n",  env);
    }

    private static void printUsage() {
        System.err.println("""
            Usage: java AppConfig [options]
            Options:
              --host <hostname>   Server hostname (default: localhost)
              --port <number>     Port number 1-65535 (default: 8080)
              --debug             Enable debug mode (default: false)
              --env  <name>       Environment name (default: dev)
            """);
    }
}
// java AppConfig --host myserver.com --port 9090 --debug --env prod
// Output:
// Host:  myserver.com
// Port:  9090
// Debug: true
// Env:   prod

Practical Examples

These examples show common real-world uses of command line arguments — file processing tools, configuration overrides, and batch operations where the same program runs differently based on the arguments passed at launch.
Java
// ── Example 1: Word counter tool ─────────────────────────────────────
// Usage: java WordCounter <filename> [--ignore-case]

import java.io.*;
import java.nio.file.*;

public class WordCounter {
    public static void main(String[] args) throws IOException {

        if (args.length < 1) {
            System.err.println("Usage: java WordCounter <file> [--ignore-case]");
            System.exit(1);
        }

        String  filename   = args[0];
        boolean ignoreCase = args.length > 1
            && args[1].equals("--ignore-case");

        Path path = Path.of(filename);
        if (!Files.exists(path)) {
            System.err.println("File not found: " + filename);
            System.exit(1);
        }

        long words = 0, lines = 0, chars = 0;
        for (String line : Files.readAllLines(path)) {
            lines++;
            chars += line.length();
            String[] tokens = line.trim().split("\s+");
            if (!tokens[0].isEmpty()) words += tokens.length;
        }

        System.out.printf("File:  %s%n",  filename);
        System.out.printf("Lines: %,d%n", lines);
        System.out.printf("Words: %,d%n", words);
        System.out.printf("Chars: %,d%n", chars);
        if (ignoreCase) System.out.println("(case-insensitive mode)");
    }
}
// java WordCounter notes.txt
// java WordCounter report.txt --ignore-case

// ── Example 2: Number range printer ──────────────────────────────────
// Usage: java RangePrinter <start> <end> [--step <n>]

public class RangePrinter {
    public static void main(String[] args) {

        if (args.length < 2) {
            System.err.println("Usage: java RangePrinter <start> <end> [--step <n>]");
            System.exit(1);
        }

        int start = Integer.parseInt(args[0]);
        int end   = Integer.parseInt(args[1]);
        int step  = 1;

        for (int i = 2; i < args.length - 1; i++) {
            if (args[i].equals("--step")) {
                step = Integer.parseInt(args[i + 1]);
                i++;    // skip the value on next iteration
            }
        }

        if (step <= 0) {
            System.err.println("Step must be positive.");
            System.exit(1);
        }

        StringBuilder sb = new StringBuilder();
        for (int n = start; n <= end; n += step) {
            if (sb.length() > 0) sb.append(", ");
            sb.append(n);
        }
        System.out.println(sb);
    }
}
// java RangePrinter 1 101, 2, 3, 4, 5, 6, 7, 8, 9, 10
// java RangePrinter 0 20 --step 50, 5, 10, 15, 20