What are exceptions? Exception hierarchy

A divide by zero, a missing file, a customer typing “two” instead of 2: things go wrong in every real program. Java reports them as exception objects that travel up the call stack looking for someone to handle them. You'll see exactly what the JVM does when one is thrown, learn to read a stack trace in seconds, meet the exception family tree, and then practise with 11 problems.

Module 9 · Exception Handling⏱ 22 minIntermediate11 practice problems
1 The story

The fee splitter that died

Riya writes a small program for the college fest: each team's registration fee is split equally among its members. It works for Robotics and Quiz. Then the Dance team registers with 0 members by mistake, and her screen fills with red text. The last line, “All teams done”, never prints.

Call stack perHead(900, 0) splitFee("Dance") main(args) exception travels up no catch? frame removed 900 / 0 has no answer in int maths Robotics pays Rs 300 each Exception in thread "main" java.lang.ArithmeticException: / by zero at Main.perHead(Main.java:3) at Main.splitFee(Main.java:7) at Main.main(Main.java:13) “All teams done” never appears myphatshala.in
Riya

Sir, my program just died. Half the screen is red. Did I break Java?

Arjun Sir

No, Java protected you. 900 divided by 0 has no int answer, so instead of printing a wrong number, the JVM threw an exception: an object that says what went wrong and where.

Riya

But why did the whole program stop? Only one team was wrong.

Arjun Sir

Because nobody caught it. The exception climbed from perHead to splitFee to main, looking for code that could handle it. Nobody could, so the JVM printed that red map, the stack trace, and stopped. Today you learn to read that map.

2 Definition

What is an exception?

Formal definition (write this in exams)

An exception is an event during program execution that disrupts the normal flow of instructions. In Java it is represented by an object of class Throwable or one of its subclasses. When an exception is thrown, normal execution stops and the JVM searches the call stack, from the current method back towards main, for a matching handler (a catch block). If none is found, the thread's default handler prints the stack trace and the thread terminates.

In simple words

An exception is an alarm with a note attached. The note says what went wrong and where. The alarm goes up through every method that is waiting, until someone says “I'll handle this.” If nobody does, the program stops and prints the note.

Words you'll meet in this lesson

ThrowCreate an exception object and start the search for a handler.
HandlerA catch block that matches the exception's type.
Call stackThe frames of all methods currently running, newest on top.
PropagationThe exception moving from a method to its caller.
Stack traceThe list of method calls (with line numbers) at the moment of the throw.
Uncaught exceptionOne that reaches the top of the stack with no handler.

Exceptions at a glance

Root classjava.lang.Throwable. Only Throwables can be thrown or caught.
Two branchesError (serious JVM problems, don't catch) and Exception (problems a program can handle)
UncheckedRuntimeException and its subclasses, plus all Errors. The compiler does not force you to handle them.
CheckedEvery other Exception (e.g. IOException). Must be caught or declared (Lesson 9.3).
Useful methodsgetMessage(), toString(), printStackTrace(), getStackTrace(), getCause()
If uncaughtStack trace printed to the error stream; the thread ends. For main, the program exits with status 1.

Exam tip: “Exception” and “Error” are both subclasses of Throwable, but Error is not a subclass of Exception. So catch (Exception e) does not catch a StackOverflowError. A favourite MCQ.

3 Types and ways to use it

The exception family tree

Error

The JVM itself is in trouble: StackOverflowError, OutOfMemoryError. Your program usually cannot fix these. Don't catch them.

Unchecked

Checked exceptions

Problems outside your code's control: a missing file (IOException), a failed database call (SQLException). The compiler makes you deal with them.

extends Exception

RuntimeException

Usually a bug or bad input: divide by zero, a null reference, a wrong index, text that isn't a number. Fix the code or validate the input.

Unchecked
Object
 └─ Throwable
     ├─ Error
     │   └─ VirtualMachineError → StackOverflowError, OutOfMemoryError
     └─ Exception
         ├─ IOException → FileNotFoundException          (checked)
         ├─ InterruptedException, SQLException …          (checked)
         └─ RuntimeException                              (unchecked)
             ├─ ArithmeticException
             ├─ NullPointerException
             ├─ ClassCastException
             ├─ IllegalArgumentException → NumberFormatException
             └─ IndexOutOfBoundsException → ArrayIndexOutOfBoundsException,
                                             StringIndexOutOfBoundsException

Six common exceptions and their real messages

public class Main {
    static String coupon;              // never set, so it is null

    static void trigger(int n) {
        int[] seats = new int[3];
        Object amount = "500";
        switch (n) {
            case 1 -> System.out.println(10 / 0);
            case 2 -> System.out.println(seats[3]);
            case 3 -> System.out.println(coupon.length());
            case 4 -> System.out.println(Integer.parseInt("12O"));
            case 5 -> System.out.println((Integer) amount);
            case 6 -> System.out.println("UPI".charAt(5));
        }
    }

    public static void main(String[] args) {
        for (int n = 1; n <= 6; n++) {
            try {
                trigger(n);
            } catch (RuntimeException e) {
                System.out.println(e);
            }
        }
    }
}
java.lang.ArithmeticException: / by zero java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3 java.lang.NullPointerException: Cannot invoke "String.length()" because "Main.coupon" is null java.lang.NumberFormatException: For input string: "12O" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap') java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 3

The try/catch in main lets the loop continue after each failure; you'll learn it properly in Lesson 9.2. The arrow-style switch needs Java 14 or later. Messages shown are from Java 21; older versions word some of them differently (detailed NullPointerException messages arrived in Java 14).

ExceptionTypical causeHow to prevent it
ArithmeticExceptionInteger division or % by zeroCheck the divisor first
ArrayIndexOutOfBoundsExceptionIndex < 0 or ≥ lengthLoop with i < a.length; validate indexes
NullPointerExceptionCalling a method or reading a field on nullInitialise variables; check for null
NumberFormatExceptionParsing text that isn't a valid numberValidate or trim input; catch and show a message
ClassCastExceptionCasting an object to a class it isn'tCheck with instanceof before casting
StringIndexOutOfBoundsExceptionBad index in charAt, substringCheck length() first

Not everything is an exception: 10.0 / 0 gives Infinity and 0.0 / 0 gives NaN. Only integer division by zero throws ArithmeticException. Floating-point maths follows the IEEE 754 rules and never throws for this.

4 Step by step

What the JVM does when something goes wrong

  1. An illegal operation happens

    return fee / members;     // members is 0
    The idiv bytecode instruction finds a zero divisor. Instead of producing a value, the JVM creates an ArithmeticException object on the heap and fills in its message (“/ by zero”) and the current call stack.
  2. Normal flow stops at once

    The rest of the line, and every line after it in that method, is skipped. There is no return value.

    The JVM never moves to the next instruction. Control will jump to a handler, wherever one is found.
  3. Look for a handler in the current method

    try { … } catch (ArithmeticException e) { … }
    Every compiled method has an exception table (see it with javap -c): rows of “from, to, handler, type”. The JVM checks whether the failing instruction lies in a protected range and whether the type matches, including parent classes.
  4. No handler? Pop the frame and try the caller

    perHead has no catch, so its frame is removed and the search moves to splitFee, then main.

    This is called propagation or stack unwinding. Local variables of popped frames are gone; the exception object itself stays on the heap, carrying the information.
  5. Nobody catches it: the stack trace

    Exception in thread "main" java.lang.NumberFormatException: For input string: "1,200"
    	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
    	at java.base/java.lang.Integer.parseInt(Integer.java:662)
    	at java.base/java.lang.Integer.parseInt(Integer.java:778)
    	at Main.parseAmount(Main.java:3)
    	at Main.main(Main.java:10)
    Read it like this: line 1 = which exception and why (the message). Then the at lines, newest call first. Skip the java.base/… library lines and find the first line from your own code: parseAmount, line 3, called from main, line 10. Library line numbers differ between JDK versions.
5 See it happen

An exception climbs the call stack: both views

Left: the line being run and the console. Right: the frames on the stack and the exception object on the heap.

Uncaught ArithmeticException in the fee splitter 6 steps
What you see splitFee("Dance", 900, 0); int share = perHead(fee, members); return fee / members; // 900 / 0 // perHead, splitFee: no catch // main: no catch either // default handler takes over Robotics pays Rs 300 each Exception in thread "main" java.lang.ArithmeticException: / by zero at Main.perHead(Main.java:3) at Main.splitFee(Main.java:7) at Main.main(Main.java:13) main calls splitFee for the Dance team. splitFee calls perHead. 900 / 0: the JVM throws. Nothing is printed. The println in splitFee is skipped. "All teams done" will never print. The program stops with exit code 1. myphatshala.in
Behind the screen Stack Heap perHead(900, 0) perHead popped splitFee("Dance") splitFee popped main(args) i=1 stack empty: thread ends #1 ArithmeticException message: "/ by zero" trace: perHead:3, splitFee:7, main:13 Two frames: main is waiting for splitFee. A third frame is pushed for perHead. Exception object created; it records the stack. No handler in perHead: frame removed. No handler anywhere: last frame removed. Default handler prints #1's trace, exit code 1. myphatshala.in
6 Real-world use

Exceptions in real systems

Every production Java system deals with exceptions every day. What matters is that they are caught at the right place, logged with their stack trace, and turned into something useful for the user.

Watch it with an example: a batch job that survives a bad record

A nightly job imports 5 student fee records. One row has text instead of a number. With the try/catch inside the loop, one bad row doesn't stop the other four.

try/catch around each record 6 steps
Animated example 101, 45000 102, 38000 103, N/A 104, 52000 105, 41000 ✓ saved ✓ saved ✓ saved ✓ saved ✗ logged ▶ log row 103: NumberFormat Exception "N/A" Imported 4 of 5. 1 error to fix. parseInt("N/A") throws → jump to catch myphatshala.in
INFO order 1042 okERROR NullPointer… at Billing.tax:41INFO order 1043 ok

1. Logs and alerts

Servers write every unexpected exception, with its full stack trace, to log files. Monitoring tools group errors by exception type and top frame, and alert the on-call engineer when one suddenly increases.

Exception→log.error("…", e)→Alert + trace for debugging
500

2. Web APIs keep serving

When code handling one request throws, the web framework catches it at the top, returns an error response (like HTTP 500 or 400) to that one user, and keeps serving everyone else.

Request→handler throws→Error response, server stays up
skip

3. Batch jobs that survive bad records

A nightly job importing 1 lakh rows should not die on row 4,512 because one amount is “1,2OO”. It catches the exception for that row, writes it to an error report, and continues.

Rows→try per row→Imported + error report
Qty: twoPlease enter digits

4. Friendly messages for bad input

A NumberFormatException from a form field becomes “Please enter a valid amount” on screen, instead of a crash or a blank page.

User input→parseInt → NumberFormatException→Validation message

5. Android crash reports

An uncaught exception in an Android app closes the app. Crash-reporting tools collect the stack trace from users' phones so developers can see exactly which line failed.

Uncaught exception→stack trace→Crash dashboard

Examples are simplified. Real systems use logging libraries and frameworks that catch exceptions at the top level; the idea of a handler higher up the call stack is the same.

7 Run the code

Riya's fee splitter, now surviving the bad team

public class Main {
    static int perHead(int fee, int members) {
        return fee / members;
    }

    static void splitFee(String team, int fee, int members) {
        int share = perHead(fee, members);
        System.out.println(team + " pays Rs " + share + " each");
    }

    public static void main(String[] args) {
        String[] teams = {"Robotics", "Dance", "Quiz"};
        int[] fees = {1200, 900, 600};
        int[] members = {4, 0, 3};
        for (int i = 0; i < teams.length; i++) {
            try {
                splitFee(teams[i], fees[i], members[i]);
            } catch (ArithmeticException e) {
                System.out.println(teams[i] + " skipped. Stack trace:");
                e.printStackTrace(System.out);
            }
        }
        System.out.println("All teams done");
    }
}
Robotics pays Rs 300 each Dance skipped. Stack trace: java.lang.ArithmeticException: / by zero at Main.perHead(Main.java:3) at Main.splitFee(Main.java:7) at Main.main(Main.java:17) Quiz pays Rs 200 each All teams done
  • return fee / members;The exception is thrown here, two frames above main.
  • try { splitFee(…); }Protects the call. If anything inside, however deep, throws an ArithmeticException, control jumps to the catch.
  • e.printStackTrace(System.out)Prints the same trace the JVM would print on a crash. Without an argument it goes to System.err.
  • All teams donePrints because the exception was handled. Compare with the crash in the story.
8 Line by line

Follow the exception from perHead to main

Watch the stack grow to three frames, then shrink as the exception travels down to main's catch block. The exception object appears on the heap.

Worked examples: real problems, solved and traced in memory

Three small programs where something goes wrong deep inside a method call. Run each one, then step through it and watch the exception object appear on the heap while the JVM pops frames off the stack until it reaches a method that can handle it.

Example 1Ed-tech · results portal

Average marks for an empty section

Problem statement

A results portal prints the average marks of each section. Section FY-B has no marks uploaded yet, so its array is empty. Show what happens when average divides by the array length 0, and keep the portal running.

Input
FY-A marks = {70, 80}
FY-B marks = {} (nothing uploaded)
Expected output
FY-A average: 75
Report failed: / by zero
Done

Approach

  1. main calls printReport, which calls average: three frames on the stack.
  2. For FY-B, total / marks.length is 0 / 0 in int arithmetic, so the JVM throws ArithmeticException.
  3. Neither average nor printReport has a handler, so both frames are popped. The catch in main handles it.
  4. The program then continues after the try/catch and prints Done.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2E-commerce · cart

A quantity typed as a word

Problem statement

A shopping cart lets customers type the quantity. One customer types two instead of 2. Integer.parseInt fails inside a helper two calls deep. Catch the problem in main and tell the customer what to fix.

Input
first item quantity  = "2"
second item quantity = "two"
Expected output
Added 2 x Notebook
Please enter digits only. For input string: "two"
Items in cart: 2

Approach

  1. addToCart asks parseQty to turn the text into an int.
  2. Integer.parseInt("two") creates a NumberFormatException with the message For input string: "two" and throws it.
  3. The exception skips the rest of parseQty and addToCart (their frames are popped) and lands in main's catch block.
  4. The cart total printed at the end shows only the item that was really added.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3Travel · seat booking

Which family does the error belong to?

Problem statement

A bus booking app reads the passenger name for a seat number. Seat 7 does not exist on a 4-seat sample bus. Catch the problem as a general RuntimeException in main, then find out its exact class and which branch of the hierarchy it belongs to.

Input
passengers = {"Asha", "Ravi", "Meena", "Imran"}
seat asked = 7
Expected output
Class: ArrayIndexOutOfBoundsException
IndexOutOfBounds family? true
Unchecked? true

Approach

  1. main calls describeSeat, which calls passengerAt; the bad index is used in the innermost method.
  2. The JVM throws ArrayIndexOutOfBoundsException. Because it is a subclass of RuntimeException, a catch (RuntimeException e) in main can handle it.
  3. e.getClass().getSimpleName() reveals the real class, and instanceof checks show its parents in the hierarchy.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 9.1 in one screen

  • An exception is an object describing a problem; throwing it stops normal flow.
  • The JVM searches for a handler in the current method, then pops the frame and tries the caller.
  • No handler anywhere → default handler prints the stack trace and the thread ends (exit code 1 for main).
  • Stack trace: line 1 = type and message; at lines newest first; find the first line from your code.
  • Throwable → Error (don't catch) and Exception.
  • RuntimeException and subclasses are unchecked; other Exceptions are checked.
  • Common: ArithmeticException, NullPointerException, ArrayIndexOutOfBounds, NumberFormatException, ClassCastException.

Interview and viva questions

What is the difference between an Error and an Exception?

Both extend Throwable. An Error signals a serious problem in the JVM or environment, such as running out of memory or stack space, that an application normally should not try to catch. An Exception signals a condition the program can reasonably handle, such as bad input or a missing file.

What happens internally when an exception is thrown?

The JVM creates the exception object (recording the stack trace), stops normal execution, and looks up the current method's exception table for a handler covering that instruction and matching the type. If none matches, it pops the frame and repeats in the caller. If the stack is exhausted, the thread's uncaught-exception handler prints the trace and the thread terminates.

What is a stack trace and how do you use it?

It is the list of method calls that were active when the exception was created, newest first, with file names and line numbers. Read the first line for the type and message, then go down to the first frame from your own code: that is usually where to start debugging. A “Caused by:” section further down shows the original exception if one was wrapped.

What is the difference between getMessage(), toString() and printStackTrace()?

getMessage() returns only the detail text (can be null). toString() returns the class name plus ": " + message when there is one. printStackTrace() prints toString() followed by every at line, to System.err by default.

Should you catch Throwable or Error?

Almost never. Errors like OutOfMemoryError mean the JVM may be in a bad state, and catching Throwable also hides programming bugs. Catch the specific exceptions you can handle; let a top-level framework handler log anything unexpected.

Practice problems

These problems train you to predict which exception a line throws, read what it tells you, and keep a program running when one item is bad. A small try/catch is used to catch the exception (Lesson 9.2 covers it fully). The last three are real business tasks.

P1Easy

Safe division

Divide 100 by each number in an array. When the number is 0, print a friendly message with the exception's message instead of crashing.

Input
divisors = {4, 0, 5}
Expected output
100 / 4 = 25
100 / 0 failed: / by zero
100 / 5 = 20
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        int[] divisors = {4, 0, 5};
        for (int d : divisors) {
            try {
                System.out.println("100 / " + d + " = " + (100 / d));
            } catch (ArithmeticException e) {
                System.out.println("100 / " + d + " failed: " + e.getMessage());
            }
        }
    }
}
100 / 4 = 25 100 / 0 failed: / by zero 100 / 5 = 20

How it works: the try is inside the loop, so only the turn with 0 fails; the loop carries on with 5. getMessage() returns the text the JVM attached to the exception, here / by zero. If the try were outside the loop, the first 0 would end the whole loop.

P2Easy

Name and message of an exception

Trigger an ArrayIndexOutOfBoundsException, catch it, and print its simple class name, its message, and its full toString().

Input
int[] seats = new int[2]; read seats[2]
Expected output
Class:    ArrayIndexOutOfBoundsException
Message:  Index 2 out of bounds for length 2
toString: java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        int[] seats = new int[2];
        try {
            System.out.println(seats[2]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Class:    " + e.getClass().getSimpleName());
            System.out.println("Message:  " + e.getMessage());
            System.out.println("toString: " + e);
        }
    }
}
Class: ArrayIndexOutOfBoundsException Message: Index 2 out of bounds for length 2 toString: java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2

How it works: every exception is an object, so it has a class and fields. getMessage() gives only the detail text. toString() (used when you print e) gives the full class name, a colon and the message. That is exactly the first line of a stack trace.

P3Easy

Count valid marks

Marks arrive as text. Convert each one with Integer.parseInt, add up the valid ones, and count the entries that are not numbers.

Input
entries = {"78", "9O", "85", "", "64"}
Expected output
Skipped: For input string: "9O"
Skipped: For input string: ""
Total of valid marks: 227
Invalid entries: 2
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        String[] entries = {"78", "9O", "85", "", "64"};
        int total = 0, bad = 0;
        for (String s : entries) {
            try {
                total += Integer.parseInt(s);
            } catch (NumberFormatException e) {
                bad++;
                System.out.println("Skipped: " + e.getMessage());
            }
        }
        System.out.println("Total of valid marks: " + total);
        System.out.println("Invalid entries: " + bad);
    }
}
Skipped: For input string: "9O" Skipped: For input string: "" Total of valid marks: 227 Invalid entries: 2

How it works: "9O" has the letter O instead of zero, and "" is empty; both make parseInt throw NumberFormatException. When it is thrown, total += … never happens, so a bad entry adds nothing. The message shows the exact input, which makes such bugs easy to find.

P4Medium

Which family is it?

For each exception object, print whether it is a RuntimeException (unchecked), another Exception (checked) or an Error.

Input
ArithmeticException, IOException, StackOverflowError, NumberFormatException, InterruptedException
Expected output
ArithmeticException -> RuntimeException (unchecked)
IOException -> Exception (checked)
StackOverflowError -> Error
NumberFormatException -> RuntimeException (unchecked)
InterruptedException -> Exception (checked)
Show solution and explanation
public class Main {
    static String family(Throwable t) {
        if (t instanceof Error) return "Error";
        if (t instanceof RuntimeException) return "RuntimeException (unchecked)";
        return "Exception (checked)";
    }

    public static void main(String[] args) {
        Throwable[] samples = {
            new ArithmeticException(),
            new java.io.IOException(),
            new StackOverflowError(),
            new NumberFormatException(),
            new InterruptedException()
        };
        for (Throwable t : samples) {
            System.out.println(t.getClass().getSimpleName() + " -> " + family(t));
        }
    }
}
ArithmeticException -> RuntimeException (unchecked) IOException -> Exception (checked) StackOverflowError -> Error NumberFormatException -> RuntimeException (unchecked) InterruptedException -> Exception (checked)

How it works: exceptions are ordinary objects, so you can create them with new without throwing them. instanceof checks the class hierarchy: NumberFormatException extends IllegalArgumentException, which extends RuntimeException. The order of the checks matters: test Error and RuntimeException first, because everything else that is left is a checked exception.

P5Medium

Read the stack trace in code

A method three calls deep divides by zero. Catch the exception in main and print each stack trace entry: the method name and line number, from the top of the stack down.

Input
main → billing → tax → 500 / 0
Expected output
java.lang.ArithmeticException: / by zero
  in tax() at line 3
  in billing() at line 7
  in main() at line 12
Show solution and explanation
public class Main {
    static int tax(int amount, int slab) {
        return amount / slab;
    }

    static int billing(int amount) {
        return amount + tax(amount, 0);
    }

    public static void main(String[] args) {
        try {
            billing(500);
        } catch (ArithmeticException e) {
            System.out.println(e);
            for (StackTraceElement el : e.getStackTrace()) {
                System.out.println("  in " + el.getMethodName() + "() at line " + el.getLineNumber());
            }
        }
    }
}
java.lang.ArithmeticException: / by zero in tax() at line 3 in billing() at line 7 in main() at line 12

How it works: when an exception object is created, the JVM records the call stack at that moment. getStackTrace() returns it as an array: index 0 is where it was thrown (tax, line 3), the last entry is main. This is the same list that printStackTrace() prints, one at … line per entry.

P6Medium

Check first, or catch?

Read a seat number from an array of booked seats in two ways: once by checking the index before reading, once by catching the exception. Both should print the same messages.

Input
booked = {12, 15, 21}, positions 1 and 5
Expected output
seat 15 | seat 15
no seat at position 5 | no seat at position 5
Show solution and explanation
public class Main {
    static String byCheck(int[] booked, int pos) {
        if (pos < 0 || pos >= booked.length) return "no seat at position " + pos;
        return "seat " + booked[pos];
    }

    static String byCatch(int[] booked, int pos) {
        try {
            return "seat " + booked[pos];
        } catch (ArrayIndexOutOfBoundsException e) {
            return "no seat at position " + pos;
        }
    }

    public static void main(String[] args) {
        int[] booked = {12, 15, 21};
        System.out.println(byCheck(booked, 1) + " | " + byCatch(booked, 1));
        System.out.println(byCheck(booked, 5) + " | " + byCatch(booked, 5));
    }
}
seat 15 | seat 15 no seat at position 5 | no seat at position 5

How it works: both give the same answer, but the check is the better habit. An index that can be wrong is an expected situation, and an if is clearer and cheaper than creating an exception object with a stack trace. Keep exceptions for situations you cannot check in advance, such as a file disappearing or a network failing.

P7Medium

Count exceptions by type

Run a list of risky operations on text values and count how many raised NumberFormatException, how many raised StringIndexOutOfBoundsException, and how many succeeded.

Input
codes = {"120", "AB", "7"}; for each: parse it, then read charAt(1)
Expected output
120: value 120, second digit 2
AB: java.lang.NumberFormatException: For input string: "AB"
7: java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
ok=1, notNumber=1, tooShort=1
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        String[] codes = {"120", "AB", "7"};
        int ok = 0, notNumber = 0, tooShort = 0;
        for (String c : codes) {
            try {
                int value = Integer.parseInt(c);
                char second = c.charAt(1);
                System.out.println(c + ": value " + value + ", second digit " + second);
                ok++;
            } catch (NumberFormatException e) {
                notNumber++;
                System.out.println(c + ": " + e);
            } catch (StringIndexOutOfBoundsException e) {
                tooShort++;
                System.out.println(c + ": " + e);
            }
        }
        System.out.println("ok=" + ok + ", notNumber=" + notNumber + ", tooShort=" + tooShort);
    }
}
120: value 120, second digit 2 AB: java.lang.NumberFormatException: For input string: "AB" 7: java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1 ok=1, notNumber=1, tooShort=1

How it works: "AB" fails at parseInt, so charAt is never reached. "7" parses, but has no index 1. Each kind of problem is a different exception class, so each lands in its own catch block. The message format shown is Java 21's; older versions word the index message differently.

P8Medium

Where did the null come from?

A lookup method returns null for an unknown city. Calling a method on that result throws NullPointerException. Catch it and print its message, then fix the call with a null check.

Input
cities known: "Pune" → "411001"; look up "Nagpur"
Expected output
Cannot invoke "String.length()" because the return value of "Main.pinFor(String)" is null
PIN not found
Show solution and explanation
public class Main {
    static String pinFor(String city) {
        if (city.equals("Pune")) return "411001";
        return null;
    }

    public static void main(String[] args) {
        try {
            System.out.println(pinFor("Nagpur").length());
        } catch (NullPointerException e) {
            System.out.println(e.getMessage());
        }
        String pin = pinFor("Nagpur");
        System.out.println(pin == null ? "PIN not found" : "PIN length " + pin.length());
    }
}
Cannot invoke "String.length()" because the return value of "Main.pinFor(String)" is null PIN not found

How it works: since Java 14, NullPointerException messages say exactly which value was null; here, the return value of Main.pinFor(String). That points straight at the method that returned null. The second version checks for null first, which is the real fix.

P9Business problem

Fest fee splitter

Each team's fee is split equally among its members. One team has registered with 0 members. Print each team's share, mark the broken team, and print how many teams were processed successfully.

Input
teams = {"Robotics", "Dance", "Quiz"}
fees = {1200, 900, 600}, members = {4, 0, 3}
Expected output
Robotics: Rs 300 each
Dance: cannot split (/ by zero), check registration
Quiz: Rs 200 each
2 of 3 teams processed
Show solution and explanation
public class Main {
    static int perHead(int fee, int members) {
        return fee / members;
    }

    public static void main(String[] args) {
        String[] teams = {"Robotics", "Dance", "Quiz"};
        int[] fees = {1200, 900, 600};
        int[] members = {4, 0, 3};
        int done = 0;
        for (int i = 0; i < teams.length; i++) {
            try {
                System.out.println(teams[i] + ": Rs " + perHead(fees[i], members[i]) + " each");
                done++;
            } catch (ArithmeticException e) {
                System.out.println(teams[i] + ": cannot split (" + e.getMessage() + "), check registration");
            }
        }
        System.out.println(done + " of " + teams.length + " teams processed");
    }
}
Robotics: Rs 300 each Dance: cannot split (/ by zero), check registration Quiz: Rs 200 each 2 of 3 teams processed

How it works:

  1. The exception is thrown inside perHead, but caught in main: it travels up one frame to find the handler.
  2. Because the try is per team, one bad registration does not stop the others.
  3. done++ sits after the risky call, so it only counts teams that really succeeded.
P10Business problem

Amounts from bank SMS alerts

An expense tracker reads the amount part of bank SMS alerts. Some are malformed. Add up the valid debits, list the bad ones with the reason, and print the total in rupees.

Input
amounts = {"1250", "499.00", "Rs 300", "75", "2,000"}
Expected output
Rejected "Rs 300": For input string: "Rs 300"
Rejected "2,000": For input string: "2,000"
Total spent: Rs 1824.00 (2 rejected)
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        String[] amounts = {"1250", "499.00", "Rs 300", "75", "2,000"};
        double total = 0;
        int rejected = 0;
        for (String a : amounts) {
            try {
                total += Double.parseDouble(a);
            } catch (NumberFormatException e) {
                rejected++;
                System.out.println("Rejected \"" + a + "\": " + e.getMessage());
            }
        }
        System.out.printf("Total spent: Rs %.2f (%d rejected)%n", total, rejected);
    }
}
Rejected "Rs 300": For input string: "Rs 300" Rejected "2,000": For input string: "2,000" Total spent: Rs 1824.00 (2 rejected)

How it works:

  1. Double.parseDouble accepts "499.00" but not text with a currency word or a comma separator; both throw NumberFormatException.
  2. Rejected lines are counted and printed, never silently ignored, so someone can fix the parser for those formats.
  3. In real money code you would use BigDecimal or paise in a long; double keeps this example short.
P11Business problem

Warehouse bin lookup

A picker app looks up product names by bin number. Some bins are out of range and one bin is empty (null). For each order line, print the product name in capitals or a clear reason why it could not be found.

Input
bins = {"Rice 5kg", null, "Atta 10kg"}
order bin numbers = {0, 1, 4, 2}
Expected output
Bin 0: RICE 5KG
Bin 1: bin is empty
Bin 4: no such bin (Index 4 out of bounds for length 3)
Bin 2: ATTA 10KG
Show solution and explanation
public class Main {
    static String pick(String[] bins, int bin) {
        return bins[bin].toUpperCase();
    }

    public static void main(String[] args) {
        String[] bins = {"Rice 5kg", null, "Atta 10kg"};
        int[] order = {0, 1, 4, 2};
        for (int bin : order) {
            try {
                System.out.println("Bin " + bin + ": " + pick(bins, bin));
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Bin " + bin + ": no such bin (" + e.getMessage() + ")");
            } catch (NullPointerException e) {
                System.out.println("Bin " + bin + ": bin is empty");
            }
        }
    }
}
Bin 0: RICE 5KG Bin 1: bin is empty Bin 4: no such bin (Index 4 out of bounds for length 3) Bin 2: ATTA 10KG

How it works:

  1. Bin 1 holds null, so bins[1].toUpperCase() throws NullPointerException.
  2. Bin 4 does not exist, so bins[4] throws ArrayIndexOutOfBoundsException before toUpperCase is even reached.
  3. Both are thrown inside pick and caught one frame up in main, each by its own catch. In production code you would check the index and the null first; this exercise is about recognising the two exceptions.

Real-world scenario questions (asked in company interviews)

Interviewers love giving you a real stack trace or a crash report and asking what you would do. Decide your answer first, then open the model answer.

Support · debuggingFresher

Reading a stack trace from a support ticket

A support ticket contains this: java.lang.NumberFormatException: For input string: "1,499", followed by six at java.base/… lines, then at com.shop.cart.PriceParser.parse(PriceParser.java:22) and at com.shop.cart.CartService.addItem(CartService.java:58). Where do you start, and what is the likely bug?

The interviewer is checking: can you read a trace quickly and connect the message to the cause?

Show a strong answer
  • The message gives the cause: the text "1,499" contains a comma, which Integer.parseInt does not accept.
  • Skip the library frames; the first frame from our code is PriceParser.parse, line 22. That's where the parse call is. CartService.addItem line 58 called it.
  • Fix: parse prices in a locale-aware way or strip separators before parsing, validate the input, and add a test for "1,499".

Avoid: starting at the bottom of the trace, or wrapping the call in a catch that hides the error.

Banking · batch job1–3 years

One bad row stops the whole interest run

A nightly job calculates interest for 2 lakh savings accounts in one loop. Last night it stopped at account 61,204 with a NullPointerException, and the remaining accounts got no interest. The manager asks why one account affected everyone, and how to prevent it.

The interviewer is checking: do you understand propagation and where to place a handler?

Show a strong answer
  • The exception was not caught inside the loop, so it propagated out of the loop and out of the job's main method, ending the whole run.
  • Catch exceptions per account inside the loop, log the account number with the stack trace, add it to a failure list, and continue.
  • Also fix the root cause (which field was null) and make the job restartable so it can resume from where it stopped.
  • Alert if the failure count is above a threshold, because many failures usually mean a systemic bug, not bad data.

Avoid: an empty catch that silently skips accounts. Customers would quietly miss interest.

E-commerce · code reviewFresher

“Just catch Exception everywhere”

A teammate's checkout code wraps every method body in try { … } catch (Exception e) { } with nothing inside the catch, “so the site never crashes.” Orders are now sometimes saved without a payment record and nobody knows why. What's wrong?

The interviewer is checking: do you know that swallowing exceptions hides bugs and corrupts data?

Show a strong answer
  • An empty catch throws away the exception and its stack trace. The failure still happened; we just can't see it, and the code carries on as if it succeeded.
  • Catch only exceptions you can really handle, at a level that can decide what to do (retry, show a message, cancel the order).
  • Anything else should propagate to a top-level handler that logs it and returns an error, so the order is not half-saved.

Avoid: believing that “no crash” means “no bug”.

SaaS · production incident3+ years

StackOverflowError after a new release

After a release, the reports service fails with java.lang.StackOverflowError. The trace shows the same two lines repeating hundreds of times: Category.getFullPath calling Category.getFullPath. A junior suggests adding catch (Exception e) around the call. What do you say?

The interviewer is checking: do you know the Error/Exception split and how to read a repeating trace?

Show a strong answer
  • StackOverflowError is an Error, not an Exception, so that catch would not even catch it, and it shouldn't.
  • The repeating frames mean unbounded recursion: each call pushes a frame until the thread's stack is full. Likely the new data has a cycle (a category whose parent chain loops back to itself) or the base case is missing.
  • Fix the recursion: add a correct stop condition, detect cycles with a visited set, or use a loop. Add a data check that prevents cyclic parents.

Avoid: increasing the stack size with -Xss as the fix. An infinite recursion will still overflow.