Java + Spring Boot track

Lesson 15 · Part 4: Exceptions & I/O

Exception Handling

try / catch / finally, Checked vs Unchecked, throws

Part 4: Exceptions & I/O3 min readFree lesson

Exception handling is a mechanism that safely handles abnormal situations (no file, incorrect input, network failure, etc.) encountered during program execution. Java provides two types of exception systems: try/catch/finally and checked/unchecked.

Learning Objectives

  • Know the flow of try / catch / finally
  • Know the difference between checked exception and unchecked(RuntimeException)
  • Exceptions can be delegated to throws
  • Create custom exception classes
  • Look at multi-catch and exception chaining (cause)

Key concepts

1) try / catch / finally

try {
    int n = Integer.parseInt("abc");
} catch (NumberFormatException e) {
    System.out.println("Not a number:" + e.getMessage());
} finally {
    System.out.println("always run");
}

finally runs unconditionally regardless of exceptions (used for resource cleanup).

2) Checked vs Unchecked

Type Example Obligation processing
Checked (Exception descendant) IOException, SQLException Must declare try/catch or throws
Unchecked (RuntimeException descendant) NullPointerException, IllegalArgumentException No forced processing
Error OutOfMemoryError Attempt to process X (system error)

3) throws

static void readFile(String path) throws IOException {
    Files.readString(Path.of(path));
}

This is a keyword that tells the caller to “handle this exception.”

4) Custom Exception

class InvalidAgeException extends RuntimeException {
    public InvalidAgeException(String msg) { super(msg); }
}

Creating exception classes with clear domain semantics allows callers to clearly plan their processing flow.

5) Multi-catch

try {
    ...
} catch (IOException | NumberFormatException e) {
    System.out.println("Failure:" + e.getMessage());
}

Starting with JDK 7+, multiple exception types are allowed in a single catch clause.

See as example

Example 1 — TryCatch.java: Basic flow

public class TryCatch {
    public static void main(String[] args) {
        try {
            int n = Integer.parseInt("abc");
            System.out.println(n);
        } catch (NumberFormatException e) {
            System.out.println("Not a number:" + e.getMessage());
        } finally {
            System.out.println("End Cleanup");
        }
        System.out.println("program continues");
    }
}

Execution result

Not a number: For input string: "abc"
End Cleanup
program continues

Note: If catch catches an exception, the program does not die and continues executing the next line.

Example 2 — MultiCatch.java: Handling multiple exceptions simultaneously

public class MultiCatch {
    public static void main(String[] args) {
        Object[] inputs = { "10", "abc", null };
        for (Object o : inputs) {
            try {
                String s = (String) o;
                int n = Integer.parseInt(s.trim());
                System.out.println("Success:" + n);
            } catch (NullPointerException | NumberFormatException e) {
                System.out.println("Failure:" + e.getClass().getSimpleName());
            }
        }
    }
}

Execution result

Success: 10
Failure: NumberFormatException
Failure: NullPointerException

Note: If two exceptions have semantically the same handling, they can be combined on one line.

Example 3 — ThrowsAndCustom.java : throws + user exception

public class ThrowsAndCustom {
    static class InvalidAgeException extends RuntimeException {
        public InvalidAgeException(String msg) { super(msg); }
    }

    static void register(String name, int age) {
        if (age < 0) {
            throw new InvalidAgeException("Age is 0 or greater:" + age);
        }
        System.out.println("Registration:" + name + " (" + age + ")");
    }

    public static void main(String[] args) {
        try {
            register("exponent", 21);
            register("unknown", -1);
        } catch (InvalidAgeException e) {
            System.out.println("Reject:" + e.getMessage());
        }
    }
}

Execution result

Registration: Index (21)
Rejected: Age greater than or equal to 0: -1

Note: The unchecked exception (RuntimeException inheritance) does not require the declaration of throws.

Example 4 — ExceptionChaining.java: Preserving exception causes

public class ExceptionChaining {
    static void load() {
        try {
            Integer.parseInt("nope");
        } catch (NumberFormatException low) {
            throw new IllegalStateException("Failed to load settings", low);
        }
    }

    public static void main(String[] args) {
        try {
            load();
        } catch (IllegalStateException e) {
            System.out.println("Outside:" + e.getMessage());
            System.out.println("Cause:" + e.getCause());
        }
    }
}

Execution result

Outside: Failed to load settings
Caused by: java.lang.NumberFormatException: For input string: "nope"

Note: Patterns that wrap low-level exceptions in higher-level semantics while preserving the cause are very useful for debugging.

Common Mistakes

  1. Catch and ignore all exceptions with catch (Exception e) (silent failure)
  2. Empty catch block → Bury the problem
  3. Overwrite the body return using return in finally
  4. Attempting to ignore checked exceptions without throwing or trying/catching them (compilation error)
  5. Overuse of RuntimeException without domain meaning (user-defined exceptions are clearer)

Cleanup

  • Exceptions are objects that represent abnormal flows.
  • Checked must be processed, unchecked is optional.
  • Clarify domain semantics with custom exceptions
  • Preserving the cause makes tracking easier.

Try it yourself

cd 04_Exception_Input/Output/15_Exception-handling/src
javac ThrowsAndCustom.java
java ThrowsAndCustom

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/ExceptionChaining.javaJava
public class ExceptionChaining {
    static void load() {
        try {
            Integer.parseInt("nope");
        } catch (NumberFormatException low) {
            throw new IllegalStateException("Failed to load settings", low);
        }
    }

    public static void main(String[] args) {
        try {
            load();
        } catch (IllegalStateException e) {
            System.out.println("Outside:" + e.getMessage());
            System.out.println("Cause:" + e.getCause());
        }
    }
}
src/MultiCatch.javaJava
public class MultiCatch {
    public static void main(String[] args) {
        Object[] inputs = { "10", "abc", null };
        for (Object o : inputs) {
            try {
                String s = (String) o;
                int n = Integer.parseInt(s.trim());
                System.out.println("Success:" + n);
            } catch (NullPointerException | NumberFormatException e) {
                System.out.println("Failure:" + e.getClass().getSimpleName());
            }
        }
    }
}
src/ThrowsAndCustom.javaJava
public class ThrowsAndCustom {
    static class InvalidAgeException extends RuntimeException {
        public InvalidAgeException(String msg) { super(msg); }
    }

    static void register(String name, int age) {
        if (age < 0) {
            throw new InvalidAgeException("Age is 0 or greater:" + age);
        }
        System.out.println("Registration:" + name + " (" + age + ")");
    }

    public static void main(String[] args) {
        try {
            register("exponent", 21);
            register("unknown", -1);
        } catch (InvalidAgeException e) {
            System.out.println("Reject:" + e.getMessage());
        }
    }
}
src/TryCatch.javaJava
public class TryCatch {
    public static void main(String[] args) {
        try {
            int n = Integer.parseInt("abc");
            System.out.println(n);
        } catch (NumberFormatException e) {
            System.out.println("Not a number:" + e.getMessage());
        } finally {
            System.out.println("End Cleanup");
        }
        System.out.println("program continues");
    }
}

Assignment

Problem 1 — Safe Division

  • File name: Homework01.java
  • Core concepts: try/catch, ArithmeticException

Requirements

  • safeDiv(int a, int b) method: Returns 0 when divided by 0, otherwise returns the result.
  • (10,2), (10,0), (7,3) call result output.

Expected output

10 / 2 = 5
10 / 0 = 0
7 / 3 = 2

Problem 2 — Custom Exception

  • File name: Homework02.java
  • Core Concept: Custom Exception + throw

Requirements

-Definition of class NotEnoughBalanceException extends RuntimeException.

  • withdraw(balance, amount): If amount > balance, the above exception occurs, otherwise the balance is returned after deduction.
  • (1000, 300), (1000, 2000) scenario processing.

Expected output

Balance: 700
Failure: Insufficient balance 1000 < 2000

Check the correct answer

After solving it yourself, compare it with the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

Homework01.javaJava
/** Safe integer division.*/
public class Homework01 {
    public static void main(String[] args) {
        int[][] cases = { {10, 2}, {10, 0}, {7, 3} };
        for (int[] c : cases) {
            System.out.println(c[0] + " / " + c[1] + " = " + safeDiv(c[0], c[1]));
        }
    }

    static int safeDiv(int a, int b) {
        try {
            return a / b;
        } catch (ArithmeticException e) {
            return 0;
        }
    }
}
Homework02.javaJava
/** Custom exception: Insufficient balance.*/
public class Homework02 {
    static class NotEnoughBalanceException extends RuntimeException {
        public NotEnoughBalanceException(String msg) { super(msg); }
    }

    static long withdraw(long balance, long amount) {
        if (amount > balance) {
            throw new NotEnoughBalanceException("insufficient balance" + balance + " < " + amount);
        }
        return balance - amount;
    }

    public static void main(String[] args) {
        try {
            long b1 = withdraw(1000, 300);
            System.out.println("Balance:" + b1);
            long b2 = withdraw(1000, 2000);
            System.out.println("Balance:" + b2);
        } catch (NotEnoughBalanceException e) {
            System.out.println("Failure:" + e.getMessage());
        }
    }
}

Learn this lesson with a mentor

Work through the Java + Spring Boot track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now