CodeGym /Courses /JAVA 25 SELF /IOException and FileNotFoundException: error handling

IOException and FileNotFoundException: error handling

JAVA 25 SELF
Level 38, Lesson 0
Available

1. I/O exception hierarchy

In Java, when working with files and other external resources, you almost always have to deal with so-called input/output exceptions. These are objects that are thrown (throw) when something goes wrong while reading or writing data. For example, if a file is not found, access is denied, or the disk suddenly “got tired.”

The main players in our lecture

Java has a whole hierarchy of such exceptions. Here are the main ones:

  • IOException — the base class for all I/O errors. If something goes wrong with files, streams, or the network — it’s almost always “to blame” (or one of its many subclasses).
  • FileNotFoundException — a “child” of IOException that appears when you try to open a file that does not exist, or the path is incorrect.

Other subclasses:

  • EOFException — also a direct “child” of IOException. Signals that reading unexpectedly reached the end of the file.
  • MalformedInputException — a “grandchild”: it extends CharacterCodingException, which, in turn, extends IOException. This error occurs if a file cannot be correctly interpreted in the specified encoding (for example, you expected "UTF-8" but got a broken sequence).
  • There are also SocketException, ZipException, and other specialized “relatives,” each with its own area of responsibility. The deeper in the hierarchy, the narrower and more specific the situation.

A small simplified diagram:

java.lang.Exception
└── java.io.IOException
    ├── java.io.FileNotFoundException
    ├── java.io.EOFException
    ├── java.nio.charset.MalformedInputException
    └── ... (others)

Interesting fact:
In Java, almost all file operations require you to declare or handle IOException — these are so-called checked exceptions. The compiler won’t let you forget about error handling!

2. When and why these exceptions occur

Opening a non-existent file

The most common case is when you try to open a file that doesn’t exist. It’s like arriving at a bus stop, but the bus doesn’t even exist in the schedule.

FileInputStream fis = new FileInputStream("abracadabra.txt"); // Boom! FileNotFoundException

Attempting to write to a file without permissions

If you try to write a file to a folder where you don’t have permissions, you’ll get a FileNotFoundException or an IOException (depending on the situation).

FileOutputStream fos = new FileOutputStream("/system/secret.txt"); // Boom! FileNotFoundException or IOException

Read/write errors due to media issues

Sometimes the file seems to exist, but the disk is damaged, the file is locked by another program, or the power suddenly goes out — then you’ll get an IOException with various messages.

Other causes

  • The file is read-only, but you’re trying to write.
  • The file path is too long or contains invalid characters.
  • The file is in use by another process.
  • The disk is full.
  • The file was deleted by another process between the check and the use.

3. Handling with try-catch

Whenever you work with files, streams, or the network — use try-catch. It’s like an airbag: if something goes wrong, the program won’t crash; it can react properly.

How to catch exceptions correctly?

In Java, you should catch more specific exceptions before the general ones. If you put a general catch (IOException e) first, then narrower ones, such as FileNotFoundException, simply won’t work — execution won’t reach them.

Correct structure:

try {
    // Work with a file
} catch (FileNotFoundException e) {
    // Handling the "file not found" situation
} catch (IOException e) {
    // Handling other I/O errors
}

Why is that?
Because FileNotFoundException is a special case of IOException. If you catch the general case earlier, the specific one won’t reach its catch.

Code example: handling errors when opening a file

import java.io.*;

public class FileReaderExample {
    public static void main(String[] args) {
        String filename = "notes.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line = reader.readLine();
            System.out.println("First line of the file: " + line);
            reader.close();
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + filename);
        } catch (IOException e) {
            System.out.println("Error reading the file: " + e.getMessage());
        }
    }
}

Note:
Even if you’ve checked that the file exists, always keep try-catch — the file can disappear at any moment (for example, another process may delete it).

4. Practice: writing code with error handling

We’ll create simple examples and make them react correctly to a missing file and other errors.

Step 1: Try to open a non-existent file

import java.io.*;

public class NotesApp {
    public static void main(String[] args) {
        String filename = "my_notes.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line;
            System.out.println("Your notes:");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            reader.close();
        } catch (FileNotFoundException e) {
            System.out.println("Oops! Notes file not found: " + filename);
            System.out.println("Tip: create the file or check the name.");
        } catch (IOException e) {
            System.out.println("An error occurred while reading the file: " + e.getMessage());
        }
    }
}

Step 2: Add handling for writing a file

import java.io.*;

public class NotesWriter {
    public static void main(String[] args) {
        String filename = "my_notes.txt";
        try {
            BufferedWriter writer = new BufferedWriter(new FileWriter(filename, true)); // append: true
            writer.write("New note!\n");
            writer.close();
            System.out.println("Note added successfully!");
        } catch (IOException e) {
            System.out.println("Error writing to file: " + e.getMessage());
        }
    }
}

Step 3: A general example with error logging

import java.io.*;

public class SafeFileCopier {
    public static void main(String[] args) {
        String source = "source.txt";
        String target = "target.txt";
        try {
            BufferedReader reader = new BufferedReader(new FileReader(source));
            BufferedWriter writer = new BufferedWriter(new FileWriter(target));
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.newLine();
            }
            reader.close();
            writer.close();
            System.out.println("File copied successfully!");
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("I/O error: " + e.getMessage());
        }
    }
}

Tip:
In real applications, you should log errors not only to the screen but also to a file or a log so that you can later figure out what exactly went wrong.

5. Table: main I/O exceptions

Exception When does it occur? How to handle?
FileNotFoundException
File not found, path does not exist, insufficient permissions Inform the user, verify path/permissions, create the file if necessary
EOFException
Unexpected end of file while reading Report corruption/incompleteness, try to partially recover the data
IOException
General I/O error (disk, permissions, lock) Check details, gracefully terminate the operation, retry if possible
MalformedInputException
Invalid encoding or file structure Report corruption, try another encoding/source

6. Typical mistakes when handling I/O exceptions

Mistake #1: Catching only the general Exception. It’s very tempting to just write catch (Exception e), but then you won’t be able to distinguish what exactly went wrong. It’s better to first catch specific exceptions (FileNotFoundException), and then the general IOException.

Mistake #2: Not closing streams on errors. If you opened a file and then an exception occurred, the stream may remain unclosed. Use try-with-resources or close resources in finally.

Mistake #3: Ignoring exception messages. Don’t just print “Error!”, show the details: e.getMessage(). This will help you understand what went wrong faster.

Mistake #4: Not handling FileNotFoundException when writing. Many people think that FileNotFoundException is only about reading. In fact, it can also occur when writing (incorrect path, no permissions to create a file, etc.).

Mistake #5: Not checking permissions. If the program runs with limited permissions, many file operations may fail. Always take this into account and inform the user.

1
Task
JAVA 25 SELF, level 38, lesson 0
Locked
Searching for a Lost Record in an Ancient Library 📚
Searching for a Lost Record in an Ancient Library 📚
1
Task
JAVA 25 SELF, level 38, lesson 0
Locked
Recording an important message in the royal archive 📜
Recording an important message in the royal archive 📜
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION