There are lots of articles about file handling in Java. However, a lot of them seem to be out-of-date, given the advent of New IO (NIO) and the enhancements in Java 1.7. So, I sat down with the J2SE 7 JavaDoc, and put together this guide to how to do modern Java file I/O.
Step 0
Before you start dealing with file streams, see if Java NIO will do all the work for you. If you just need to read the lines of text from the file into memory, the Files
class has a convenience method readAllLines()
that will do the job as a one-liner.
If you need to do something more complicated, like obtain a file data stream you can parse to a parser, or parse files that might be bigger than available memory, then continue on…
Step 1
Decide whether you want file input, file output, or files you can both read and write.
If you want input or output, you also have to decide whether you want the files to be binary data or text.
Direction | Type | What to obtain | How to obtain it |
---|---|---|---|
Input | Binary | InputStream | Files.newInputStream(Path) |
Input | Text | Reader | Files.newBufferedReader(Path) |
Output | Binary | OutputStream | Files.newOutputStream(Path) |
Output | Text | Writer | Files.newBufferedWriter(Path) |
Both | Binary | SeekableByteChannel | Files.newByteChannel(Path, ...) |
These are the NIO (new IO) methods for Java 1.7, and they all take Path objects as arguments. To get a Path object for a file:
Path path = FileSystems.getDefault().getPath("file.txt");
So your code would look like:
Path path = FileSystems.getDefault().getPath("file.txt");
Reader rdr = Files.newBufferedReader(path);
Note use of the abstract Reader
class. The object will actually be a BufferedReader
wrapping a FileReader
wrapping an InputStream
, but by using just the abstract type the code can be reused with any kind of reader.
You can also use the older Java I/O classes, which are your only choice for Java older than 1.7:
Direction | Type | What to obtain | How to obtain it |
---|---|---|---|
Input | Binary | InputStream | new InputStream(File) |
Input | Text | Reader | new BufferedReader(new InputStreamReader(new InputStream(File))) |
Output | Binary | OutputStream | new OutputStream(File) |
Output | Text | Writer | new BufferedWriter(new OutputStreamWriter(new OutputStream(File))) |
Both | Binary | RandomAccessFile | new RandomAccessFile(File) |
For the older I/O classes, you need a File
object. If you’re using a current version of Java, you can turn a Path
object into a file object by calling its getFile()
method. Or for any version of Java, you can construct a File
object from a path string via its normal constructor.
As you might have noticed, getting Reader
and Writer
objects in old I/O is a pain. You might be tempted to use the FileReader
and FileWriter
classes, which make it convenient to go straight from a File
to a Reader
or Writer
. Unfortunately, FileReader
and FileWriter
use the default character encoding for the Java platform; and sadly, that’s the legacy UTF-16 encoding, which is almost certainly not what you want unless you’re running Windows, and possibly not even then. Yes, you can change the encoding, but that requires a command-line argument to the Java VM at run time, which is ugly.
Step 2: Buffering
If you have a SeekableByteChannel or RandomAccessFile, it’s going to be binary and unbuffered and rather inconvenient, so you can stop reading at this point.
If you have a binary InputStream
or OutputStream
at this point, decide if you want buffering for your binary data.
If you do, wrap your Stream in a BufferedOutputStream
or BufferedInputStream
as appropriate.
If you have a text Reader
or Writer
, Java NIO will have automatically given you a buffered one. That’s the right decision, so I included the buffering in the old I/O table for step 1. You really don’t want to do unbuffered text I/O, particularly not with UTF-8 text.
Step 3: Convenience
If you have a Writer
, you’ll probably want to wrap it in a PrintWriter
to make life easier.
If you have a Reader
, you’ll probably want to parse the input in some way. I recommend investigating the Scanner
class.
Things to avoid
Here’s a list of some I/O classes to avoid using, why you should avoid them, and what to use instead:
Class | Why you shouldn’t use it | What to use instead |
---|---|---|
StringBufferInputStream | It doesn’t convert characters properly. | StringReader |
LineNumberInputStream | It doesn’t convert characters properly. | LineNumberReader |
FileReader | It reads UTF-16. | InputStreamReader |
FileWriter | It writes UTF-16. | OutputStreamWriter |