Alexander Claes
← Learning Java

Reading and Writing

A day trip into basic input and output.

Alexander Claes Alexander Claes 6 min read
Learning Java
A keyboard and translucent terminal with arrows carrying data blocks into and out of the program.

So far, a program can work with values we write directly into its source code. Input and output let it communicate with the outside world: read a name, accept a setting, and display a result.

This article focuses on console input and output, rather than reading and writing files. We will explore standard streams, user input, command-line arguments, modern Java conveniences, and a small interactive program.

The standard streams

A stream is a flow of data into or out of a program. Java provides three standard streams: System.in for input, System.out for normal output, and System.err for error messages or diagnostics.

When you run a program in a terminal, input commonly comes from the keyboard and both output streams appear on screen. They are separate channels, however: the environment can redirect them to files or connect them to another program.

System.out.println("Your order is ready.");
System.err.println("Could not load the receipt.");

Writing to System.err does not throw an exception or stop the program. It chooses a different output destination. See the System documentation for the standard streams.

Writing text: print, println, and printf

System.out.print("Hello, ");
System.out.println("Ada!");
System.out.printf("You have %d messages.%n", 3);

print leaves the cursor on the same line. println ends with a line separator. printf formats values into a template: %d stands for an integer, %s for text, and %n for a platform-appropriate line separator.

A prompt often uses print so the user can type beside it. You can call System.out.flush() to make sure buffered output is sent before waiting for input.

Reading user input with Scanner

System.in supplies bytes. Scanner provides a convenient way to read text and numbers from it. This complete example reads one line; save it as Greeting.java.

import java.util.Scanner;

public class Greeting {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("What is your name? ");
        System.out.flush();

        if (!input.hasNextLine()) {
            System.out.println("No input received.");
            return;
        }

        String name = input.nextLine();
        System.out.println("Hello, " + name + "!");
    }
}

nextLine() reads the line without its ending line separator. When input is interactive, reading may wait until the user types and presses Enter. hasNextLine() checks whether another line is available; it can also wait. When input ends, the check returns false instead of letting nextLine fail.

Use one reader for standard input. Closing a Scanner also closes its underlying input source, so these examples leave the shared System.in open. For a Scanner reading a file your code owns, closing it is normally appropriate. See the Scanner API.

Text is not automatically a number

If someone enters 25, reading the line gives you the String "25". To calculate with it, parse it into a number.

String text = "25";
int age = Integer.parseInt(text);
System.out.println(age + 1); // 26

Parsing asks Java to interpret characters as a value. Text such as "hello", or a number outside the int range, cannot be parsed as an int and throws NumberFormatException. A try/catch block lets us handle that specific failure.

try {
    int age = Integer.parseInt("hello");
    System.out.println(age);
} catch (NumberFormatException exception) {
    System.out.println("Please enter a whole number.");
}

Successful parsing is only the first check. For an age, a negative integer is valid Java input but probably invalid application data. Validate the meaning after converting the text.

The nextInt and nextLine surprise

Scanner can also parse a number directly with nextInt. However, token-reading methods and line-reading methods do different jobs: after nextInt reads a number, nextLine reads the rest of that same line, which can be empty.

// Inside a method, using an existing Scanner named input:
int age = input.nextInt();
String name = input.nextLine(); // May read an empty remainder

For a beginner-friendly line-based conversation, consistently read with nextLine and then parse the resulting text. That avoids switching between tokens and lines. It also gives you the original input to validate or explain to the user.

Reading command-line arguments

Interactive input arrives while the program is running. Command-line arguments are supplied when starting it. Java passes them to the familiar main(String[] args) method as an array of Strings.

An array is an ordered group of elements. args.length tells us how many arguments there are; args[0] accesses the first. Always check the length before accessing an element that might not exist.

public class Welcome {
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage: java Welcome <name>");
            return;
        }
        System.out.println("Welcome, " + args[0] + "!");
    }
}

Save this as Welcome.java, then run these commands in a terminal from its directory:

javac Welcome.java
java Welcome "Ada Lovelace"

The quoted name is passed as one argument, so the output is Welcome, Ada Lovelace!. The class name itself is not included in args. Numeric arguments are Strings too and require parsing before arithmetic. See Oracle’s command-line argument guide.

A note about modern Java

The examples above use the traditional class and main structure. That remains useful and works on widely used versions such as Java 17 and 21. Java 25 also provides the java.lang.IO convenience class for simple line-oriented input and output.

On Java 25 or later, this example can use the same familiar program structure:

public class ModernGreeting {
    public static void main(String[] args) {
        String name = IO.readln("What is your name? ");
        if (name == null) {
            IO.println("No input received.");
            return;
        }
        IO.println("Hello, " + name + "!");
    }
}

IO.readln(prompt) displays the prompt and reads a line. It returns null at the end of input; simply pressing Enter produces an empty String, not null. IO lives in java.lang, so it needs no explicit import.

Use IO.readln or Scanner for a given standard-input flow, rather than mixing them: readers can buffer input. If IO cannot be found, check your JDK version with java --version and your IDE’s project settings. The Java 25 IO API documents the version and behavior.

Going interactive: a small conversation

Let’s combine prompts, reading, parsing, validation, and repetition. This program asks for an age until it receives a non-negative integer, the user types quit, or input ends. Save it as AgeNextYear.java.

import java.util.Scanner;

public class AgeNextYear {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        while (true) {
            System.out.print("Your age (or quit): ");
            System.out.flush();

            if (!input.hasNextLine()) {
                System.out.println("Goodbye!");
                break;
            }

            String text = input.nextLine().trim();
            if ("quit".equalsIgnoreCase(text)) {
                System.out.println("Goodbye!");
                break;
            }

            try {
                int age = Integer.parseInt(text);
                if (age < 0) {
                    System.out.println("Age cannot be negative.");
                    continue;
                }
                long nextAge = (long) age + 1;
                System.out.println("Next year you will be " + nextAge + ".");
                break;
            } catch (NumberFormatException exception) {
                System.out.println("Please enter a whole number.");
            }
        }
    }
}

while (true) repeats the conversation. break leaves the loop; continue starts its next iteration. The catch block handles invalid numeric text and lets the loop ask again. trim() removes surrounding ordinary whitespace, and equalsIgnoreCase accepts quit regardless of letter case. The long calculation avoids overflowing when adding one to the largest int.

javac AgeNextYear.java
java AgeNextYear

Try entering hello, then -2, then 25. Each invalid entry gets a useful response; the valid one produces 26. Run it again and enter quit. Thinking through those paths is part of designing an interactive program, not an afterthought.

The mental model to keep

Input enters the program, your code interprets and validates it, and output communicates the result. Standard streams provide the channels. Scanner or IO help read text, command-line arguments provide startup values, and loops keep a conversation going. Once those roles are clear, console programs become much easier to reason about.