Java Arena
Step 2 of 7

Choosing what to catch

A time of day such as 7:45 is easy for people to read. A program that compares or adds up times often turns it into a single number, the minutes after midnight: 7 hours and 45 minutes is 7 * 60 + 45 = 465. Two different things can go wrong on the way, and each throws its own exception:

public class Main {
    public static void main(String[] args) {
        String[] times = {"7:45", "745", "7:4five"};
        for (String time : times) {
            try {
                String[] parts = time.split(":");
                int hours = Integer.parseInt(parts[0]);
                int minutes = Integer.parseInt(parts[1]);
                System.out.println(time + " is " + (hours * 60 + minutes) + " minutes after midnight");
            } catch (NumberFormatException e) {
                System.out.println(time + ": the hours and minutes must be numbers");
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println(time + ": write the time as hours:minutes");
            }
        }
    }
}
Output
7:45 is 465 minutes after midnight
745: write the time as hours:minutes
7:4five: the hours and minutes must be numbers
  • 745 has no colon, so split gives just one part, and parts[1] throws an ArrayIndexOutOfBoundsException (module 15).
  • In 7:4five, the minutes aren't a number, and parseInt throws a NumberFormatException.

A try can have several catch blocks, one after the other. When an exception is thrown, Java checks them from the top and runs the first one whose class matches. At most one catch block runs, and then the program goes on after the last one.

A family of classes

Each kind of exception is a class, and the classes inherit from each other, just like the classes of module 34. Here's a part of the family, each class under its parent:

  • Exception
    • IOException: reading or writing data failed. Reading a missing file the way this course does, with Paths.get(...), throws a NoSuchFileException, one of its descendants. Some other ways of opening a file throw a FileNotFoundException instead, also an IOException. Module 43 meets it.
    • RuntimeException
      • ArithmeticException, such as / by zero
      • IllegalArgumentException: a method got a value it can't work with
        • NumberFormatException
      • IndexOutOfBoundsException
        • ArrayIndexOutOfBoundsException and StringIndexOutOfBoundsException
      • NullPointerException

A NumberFormatException is an IllegalArgumentException, which is a RuntimeException, which is an Exception. So a catch block catches its own class and every class below it:

  • catch (IndexOutOfBoundsException e) catches the index problems of lists, arrays and Strings.
  • catch (RuntimeException e) catches everything in its branch.
  • catch (Exception e), as in module 20, catches all of them.

Catch a parent class when every child gets the same reaction, and separate classes when each problem needs its own. If one message covers both problems with the times, one block is enough:

String[] times = {"745", "7:4five"};
for (String time : times) {
    try {
        String[] parts = time.split(":");
        int minutes = Integer.parseInt(parts[0]) * 60 + Integer.parseInt(parts[1]);
        System.out.println(minutes);
    } catch (RuntimeException e) {
        System.out.println(time + " isn't a time like 7:45");
    }
}
Output
745 isn't a time like 7:45
7:4five isn't a time like 7:45

The more specific class first

Since a parent class catches its children, the order of the catch blocks matters. A catch for a child class after its parent's could never run, and javac refuses it:

public class Main {
    public static void main(String[] args) {
        try {
            int minutes = Integer.parseInt("4five");
            System.out.println(minutes);
        } catch (IllegalArgumentException e) {
            System.out.println("That's not a valid value");
        } catch (NumberFormatException e) {
            System.out.println("That's not a number");
        }
    }
}
What javac prints
Main.java:8: error: exception NumberFormatException has already been caught
        } catch (NumberFormatException e) {
          ^
1 error

Swap the two blocks, and it compiles: a NumberFormatException then goes to its own block, and any other IllegalArgumentException to the second one. A common pattern is the specific classes first and a catch (Exception e) at the end, for anything else.

finally

A try can end with a finally block. It runs after the try block and the catch block, whatever happened: when nothing was thrown, when an exception was caught, and even when the try or catch block leaves the method with return:

public class Main {
    public static void main(String[] args) {
        System.out.println(minutesOf("7:45"));
        System.out.println(minutesOf("745"));
    }

    public static int minutesOf(String time) {
        try {
            String[] parts = time.split(":");
            return Integer.parseInt(parts[0]) * 60 + Integer.parseInt(parts[1]);
        } catch (RuntimeException e) {
            return -1;
        } finally {
            System.out.println("checked " + time);
        }
    }
}
Output
checked 7:45
465
checked 745
-1

The return decides the value, then the finally block runs, and only then does the method really return. That's why checked 7:45 comes before main prints 465. An exception that no catch block catches runs the finally block too, on its way out of the method.

finally is for work that must be done however the try block ends, such as giving back something the program borrowed. The most common case, closing a file, has a shorter form of its own, in step 4.

Your task

Challenge 1 of 3

A kitchen timer is set with minutes and seconds with a colon between them, such as 2:30. The program below reads one line and prints the time in seconds, but it crashes on bad input. Add a try with two catch blocks, so that:

  • a line without a colon, such as 150, prints Use minutes:seconds, like 2:30
  • a line whose parts aren't whole numbers, such as 2:3o, prints Minutes and seconds must be whole numbers

A good time still prints the seconds: Timer set for 150 seconds for 2:30. With the input 150 the program prints:

Use minutes:seconds, like 2:30

Challenge 2

What does this program print? Keep in mind which exception class is a child of which.

Challenge 3

An online course shows your progress written as done/total, such as 7/20 for 7 lessons done out of 20. The program should print 35% done for 7/20, The total can't be 0 when the total is 0, Use whole numbers, like 7/20 when a part isn't a whole number, and Write it as done/total, like 7/20 for any other problem. It doesn't compile. Read javac's messages and fix the program, without changing what any block prints. With the input 7/0 it prints:

The total can't be 0