Concepts / Introduction to Java File Input and Output

Introduction to Java File Input and Output

A File object represents a file name, not a file. The named file may or may not exist, and creating the object creates nothing on disk.

  • Programming

A Name Is Not a File

When beginners first meet the java.io.File class, many assume that creating a File object is the same as opening or creating a file on disk. The word File in the class name encourages this reading. This article tests that assumption directly, because the assumption is wrong in a way that shapes every later step of file handling in Java.

The correct idea is short. A File object represents a file name, not a file. The named file may or may not exist, and creating the object creates nothing on disk. Keep the wrong belief in mind as you read. Each section below either confirms or corrects a part of it.

What do you think happens?

A program creates a File object for the name notes.txt in a folder that contains no such file. Before reading on, what can the program conclude about whether notes.txt exists?

  • It exists now, because a File object was created for it

  • It does not exist, because creating a File object creates nothing on disk

  • It exists only after the program reads from it

Reveal answer
Answer:

The program cannot conclude that notes.txt exists. Creating the File object created nothing on disk, so the file is still absent unless something else made it.

If a File object were the file itself, creating it would produce notes.txt. It does not. The object only holds the name, and whether the named file exists is a separate question you must ask.

Tracing What new File Creates

The clearest way to hold the two ideas apart is to set them side by side. The File object lives in your program and carries a name. The file lives in the file system and carries contents. The name and the file are linked only when the operating system finds a match for that name.

File objectholds the name notes.txtnotes.txt entrymay or may not exist ondiskexists() asks about
After new File(notes.txt) runs, the program holds a File object with a name. The disk entry is a separate thing, and the program must ask across the arrow whether it exists.

Read the diagram as a warning as much as an explanation. Every question you can ask about the file is a question asked across the arrow, from the object toward the disk. Nothing inside the object itself answers those questions without that check.

Two Ways to Build a File Object

A File object is built with one of two constructors. The first takes a single path string. The second takes a directory, given as a File object, and a name or relative path. Which one you choose depends on how much of the path you already have as separate pieces. Both produce the same kind of object, so everything in the later sections applies to either.

java
Notes on the code
  • new File(String) builds the object from one complete path string
  • new File(File, String) builds the object from a directory object and a name
  • Both objects hold the same name, reports/summary.txt, so they refer to the same entry
new File(String)one complete pathFile objectholds a name onlynew File(File,String)directory object plus namebuildsbuilds
The two constructors take different inputs but both return a File object that holds a name. The directory-plus-name form lets you keep the folder and the file name as separate pieces.

Asking Questions About a Name

Because a File object holds a name, the useful operations are questions and requests about that name. Does the named entry exist? Can the program read or write it? Is it a directory? Should it be deleted? If it is a directory, what does it contain? The table below groups the main instance methods by the question each one answers.

MethodQuestion it answers about the named entry
exists()Does the named file or directory exist?
canRead()Can the program read the named entry?
canWrite()Can the program write to the named entry?
isDirectory()Does the name refer to a directory?
delete()Request removal of the named entry
list()Names of the contents, returned only when the object refers to a directory
listFiles()File objects for the contents, returned only when the object refers to a directory

Main instance methods of java.io.File, grouped by the question each answers

Notice the last two rows. list() and listFiles() return directory contents only when the object refers to a directory. Calling them on an object that names a regular file does not expose any children, so checking isDirectory() first is the safe habit.

java
Output
false
false
(one line for each entry in the current folder)
Notes on the code
  • exists() reports on the named entry, not on the object itself
  • isDirectory() is false when the name does not refer to a directory
  • listFiles() is used here on the current folder, which is a directory

Checking State Before Writing

The source explains that exists() helps you avoid overwriting an existing file when you create a new output stream. A File object can also be passed to a stream constructor such as FileReader, which is how a named file becomes a source or destination of data. Before any stream is opened for writing, the program can ask the File object several questions. The flowchart below shows one reasonable order for those questions. It is a design choice made for illustration, not a rule imposed by the class.

File objectname chosen by programisDirectory()?Stop: name is adirectorycanWrite()?Stop: not writableexists()?Open output streamcheckyesnonoyesyesno
The program asks isDirectory(), then exists(), then canWrite() before it opens an output stream. Each answer sends the program down a different path.
Worked example

Choosing whether to write to a report file

A program must write a summary to reports/summary.txt. It should not overwrite a file that is a directory or that it cannot write to. Walk through the checks in order.

  1. 1
    Build the object

    File target = new File(folder, "summary.txt"); This creates the object only. Nothing on disk has changed yet.

  2. 2
    Check for a directory

    target.isDirectory() returns false, so the name is not a directory and the program continues.

  3. 3
    Check existence

    target.exists() returns true, because a summary.txt was saved by an earlier run. The program must not blindly overwrite it, so it moves to the writability check.

  4. 4
    Check writability

    target.canWrite() returns true, so the program is allowed to write to the existing entry and opens the output stream.

Result

The program opens the output stream only after three questions about the named entry have been answered. Had canWrite() returned false, the program would have stopped and reported the problem instead.

Mistakes With File Objects

  • Common mistake

    Assuming that new File(...) creates the file on disk

    What it looks like

    File f = new File("notes.txt"); then expecting notes.txt to appear in the folder.

    Why it fails

    A File object represents a file name. Creating the object creates nothing on disk.

    Do this instead

    Ask exists() to find out whether the named entry is there before you rely on it.

  • Common mistake

    Treating a file's existence as proof that it can be written

    What it looks like

    Checking only exists() and then opening an output stream.

    Why it fails

    exists() answers one question. canWrite() answers a different question about the same name.

    Do this instead

    Ask exists(), isDirectory(), and canWrite() as separate questions before writing.

  • Common mistake

    Calling listFiles() on an object that names a regular file

    Why it fails

    list() and listFiles() return directory contents only when the object refers to a directory.

    Do this instead

    Check isDirectory() first, then list the contents.

  • Common mistake

    Expecting the object to update itself after delete()

Practice: Tracing a Name Through the Checks

MEDIUM

A program builds File log = new File("data", "run.log") and then calls log.exists(), which returns false. The program then calls log.delete(). Without running any code, state what log.exists() returns on a later call, and explain why the answer does not depend on the object having been created.

Hints
  • Creating the object created nothing on disk, and delete() only removes an entry that is there.
  • Each exists() call asks the disk the same question, so think about what the disk contains at the time of the later call.

Key Takeaways

  • A File object represents a file name, not a file, and creating it creates nothing on disk.
  • The two constructors, new File(String) and new File(File, String), build the same kind of object from different inputs.
  • Methods such as exists(), canRead(), canWrite(), isDirectory(), and delete() ask questions about the named entry, and list() and listFiles() return contents only for directories.
  • Check file state before writing, because a File object can be passed to streams such as FileReader and the disk answers every check.