You need to write data to a file in Java. How can you do this?
If you read the Sentry answer to How to Read to a File in Java, you’ll know that there are numerous solutions to reading from files in Java. Similarly, there are many ways you can write data to a file, and certain implementations may be more suitable to your needs than others, depending on the amount and type of data you are writing to a file.
Let’s take a look at some of Java’s built-in file-writing solutions, and consider which situations they are useful in.
Files
class to write textual data to a file.BufferedWriter
will help eliminate the overhead of slow write operations.FileOutputStream
optionally wrapped in a BufferedOutputStream
.Files
ClassTo write a small simple file, the Files
class has a very intuitive approach using the write()
method.
In this method, you pass the data encoded as bytes and a path to the file, and the Files
class handles the file-writing process as shown below:
public void writeToFile(Path path, String content) { try { Files.write(path, content.getBytes()); } catch (IOException e) { // exception handling ... } }
Since Java 11, a writeString()
method is available to handle passing in a String
directly, bypassing the need to first encode the String
to bytes.
FileWriter
ClassThe FileWriter
class has been around since Java 1.1, and it’s a convenient class for writing character-oriented files. The syntax for FileWriter
is concise and intuitive.
An advantage of using the FileWriter
class is that it has a boolean
parameter append
, which can be supplied when invoking a new class. If append
is true, then the data that is written will be appended to the end of the file, instead of overwriting the file entirely.
Here’s a code example to write and append to a file:
public void writeToFile(String path, String content, boolean append) { try (FileWriter fileWriter = new FileWriter(path, append)) { fileWriter.write(content); } catch (IOException e) { // exception handling ... } }
In this example, the try-with-resources construct is used, which negates the need to explicitly close the writer when we are finished with it. Note that if you did not use the try-with-resources, you will need to remember to close the writer yourself once you’re finished writing.
BufferedWriter
ClassThe BufferedWriter
class, as its name suggests, supports buffering. When the write()
method is called, the data is written to the BufferedWriter
’s internal buffer, and persisted to file only when this internal buffer is completely filled. This buffering process reduces the number of expensive write operations the code needs to make, so the BufferedWriter
is most suited to writing large files efficiently.
Here’s an example using the BufferedWriter
class:
public void writeToFile(String path, String content, boolean append) { try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path, append))){ bufferedWriter.write(content); } catch (IOException e) { // exception handling ... } }
In this example, the BufferedWriter
is created with the try-with-resources construct and when the try-with-resources automatically closes the BufferedWriter
, the FileWriter
which the BufferedWriter
writes to will also be closed. So in this case, the FileWriter
will be closed at the same time the BufferedWriter
instance is closed.
FileOutputStream
and BufferedOutputStream
ClassesIn the three approaches we’ve explored so far, we have focussed on writing character-oriented data to file. However, the FileOutputStream
class can be used to write streams of byte-oriented data, such images or Unicode data, and other data that can’t be represented as text. Of course, you can write textual data to file using a FileOutputStream
by simply encoding the String
text to bytes.
public void writeToFile(String path, byte[] content) { try (FileOutputStream outputStream = new FileOutputStream(path)) { outputStream.write(content); } catch (IOException e) { // exception handling ... } }
Similarly, the BufferedOutputStream
class adds buffering to the underlying OutputStream
for increased efficiency.
In our FileOutputStream
example above, we could wrap the underlying OutputStream
in a BufferedOutputStream
to buffer all bytes until they are written.
public void writeToFile(String path, byte[] content) { try(BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(path))) { bufferedOutputStream.write(content); } catch (IOException e) { // exception handling ... } }
There are no set criteria for when a BufferedOutputStream
should be used, so you should consider buffer size, disk speed, and network speed (if applicable) and how these factors influence potential performance gains or losses before settling on this option for writing to a file.