Handling Exceptions in File Operations
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.
Predicting What a File Object Does
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.
What do you think happens?
Suppose a program creates a File object for the name notes.txt in a folder that contains no such file. What does exists() return for that object?
Reveal answer
false, because the File object holds only a name and creates nothing on disk
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. Since no notes.txt exists in that folder, exists() reports that it does not. Keep this answer in mind, because the rest of the article explains why it is correct.
Keep the wrong belief in mind as you read. Each section below either confirms or corrects a part of it, and by the end you should be able to explain what a File object actually holds and what it can tell you.
What a File Object Holds
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.
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. Creating the File object only creates the box on the left. Nothing on the right changes. Whether the arrow reaches a real file is a question you must ask, and the methods in the following sections are how you ask it.
Building a File Object
A File object is built with one of two constructors. 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.
Asking Questions About a Named Entry
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, and what does it contain if it is a directory. The table below groups the main instance methods by the question each one answers.
| Question | Method | What it reports or does |
|---|---|---|
| Does the named entry exist? | exists() | Whether something with this name is present |
| Can the program read it? | canRead() | Whether reading the named entry is permitted |
| Can the program write to it? | canWrite() | Whether writing to the named entry is permitted |
| Is it a directory? | isDirectory() | Whether the named entry is a directory |
| Should it be removed? | delete() | Requests removal of the named entry |
| What does a directory contain, as names? | list() | Returns the entry names inside a directory |
| What does a directory contain, as File objects? | listFiles() | Returns the entries inside a directory as File objects |
Main instance methods of java.io.File, grouped by the question each answers
The methods that ask about contents, list() and listFiles(), return contents only when the object refers to a directory. Asking a regular file for its entries answers nothing useful, so check isDirectory() first when you do not know what kind of entry you hold.
false
false
false- Each method asks a question about the same named entry
- If data/notes.txt is absent, every answer is false, because nothing is there to read or be a directory
Tracing a File Through delete()
Checking existence before and after delete()
Assume data/notes.txt exists on disk when the program starts. What does exists() report before and after delete() is called?
- 1Create the object
new
File("data/notes.txt")holds the name. It does not touch the disk. - 2Ask before deleting
exists()looks for the named entry. The file is present, so it returns true. - 3Request deletion
delete()acts on the named entry. The file is removed from the file system. - 4Ask again
exists()now looks for the same name and finds nothing, so it returns false. The File object itself is unchanged; only the disk changed.
exists() returns true before delete() and false afterward. The File object holds the same name in both cases.
true
false- The first
exists()call sees the file because it was present before the program started - The second call sees no file, so the answer changes without the File object changing
Listing a Directory's Entries
When the File object refers to a directory, list() and listFiles() give you its contents. The difference is the form of each entry. list() returns the entries as names, which you can use as text. listFiles() returns each entry as a File object, which you can then ask questions of with the methods above.
notes.txt
report.txtisDirectory()guards the call so the listing only runs on a directory- Each element is a File, so
getName()can be called on it - The output shown assumes data/ holds
notes.txtandreport.txt
Checking State Before Writing
The source explains that exists() helps you avoid overwriting an existing file when you create a new output stream. Before you write, you ask the object what it finds at that name, and only then decide whether to proceed. The flowchart shows that decision in its simplest form.
Safe to create notes.txt- The check runs before any output stream is created
- The output shown assumes data/notes.txt is absent
Passing a File to FileReader
Stream classes such as FileReader accept a File object in their constructors. This is how a named file becomes a source or destination of data. The File object supplies the name, and the stream uses that name to reach the contents on disk. The File object does not carry the data; the stream does the reading.
- The state checks come before the stream is created
- The File object is passed straight into the
FileReaderconstructor - Closing the reader is omitted here to keep the focus on the File
Mistakes Beginners Make with File Objects
Common mistake Assuming new File(...) creates the file on disk
What it looks likeFile f = new File("data/notes.txt"); then expecting notes.txt to appear in the folder
Why it failsA File object represents a file name, and creating the object creates nothing on disk
Do this insteadCreate the file explicitly through an output stream, after checking exists()
Common mistake Calling list() or listFiles() on a regular file
Why it failsDirectory contents are returned only when the object refers to a directory
Do this insteadCheck isDirectory() before asking for entries
Common mistake Opening an output stream without checking exists() first
Why it failsWithout the check, an existing file can be overwritten
Do this insteadAsk exists() before creating the output stream and decide what to do if the answer is true
Common mistake Expecting the File object to change when the file is deleted
What it looks likeAfter delete(), printing the same File object's name still shows notes.txt
Why it failsThe object holds the name, and the name is unchanged; only the disk state changed
Do this insteadAsk exists() again to learn the new state of the file
Practice Checking a File's State
A program holds a File object for the name report.txt inside a folder called logs. Write the steps that check whether report.txt exists, whether it can be read, and whether it is a directory, in that order. Then say which answer would stop the program from opening a FileReader.
Hints
- Build the object with either constructor and check
exists()before anything else - A missing file means
canRead()cannot be true for that name - A directory is not a file a
FileReadercan read as text, soisDirectory()being true is a reason to stop
Key Takeaways
- A File object represents a file name, not a file, and creating it creates nothing on disk
- Two constructors build File objects: new
File(String)from a path string, and newFile(File, String)from a directory and a name exists(),canRead(),canWrite(),isDirectory(), anddelete()ask questions about the named entry, andlist()andlistFiles()return contents only for a directory- Check a file's state with
exists()before creating an output stream, so an existing file is not overwritten by accident - Passing a File object to
FileReaderconnects a named file to a stream, but the stream does not check the state for you