Handling Invalid Directories
A Java File object represents a file or directory, and creating one with new File(name) asks the program to refer to a path rather than read it.
A Program That Asks for a Folder
Many programs need to work with folders on the disk. A common example is a small tool that asks the user to type a directory name and then shows the files inside it. That tool looks simple, but it has a real problem to solve: the user might type the name of a folder that does not exist, or the name of an ordinary file instead of a folder. A program that blindly tries to list whatever it is given will fail or behave unpredictably. This article follows one complete Java program, called DirectoryList, that handles those inputs safely.
The source describes the program's purpose this way: it lists the files in a directory specified by the user, and if the name entered is not a directory, a message is printed and the program ends. Keep that description in mind as you read. The goal is not only to know what the program prints, but to understand how it decides what to do.
What do you think happens?
Suppose the user types the name of an ordinary file, not a folder, into DirectoryList. Before reading on, decide what the program does: does it print a file listing, or does it print one of its messages? Which if statement controls that decision?
Reveal answer
The program prints a message saying the file is not a directory, and it does not print a file listing.
The outer test checks whether the path is not a directory. A regular file is not a directory, so the outer block runs. Inside that block, exists() returns true because something by that name is present, so the program takes the branch for something that exists but is not a directory. The listing branch is only reached when isDirectory() is true.
What new File(name) Actually Does
The File class in Java can represent either a file or a directory. The key idea is that creating a File object refers to a path; it does not read anything from the disk. The line directory = new File(directoryName) takes the text the user typed and wraps it in an object. At that moment the program has a reference to a location, not the folder's contents, and it has not yet confirmed that the location exists.
After the object is created, the program asks it questions. The isDirectory() method checks whether the object represents a directory, and exists() checks whether the path is present at all. Those questions are what separate a missing path, an ordinary file, and a valid folder. The diagram below shows the relationship between the variable, the path string, and the folder on disk.
Two Checks: exists() and isDirectory()
The validity decision rests on two questions that answer different things. The exists() method answers whether anything is present at the path. The isDirectory() method answers whether the thing at the path is a directory. A directory is valid when it exists and is a directory, and the two checks together decide which message or listing the program produces. Here is the full program. The message wording is illustrative and was written to match the branch structure described in the source; the structure itself is the point to study.
import java.io.File;
import java.util.Scanner;
public class DirectoryList {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Enter a directory name: ");
String directoryName = in.nextLine();
File directory = new File(directoryName);
if (directory.isDirectory() == false) {
if (directory.exists() == false) {
System.out.println("There is no such directory.");
} else {
System.out.println("The file is not a directory.");
}
} else {
String[] files = directory.list();
for (int i = 0; i < files.length; i++) {
System.out.println(" " + files[i]);
}
}
}
}- 1Line 1-2 The File and Scanner classes are imported so the program can refer to them.
- 2Line 6-8 The user's typed text is read and wrapped in a File object named directory. No folder contents are read yet.
- 3Line 9 The outer test asks whether the object is not a directory. This is the negated test that controls the whole decision.
- 4Line 10-14 Inside the outer block,
exists()decides between no such directory and a file that is not a directory. - 5Line 15-19 Only when
isDirectory()is true does the else branch run.list()returns the file names, and the loop prints each one with an indent.
- new
File(directoryName)creates an object that refers to the typed path without reading the disk - The outer if tests
isDirectory()== false, a negated test that runs when the path is not a directory - The inner if separates a missing path (
exists()== false) from an existing non-directory - The else branch runs only when
isDirectory()is true, and only then islist()called
| Path the user typed | exists() | isDirectory() | Branch the program takes |
|---|---|---|---|
| Nothing by that name | false | false | Outer block, inner exists() == false: no such directory |
| Ordinary file | true | false | Outer block, inner else: file is not a directory |
| Valid folder | true | true | Else branch: list() is called and names are printed |
How the two checks combine to choose a branch
Following Three Kinds of Input
The easiest way to build the right mental model is to trace the program for different inputs. The flowchart below shows the order of checks for any typed path. Follow an input from the top, answer each decision, and note which terminal box it reaches.
Tracing a valid folder through the program
The user types the name of a folder that exists and is a directory. Which lines run, and where does the printed output come from?
- 1Create the object
new
File(directoryName)creates a reference to the typed path. The disk has not been read yet. - 2Outer test
isDirectory()returns true, so the testisDirectory()== false is false. The outer block is skipped. - 3Else branch
Control moves to the else branch, the only branch that calls
list(). - 4Retrieve names
list()returns an array of file names in the directory and stores it in files. - 5Print with the loop
files.lengthsets the loop limit, and each entry is printed on its own indented line. The printed output comes from this loop, not from either message.
The program prints an indented list of the names inside the folder and does not print any error message.
What do you think happens?
The user types the name of a path that does not exist anywhere on the disk. Which if statement sends the program to its message, and which message does it print?
Reveal answer
The outer test isDirectory() == false is true, then the inner test exists() == false is true, so the program prints the no such directory message.
Nothing is present at the path, so exists() returns false, and the inner if takes its first branch. The listing code never runs.
Mistakes When Reading the Branch Logic
Common mistake Reading isDirectory() == false as the condition for a valid folder
What it looks likeif (directory.isDirectory() == false) { ... list() ... }Why it failsThe outer block runs when the path is not a directory. Calling
list()there would ask a regular file for its contents.Do this insteadTreat the outer block as the error path. list() belongs in the else branch, which runs only when isDirectory() is true.
Common mistake Assuming exists() alone proves the path is a directory
What it looks likeA regular file with the typed name makes exists() true, but the program must still reject it.
Why it failsexists()only reports that something is present at the path. It does not say what kind of thing it is.Do this insteadUse isDirectory() to decide whether the path is a directory, and use exists() to separate a missing path from a file.
Common mistake Thinking new File(name) reads the folder's contents
Why it failsThe source states that creating the object does not read anything from the disk by itself. It only creates an object that refers to the path.
Do this insteadExpect the disk to be consulted only when the program asks a question such as isDirectory(), exists(), or list().
Common mistake Forgetting that the messages end the program, so no listing follows an error message
Tracing the Branches Yourself
Practice by tracing rather than running. For each input below, decide which branch the program takes before you check the answer. Use the flowchart and the comparison table as references, and name the if statement that controls the decision.
Trace DirectoryList for these three inputs: (a) a path that is not present on the disk, (b) an ordinary text file, (c) a folder that exists. For each one, state which test is evaluated first, which branch runs, and whether list() is called.
Hints
- Start with the outer test
isDirectory()== false. Ask whether it is true for this input. - Only the else branch calls
list(), so ask whether that branch can be reached for a regular file. - For a missing path,
exists()returns false, so the inner if takes its first branch.
Explain in one or two sentences why the program can safely call list() without checking the path's contents first, using only the order of the checks in DirectoryList.
Hints
- Look at which branch contains the
list()call. - Think about what happens before that branch is reached.
Key Takeaways
- A File object created with new
File(name)refers to a path and does not read the disk by itself; the program then asks it questions. isDirectory()checks whether the path is a directory, andexists()checks whether anything is present, and together they choose the branch.- The outer test
isDirectory()== false is a negated test; its block handles missing paths and non-directory files, andlist()runs only in the else branch. list()returns an array of file names, which the program prints with a loop once the validity check has passed.