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.
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?
Reveal 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.
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.
- 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
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.
| Method | Question 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.
false
false
(one line for each entry in the current folder)exists()reports on the named entry, not on the object itselfisDirectory()is false when the name does not refer to a directorylistFiles()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.
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.
- 1Build the object
File target = new
File(folder, "summary.txt"); This creates the object only. Nothing on disk has changed yet. - 2Check for a directory
target.isDirectory()returns false, so the name is not a directory and the program continues. - 3Check existence
target.exists()returns true, because asummary.txtwas saved by an earlier run. The program must not blindly overwrite it, so it moves to the writability check. - 4Check writability
target.canWrite()returns true, so the program is allowed to write to the existing entry and opens the output stream.
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 likeFile f = new File("notes.txt"); then expecting notes.txt to appear in the folder.
Why it failsA File object represents a file name. Creating the object creates nothing on disk.
Do this insteadAsk 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 likeChecking only exists() and then opening an output stream.
Why it failsexists()answers one question.canWrite()answers a different question about the same name.Do this insteadAsk exists(), isDirectory(), and canWrite() as separate questions before writing.
Common mistake Calling listFiles() on an object that names a regular file
Why it failslist()andlistFiles()return directory contents only when the object refers to a directory.Do this insteadCheck isDirectory() first, then list the contents.
Common mistake Expecting the object to update itself after delete()
Practice: Tracing a Name Through the Checks
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 newFile(File, String), build the same kind of object from different inputs. - Methods such as
exists(),canRead(),canWrite(),isDirectory(), anddelete()ask questions about the named entry, andlist()andlistFiles()return contents only for directories. - Check file state before writing, because a File object can be passed to streams such as
FileReaderand the disk answers every check.