Printing Output and Reading Input

Print with IO.println and System.out, format numbers safely, keep errors on stderr, and read piped input line by line.

SyntaxIO.println("value: " + value); String line = IO.readln(); // null at end of input System.out.printf("%-10s %5d%n", name, count); System.err.println("warning: ...");

Java 25 adds the java.lang.IO class for simple console work. It sits on top of System.out and System.in, which you will still see everywhere.

CallWhat it does
IO.println(x)Prints x and a line separator to standard output.
IO.print(x)Prints without a line separator.
IO.readln()Reads one line from standard input; returns null at end of input.
IO.readln("Name: ")Prints the prompt, then reads a line.
System.out.printf(...)Formatted output, like String.format.
System.err.println(x)Prints to standard error.

stdout and stderr

Standard output is for the program's result; standard error is for diagnostics. In a container both are collected as logs, but shell pipes and many tools only pass stdout on. A script that prints warnings to stdout corrupts the CSV or JSON another program reads from it. Inside a real service, use a logger rather than println: it adds levels, timestamps and structure, and you can turn it down without a redeploy.

Formatting

printf, String.format and "...".formatted(...) share the same format syntax: %s for any value, %d for whole numbers, %.2f for two decimal places, %-12s to left-align in a 12-character column, %n for the platform's line separator. Formatting uses the default locale, so %.2f prints 59,97 on a German machine. For anything a machine will read, pass Locale.ROOT.

Reading input

Command-line tools read input line by line until readln() or BufferedReader.readLine() returns null, which means the input is finished: the pipe closed or the user pressed Ctrl+D (Ctrl+Z on Windows). Treat each line as untrusted: strip it, check it, and handle the number that fails to parse instead of letting the exception end the program.

Scanner reads tokens and numbers, which is handy, but its number parsing also depends on the locale, and mixing nextInt() with nextLine() leaves the rest of the line behind. For line-based input, read whole lines and parse them yourself.

The examples replace System.in with fixed text so they run without typing; on your machine you would pipe the data in, for example printf 'GET /users\n' | java Main.java.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Write a command-line tool that reads IDs from a pipe and prints one result per line for the next command.
  • Send progress and warnings to stderr so a script's stdout stays valid CSV or JSON.
  • Print aligned timing tables while profiling endpoints locally before adding proper metrics.

More examples

Process piped input line by line

readln() returns null once the input ends, which is the loop's stop condition. Blank lines are skipped instead of crashing on a missing second part.

Example · java
void main() {
    // Stands in for: printf 'GET /users\nPOST /orders\n\nDELETE /users/7\n' | java Main.java
    System.setIn(new ByteArrayInputStream(
        "GET /users\nPOST /orders\n\nDELETE /users/7\n".getBytes(StandardCharsets.UTF_8)));

    int count = 0;
    String line;
    while ((line = IO.readln()) != null) {
        if (line.isBlank()) {
            continue;
        }
        String[] parts = line.strip().split(" ", 2);
        IO.println(parts[0] + " -> " + parts[1]);
        count++;
    }
    IO.println(count + " requests");
}

// Output:
// GET -> /users
// POST -> /orders
// DELETE -> /users/7
// 3 requests

BufferedReader with bad input handled

BufferedReader is the classic way to read lines and is what you will find in older code. strip() removes the surrounding spaces that would otherwise make parseInt fail, and the catch keeps one bad line from ending the run.

Example · java
void main() throws IOException {
    System.setIn(new ByteArrayInputStream("8080\n  9090 \nabc\n".getBytes(StandardCharsets.UTF_8)));

    var reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
    for (String line = reader.readLine(); line != null; line = reader.readLine()) {
        try {
            int port = Integer.parseInt(line.strip());
            IO.println("valid port: " + port);
        } catch (NumberFormatException e) {
            IO.println("skipped: " + e.getMessage());
        }
    }
}

// Output:
// valid port: 8080
// valid port: 9090
// skipped: For input string: "abc"

Fix the locale for machine-readable numbers

Scanner and String.format both follow a locale, and on a server the default comes from the machine's settings: with a German default, %.2f prints 59,97 and nextBigDecimal expects a comma. Fix it to Locale.ROOT for files, logs and API payloads, and use a specific locale only for text shown to people.

Example · java
void main() {
    var scanner = new Scanner("3 19.99 widget").useLocale(Locale.ROOT);
    int quantity = scanner.nextInt();
    BigDecimal price = scanner.nextBigDecimal();
    String item = scanner.next();

    BigDecimal total = price.multiply(BigDecimal.valueOf(quantity));
    IO.println(quantity + " x " + item + " = " + total);

    // Locale.ROOT always uses '.' as the decimal separator, whatever the server's settings.
    // %.2f formats the BigDecimal directly; converting money to double first would lose exactness.
    IO.println(String.format(Locale.ROOT, "{\"total\":%.2f}", total));
}

// Output:
// 3 x widget = 59.97
// {"total":59.97}

Discussion

  • Be the first to comment on this lesson.