Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was mentoring a junior dev who was building a file-import tool for a client's payroll system. On his local machine, everything worked perfectly. But the second it hit production, the application started crashing randomly. It turned out that some users were uploading files with slightly corrupted encoding or missing headers. Because he hadn't handled the potential exceptions, the program would just stop dead in its tracks. Even worse, because the file streams weren't being closed properly during those crashes, the server eventually ran out of file handles and stopped accepting any new uploads. He spent an entire weekend manually restarting servers—all because he didn't have a safety net for his "risky" code.

Catching the Chaos

In Java, any code that interacts with the "outside world"—like reading a file, connecting to a database, or taking user input—is inherently risky. You can't guarantee the file exists or the network is up. This is where the try-catch block comes in. You wrap the dangerous code in a try block, and if something goes wrong, Java throws an exception. Instead of letting that exception crash your entire program, the catch block intercepts it.

Here is how that looks in practice. Instead of just calling a method and hoping for the best, we wrap it like this:

try {
    String data = readFile("user_profile.txt");
    System.out.println("Profile loaded: " + data);
} catch (FileNotFoundException e) {
    System.err.println("I couldn't find the profile file. Using default settings instead.");
    // Here, we handle the error gracefully instead of crashing
}

I've seen developers make the mistake of catching the generic Exception class for everything. Try to avoid that. Catch the specific exception you expect—like FileNotFoundException or IOException. If you catch everything, you might accidentally hide a NullPointerException that you actually should be fixing in your logic.

The Safety Net of Finally

Now, here is the part that my junior dev missed: the finally block. Regardless of whether the try block succeeded or the catch block was triggered, the finally block always runs. This is the gold standard for cleanup. If you opened a database connection or a file stream, you must close it here. If you don't, you end up with memory leaks or locked files that haunt you at 3:00 AM.

Consider this flow:

Scanner scanner = null;
try {
    scanner = new Scanner(new File("settings.conf"));
    // Imagine some complex logic here that might throw an error
} catch (FileNotFoundException e) {
    System.out.println("Settings file missing!");
} finally {
    if (scanner != null) {
        scanner.close();
        System.out.println("Resource closed safely.");
    }
}

The finally block is non-negotiable when dealing with external resources. Even if there is a return statement inside the try or catch blocks, Java will still execute the finally block before the method actually returns. It's the most reliable way to ensure your application doesn't leave a mess behind.

Knowing When to Let it Crash

You might be tempted to wrap your entire main method in one giant try-catch to "prevent crashes." Don't do this. It's a bad habit. Some errors are "unrecoverable"—meaning if they happen, the app should stop because it can't possibly continue in a valid state. If your core database is missing, catching that exception and printing "Oops!" isn't helpful; the program can't do its job. Only catch exceptions that you actually have a strategy to handle or recover from.




📋 Practical Task

Exercise: The Robust Configuration File Loader

You are tasked with writing a utility that reads a system version number from a file named version.txt. However, the file might be missing, or the system might prevent you from reading it.

Write a program that does the following:

  • Creates a BufferedReader to read from version.txt.
  • Wraps the reading logic in a try block.
  • Uses a catch block to handle IOException (which covers file not found and other read errors), printing a user-friendly message like "Error: Version file could not be read."
  • Uses a finally block to ensure the BufferedReader is closed, regardless of whether the read was successful or failed. (Note: You will need to declare the reader outside the try block to make it accessible in the finally block).

Bonus Challenge: Ensure that your finally block checks if the reader is null before attempting to call .close() to avoid a NullPointerException.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.