Reading Input in Java
System.in, BufferedReader, and Scanner Every Java beginner eventually asks the same question: "Okay, I can print output - how do I actually read something the user types?" Java gives you a few ways to do this, each with trade-offs. Let's go through them in the order most people discover them. A quick trivia detour: what is println, really? Before we get to input, a fun fact that doubles as a common interview question: what class does println actually belong to? The answer is PrintStream. When you write System.out.println(...), you're calling println on System.out - an object of type PrintStream. And System.out itself is declared inside the System class as: public static final PrintStream out = null; Yes, literally initialized to null in the source - it only becomes a working object because native code inside the JVM reassigns it during startup, before your program ever runs. It's a nice example of how much setup happens before main even starts. The most basic way: System.in.read() The lowest-level way to read input is System.in.read(), which reads a single byte and returns it as an int: public class StoreApplication { public static void main(String[] args) throws IOException { System.out.println("Enter a Number: "); int number = System.in.read(); System.out.println("You entered: " + (number - 48)); } } The - 48 looks strange until you remember that read() returns the ASCII value of the character typed, not the number itself. The ASCII value of the character '0' is 48, '1' is 49, and so on - so subtracting 48 converts the character code back into the actual digit. This only works because the example assumes a single-digit number. read() reads one byte at a time, so the moment a user types 42, this approach breaks - you'd read '4' and stop, never seeing the 2. For anything beyond a single digit, you need something better. A more practical option: BufferedReader import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class StoreApplication { public static void main(String[] args) throws IOException { System.out.println("Enter a Number: "); InputStreamReader reader = new InputStreamReader(System.in); BufferedReader bfNumber = new BufferedReader(reader); int num = Integer.parseInt(bfNumber.readLine()); System.out.println("You entered: " + num); } } BufferedReader reads a full line of text at once via readLine(), which you then convert to the type you actually need (Integer.parseInt for an int, in this case). It's not limited to console input either - the same BufferedReader can read from a file, a network connection, or anything else wrapped in a Reader. Don't forget to close it BufferedReader is a resource - like a file handle or a database connection - and it's the developer's responsibility to close it once you're done, or you risk leaking it: import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class StoreApplication { public static void main(String[] args) throws IOException { System.out.println("Enter a Number: "); InputStreamReader reader = new InputStreamReader(System.in); BufferedReader bfNumber = new BufferedReader(reader); int num = Integer.parseInt(bfNumber.readLine()); System.out.println("You entered: " + num); bfNumber.close(); } } Manually calling close() like this works, but it has a sharp edge: if an exception happens between opening the reader and this close() call, the resource never gets released. That's exactly the problem try-with-resources was introduced to solve - I covered that in detail in my try-with-resources article, which is worth reading right after this one. The newer option: Scanner Before Java 5, BufferedReader was really the only practical option for reading console input. Java 5 introduced the Scanner class, which is friendlier for simple cases: import java.util.Scanner; public class StoreApplication { public static void main(String[] args) { System.out.println("Enter a Number: "); Scanner reader = new Scanner(System.in); int num = reader.nextInt(); System.out.println("You entered: " + num); reader.close(); } } No manual parsing needed - nextInt() reads and converts in one step. Scanner has similar methods for other types too: nextLine(), nextDouble(), nextBoolean(), and so on. So which one should you actually use? System.in.read() - essentially never, outside of understanding how input works at a low level. Too limited for real use. Scanner - great for quick scripts, small programs, and competitive-programming-style input where convenience matters more than raw performance. BufferedReader - generally faster for reading large volumes of input, and more flexible since it isn't tied to parsing specific types the way Scanner is. It is a common choice in performance-sensitive code and when reading from files. Both are resources and both need closing - and in both cases, try-with-resources is the safer way to do that instead of a manual close() call. Wrapping up Reading input in Java looks trivial once you've done it a few times, but each option - System.in.read(), BufferedReader, Scanner - represents a different point on the trade-off between low-level control, performance, and convenience. Knowing all three (and why Scanner eventually replaced BufferedReader for a lot of everyday use cases) is a small thing that signals you understand more than just the syntax. Which one do you reach for by default - Scanner or BufferedReader? Let me know in the comments. Top comments (0)
Comments
No comments yet. Start the discussion.