Channels ▼

Eric Bruno

Dr. Dobb's Bloggers

Java SE 7 - New File IO

August 29, 2011

Java SE 7 has many enhancements and new APIs. In a previous blog, I discussed the new Fork/Join framework. In this blog, I'll talk briefly about the new Java.NIO 2.0 APIs, and specifically about how much easier it is to work with files. To be clear, although NIO used to mean New IO, Oracle now says that it really stands for Non-blocking IO.

Java NIO 2.0 (sometimes referred to as NIO.2) provides comprehensive support for the filesystem. Besides enhancements to the way you create, open, read, and write to files, you can now create custom filesystems and file providers in Java as well. One example is the ZIP file provider, which comes with the API and treats an archive file as a filesystem in its own right. In this specific case, the ZIP filesystem makes it much easier to work with ZIP archive files for both creation and expansion. We'll examine Java SE 7 File System Providers in a future blog. For now, let's dive into the new File IO APIs.

Java 1.6 File IO: Ancient History

Prior to Java SE 7, File IO involved a fair amount of code, some of it nonintuitive and difficult to remember even for Java veterans. For instance, I would often find myself searching my older code and cutting/pasting into new projects. Here's an example of some of the tedious code required in Java SE 6 to open a file:

        // Try loading from the current directory
        InputStream is = null;
        try {
            File f = new File("config.xml");
            is = new FileInputStream( f );
        }
        catch ( Exception e ) { /*…*/ }

        // Or, if that fails, you can open the file by searching in the 
        // Java application's class path:

        try {
            if ( is == null ) {
                // load from classpath
                is = getClass().getResourceAsStream("config.xml");
            }
        }
        catch ( Exception e ) {
            System.out.println("config.xml file does not exist");
            return;
        }

And once you open the file, you should probably buffer its input data for efficiency. That means you need to work with its bytes one (or a bunch) at a time:

        BufferedInputStream bis = new BufferedInputStream(is);
        int data = -1;
        while ( (data = bis.read()) != -1 ) {
            // ...
        }

Or perhaps you know the format of the data, such as a file that contains a list of values of the primitive type double. To work with such data-formatted file content, you could use the DataInputStream class:

        DataInputStream dis = new DataInputStream(is);
        int length = dis.readInt(); // first int is count of doubles
        double[] d = new double[length];
        for ( int i = 0; i < length; i++ ) {
            d[i] = dis.readDouble();
        }

Alternatively, you can read a file using the FileReader class, which is altogether very different:

        File f = new File("config.xml");
        FileReader fr = new FileReader(f);
        BufferedReader reader = new BufferedReader(fr);
        String line = reader.readLine();
        while ( line != null ) {
            // ...
            line = reader.readLine();
        }

Lots of choices, lots of code. Java SE 7 does much to simplify and unify all of this. Let's look at some examples to contrast now.

Java 1.7 Files Class

First, get to know the Files class — new in Java SE 1.7. With it, you can easily create new files, create temporary files, create new (and temporary) directories, copy and move files various different ways, create symbolic links, open existing files, delete files, work with file attributes, read from files, and write to files. Just about everything you've ever wanted to do with a file can now be done through this one class. What I like about this approach is that it's easy to remember, and saves me from fumbling through the API docs or through past projects to find File IO code.

The Files class is your one entry point for most file operations, and all you need to do is type"'Files" in your IDE to see the list of API methods at your disposal. Awesome!

Want to open an existing file by path? Here it is:

    Path path = FileSystems.getDefault().getPath(".", name);

Want to read all of the bytes in that file? No more looping, here it is:

    byte[] filearray = Files.readAllBytes(path);

Would you rather deal with the file line-by-line? No problem:

    List<String> lines = Files.readAllLines(path, Charset.defaultCharset() );

Is it a large file that you'd rather use buffered IO with? Here you go:

    BufferedReader reader = 
                Files.newBufferedReader(path, Charset.defaultCharset() );
    String line = null;
    while ( (line = reader.readLine()) != null ) { /* … */ }

Surely, it must take more than that to create and write to file, right? Wrong:

    String content = …
    Files.write( path, content.getBytes(), 
                         StandardOpenOption.CREATE); // create new, overwrite if exists

You can buffer the output and write all of the bytes with one extra line of code:

    BufferedWriter writer =  
        Files.newBufferedWriter( path, Charset.defaultCharset(), 
                                                      StandardOpenOption.CREATE);
    writer.write(content, 0, content.length());

In fact, the StandardOpenOption class in Java 1.7 contains all of the file and directory attributes you need to deal with file existence create, delete, overwrite, append, and other options. Again, having this all in one helper class helps a great deal.

But what about all that existing File IO code you have? Do you need to throw it all away and start over to use the new Files class? No way! The old and new File IO APIs can be used together:

    Path path = FileSystems.getDefault().getPath(".", name);
    InputStream in = Files.newInputStream(path);
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            
    List<String> lines = new ArrayList<>();
    String line = null;
    while ( (line = reader.readLine()) != null )
        lines.add(line);

    // ...

The following code (which is complete and should compile if you have Java SE 1.7 installed) contains a list of helper methods to illustrate the new File IO operations:

package java7fileio;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Eric Bruno
 */
public class Java7FileIO {
    String myXML =  "<Configuration>\n" +
                    "   <Server>\n" +
                    "       <Name>MyServer</Name>\n" +
                    "       <Address>192.168.0.1</Address>\n" +
                    "   </Server>\n" +
                    "   <RetryCount>10</RetryCount>\n" +
                    "   <Logs>\n"+
                    "       <FilePath>/logs/out.txt</FilePath>\n"+
                    "       <Level>INFO</Level>\n"+
                    "   </Logs>\n"+
                    "</Configuration>";

    public static void main(String[] args) {
        Java7FileIO j7fio = new Java7FileIO();
        List<String> lines = null;
        
        // Write XML String bytes to a file
        j7fio.writeFileBytes("config.xml", j7fio.myXML);

        // Read all bytes from small file at once
        System.out.println("\n-- TEST 1 ---------------------------");
        String config = new String(j7fio.readSmallFileBytes("config.xml"));
        System.out.println(config);

        // Read all lines from a small file at once
        System.out.println("\n-- TEST 2 ---------------------------");
        lines = j7fio.readSmallFileLines("config.xml");
        for ( String line: lines )
            System.out.println(line);
        
        // Read all lines for a larger file
        System.out.println("\n-- TEST 3 ---------------------------");
        lines = j7fio.readLargeFileLines("log.txt");
        for ( String line: lines )
            System.out.println(line);

        // Read all lines for a larger file
        System.out.println("\n-- TEST 4 ---------------------------");
        lines = j7fio.readLargeFileLinesMixed("log.txt");
        for ( String line: lines )
            System.out.println(line);
    }
    
    public Java7FileIO() {
    }
    
    public byte[] readSmallFileBytes(String name) {
        byte[] filearray = null;
        try {
            Path path = FileSystems.getDefault().getPath(".", name);
            return Files.readAllBytes(path);
        }
        catch ( IOException ioe ) {
            ioe.printStackTrace();
        }
        return filearray;
    }
    
    public List<String> readSmallFileLines(String name) {
        try {
            return Files.readAllLines(
                            FileSystems.getDefault().getPath(".", name), 
                            Charset.defaultCharset() );
        }
        catch ( IOException ioe ) {
            ioe.printStackTrace();
        }
        return null;
    }
    
    public List<String> readLargeFileLines(String name) {
        try {
            BufferedReader reader = 
                Files.newBufferedReader(
                    FileSystems.getDefault().getPath(".", name), 
                    Charset.defaultCharset() );

            List<String> lines = new ArrayList<>();
            String line = null;
            while ( (line = reader.readLine()) != null )
                lines.add(line);

            return lines;
        } 
        catch (IOException ioe) {
            ioe.printStackTrace();
        }        
        return null;
    }
    
    public List<String> readLargeFileLinesMixed(String name) {
        try {
            Path path = FileSystems.getDefault().getPath(".", name);
            InputStream in = Files.newInputStream(path);
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            
            List<String> lines = new ArrayList<>();
            String line = null;
            while ( (line = reader.readLine()) != null )
                lines.add(line);

            return lines;
        }
        catch ( IOException ioe ) {
            ioe.printStackTrace();
        }
        
        return null;
    }
    
    void writeFileBytes(String filename, String content) {
        try {
            Files.write( FileSystems.getDefault().getPath(".", filename), 
                         content.getBytes(), 
                         StandardOpenOption.CREATE);
        }
        catch ( IOException ioe ) {
            ioe.printStackTrace();
        }
    }
    
    void writeFileBytesBuffered(String filename, String content) {
        try {
            BufferedWriter writer = 
                Files.newBufferedWriter(
                        FileSystems.getDefault().getPath(".", filename), 
                        Charset.forName("US-ASCII"), 
                        StandardOpenOption.CREATE);

            writer.write(content, 0, content.length());
        }
        catch ( IOException ioe ) {
            ioe.printStackTrace();
        }
    }
}

Happy coding!

EJB

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video