So far we have talked a lot about how control flow and basic variables types / operations. Now let’s talk about how we can take input and provide output from our Java programs.
Console I/O
Over the course of some of our earlier lessons we have been outputting to the console through System.out.println() calls. We can also receive input from the console through something called a Scanner. It would looks something like this:
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
Now, in this case it can be hard to tell what exactly happened from the console because we aren’t outputting anything to indicate the program state. In order to add a little more context we can build off of our Hello World program, except instead of simply outputting “Hello World!” we can take a specific name in from our Scanner.
System.out.println("What is your name?");
Scanner scanner = new Scanner(System.in);
String name = scanner.nextLine();
scanner.close();
System.out.println("Hello " + name + "!");
This will give us the following output (substituting the name that you entered.)
What is your name?
Ben
Hello Ben!
Closing your Streams
Some of you may have noticed the addition of a line for:
scanner.close();
This is needed in order to prevent a Memory Leak.
I’ll keep this brief for the sake of a beginner’s tutorial but the basic idea is that opening a scanner and similar I/O “streams” requires system resources, and if you do not close those resources when you are done with them then those resources will “leak” and the memory used to allocate those resources will not be reclaimed.
File Input
In addition to taking input / output from the console, we can also read and write from files.
To expand upon our “greeting” snippet from earlier, what if we decided to read in our names from a file rather than from user input through the console?
First let’s say we had a file containing a name:
Ben
Simple right?
For convenience’s sake we could simply stick it into the same folder with our source code in Eclipse. However, we are going to create a separate folder for it in Eclipse so that we can properly organize our source and our resources.
We will start by simply creating a folder within our Eclipse project for our resources.
In order for Eclipse to load from this folder we will need to go into the “Configure Build Path” option in Eclipse.
Then we will add a folder to the build path for our resources directory.
From here we can select our resources directory.
Onto the actual code, in order to actually read our file we can load it from Eclipse’s “class loader.”
String filename = InputOutput.class.getClassLoader().getResource("names.txt").getFile();
This allows us to get the filename, but in my case specifically I will have spaces in my filename since I have placed this in a “Code To Live By” project. I’ll again keep this brief so I will simply say that our spaces will be encoded as %20, so we will use something called a URLDecoder to decode this.
filename = URLDecoder.decode(filename, "UTF-8");
It is around this time that Java may complain about something called an “Unhandled exception type.” This is something I intend to cover in a future chapter, but for now we can stop Eclipse’s complaining by changing our main block to the following:
public static void main(String[] args) throws IOException {
Returning to our file, we can create something called a BufferedReader which will allow us to actually read our file.
BufferedReader reader = new BufferedReader(new FileReader(filename));
BufferedReader provides us with a nice readLine() method so we will use exactly that to read in all of our names.
for (String name = reader.readLine(); name != null; name = reader.readLine()) {
System.out.println("Hello " + name + "!");
}
Some of you may remember Java’s for loop structure from our page on Loops but just to break it down quickly:
- Our initialization reads in the first line in the file, in our case the first name.
- Our loop condition checks that our name is not null, that we not reached the end of the file.
- We are incrementing our loop variable by reading in the next line of the file.
So to put all of this together we might have something like this:
// Use the Eclipse class loader to get our names.txt file
String filename = InputOutput.class.getResource("names.txt").getFile();
// Decode our filepath to account for spaces / special characters
filename = URLDecoder.decode(filename, "UTF-8");
// Create a "Reader" for our file
BufferedReader reader = new BufferedReader(new FileReader(filename));
// Loop over each line in the file
for (String name = reader.readLine(); name != null; name = reader.readLine()) {
System.out.println("Hello " + name + "!");
}
// Close the reader to prevent memory leaks
reader.close();
File Output
But wait, there’s more! We also have the ability to write to files.
What if instead of printing our greeting we also wanted to keep a record of who we have greeted?
We can create a file to write to using something like this:
OutputStream file = new FileOutputStream("resources/records.txt");
We are using the same “resources” directory as before but now we are creating a “records.txt” file.
Then we can create a BufferedWriter similar to the BufferedReader we used earlier to read our names.txt file.
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(file, "UTF-8"));
Then in order to write we can simply call a write() method to write to the file, in our case of record of who has been greeted. We can also call a newLine() for cases in which our names.txt file will contain multiple names.
writer.write(name + " has been greeted.");
writer.newLine();
So, to put it all together we might end up with something like this (some of the earlier comments omitted for brevity:)
String filename = InputOutput.class.getClassLoader().getResource("names.txt").getFile();
filename = URLDecoder.decode(filename, "UTF-8");
BufferedReader reader = new BufferedReader(new FileReader(filename));
OutputStream file = new FileOutputStream("resources/records.txt");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(file, "UTF-8"));
for (String name = reader.readLine(); name != null; name = reader.readLine()) {
System.out.println("Hello " + name + "!");
writer.write(name + " has been greeted.");
writer.newLine();
}
reader.close();
writer.close();
You should still see the greetings from earlier in the console, but then if you were to open “records.txt” you may see something like this (again substituting for the name(s) that you entered.)
Now you have a good idea of how to use input / output in Java!