Java try-catch Block
Table of Contents + −
In the last lesson you learned an introduction to exceptions. Until now, when something went wrong, the program just stopped. But real programs cannot give up at the first problem. Java gives you a clean way to handle errors instead of crashing, and the tool for that is try-catch.
🤔 What is an exception?
An exception is an event that disrupts the normal flow of your program:
- Normal flow means Java runs your lines top to bottom, one after another.
- An exception is what happens when a line hits a problem it cannot handle.
- Java creates an exception object that describes the problem, then “throws” it.
- Throwing means Java stops the current line and looks for someone to handle the error.
- It is not a typo. Your code compiled fine. This happens while the program runs.
These kinds of things throw an exception:
- Dividing an integer by zero.
- Turning the word “hello” into a number.
- Calling a method on a
nullvariable. - Asking for item 5 in an array of only 3.
💥 What happens with no handling
Here is a program that divides by zero and nobody handles it.
public class Main { public static void main(String[] args) { int a = 10; int b = 0; System.out.println(a / b); // ❌ throws an exception here System.out.println("This line never runs"); }}Dividing by zero throws an ArithmeticException. Nobody handles it, so it travels up and leaves main. Java has no one left to ask. So it stops the program and prints a stack trace, the report Java prints when an unhandled exception ends the program.
Output
Exception in thread "main" java.lang.ArithmeticException: / by zero at Main.main(Main.java:5)Read that report:
- The first line names the type,
ArithmeticException, and the message, ”/ by zero”. - The
atline tells you exactly where: insideMain.main, line 5 ofMain.java. - That
at Main.mainpart is gold for debugging. It points at the broken line. - The last
printlnnever ran. Once thrown, everything after it was skipped.
🧩 The try-catch syntax
The fix is to wrap the risky code in a try block and handle any problem in a catch block. The try block holds the code that might throw. The catch block holds your plan for when it does.
try { // code that might throw an exception} catch (ExceptionType e) { // what to do if it does}What each part means:
trywraps the risky lines. You tell Java to watch them for trouble.catchnames one exception type and gives that error a variable, here callede.- The body of
catchis your recovery code. It runs only when a matching exception is thrown.
So the catch is not a backup that always runs. It is a safety net that catches you only if you fall.
🔀 How the flow actually works
The flow depends on whether an exception is thrown:
- No problem in try? Java skips the catch entirely.
- A line in try throws? Java stops right there. The rest of the try block is skipped.
- Java jumps to the catch. If the type matches, that catch body runs.
- After the catch finishes, the program continues past the whole try-catch.
That “rest of try is skipped” rule surprises people, so keep it in mind.
💡 Worked example: handling the divide by zero
Let’s take that crashing program and make it survive. Same code, now wrapped in try-catch.
public class Main { public static void main(String[] args) { int a = 10; int b = 0;
try { System.out.println(a / b); // risky line System.out.println("after divide"); // skipped when divide throws } catch (ArithmeticException e) { System.out.println("Cannot divide by zero!"); // ✅ handled here }
System.out.println("Program continues normally."); }}Walk through it:
- Java runs
a / b, which throws anArithmeticException. - The “after divide” line is inside try but never runs. That is the skip rule.
- Java jumps to the catch, the type matches, and it prints the friendly message.
- Then the program leaves the try-catch and runs the final line as normal.
Output
Cannot divide by zero!Program continues normally.No crash, no stack trace. A fatal crash became a handled situation. That is the whole point of try-catch.
📋 Reading the exception with getMessage
The caught exception is an object that carries information:
- getMessage() returns a short text description of what went wrong.
- It is great for logging and for showing helpful detail.
Here we reach past the end of an array, then read the message.
public class Main { public static void main(String[] args) { try { int[] numbers = {1, 2, 3}; System.out.println(numbers[5]); // ❌ index 5 does not exist } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Bad index: " + e.getMessage()); } }}The array has 3 items, so valid indexes are 0, 1, and 2. Asking for index 5 throws an ArrayIndexOutOfBoundsException. We catch it and call e.getMessage() for the exact detail.
Output
Bad index: Index 5 out of bounds for length 3The catch type matches the actual error. That match is how Java picks this catch. Catch the wrong type and the catch is skipped, like a key that does not fit the lock.
🔢 Parsing input that is not a number
You will turn text into numbers constantly. Integer.parseInt takes a String and returns an int. But if the text is not a valid number, it throws a NumberFormatException.
This program reads a price from text that has a stray letter.
public class Main { public static void main(String[] args) { String input = "12x";
try { int price = Integer.parseInt(input); // ❌ "12x" is not a number System.out.println("Price is " + price); } catch (NumberFormatException e) { System.out.println("That is not a valid number: " + e.getMessage()); } }}The text "12x" cannot become an int, so parseInt throws. We catch the NumberFormatException and tell the user clearly. In a real app this is where you ask them to type the value again.
Output
That is not a valid number: For input string: "12x"🎯 Catching specific types vs a broad Exception
Every catch names a type. It can be specific like ArithmeticException, or the broad parent Exception, which matches almost everything. Catching Exception is tempting but usually a poor choice:
- A broad catch hides surprises. A bug you did not expect gets swallowed.
- A broad catch cannot react smartly. A divide-by-zero and a bad file need different responses.
- A specific catch documents your intent.
catch (NumberFormatException e)says exactly what you planned for. - So catch the specific types you can handle. Use broad
Exceptiononly as a last-resort net, and log what you caught.
// ❌ Too broad: swallows every problem, even bugs you did not plan fortry { int price = Integer.parseInt(input);} catch (Exception e) { System.out.println("Something went wrong");}
// ✅ Specific: you know exactly what you are handlingtry { int price = Integer.parseInt(input);} catch (NumberFormatException e) { System.out.println("Please enter a valid number");}🧱 Multiple catch blocks and their order
One try can have several catch blocks after it:
- Different lines can throw different exceptions.
- Java checks the catches top to bottom.
- It uses the first one whose type matches.
Here we call a method on a null String.
public class Main { public static void main(String[] args) { try { String text = null; System.out.println(text.length()); // ❌ calling a method on null } catch (ArithmeticException e) { System.out.println("Math error"); } catch (NullPointerException e) { System.out.println("Tried to use a null value"); // ✅ this one runs } }}Calling length() on null throws a NullPointerException. Java checks the first catch, ArithmeticException, sees no match, and skips it. The second catch matches and runs. So each kind of error gets its own response.
Output
Tried to use a null valueOrder matters here. Specific exceptions must come before general ones. Exception is the parent of almost everything. So a catch for Exception placed first grabs the error before any specific catch gets a turn. Java refuses to compile that, because the specific catch would be unreachable.
// ❌ Won't compile: the broad Exception catch makes the specific one unreachabletry { int x = Integer.parseInt("abc");} catch (Exception e) { // grabs everything first System.out.println("general");} catch (NumberFormatException e) { // can never be reached System.out.println("number problem");}
// ✅ Specific first, general lasttry { int x = Integer.parseInt("abc");} catch (NumberFormatException e) { // specific, checked first System.out.println("number problem");} catch (Exception e) { // general safety net, last System.out.println("general");}➕ Multi-catch with the pipe symbol
Sometimes two exceptions need the same response. Writing two near-identical catch blocks is repetitive. So Java lets you combine them in one catch using the | symbol. This is called multi-catch.
This single catch handles a bad number and a bad array index the same way.
public class Main { public static void main(String[] args) { String[] data = {"10", "20"};
try { int value = Integer.parseInt(data[5]); // could fail two ways System.out.println(value); } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { System.out.println("Bad data: " + e.getMessage()); // ✅ one handler, two types } }}Read the catch line as “catch a NumberFormatException OR an ArrayIndexOutOfBoundsException”. Whichever is thrown, this one block handles it. Here data[5] is out of bounds, so the array exception is caught.
Output
Bad data: Index 5 out of bounds for length 2📦 Checked vs unchecked exceptions
Two families of exceptions decide when you must write try-catch:
- Unchecked exceptions like
ArithmeticExceptionandNullPointerExceptionare the ones we have used. Java does not force you to handle them. They usually come from bugs in your logic. - Checked exceptions like
IOExceptioncome from reading a file or opening a connection. Java forces you to deal with them, by try-catch or by declaring the method passes them on. - Simple rule: unchecked means Java trusts you to avoid it. Checked means Java insists you have a plan.
You will work with checked exceptions a lot once you start reading files.
⚠️ Common Mistakes
A few try-catch slip-ups trip up almost everyone.
Catching Exception too broadly. A blanket catch grabs problems you never meant to handle, including real bugs.
// ❌ Hides any and every problem behind one vague linetry { process(order);} catch (Exception e) { System.out.println("error");}
// ✅ Catch what you can actually handletry { process(order);} catch (NumberFormatException e) { System.out.println("Order amount was not a number");}An empty catch that swallows the error. A catch with nothing inside makes the exception vanish without a trace. The program limps on in a broken state and you have no idea why.
// ❌ Silence: the error disappears and you can never debug ittry { int x = Integer.parseInt(input);} catch (NumberFormatException e) {}
// ✅ At the very least, report what happenedtry { int x = Integer.parseInt(input);} catch (NumberFormatException e) { System.out.println("Could not read number: " + e.getMessage());}Wrong catch order. Putting the general Exception before a specific type makes the specific catch unreachable, and Java will not even compile it.
// ❌ Specific catch can never run, compile errortry { risky();} catch (Exception e) { handleGeneral();} catch (NullPointerException e) { handleNull();}
// ✅ Specific first, then the general nettry { risky();} catch (NullPointerException e) { handleNull();} catch (Exception e) { handleGeneral();}✅ Best Practices
- Wrap only the risky code. Keep the try block small and around the line that can actually fail. A giant try makes it unclear which line broke.
- Catch specific exceptions. Handle
ArithmeticExceptionorNumberFormatExceptionby name, not just a blanketException, so each problem gets the right response. - Order specific before general. If you do use a broad
Exceptioncatch, put it last as a final net. - Never leave a catch empty. Log it, show a message, or recover. A swallowed error is a bug you will hunt for hours.
- Use the exception’s message.
e.getMessage()tells you and your logs what actually went wrong. - Use multi-catch for shared handling. When two exceptions need the same response, combine them with
|instead of repeating yourself.
🧩 What You’ve Learned
Nicely done. Let’s recap.
- ✅ An exception is an event that disrupts normal flow; unhandled, it stops the program and prints a stack trace.
- ✅ Put risky code in a
tryblock and handle errors in acatchblock of the matching type. - ✅ When an exception is thrown, the rest of try is skipped and Java jumps to the matching catch, then continues after it.
- ✅ Prefer specific exception types over a broad
Exception, and usegetMessage()for detail. - ✅ Use multiple catch blocks ordered specific to general, or multi-catch with
|when several errors share one response. - ✅ Exceptions come in checked and unchecked families; never leave a catch empty.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What is an exception?
Why: An exception is an event that breaks the normal top-to-bottom flow when a line hits a problem it cannot handle.
- 2
When an exception is thrown inside a try block, what happens to the rest of the try block?
Why: Java stops at the throwing line, skips the rest of try, and jumps to the matching catch block.
- 3
How should you order multiple catch blocks?
Why: Specific types must come first; a general Exception placed first would make later specific catches unreachable and fail to compile.
- 4
What does the | symbol do in a catch like catch (A | B e)?
Why: Multi-catch with | lets a single catch block handle two or more exception types that share the same response.
🚀 What’s Next?
Sometimes there is cleanup you must do whether or not an error happens, like closing a file. For that, try-catch has a companion block that always runs: finally. Let’s learn it next.