> Canonical lecture: sources/14-w26-lectures/week-07/week7-lec1-file-io-essentials.tex §"What File actually represents."
A java.io.File object does not hold the contents of a file. It holds a path: the address where a file might or might not exist on disk. Constructing new File("hamlet.txt") never reads a single byte; it just builds a Java object that records the string "hamlet.txt" and offers methods to query the disk later (exists(), canRead(), length(), getAbsolutePath()).
Two ideas flow from that one fact. First, paths come in two flavors: relative (interpreted from the program's current working directory) and absolute (interpreted from the filesystem root). Second, before passing a File to a Scanner constructor, you can ask the file whether it actually exists and is readable, which lets you write programs that fail gracefully instead of crashing.
File as path, not content
Student can explain that a File object stores a path (not file contents), and predict whether constructing a File performs any disk I/O.
The java.io.File class is one of the most misnamed classes in the JDK. A File object does not hold the contents of a file: it holds the file's path, the string that names where a file might be on disk. new File("hamlet.txt") performs zero disk I/O; it just stores the string "hamlet.txt" inside a Java object. The disk is touched only when something else (a Scanner, a PrintStream, a method like exists()) asks the operating system about that path.
This separation is deliberate. A path can refer to a file that does not exist yet (you might be about to create it), a file you do not have permission to read, a directory rather than a file, or a perfectly normal readable file. The File class lets you build the reference, query the disk to find out which case you are in, and pass the reference to other classes that will actually open it.
import java.io.*;
final File f = new File("hamlet.txt"); // No disk I/O. Just a path object.
System.out.println(f.exists()); // Now we ask the OS: true or false?
System.out.println(f.getAbsolutePath()); // String formatting only: still no read.
In other languages
- Python:
pathlib.Path("hamlet.txt")is the direct analog: a path object you can query before opening. - C: there is no path object; you pass a
const char *filename directly tofopen.stat()andaccess()do whatFile.exists()/File.canRead()do.
Relative vs absolute path
Student can distinguish relative from absolute file paths, identify the current working directory's role, and predict where a relative path will be resolved.
A path string can be absolute, fully specified from the filesystem root (/data/hamlet.txt on Linux/Mac, C:/data/hamlet.txt on Windows), or relative, interpreted starting from the program's current working directory. new File("hamlet.txt") is a relative path: it means "look for hamlet.txt in whatever directory the JVM was started from." new File("/data/hamlet.txt") is an absolute path: it always points to the same place no matter where the program runs.
The current working directory is set by whoever launched the program: usually the IDE, the terminal cd location, or the java command's invocation directory. Students who run their program from one directory and the file from another get a FileNotFoundException even though the file clearly exists, because the relative path resolves against the wrong starting point. Java accepts forward slashes (/) on every OS, including Windows, which lets you avoid the \\ escape mess in source code.
final File rel = new File("data/scores.txt"); // resolved from CWD
final File abs = new File("/data/scores.txt"); // always the same place
System.out.println(rel.getAbsolutePath()); // shows where rel resolved
In other languages
- Python:
os.getcwd()reveals the current working directory;pathlib.Path.resolve()converts relative to absolute. - C:
getcwd()returns the CWD as a string; relative paths infopenare resolved against it.
File.exists() and File.canRead()
Student can use File.exists() and File.canRead() to validate a path before opening, and explain why a throws FileNotFoundException clause is still required.
Once you have a File object, you can ask the operating system about the path before trying to open it. f.exists() returns true if the path resolves to something on disk (a file or directory). f.canRead() returns true if the path resolves to a file the JVM has read permission on. Both queries do real disk I/O (they hit the filesystem to find out), but they do not open the file or fail with an exception if the answer is no.
This makes them ideal for pre-flight checks. BJP's getInput method (p. 425) uses canRead() in a fencepost loop: prompt for a filename, build a File, while not readable re-prompt. The user gets clean error messages instead of stack traces. The same pattern works for any input that names a file. Even after canRead() returns true, you still must declare throws FileNotFoundException on main: the compiler cannot prove the file is still there at the moment the Scanner constructor runs (race conditions exist) and the language requires the declaration anyway.
final File f = new File("hamlet.txt");
if (f.exists() && f.canRead()) {
final Scanner input = new Scanner(f);
// safe to read
} else {
System.out.println("File missing or unreadable: " + f.getAbsolutePath());
}
In other languages
- Python:
pathlib.Path("x").exists()andos.access(path, os.R_OK)are direct equivalents. - C:
access(path, R_OK)from<unistd.h>checks readability without opening.