In this chapter, you will learn how to use Java to interact with the computer’s file system. We will use Java to read and save text files on the user’s computer. This will enable you to create programs which can save and load information for later use.
Files
A file is a piece of information stored on the user’s device in the form of binary digits, or bits. Files are stored on a device called a drive (mechanical, solid state, flash, DVD, tape, etc.). With the help of the operating system, the computer’s processor will load a file into RAM (temporary memory), where it can be accessed and manipulated by programs running on the PC.
Computers use different programs to interpret the contents of files saved using different file formats.
For your reference, here are standard file size conversions.
- One Bit = Smallest Possible Unit
- One Byte = 8 Bits
- One Kilobyte = 1024 Bytes or 8192 Bits
- One Megabyte = 1024 Kilobytes
- One Gigabyte = 1024 Megabytes
- One Terrabyte = 1024 Gigabytes
- One Petabyte = 1024 Terrabytes
When you write a document and save it, the file is encoded (literally – turned into binary code) into a particular file format such as docx, txt, or pdf. These files are ultimately different arrangements of bits. Then, when you open the file, the program responsible for that particular file format is used to decode the file and make it into something you can work with visually on your computer screen.
Files can vary widely in size depending on the amount of information they represent. A 2 hour long 4K movie, for example, will be orders of magnitude larger in size than a small image file. Additionally, if you save a single image using two different file formats, the sizes may be different. This may be because one format discards some of the information (lower quality), or one file format is simply employing more advanced compression algorithms than the other.
Plain text documents are among the simplest types of files, and the type we’ll be working with in this guide. We will be working with standard existing APIs and file formats in this series.
These conversions follow general metric conventions, however, remember each step up is 1024 times larger than the previous, not 1000 like it would be if you were, say, going from one kilometer to meters (1km = 1000m).
File Scanner: Loading a File
In this first exercise, we will use Java’s Scanner object to read the contents of a file. This is the same object we use to read input from the console when users type things. But now, rather than the system input stream (System.in), we will be giving it a file to open. We will also be introducing the File object, which can be used to represent a file in our code.
For demonstration, let’s load a text file the easiest way possible and print its contents to the screen.
Begin by creating a new Java Project with a main method. Then, make a file called “test.txt” and type whatever you want in it. For my test file, I wrote the sentence “This is a test.” repeatedly across 3 lines.
Where you save test.txt is very important. The file will be loaded from the same directory the program is running from, or using as it’s working directory. Usually, this is the project’s top level folder. However, in some cases it may be different.
To determine where you place test.txt, add the following line to the main method of your program and run the program.
System.out.println(System.getProperty("user.dir"));
Code language: CSS (css)
In my situation, the program returns this value “C:\Users\Kevin\IdeaProjects\FileScanner”
So I need to save my test.txt file to my “FileScanner” folder, and NOT “FileScanner\src” where the program’s source code is located.
Now import the following:
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.io.File;
Code language: CSS (css)
Now it’s time to create a try/catch block. Whenever you open a file, we’re required to surround the action in a try/catch block. This is because opening a file can throw a FileNotFound exception.
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.io.File;
public class Main {
public static void main(String[] args) {
//Try to open a file
try{
} catch (FileNotFoundException e) {
System.out.println("Couldn't find the file!");
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
Next, we need to create a File object and a Scanner object. The File object should be declared first, and will be given the argument of the file name when it’s initialized. Then, the scanner needs to open that file. So pass the file object we created to the new Scanner. This should all be done in the try block!
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.io.File;
public class Main {
public static void main(String[] args) {
//Try to open a file
try{
//make the test file
File myTestFile = new File("test.txt");
//open the scanner
Scanner myFileScanner = new Scanner(myTestFile);
} catch (FileNotFoundException e) {
System.out.println("Couldn't find the file!");
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
Now, use the scanner object to iterate through each line of the file and print each line to the console. This should be a while block in the try block after we open the file.
while (myFileScanner.hasNextLine()){
String outputLine = myFileScanner.nextLine();
System.out.println(outputLine);
}
Code language: JavaScript (javascript)
Finally, we can close the scanner using the .close method and test the program.
When complete, this is what my example code looks like for reading a text file:
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.io.File;
public class Main {
public static void main(String[] args) {
System.out.println("I am trying to load test.txt from: " + System.getProperty("user.dir"));
try {
//make the test file
File myTestFile = new File("test.txt");
//open the scanner
Scanner myFileScanner = new Scanner(myTestFile);
//iterate through each line in the file
while (myFileScanner.hasNextLine()) {
//print he contents to the screen
String outputLine = myFileScanner.nextLine();
System.out.println(outputLine);
}
//close the scanner when done
myFileScanner.close();
} catch (FileNotFoundException e) {
//if we run into a problem, just throw another exception
System.out.println("Couldn't find the file!");
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
This is a test. This is a test. This is a test.
Remember, if for any reason you get a FileNotFound exception, double check that test.txt is in the same directory given by System.getProperty("user.dir")
.
Saving Text (FileWriter)
So now that we’ve loaded a file, let’s do the opposite and try to save a file. We will achieve this using the FileWriter object.
A FileWriter‘s purpose is fairly self-explanatory. It writes files. More specificially, it’s a type of OutputStreamWriter designed just for saving text files. You can think of it like “System.out.println()” for text files. We must import it to use it.
import java.io.FileWriter;
Code language: CSS (css)
Just as we had to place the code reading a text file within a try/catch block, we must do something similar for a FileWriter. FileWriters can throw IOExceptions, so that’s the type of exception we’ll catch (an IOException is a more generic input/output steam exception type).
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
//figure out where this file is going
System.out.println("I am trying to save my-new-file.txt to: " + System.getProperty("user.dir"));
try {
//make the test file
FileWriter myFileWriter = new FileWriter("my-new-file.txt");
} catch (IOException e) {
System.out.println("Error saving.");
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
On line 12 above, I created a new FileWriter object. Its argument is the name of the file being saved. I’m going to call my file the unoriginal, “my-new-file.txt” and it will be saved to the same directory (user.dir) the project runs from.
Finally, let’s try writing some text to this file. This is achieved with the FileWriter.write() method. We can simply pass a string, or array of chars, to this method and it will be written to the file.
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
//figure out where this file is going
System.out.println("I am trying to save my-new-file.txt to: " + System.getProperty("user.dir"));
try {
//make the test file
FileWriter myFileWriter = new FileWriter("my-new-file.txt");
//write something to this
myFileWriter.write("This is the text getting saved.");
//close the file writer
myFileWriter.close();
System.out.println("Done.");
} catch (IOException e) {
System.out.println("Error saving.");
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
When I run my program, this is the output:
I am trying to save my-new-file.txt to: C:\Users\Kevin\IdeaProjects\FileScanner Done.
To see if it worked, I simply go to that directory and open the newly created file.

Success!
You can also pass File objects to a FileWriter in order to save them.
Note that Scanners and FileWriters are not necessairly the most efficient way of saving and loading text files.
Buffered Readers and Writers
We can use the BufferedReader and BufferedWriter classes in combination with a FileReader or FileWriter in order to more efficiently load and save text files.
First, let’s establish how to use BufferedReaders and BufferedWriters. Then, I’ll explain the differences between them and Scanners/FileWriters.
BufferedReader
We can use a BufferedReader similarly to how we used a Scanner earlier to read text from a file. Examine this code:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
//figure out where this file is going
System.out.println("I am trying to open test.txt with a BufferedReader from " + System.getProperty("user.dir"));
try {
//BufferedReaders require a FileReader to work
FileReader fr = new FileReader("test.txt");
BufferedReader br = new BufferedReader(fr);
//While ready, print each line from the BufferedReader
while(br.ready()){
System.out.println(br.readLine());
}
//Close both when complete
br.close();
fr.close();
} catch (IOException e) {
System.out.println("Error saving.");
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
I am trying to open test.txt with a BufferedReader from C:\Users\Kevin\IdeaProjects\FileScanner This is a test. This is a test. This is a test.
First, I had to create a FileReader and tell it which file I wanted to open (line 12). This is because a BufferedReader requires a FileReader to work.
The FileReader class loads a stream of chars. However, if you print the contents of the file read using just the FileReader’s .read() method, you will only see the numerical value of each char. The FileReader class also doesn’t have a readLine() method to read an entire line at once.
The BufferedReader is used efficiently to read character streams using a buffer. They can read a chunk of characters all at once and quickly return them. This is more efficient than reading the characters and parsing them individually.
Like any stream, I close them both when I’m done with them using the .close()
method.
The read Method
Change .readLine() from line 16 in the previous example to use the .read() method instead, which reads a single char rather than an entire line. Then, see what happens.
Since the .read() method returns a single character, your program will now print out the numerical value of each character read.
I am trying to open test.txt with a BufferedReader from C:\Users\Kevin\IdeaProjects\FileScanner 84 104 105 115 32 105 ...(continued)
To print the file using the .read() method instead of readLine, you could print each character individually, and cast each number back to a char.
while(br.ready()){
System.out.print((char)br.read());
}
Code language: PHP (php)
BufferedWriter
As far as the code goes, we can use BufferedWriter basically the same way we used the FileWriter. But like a BufferedReader, it’s capable of working with buffered “chunks” of chars at once. Therefore, it’s preferred to a FileWriter whenever possible.
We initialize a BufferedWriter object with a FileWriter, and then we can use the .write(str)
method to write a string, just as we did with the FileWriter.
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
//figure out where this file is going
System.out.println("I am trying to save my-new-file.txt to: " + System.getProperty("user.dir"));
try {
//make the test file
FileWriter myFileWriter = new FileWriter("my-buffered-test-save.txt");
//write something to this
BufferedWriter myBuffWriter = new BufferedWriter(myFileWriter);
myBuffWriter.write("This is the text getting saved with the BufferedWriter.");
//close the file writer
myBuffWriter.close();
myFileWriter.close();
System.out.println("Done.");
} catch (IOException e) {
System.out.println("Error saving.");
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
Running the above code should produce a new file “my-buffered-test-save.txt” in the project directory.
New Lines
On many systems, the newline character is \n
. However ,this may not always be the case. It can vary by system.
For this reason, according to the Java documentation, you should use the .newLine() method provided by the BufferedWriter whenever you want to create new lines in files, rather than just using \n
to end each line.
try {
//make the test file
FileWriter myFileWriter = new FileWriter("many-lines.txt");
//write something to this
BufferedWriter myBuffWriter = new BufferedWriter(myFileWriter);
myBuffWriter.write("This is.");
myBuffWriter.newLine();
myBuffWriter.write("a line with");
myBuffWriter.newLine();
myBuffWriter.write("multiple lines saved.");
//close the file writer
myBuffWriter.close();
myFileWriter.close();
System.out.println("Done.");
} catch (IOException e) {
System.out.println("Error saving.");
throw new RuntimeException(e);
}
Code language: JavaScript (javascript)
The above code will output a new text file spread out across 3 lines.
When To Use Scanners
Given the efficiency advantages of buffered readers over Scanners, when might you still prefer to use a Scanner?
Consider this, I have this test file “text-and-numbers.txt” which contains… text and numbers.
This is a line of text
This is the number 500
My name is Kevin
This is the number 707
Code language: plaintext (plaintext)
I only want to get the ints from this file and print them to the screen. How can I achieve this while reading the file?
Well, I can use the nextInt() method from the Scanner class.
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
try {
//open text-and-numbers.txt with a scanner
File openFile = new File("text-and-numbers.txt");
Scanner fileScan = new Scanner(openFile);
//while there is info to be read
while (fileScan.hasNext()) {
//go to the next piece of info (advance the scanner)
fileScan.next();
//if the next piece of info is an int
if (fileScan.hasNextInt()) {
//print out that int
System.out.print(fileScan.nextInt());
}
}
fileScan.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
The above code outputs:
500707
Remember, the Scanner can parse each piece of information in the file individually. If the next piece of information is an int, it can extract the value as an int.Buffered Readers cannot do this – they always return the next char as a char, or the next line as a String.
In summary, use buffered file readers and writers wherever possible. Whenever you’re saving and loading strings of text. You should use Scanners when you want to extract (parse) information directly from the stream as a certain data type other than char or String. Using a Scanner is computationally more expensive than a BufferedReader.
Calculating Performance Difference
I generated a thousand-line long dummy text document using loremipsum.io.
You can use Java’s Instant and Duration classes from the time package to roughly measure the time it takes for code to start and finish executing.Basic Time Test
import java.time.Instant;
import java.util.Scanner;
public class MyClass {
public static void main(String[] args) {
Instant start = Instant.now();
//code to test
Instant end = Instant.now();
Duration totalTime = Duration.between(start, end);
System.out.println("That took : "+ totalTime.toMillis() +" milliseconds");
}
}
Code language: JavaScript (javascript)
I then tested how long it took to read the long file using a Scanner. All I’m doing is reading each line and assigning it to an arbitrary String that isn’t displayed.
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
try {
//open text-and-numbers.txt with a scanner
Instant start = Instant.now();
File openFile = new File("thousand-lines.txt");
Scanner fs = new Scanner(openFile);
while(fs.hasNextLine()){
String s = fs.nextLine();
}
fs.close();
Instant end = Instant.now();
Duration totalTime = Duration.between(start, end);
System.out.println("That took: "+ totalTime.toMillis() +" milliseconds");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Code language: JavaScript (javascript)
That took: 35 milliseconds
I ran the test file several times to make sure I got consistent results. On my PC, it took 34-35ms to load each line of the file using a Scanner. If you run the code your result will likely be different.
Next, I did the same test but with a BufferedReader instead.
Instant start = Instant.now();
File openFile = new File("thousand-lines.txt");
FileReader fr = new FileReader(openFile);
BufferedReader buffRead = new BufferedReader(fr);
while(buffRead.ready()){
String s = buffRead.readLine();
}
buffRead.close();
fr.close();
Instant end = Instant.now();
Duration totalTime = Duration.between(start, end);
System.out.println("That took: "+ totalTime.toMillis() +" milliseconds");
Code language: JavaScript (javascript)
That took: 6 milliseconds
This time it took 6-7 milliseconds, a significant decrease from the 35 it took before! If I did this with an even larger file, the difference would be even greater. Always remember, computing power costs money. If there is a more efficient way to achieve something, do it!
User Config Assignment
For this assignment, we’ll apply what we’ve learned so far. Create a program that allows a user to enter their name, age, and favorite color. It will save this information to a file on the user’s computer, and load it the next time they enter the program. They can then update this information, and the config file will be changed. Rather than just displaying the entire text file to the end user, we will extract and save these values. Their name and favorite color will be saved as Strings, and their Age is going to be an int.
There are several ways to do this, but I would encourage you to use multiple methods.
Here is what the program should look like when executed the first time.
No config found! You must enter some settings. Enter your name: Kevin O Enter your age: 30 Enter your favorite color: Purple Your settings have been saved!
On subsequent executions, the output is different.
Welcome back, Kevin O You are 30 years old and your favorite color is Purple Would you like to change these settings? 1)yes 2)exit 1 Enter your name: Bobert ...
- Static variables
- String name, color
- int age
- Scanner System.in
- Main Method – Contains the main logic
- Check if config file exists. If it does, display the information and allow the user to update. If it doesn’t, create it.
- Note that the
File.exists()
method can see if a file exists!
- enterSettings() Method
- Allows the user to enter the name, age, and favorite color to be stored in memory (the static program vars)
- loadSettings() Method
- Loads the settings back into the variables from the config file
- saveSettings() Method
- Saves the variables into the config file
I have created a template to help you get started. The main method is done and the variables are declared. You need to complete the remaining methods.
There are other tools in the Java API we can use to easily save and retreive information. We will discuss them in later chapters. Additionally, you can save and load files using any file extension you wish, provided you’re using the right tools to encode and decode the files. For example, our config file could have been saved as .cfg if we wanted, or .ini, or .info. A file extension is just a part of the file’s name. You can make up file extensions.
It’s best practice to use common file extensions where possible, unless you’re truly designing a new file format entirely. This prevents confusion and helps the operating system open the correct program for each file type. For example, you could rename “mytextfile.txt” to “mytextfile.png” – this would be a poor decision, as .png is not a UTF-8 text format. PNG is an image format. If you did this, a photo viewer would attempt (and fail) to read the text document, rather than a text editor opening like it should.