As we’ve seen, an attempt to create a FileReader stream may throw a FileNotFoundException. The way this happens is if the user provides a name for a file that either doesn’t exist or isn’t located where its name says it should be located. The question that needs to be considered: Is there any way we can detect these kinds of errors before attempting to read the file?
The java.io.File class provides methods that we can use for this task. The File class provides a representation of the computer’s file and directory information in a platform-independent manner. As you know, a file is a collection of data, whereas a directory is a collection of files. (To be exact, a directory is a file that stores its files’ names and attributes, not the files themselves.) In this section, we will provide details about the File class and how to use the methods available in the class.
Subsection11.4.1Names and Paths
In order to correctly specify a file’s location, it is necessary to know a little about how files are stored on your computer’s disk drive. File systems are organized into a hierarchy. A path is a description of a file’s location in the hierarchy. For example, consider the hierarchy of files in Figure 11.4.1.
Assume that your Java program is named MyClass.class. When a program is running, the program’s directory is considered the current directory. Any files located in the current directory can be referred to by name alone—for example, MyClass.java. To refer to a file located in a subdirectory of the current directory, you need to provide the name of the subdirectory and the file: datafiles/data.txt. In this case, we are assuming a Linux file system, so we are using the / as the separator between the name of the directory (datafiles) and the name of the file (data.txt). This is an example of a relative path name, because we are specifying a file in relation to the current directory.
Alternatively, a file can be specified by its absolute path name. This would be a name whose path starts at the root directory of the file system. For example,
/root/java/examples/datafiles/data.txt
would be the absolute path name for the file named data.txt on a Linux system. When you supply the name of a file to one of the stream constructors, you are actually providing a path name. If the path consists of just a name, such as data.txt, Java assumes that the file is located in the same directory as the program itself.
Subsection11.4.2Validating File Names
Before reading a file it is often necessary to determine that the file’s name is a valid one and that the file can be read. The File class (Figure 11.4.2) provides platform-independent methods for dealing with files and directories. It contains methods that list the contents of directories, determine a file’s attributes, and rename and delete files. Note the several static constants provided. These allow path names to be specified in a platform-independent way.
For example, on a Linux system, the File.separator character will be the / and on a Windows system it will be the \,backslash. File.separator will be initialized to the appropriate separator for the particular system being used.
Principle11.4.3.PROGRAMMING TIP:File Separators.
To help make your programs platform independent, use the File.separator constant instead of a literal value whenever you are specifying a path name.
As an example of how you might use some of File’s methods, let’s write a method that tests whether the file name entered by the user is the name of a valid, readable file.
A file might be unreadable for a number of reasons. It might be owned by another user and readable only by that user. Or it might be designated as not readable by its owner. We’ll pass the method the name of the file (a String), and the method will return true if a readable file with that name exists. Otherwise, the method will throw an exception and return false:
private boolean isReadableFile(String fileName) {
try {
File file = new File(fileName);
if (!file.exists())
throw (new FileNotFoundException("No such File:"
+ fileName));
if (!file.canRead())
throw (new IOException("File not readable: "
+ fileName));
return true;
} catch (FileNotFoundException e) {
System.out.println("IOERROR: File NOT Found: "
+ fileName + "\n");
return false;
} catch (IOException e) {
System.out.println("IOERROR: " + e.getMessage() + "\n");
return false;
}
} // isReadableFile
The method simply creates a File instance and uses its exists() and canRead() methods to check whether its name is valid. If either condition fails, an exception is thrown. The method handles its own exceptions, printing an error message and returning false in each case.
Before attempting to write data to a file, we might want to check that the file has been given an appropriate name. For example, if the user leaves the file name blank, we should not write data to the file. Also, a file might be designated as unwriteable in order to protect it from being inadvertently overwritten. We should check that the file is writeable before attempting to write to it:
private boolean isWriteableFile(String fileName) {
try {
File file = new File (fileName);
if (fileName.length() == 0)
throw (new IOException("Invalid file name: "
+ fileName));
if (file.exists() && !file.canWrite())
throw (new IOException(
"IOERROR: File not writeable: " + fileName));
return true;
} catch (IOException e) {
display.setText("IOERROR: " + e.getMessage() + "\n");
return false;
}
} // isWriteableFile()
The first check in this code tests that the user has not forgotten to provide a name for the output file. It is unlikely that the user wants to name the file with the empty string. We use the exists() method to test whether the user is attempting to write to an existing file. If so, we use the canWrite() method to test whether the file is writeable. Both kinds of errors result in IOExceptions.
ExercisesSelf-Study Exercise
1.File Info.
The other methods of the File class (Figure 4.6.1) are just as easy to use as the ones we have illustrated in this section. Complete the getFileAttributes() method that takes the name of a file as its single parameter and prints the following information about the file: its absolute path, its length, and whether it is a directory or a file. The main() program is completed for you.