ERROR ON PREV
Exceptions
  1. "Hello, World!"
  2. Variables and Types
  3. Arrays
  4. While, If, For
  5. ...Problem Set 0
  6. Static Methods
  7. Static Fields
  8. String Conversion
  9. Objects
  10. Threading
  11. Strings
  12. ...Problem Set 1.5
  13. Packages
  14. Complex Numbers
  15. Abstract classes
  16. Interfaces
  17. Autoboxing
  18. ...Problem Set 1
  19. enum
  20. Inner Classes
  21. Polymorphism
  22. Tanks!
  23. Callbacks
  24. Exceptions
  25. File I/O
  26. ...Problem Set 2
  27. Regular Expressions

Exceptions

NullTest.java
1public class NullTest {
2    void doNothing() {}
3    public static void main(String[] args) {
4        NullTest n1 = new NullTest();
5        NullTest n2 = null;
6        try {
7            System.out.println("n1="+n1);
8            System.out.println("n2="+n2);
9            n1.doNothing();
10            n2.doNothing();
11        } catch(NullPointerException npe) {
12            System.out.println("Don't worry about that null!");
13        }
14    }
15}
$ javac NullTest.java
$ java NullTest
n1=NullTest@164f1d0d
n2=null
Don't worry about that null!

You learned about the value of null and about NullPointerException in the lesson on objects. We learned how to handle that error by checking to see if a pointer was null. While that is probably the best way of handling a NullPointerException, it is not the only way. The try {} catch {} block provides an alternative.

Notice what it looks like when we print out the NullTest class. NullTest inherits the toString() method from class Object. It is not very pretty or informative, but you can easily see the difference between an object allocated with "new" and a null.

What if you want to see that familiar stack trace with the line numbers, etc.? Well, you still can using the printStackTrace() method.

NullTest2.java
1public class NullTest2 {
2    void doNothing() {}
3    public static void main(String[] args) {
4        NullTest n1 = new NullTest();
5        NullTest n2 = null;
6        try {
7            System.out.println("n1="+n1);
8            System.out.println("n2="+n2);
9            n1.doNothing();
10            n2.doNothing();
11        } catch(NullPointerException npe) {
12            System.out.println("Don't worry about that null!");
13            System.out.println("Stack trace below:");
14            npe.printStackTrace();
15            System.out.println("Stack trace above.");
16        }
17    }
18}
$ javac NullTest2.java
$ java NullTest2
n1=NullTest@23fc4bec
n2=null
Don't worry about that null!
Stack trace below:
java.lang.NullPointerException
	at NullTest2.main(NullTest2.java:10)
Stack trace above.

Not only can you catch exceptions, but you can throw them as well. You can use keyword throw to do it.

Try.java
1public class Try {
2    public static void main(String[] args) {
3        double num = 3denom = 0;
4        try {
5            System.out.println("before exception");
6            if(denom == 0) throw new Exception("Denominator is zero");
7            System.out.println("ratio is "+(num/denom));
8        } catch(Exception e) {
9            System.out.println("Stack trace below:");
10            e.printStackTrace();
11        }
12    }
13}
$ javac Try.java
$ java Try
before exception
Stack trace below:
java.lang.Exception: Denominator is zero
	at Try.main(Try.java:6)

Notice that we explicitly caught the exception. What happens if you try to use throw without catch?

Try2.java
1public class Try2 {
2    public static void main(String[] args) {
3        double num = 3denom = 0;
4        System.out.println("before exception");
5        if(denom == 0) throw new Exception("Denominator is zero");
6        System.out.println("ratio is "+(num/denom));
7    }
8}
$ javac Try2.java
Try2.java:5: unreported exception java.lang.Exception; must be caught or declared to be thrown
        if(denom == 0) throw new Exception("Denominator is zero");
                       ^
1 error

The answer is that the code won't compile because the compiler checks to see that the exception is handled properly. Possibly, you don't want to handle the exception in the method you wrote, so you can pass the responsibility on by supplying a throws clause to the method.

Try3.java
1public class Try3 {
2    public static void main(String[] args) throws Exception {
3        double num = 3denom = 0;
4        System.out.println("before exception");
5        if(denom == 0) throw new Exception("Denominator is zero");
6        System.out.println("ratio is "+(num/denom));
7    }
8}
$ javac Try3.java
$ java Try3
before exception
Exception in thread "main" java.lang.Exception: Denominator is zero
	at Try3.main(Try3.java:5)

In the above we added a "throws" clause to the function definition and the compiler is happy. Another option is to use a different type of exception.

Try4.java
1public class Try4 {
2    public static void main(String[] args) {
3        double num = 3denom = 0;
4        System.out.println("before exception");
5        if(denom == 0) throw new RuntimeException("Denominator is zero");
6        System.out.println("ratio is "+(num/denom));
7        if(true) throw new Error();
8    }
9}
$ javac Try4.java
$ java Try4
before exception
Exception in thread "main" java.lang.RuntimeException: Denominator is zero
	at Try4.main(Try4.java:5)

There are two basic types of exceptions that are not checked by the compiler (called unchecked exceptions). They are RuntimeException and Error. Conceptually, Error's are things you probably should not try and catch, whereas RuntimeExceptions are fair game.

While most of the examples of exceptions above take a String as argument, it is not required. The Error we generate above takes an empty constructor.

BadDenom.java
1public class BadDenom extends RuntimeException {
2    public static void main(String[] args) {
3        double num = 3denom = 0;
4        System.out.println("before exception");
5        if(denom == 0) throw new BadDenom();
6        System.out.println("ratio is "+(num/denom));
7    }
8}
$ javac BadDenom.java
$ java BadDenom
before exception
Exception in thread "main" BadDenom
	at BadDenom.main(BadDenom.java:5)

You should feel free to create your own types of exceptions, subclassing either Exception, RuntimeException, or Error. In this case, we chose to subclass RuntimeException.

BadDenom2.java
1public class BadDenom2 extends Exception {
2    public static void main(String[] args) {
3        try {
4            double num = 3denom = 0;
5            System.out.println("before exception");
6            if(denom == 0) throw new BadDenom2();
7            System.out.println("ratio is "+(num/denom));
8        } catch(BadDenom2 bd) {
9            throw new RuntimeException(bd);
10        } finally {
11            System.out.println("goodbye!");
12        }
13    }
14}
$ javac BadDenom2.java
$ java BadDenom2
before exception
goodbye!
Exception in thread "main" java.lang.RuntimeException: BadDenom2
	at BadDenom2.main(BadDenom2.java:9)
Caused by: BadDenom2
	at BadDenom2.main(BadDenom2.java:6)

In this example we subclassed Exception instead of RuntimeException. Naturally, since we did this, we had to provide a try / catch block. Here we have added the finally block. You can put things in a finally block if you want them to exit after the try block, and they will get executed regardless of what exceptions are thrown.

We also illustrate the concept of wrapping and re-throwing exceptions. On line 9 we create a new RuntimeException, giving it our BadDenom2 exception as an argument. When the stack trace prints, we see both exceptions in the output.

BadDenom3.java
1public class BadDenom3 extends Exception {
2    public static void main(String[] args) {
3        try {
4            double num = 3denom = 0;
5            System.out.println("before exception");
6            if(denom == 0) throw new BadDenom2();
7            System.out.println("ratio is "+(num/denom));
8        } catch(BadDenom bd) {
9            System.out.println("caught bd");
10        } catch(BadDenom2 bd2) {
11            System.out.println("caught bd2");
12        } finally {
13            System.out.println("goodbye!");
14        }
15    }
16}
$ javac BadDenom3.java
$ java BadDenom3
before exception
caught bd2
goodbye!

Here we demonstrate that it is possible to have multiple catch clauses for a single try.

BadDenom4.java
1public class BadDenom4 extends Exception {
2    public static void main(String[] args) {
3        try {
4            double num = 3denom = 0;
5            System.out.println("before exception");
6            if(denom == 0) throw new BadDenom();
7            System.out.println("ratio is "+(num/denom));
8        } finally {
9            System.out.println("goodbye!");
10        }
11    }
12}
$ javac BadDenom4.java
$ java BadDenom4
before exception
goodbye!
Exception in thread "main" BadDenom
	at BadDenom4.main(BadDenom4.java:6)

And here we show you that you can just have try / finally.