How to Write to a File in Java

Lewis D.

The Problem

You need to write data to a file in Java. How can you do this?

The Solution

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.

  • For a simple, one-time execution, you can use the Files class to write textual data to a file.
  • If your application is designed to write to a file many times, then a BufferedWriter will help eliminate the overhead of slow write operations.
  • If you are writing non-textual data to a file, you could consider using a FileOutputStream optionally wrapped in a BufferedOutputStream.

The Files Class

To 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.

The FileWriter Class

The 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.

The BufferedWriter Class

The 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.

The FileOutputStream and BufferedOutputStream Classes

In 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.

Further Reading

Loved by over 4 million developers and more than 90,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.

Share on Twitter
Bookmark this page
Ask a questionJoin the discussion

Related Answers

A better experience for your users. An easier life for your developers.

    TwitterGitHubDribbbleLinkedinDiscord
© 2024 • Sentry is a registered Trademark
of Functional Software, Inc.