Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

Java offers a variety of tools and functions to engage with user input. System.in.read() is one of the frequently used methods for reading input from the console. In this article, we’ll explore its functionality and how it can be used in Java.

2. What Is System.in.read()?

The Java method System.in.read() reads a byte from the standard input stream, usually linked to the keyboard or another source. It’s part of the System class and provides a low-level mechanism for reading byte-oriented input:

public static int read() throws IOException

Here, the method returns an integer representing the ASCII value of the character read. We need to cast the ASCII integer value to a character to see the actual value. If the end of the stream has been reached, it returns -1.

It’s important to note that System.in.read() reads only a single byte at a time. If we need to read a whole line or handle different data types, we may need to use other methods or classes such as BufferedReader or Scanner.

3. Other Input Sources

While System.in is commonly used for console input, System.in.read() can also be redirected to read from various other sources, including files, network connections, and user interfaces.

These alternative input sources enable a wide range of applications, such as reading configuration files, managing client-server communication, interacting with databases, handling user interfaces, and interfacing with external devices like sensors and IoT devices.

4. Reading a Single Character

The most straightforward use of System.in.read() is to read a single character:

void readSingleCharacter() {
    System.out.println("Enter a character:");
    try {
        int input = System.in.read();
        System.out.println((char) input);
    }
    catch (IOException e) {
        System.err.println("Error reading input: " + e.getMessage());
    }
}

Since System.in.read() throws an IOException, a checked exception, we need to handle it. In the above example, we capture the IOException and output an error message to the standard error stream.

Let’s perform a test to confirm that everything works as expected by reading a character from the input stream:

@Test
void givenUserInput_whenUsingReadSingleCharacter_thenRead() {
    System.setIn(new ByteArrayInputStream("A".getBytes()));
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    System.setOut(new PrintStream(outputStream));
    SystemInRead.readSingleCharacter();

    assertEquals("Enter a character:\nA", outputStream.toString().trim());
}

Here, we redirect System.in.read() to read from a ByteArrayInputStream. By doing so, we avoid prompting the user for input while running the test, and we can assert the console output.

5. Reading Multiple Characters

While System.in.read() reads one byte at a time, we might often need to read multiple characters. One common approach is to read continuously within a loop until a specific condition is met:

void readMultipleCharacters() {
    System.out.println("Enter characters (Press 'Enter' to quit):");
    try {
        int input;
        while ((input = System.in.read()) != '\n') {
            System.out.print((char) input);
        }
    } catch (IOException e) {
        System.err.println("Error reading input: " + e.getMessage());
    }
}

Here, we use System.in.read() inside a while loop that continues execution until we press the enter key. Let’s test the new behavior with a unit test:

@Test
void givenUserInput_whenUsingReadMultipleCharacters_thenRead() {
    System.setIn(new ByteArrayInputStream("Hello\n".getBytes()));
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    System.setOut(new PrintStream(outputStream));
    SystemInRead.readMultipleCharacters();

    assertEquals("Enter characters (Press 'Enter' to quit):\n" + "Hello", outputStream.toString().trim());
}

6. System.in.read() With Parameters

There are two other versions available for the System.in.read() method, which returns the number of bytes read from the input stream.

6.1. Read With Multiple Parameters

This method reads from the input stream and stores the data in a byte array, starting at the specified offset and continuing up to the specified length. The method may read fewer bytes than the specified length if it encounters the end of the stream:

void readWithParameters() {
    try {
        byte[] byteArray = new byte[5];
        int bytesRead;
        int totalBytesRead = 0;

        while ((bytesRead = System.in.read(byteArray, 0, byteArray.length)) != -1) {
            System.out.print("Data read: " + new String(byteArray, 0, bytesRead));
            totalBytesRead += bytesRead;
        }

        System.out.println("\nBytes read: " + totalBytesRead);

    } catch (IOException e) {
        e.printStackTrace();
    }
}

In the above example, we read bytes from the standard input stream into a byte array and print the number of bytes read and the data. We can perform a test to verify its expected behavior:

@Test
void givenUserInput_whenUsingReadWithParameters_thenRead() {
    System.setIn(new ByteArrayInputStream("ABC".getBytes()));
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    System.setOut(new PrintStream(outputStream));
    SystemInRead.readWithParameters();

    assertEquals("Data read: ABC\n" + "Bytes Read: 3", outputStream.toString().trim());
}

6.2. Read With Single Parameter

This method is another version of the System.in.read() method, which accepts an array of byte as a parameter. It internally calls System.in.read(byte[] b, int off, int len).

The method is defined with the following signature:

public static int read(byte[] b) throws IOException

7. Limitations

While System.in.read() is straightforward, it has some limitations:

  • It reads only a single byte at a time, making it less efficient for reading completed lines or handling multi-byte characters.
  • The method blocks until it receives input. It might cause the application to appear unresponsive if the user does not provide input.
  • For more complex input processing, such as handling integers or strings, it’s better to use other classes like BufferedReader or Scanner.

8. Conclusion

In this article, we looked at the System.in.read() method in Java and explored how it can be used in our application. It provides a fundamental yet powerful way to handle user input directly from the standard input stream.

By understanding its usage, handling errors, and incorporating it into more complex input scenarios, we can create interactive and user-friendly applications.

As always, the full source code is available over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.