Real programs read and write files and handle errors gracefully. This unit covers the File class, reading/writing text files with Scanner and PrintWriter, and Java's exception handling system.
A File object represents a path to a file or directory on disk. Creating a File object doesnot create an actual file — it's just a reference to a location that may or may not exist.
import java.io.File;
// A File object is just a path — the file may not exist yet
File f = new File("data.txt");
File dir = new File("output/reports");
File abs = new File("C:/Users/student/Desktop/scores.txt");
// The file is NOT created on disk by this.
// It's just a Java object that knows the path.
System.out.println(f.getName()); // data.txt| Method | Returns | Description |
|---|---|---|
getName() | String | The file name (not the full path) |
exists() | boolean | true if the file/directory exists on disk |
length() | long | File size in bytes (0 if doesn't exist) |
getAbsolutePath() | String | The full path from the root of the filesystem |
canRead() | boolean | true if the program can read this file |
canWrite() | boolean | true if the program can write to this file |
createNewFile() | boolean | Creates the file if it doesn't exist. Returns true if created. Throws IOException. |
delete() | boolean | Deletes the file. Returns true if successful. |
import java.io.File;
import java.io.IOException;
File f = new File("data.txt");
System.out.println(f.getName()); // data.txt
System.out.println(f.exists()); // false (doesn't exist yet)
System.out.println(f.getAbsolutePath()); // C:\projects\data.txt
f.createNewFile(); // actually creates the file on disk now
// throws IOException — must handle it!
System.out.println(f.exists()); // true
System.out.println(f.length()); // 0 (empty file)
System.out.println(f.canRead()); // true
System.out.println(f.canWrite()); // true
f.delete(); // removes the file
System.out.println(f.exists()); // falsenew File("x.txt") does NOT create a file. createNewFile() does. exists() returnsfalse until the file is actually on disk. Watch for this distinction in trace questions.
PrintWriter writes text to a file sequentially. It has the same print/println methods you already know from System.out.
import java.io.PrintWriter;
import java.io.File;
import java.io.IOException;
public class WriteDemo {
public static void main(String[] args) throws IOException {
// Creates the file if it doesn't exist; overwrites if it does
PrintWriter out = new PrintWriter(new File("output.txt"));
out.println("Name: Alice");
out.println("Score: 95");
out.print("Grade: ");
out.println("A");
// IMPORTANT: must close or flush to save!
out.close();
// output.txt now contains:
// Name: Alice
// Score: 95
// Grade: A
}
}If you don't call out.close(), the data may never actually be written to disk. PrintWriter buffers output — close() flushes the buffer and releases the file.
You already know Scanner for keyboard input (System.in). It works the same way with files — just pass a File object instead.
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
public class ReadDemo {
public static void main(String[] args) throws FileNotFoundException {
Scanner in = new Scanner(new File("data.txt"));
// Same methods as reading from keyboard
String line = in.nextLine(); // reads entire line
String word = in.next(); // reads one token
int num = in.nextInt(); // reads an integer
double dec = in.nextDouble(); // reads a double
in.close();
}
}| Reading | Checking (has more?) |
|---|---|
next() | hasNext() |
nextLine() | hasNextLine() |
nextInt() | hasNextInt() |
nextDouble() | hasNextDouble() |
The hasNext...() methods return true if there's more data of that type to read — essential for loops.
The most common file-reading pattern: loop until there's no more data.
Scanner in = new Scanner(new File("names.txt"));
while (in.hasNextLine()) {
String line = in.nextLine();
System.out.println(line);
}
in.close();Scanner in = new Scanner(new File("numbers.txt"));
int sum = 0;
int count = 0;
while (in.hasNextInt()) {
sum += in.nextInt();
count++;
}
System.out.println("Sum: " + sum);
System.out.println("Count: " + count);
System.out.println("Average: " + (double) sum / count);
in.close();// File contents: "Alice 95 Bob 87 Charlie 92"
Scanner in = new Scanner(new File("mixed.txt"));
while (in.hasNext()) {
if (in.hasNextInt()) {
int score = in.nextInt();
System.out.println("Score: " + score);
} else {
String name = in.next();
System.out.println("Name: " + name);
}
}
in.close();
// Output:
// Name: Alice
// Score: 95
// Name: Bob
// Score: 87
// Name: Charlie
// Score: 92hasNext() checks for any token.hasNextLine() checks for another line.hasNextInt() checks if the next token is an integer. When the file is exhausted, all return false.
An exception is an event that disrupts the normal flow of a program. When an error occurs, Java creates an exception object and throws it. If nobody catches it, the program crashes with a stack trace.
int[] arr = {1, 2, 3};
System.out.println(arr[5]); // throws ArrayIndexOutOfBoundsException
// If unhandled, the program crashes and prints:
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
// at Main.main(Main.java:3)All exceptions are objects that extend Throwable. The two main branches are Error (serious, unrecoverable) and Exception (can be handled).
Throwable
├── Error (serious — don't catch these)
│ ├── StackOverflowError
│ └── OutOfMemoryError
│
└── Exception (can be caught and handled)
├── IOException (checked)
│ └── FileNotFoundException (checked)
├── ClassNotFoundException (checked)
│
└── RuntimeException (unchecked)
├── NullPointerException
├── ArrayIndexOutOfBoundsException
├── ArithmeticException
├── ClassCastException
├── IllegalArgumentException
└── NumberFormatException| Checked | Unchecked | |
|---|---|---|
| Extends | Exception (not RuntimeException) | RuntimeException |
| Compiler enforces? | Yes — must handle or declare | No — compiler doesn't check |
| When? | External problems (file not found, network error) | Programmer bugs (null, bad index) |
| Handle with | try/catch or throws | Fix the bug in your code |
| Examples | IOException, FileNotFoundException | NullPointerException, ArithmeticException |
import java.io.File;
import java.util.Scanner;
// This WON'T compile:
// Scanner in = new Scanner(new File("data.txt"));
// Error: unreported exception FileNotFoundException; must be caught or declared
// Fix 1: declare it with throws
public static void main(String[] args) throws FileNotFoundException {
Scanner in = new Scanner(new File("data.txt"));
}
// Fix 2: catch it with try/catch
public static void main(String[] args) {
try {
Scanner in = new Scanner(new File("data.txt"));
} catch (FileNotFoundException e) {
System.out.println("File not found!");
}
}try wraps code that might throw. catchhandles the exception. finally runs no matter what — whether an exception occurred or not.
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
Scanner in = null;
try {
in = new Scanner(new File("data.txt"));
int num = in.nextInt();
System.out.println("Read: " + num);
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (Exception e) {
System.out.println("Something else went wrong: " + e);
} finally {
// ALWAYS runs — even if an exception was thrown
if (in != null) {
in.close();
}
System.out.println("Cleanup done.");
}| Scenario | What runs |
|---|---|
| No exception | try block → finally block |
| Exception is caught | try (up to error) → matching catch → finally |
| Exception not caught | try (up to error) → finally → program crashes |
try {
int[] arr = {1, 2, 3};
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Bad index!");
} catch (Exception e) {
System.out.println("Some other error");
}
// Output: Bad index!
// Catch blocks are checked TOP to BOTTOM.
// Put more specific exceptions FIRST.
// Putting Exception first would catch everything — the specific
// catches below it would never run (compiler error).throw creates and throws an exception.throws declares that a method might throw an exception (passes the responsibility to the caller).
| Keyword | Where | Purpose |
|---|---|---|
throw | Inside a method body | Actually throws an exception object |
throws | In the method signature | Declares what exceptions the method might throw |
// throws — in the method SIGNATURE (declares possibility)
public static double divide(int a, int b) throws ArithmeticException {
if (b == 0) {
// throw — in the method BODY (actually throws)
throw new ArithmeticException("Cannot divide by zero");
}
return (double) a / b;
}
// Caller must handle it:
try {
double result = divide(10, 0);
} catch (ArithmeticException e) {
System.out.println(e.getMessage()); // Cannot divide by zero
}import java.io.IOException;
// throws passes the buck to the caller
public static void readConfig(String path) throws IOException {
File f = new File(path);
if (!f.exists()) {
throw new IOException("Config file missing: " + path);
}
// ... read the file
}
// Whoever calls readConfig must either:
// 1. Wrap it in try/catch, OR
// 2. Add "throws IOException" to their own method signatureOn exams, you'll trace through code with try/catch and predict the output. The key rule: when an exception is thrown, the rest of the try block is skipped and execution jumps to the matching catch.
try {
System.out.println("A");
int x = 10 / 0; // ArithmeticException thrown here
System.out.println("B"); // SKIPPED
} catch (ArithmeticException e) {
System.out.println("C");
} catch (Exception e) {
System.out.println("D"); // not reached — caught above
} finally {
System.out.println("E");
}
System.out.println("F");A
C
E
Fpublic static int process(int x) {
System.out.println("P1");
int result = 100 / x;
System.out.println("P2");
return result;
}
public static void main(String[] args) {
try {
System.out.println("M1");
int val = process(0); // throws ArithmeticException inside process
System.out.println("M2"); // SKIPPED
} catch (ArithmeticException e) {
System.out.println("M3");
} finally {
System.out.println("M4");
}
System.out.println("M5");
}M1
P1
M3
M4
M5finally ALWAYS runs (even if there's a return in try or catch)