Chapter:Exceptions

From Juneday education
Jump to: navigation, search

This chapter is obsolete (not maintained) and replaced by:


Contents

Meta information about this chapter

Expand using link to the right to see the full content.

Introduction

This chapter introduces the exceptions mechanism of the Java language and runtime system. It addresses the two main types of exceptions, runtime exceptions and checked exceptions. On a basic level, the chapter also discusses when we could use runtime exceptions and when we might use checked exceptions and what the consequences are of the respective choice.

The chapter also introduces the creation of custom exceptions and chaining constructions (wrapping a cause exception inside another exception).

Purpose

The purpose of this chapter is to prepare the students for the assignments and for follow-up Java courses. The Java API is full of methods which throws checked and unchecked exceptions, so the students will pretty soon be forces to handle-or-declare when using many API classes, such as classes for I/O.

Rather than just giving the students template code for try-catch-finally, we aim to give the students a broader understanding of why exceptions are thrown. This is an introductory Java book so we don't go very deep into design decisions, but we try to at least inspire the students to think about where to handle exceptions and what the difference for client code is if we use runtime (unchecked) exceptions or checked exceptions.

Goal

The goal is that the students should have a solid base knowledge about exceptions in Java so that they can both write handlers for exceptions thrown by API code and also the basics for how to throw an exception programmatically. The students should, after the completion of this chapter have enough knowledge to solve the assignment 2 in which exceptions are used, as well as knowing what to do when they run into an API method which declares that it throws some exceptions. The students should also know the rules about handle-or-declare for checked exceptions, and what the concept of an unchecked exception is.

Instructions to the teacher

Common problems

There are a lot of misconceptions about exceptions floating around which we will try to counter in this chapter. One misuse of exceptions is the practice of writing a handler (a catch-clause) which doesn't do anything or at most prints something. Some IDEs actually make this situation a practice for beginners, when they detect a call to a method which throws a checked exception and adds a try-block around the call and a catch-block which catches the exception and does nothing to handle it.

Students may be tempted to write try-catch with a meaningless catch-block, just to get rid of a compiler error complaining about some checked exception which needs to be handled (or declared).

Try to help the students understand the thought behind exceptions and that exceptions can be a valuable tool to make a robust system. Also, try to get the students think about the difference between programmer errors (which may trigger runtime exceptions) and contingencies which can be handled and signaled through the use of checked exceptions.

The students may have a difficult time taking in the full picture and discussion, since they haven't yet seen code for a larger system which uses tiers for instance. The students will probably need to read the explanatory texts a few times, see the lectures a few times and do the exercises before they start to comprehend exceptions on a deeper level. It is OK if the students of this introductory course get a more shallow understanding about exceptions and focus more on the rules and syntax.

If they know how to handle-or-declare a checked exceptions, that's an important first step towards a deeper discussion, which might come in a later course or a book on more advanced topics. Make sure that the students at least understand the semantic and syntactic differences between unchecked and checked exceptions. The exercises for the "Writing your own exceptions" might be useful for a discussion on when we can use checked exceptions and when we can use unchecked (runtime) exceptions.

The idea of those exercises is, using a class for a bank account as an example, to show that trying to withdraw a too great amount is a contingency which can be signaled using a custom checked exception and declaring the withdraw method with a throws declaration. This will force the client code (methods calling withdraw on some account object) to prepare for the case that a withdrawal might be denied because of lacking funds.

The bank account class has to deal with some other potential problems too, like some programmer trying to create an account with an initial amount with a negative value. This should not be allowed, and here the class uses a runtime exception to abort such operations. The constructor, deposit and withdraw methods all may throw a runtime exception is some client code doesn't behave and only use positive numbers (or zero) when calling them. Using a runtime exception to abort a faulty call to deposit violating the rule that only positive amounts (or zero) are legal, doesn't create the need for handle-or-declare which is good for all well-behaving clients. If you follow this rule, the reward is that you don't need to handle-or-declare, since we are using runtime exceptions. The methods will, however, punish code in violation with this simple rule with a runtime exception (which must be handled at some level, or the code will crash the application).

We introduce the idea that crashing because of bugs sometimes is preferable to just silently handle buggy operations. A crash at least lets us detect the problem and fix the bug. This might be a hard concept for the students to grasp, but at least we try to make them reflect on this.

Videos

All videos in this chapter:

See below for individual links to the videos.

What is an Exception

Description

This section introduces the concept of an exception. An exception is a signal of your program's running into some situation which is not the normal case or what you hoped for.

You can think of exceptions as being of two types: Contingencies (eventualities which may occur but are not very normal) and failures which are when something goes very wrong.

A contingency is when something rare happens but we know that it could happen and should be prepared for it. If there is some rule in our system which is being broken, then we could use the exception mechanism of Java to signal that this rule was broken. If we think about that, passing information about this rule having been violated using an exception object, allows us to have a feedback mechanism from a method which doesn't use the return statement.

A method could have a return value for all normal conditions but when something rare happens, the method could instead throw an object of some exception type to the caller (the return statement would then never be reached and the method would not be possible to evaluate to any value - instead the caller would have to deal with the exceptional case that occurred).

This introduction also discusses in the video lecture that in Java, there are two main types of Exception objects. The first type is "checked exceptions" which are exceptions that a method could declare that it can throw, and that the compiler forces the calling code to handle using some special code (which we will show in a later lecture). A method which can "throw" an object whose type is a checked exception must either declare that it throws this exception, or handle it internally. If the method doesn't handle such exception and instead declares that they may be thrown from the method, any code which calls the method end up in the same situation.

If code in a method A calls method B, and method B is declaring that it might throw a checked exception, then the method A author must choose between two options: 1. Handle the exception which might be thrown by B, or, 2. declare that A too throws this exception.

These rules allow us to write code which "warns" for exceptional cases and force calling code to handle this or pass the problem on to some code which does handle it.

What could be a contingency exception (an eventuality which we know could happen but hope it doesn't)? Let's pretend we have a web shop which has some code for processing orders from the customers. The company has discovered that some orders look very suspicious and seem to come from the same customer but too quickly for being the result of a real human sitting in front of a web browser and actually placing the orders. The company then adds a business rule, that no two orders may be placed by the same customer within five seconds (because it must be a machine placing the orders rather than a human).

The method for processing orders must now obey this rule and check that an order is not placed by a customer who just placed an order less than six seconds ago. This should not be so frequent and could be considered a contingency. Let's further say that the method placeOrder(Order o) normally returns an int with the order number from the order database. How would we deal with the case of an order being denied because of the five second rule? We would declare that the method throws a checked exception. This would force the code calling processOrder(Order o) to write code which prepares for this situation. The processOrder(Order o) method would in turn have code which "throws" the exception if it finds that the rule is violated.

This all allows the code which calls processOrder(Order o) to focus on the normal case and getting the int signifying the order id from the order database. If the rule would be broken, the calling code includes code which will also deal with this exceptional case. The calling code would just deny and cancel the order, possibly giving the customer notice of this.

What about faults, then? Bad things which we expect (or hope) never will happen, is typically to be considered faults. Their reasons could be hardware failures, network problems (including some server being unavailable), or plain programming errors. These types of exceptional cases are often very hard to recover from. If a file is missing which we depend on, what could the program do about this? Nothing really. The program must notify the user but often that's all the program can do. If a failure depend on a programming error, it is probably best that the exception is logged (and sent to the developers somehow) and that the program exits gracefully. It could be argued that a buggy program is better off dying with the problem logged, than trying to keep running with a faulty state.

To give an example of when a program is better off dying because of a programming error, is when the program part which is saving critical data for the user can't save the data because of some programming error (the programmer forgot to initialize the variable holding the file name of the save-file, for instance). Rather than keeping running with the user thinking that her data is being saved in a safe place, the program should actually die with an error message (and the cause of the error logged for the developer so that they can fix the error!).

Videos

  1. What is an exception? Part 1 (eng) (sv) (download presentation)
  2. What is an exception? Part 2 (eng) (sv) (same PDF as above)

Exercises for what is an Exception

This section is more of a theoretical nature and we haven't learned enough syntax for coding exercises yet. But we have learned a little about the two main families of Exceptions - checked exceptions and runtime exceptions (unchecked). Enough to read about a class in the online documentation and look at a constructor which the documentation says throws an exception (i.e. that it might throw an exception).

So there are just two simple exercises for this section to warm you up (and to encourage you to read the documentation about the Java API classes!).

1

Let's look at the API which comes with your Java installation. Look at the documentation for the class Scanner (in the java.util package). Read the description of the class to understand what it is used for. Now, locate the description of the constructor which takes one single argument in the form of a File. What is the exception it is declared that this constructor throws? Is it a checked exception or a runtime exception?

Hint: If the exception class extends Exception and not RuntimeException, it is a checked exception. Follow the link to the exception class to see the inheritance hierarchy of this class.

2

Describe whith your own words, why you think the constructor from the previous question throws this specific exception type. It it didn't throw an exception, what do you think would happen?

Solutions

Expand using link to the right to see the full content.

1 - The Scanner constructor

The constructor in question (the one which takes a single argument of type reference to File) declares that it throws a FileNotFoundException. When we look at the documentation for this class of exceptions, we'll see the following inheritance tree:

java.lang.Object
  java.lang.Throwable
    java.lang.Exception
      java.io.IOException
        java.io.FileNotFoundException

This means that FileNotFoundException is an IOException, which is an Exception! We immediately see that it doesn't extend RuntimeException anywhere in the tree, so it must be a checked exception. Simple, eh? If it doesn't extend at any point RuntimeException, it is a checked exception.

The declaration of this constructor is actually (note the throws keyword):

public Scanner(File source) throws FileNotFoundException

Declaring that a method throws some exception, is almost always used for checked exceptions, so that users of the class can figure this out just by looking at the prototype (declaration) of the constructor or method. And such exceptions, checked exceptions, are enforced by the compiler according to the handle-or-declare rule. Even if we can declare that a method or constructor also throws a runtime exception, the compiler wouldn't care, since they are the exception (no pun intended) to the rule of handle-or-declare.

2 - Why the throws thing?

We can't give you your own words, but we'll provide an example of a feasible answer explaining what would happen if the constructor didn't throw an exception when something is wrong.

The Scanner class is used to parse (analyze and interpret) text. The text might be connected to some file, which is the reason for this constructor to exist - we can create a new Scanner and give a file object reference as an argument to this constructor. The Scanner object would then use the file represented by the File object as the source to read and parse text from. Luckily for us, the constructor throws an exception of type FileNotFoundException if the File object represents a File which doesn't exist on our hard drive. This is actually a feature, because it indicates that the parsing (reading and interpreting text) from this file won't work, for the simple reason that the file doesn't exist. Rather than letting us create a Scanner believing that it can read from the file represented by the File object, it throws a checked exception so that we will have to react on this case, rather than trying to scan a file which isn't there.

Two main types of Exceptions

Description

First of all, it's important to understand that exceptions in Java are represented by ordinary Java objects. These objects are of some class extending the class Exception (which is a subclass to Throwable). Well, they ordinary objects as they may be, there's one thing about exceptions which make them stand out from other objects, from the Java language perspective. Exception objects can be thrown (typically from a method) and they can be caught (by code calling a method).

The Exception class has one subclass which stands for one family of exceptions called RuntimeException. Runtime exceptions behave just like any other exceptions in that they can be thrown and caught using the language constructs for this. All other exceptions (subclasses to Exception but not in the branch extending RuntimeException) are called checked exceptions.

What earns checked exceptions their name, is that a method which throws such an exception must declare that it does so. The compiler will impose some restrictions on code calling a method which declares that it throws (actually might throw) a checked exception. The compiler checks such code. The rule imposed by the compiler is that code calling a method which may throw a checked exception, must be in a method either declares that it too throws a checked exception, or it must handle the exception which might be thrown. Handling the case that an exception might be thrown is done by catching the exception and providing code which does something about the situation.

              Throwable
                 ^
                 |
             Exception

              ^       ^
              |        \
RuntimeException       "all other, checked exception classes"
      ^                                  ^
      |                                  |
"unchecked exceptions"              subclasses here are checked exceptions

Often the exception classes under RuntimeException, the unchecked exceptions, are exceptions which comes from some kind of programmer mistakes, like trying to divide an integer with zero, trying to use an object via a reference which is null or trying to type case an object to a class which it cannot be cast to.

Since programmers aren't mandated by the compiler to write code to handle such exceptions, when they are thrown from a method, the exception object propagates unhandled to the caller. Since the caller doesn't handle it (doesn't catch it), the exception keeps propagating to the caller of the caller etc until it either reaches a handler or reaches the main method unhandled. If not even the main method has a handler for unchecked exceptions, the program ends abruptly with an error message and the whole chain of calls, all the way from main down to the method where the problem occurred.

Imagine an application whose main method calls method a() which calls method b() which calls method c() and an unchecked exception is thrown from c(). Let's pretend that the mistake in c() was that the programmer for c() tried to use a reference which was null, to call an instance method. This doesn't work, of course, and a NullPointerException will be thrown from c() by the runtime system (the JVM). NullPointerException is a RuntimeException and thus unchecked. If there is no handler anywhere in the chain of method calls which handles also RuntimeExceptions, the exception thrown by c() will propagate all the way up to main and crash the program with something like this, as the error message:

Exception in thread "main" java.lang.NullPointerException
	at Program.c(Program.java:12)
	at Program.b(Program.java:9)
	at Program.a(Program.java:6)
	at Program.main(Program.java:3)

You can follow the call chain if you start at the bottom (this is also called a call stack). Main calls, a, which calls b, which calls c which throws a NullPointerException.

So, are unchecked exceptions bad, then? Wouldn't it have been better if NullPointerException had been checked, so that the programmers would have been forced by the compiler to write a handler? To answer that question, we must think about what went wrong and why, and what could we do about it in a handler?

Looking closer at the code when the program crashed, showed the following version of the method c:

static void c(String s){
  s.toUpperCase();
}

The programmer called s.toUpperCase() on a reference to a String (for some reason). The programmer did so, just assuming that the reference s was a reference to an actual String object and not null. Apparently, though, s was null and the call to toUpperCase() failed with a NullPointerException. Now, what could possibly a handler somewhere have done about this? If the handler were declared in the main method, for instance, it is probable that it couldn't really know what happened and where and what to do about it. It knows, however, that something went wrong. We know now, after looking at the code, that the cause of the problem was that someone didn't make sure the reference s wasn't null before using it as a reference to an actual String object.

In this, somewhat simplified and constructed, example, we saw that the problem was actually a bug which could have been fixed with an if-statement checking whether s was null before using s as a reference to a real String, and taken care of null in the else-clause. So actually, just because no one handled the exception and the program crashed with a stack trace (the call stack printout), we could locate the bug and fix it. We are not so sure, however, the the user running the program would make much sense of the error message printout.

Checked exceptions on the other hand, come with the rule that a method must declare that such an exception might be thrown, and that calling code must either handle it or also declare that it might throw an exception (and pass the problem and requirement of handling it up the chain to its callers). The compiler will make sure that some code somewhere handles (catches) the exception thrown by such a method.

When could we use checked exceptions then? Well, first of all, the Java standard API is full of methods which are declared to throw checked exceptions. If we want to use an object from some class in the API and some method in that object "throws" a checked (or several) exception, then we have no choice but to follow the rules imposed by the compiler.

Sometimes we will actually write our own classes for checked exceptions and use them for our own method declarations. Doing so will work as expected, we will pose the restrictions following the rules of "handle or declare that your method throws" on to users of our methods.

One use case for using checked exceptions is when a method might run into some contingency (eventuality). A contingency could be something which isn't exactly good or common (happens rarely) but isn't really a critical error either. In order to understand how exceptions could be used to signal contingencies, we might first want to know what happens when a method throws an exception.

In a normal program flow, when a method calls another method, the control of the program is temporarily moved to the called method. Only when that method exits (for instance using a return statement), control is returned to the calling method, which moves on to its next statement. But when a method throws an exception, execution immediately leaves the method, even if no return statements have been reached yet.

The only way for a calling method to structurally and orderly manage a situation where a call to a method throws an exception, is to have a handler. Otherwise, control and execution will leave also the calling method and the exception will "bubble up" the call chain (be propagated to the caller of this method).

If we go back to contingencies hand how we can use exceptions to signal that something unusual has happened, we can actually view the throwing of an exception as an alternative return path. The normal return statements can then be used to exit the method nicely with some value representing information back to the caller. But if something extraordinarily happens, we can use an exception instead. If it is a checked exception, we can be sure that this will be handled sooner or later, and thus the exception can serve as information bearer of the contingency.

For completeness, we'll mention here that many (most) of the checked exceptions thrown by methods in the Java API actually represent more severe events than merely contingencies. Some checked exceptions thrown by methods in the API include events such as a file missing, a file system error, or a Java class not being found (but needed to be loaded). These are pretty serious situations (depending on the context of course), but the authors of the API decided to make them checked exceptions anyway.

It is possible to have opinions on the choice of making serious problems as checked exceptions as it forces us to write handlers wherever we use these methods and even though there is often not much the program can do to recover from the case of e.g. a broken file system. But we (the authors) are of the opinion that it was indeed a good choice. Making even severe problems checked exceptions, forces us as programmers to prepare for the event that something bad like that actually can (and sometimes do) happen. It makes us write code which is prepared for the worst, so to speak, and at least we can handle it in the eyes of the user of our software in that we can catch the problem and formulate a more understandable error message to the user, than the normal stacktrace printout they would otherwise see.

If we really dislike the decision to make something really bad a checked exception, we could actually use a strategy to make things easier for our colleagues using our methods, if our methods must call API-methods which throw checked exceptions. The strategy is to let our method provide handlers for the checked exceptions from the API calls. In this way users of our methods aren't obliged to write handlers (as they would have been if we just declared that we throw checked exceptions too, instead of handling them).

But wouldn't information get lost about the potentially really bad thing which lead to the exception our method caught? Yes it would. So the solution to this problem, according to the strategy, is that our handlers actually wrap the checked exception inside a runtime exception we throw from the handler! Runtime exceptions thrown by us don't come with the requirement for our callers to handle-or-declare. This allows our callers to proceed and call us as if everything should work fine, but in the event of something bad happening, a runtime exception will get thrown at them. Since they don't handle runtime exceptions, it will propagate up the call chain. This allows our project to put an exception handler somewhere, which actually also handle runtime exceptions, and make the decision of what to do about it. It it is really bad, the global (or general) exception handler can make the decision to terminate the application in a controlled way.

Videos

  1. Two main types of Exceptions (download presentation) (same PDF for all videos below)
  2. Two main types of Exceptions part 1 (eng) (sv)
  3. Two main types of Exceptions part 2 (eng) (sv)

Exercises - Two main types of exceptions

1 - Give examples of runtime exceptions

Go to the API documentation page for the class RuntimeException. Give five examples of subclasses to RuntimeException. This can be found under RuntimeException's documentation page section called "Direct Known Subclasses". Did you recognize any from the lecture slides?

2 - Find four subclasses to IOException

Go to the API and find four (immediate) subclasses to IOException, using the same technique as above. Write them down and write a short description of each one of the four. Are all subtypes to IOException checked or unchecked?

3 - What is the full package name for some exceptions?

What are the names of the packages for the following exception classes?

  • NullPointerException
  • FileNotFoundException
  • MalformedURLException

What do the package names tell you about the respective classes?

4 - Runtime exceptions can be documented

Go to the class File and look at the constructor which takes a single String reference as argument. What does the documentation say will happen if the value of the parameter pathname is null?

Does the constructor have a throws declaration, that is, does it say that it throws any exception, like the constructor of the Scanner did?

5 - What is the call stack?

The call stack is a data structure which keeps track of the order methods are calling other methods. In a stacktrace from a crash like the one below, what methods were called in what order? Hint: main is always called first, by the JVM no less!

Exception in thread "main" java.lang.RuntimeException: Important file not found, omg
	at Critical.loadSuperImportantFile(Critical.java:24)
	at Critical.initApplication(Critical.java:13)
	at Critical.main(Critical.java:10)

Solutions

Expand using link to the right to see the full content.

1 - Examples of runtime exceptions

For instance:

  • ArithmeticException
  • ClassCastException
  • NullPointerException
  • IllegalArgumentException
  • NegativeArraySizeException

The first three were mentioned in the lecture.

Please follow the links in the API to the classes you chose, and read the description about the various exceptions.

2 - Find four subclasses to IOException

For instance the following four classes are subclasses to IOException, and since IOException is a checked exceptions, they are too:

  • FileNotFoundException - Signals that an attempt to open the file denoted by a specified pathname has failed
  • FileSystemException - Thrown when a file system operation fails on one or two files. This class is the general class for file system exceptions.
  • MalformedURLException - Thrown to indicate that a malformed URL has occurred. Either no legal protocol could be found in a specification string or the string could not be parsed. (a URL is an address with a protocol like http://virt08.itu.chalmers.se where http is the protocol)
  • UnknownHostException - Thrown to indicate that the IP address of a host could not be determined. (A host is a node, like a computer for instance, on the network, and the IP address of a host is the internet protocol address. The IP address for virt08.itu.chalmers.se is, for instance 129.16.69.98 at the time this text was written)

3 - What is the full package name for some exceptions?

  • NullPointerException - java.lang - this is probably quite common, since classes in java.lang doesn't require import statements
  • FileNotFoundException - java.io - this has probably to do with input/output (IO)
  • MalformedURLException - java.net - this has proably to do with networking

4 - Runtime exceptions can be documented

The documentation says in the detailed desctiption:

Throws: NullPointerException - If the pathname argument is null

Note that this is not part of the method declaration! You need to read the detailed description in order to get this information!

The full declaration of the constructor in java.io.File which takes a single String argument is:

public File(String pathname)

As you can see, there is no throws after the argument parentheses, like there was for the Scanner constructor. NullPointer exception is a RuntimeException, and these are usually not part of the declarations, because there is no rules about having to handle-or-declare runtime exceptions enforced by the compiler. Even if the declaration did say throws RuntimeException (or throws NullPointerException), the compiler wouldn't care, since it knows that there is no obligation for calling code to care about such cases.

If you are really curious, here's the full source code for the File constructor in question (from jdk version 7):

    public File(String pathname) {
        if (pathname == null) {
            throw new NullPointerException();
        }
        this.path = fs.normalize(pathname);
        this.prefixLength = fs.prefixLength(this.path);
    }

As you see, a simple if-statement is used to see if the parameter is null. And here you actually can see the code for throwing a NullPointerException (without any error message string). More on syntax in coming sections below!

5 - What is the call stack?

In this stacktrace, we can see that main was called first (since it is the entry point of any Java application) and main called, on line 10, initApplication. Next, initApplication, on line 13, called loadSuperImportantFile, Which caused a RuntimeException on line 24. The rest of the stacktrace is omitted, since we wanted to focus on code we wrote ourselves.

The call-chain was then: main -> initApplication -> loadSuperImportantFile -> exception!

Exception in thread "main" java.lang.RuntimeException: Important file not found, omg
	at Critical.loadSuperImportantFile(Critical.java:24)
	at Critical.initApplication(Critical.java:13)
	at Critical.main(Critical.java:10)

Examples (with code) using exceptions

Description

This section deals with the syntax and code to use when dealing with exceptions. We'll show you the syntax for declaring that a method throws an exception, how to throw an exception and how to catch an exception in a handler. We'll also look at what an exception object is, how to create one and what methods an exception has for the handler who caught it.

Let's start with checked exceptions, because calling such a method will according to the rules force us to either declare that we too are in a method which throws an exception, or to write a handler catching the exception.

Stuff which typically throw checked exceptions in the API classes, is stuff which has to do with I/O (input/output) like opening files, reading from or writing to files. In fact, there's a whole family of checked exceptions dedicated to the problems which may occur with I/O, IOException and its subclasses.

If we want to open a file for writing, the API provides quite a few classes for us to do so. We'll look at two of them. If we want to write text to a file, we need some kind of object which is a writer with some convenient methods for writing such as println(). The PrintWriter class seems just right for us. So, in order to create a PrintWriter for a file, we must construct the PrintWriter providing an object which can write to a file. We'll use FileWriter to represent that object. This book doesn't cover file management techniques, so please be aware that this is just an example for you, to see how to deal with IOExceptions. The constructor for PrintWriter can take a FileWriter as argument, so we'll look at the constructor for FileWriter next. In the API documentation, we find this constructor for FileWriter which seems just right for our example:

public FileWriter(String fileName) throws IOException

That seems simple enough, just provide the file name, and we'll get an object which knows how to write to that file. It will fit perfectly for the constructor of our PrintWriter! What we decide to do, is this:

PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));

But there's this thing about the constructor declaration above. Did you notice the throws IOException part? It tells us that in order to call this constructor, we'd have to handle checked exceptions, in particular some kind of IOException. That shows us the syntax for declaring that a constructor or method throws some exception! Just add a throws clause to the declaration and specify the families of exceptions which may be thrown.

Ok, so back to our example. We need to create a PrintWriter, so we needed to create a FileWriter for its constructor. But the constructor says that it might throw some exception which we must handle somehow. Why is it doing this to us? Come, on, what could possibly go wrong when opening a file for writing? Actually quite a lot might go wrong! What if the String provided for the file name, is actually the name of a directory? That wouldn't be particularly easy to write to. And what if the file doesn't exist, so the FileWriter tries to create it, but the file system and operating system says that we are not allowed to do that for some reason? Or what if the file indeed does exist, but the file system and operating system doesn't allow us to write to it, because it is write protected or owned by some other user etc?

So we'd better be thankful for the heads up and decide how to handle such events. We have now learned the rules, and we don't want to argue with the compiler (it won't be convinced by us anyway), so we know what to do. Either we declare that our method where we want to open the file, also throws at least an IOException. Or, we have to handle the possible IOException which might be thrown by the constructor of FileWriter.

So let's look at the basic syntax for writing a handler. The technique is to put stuff which might throw an exception we'll handle in a block called a try-block:

try{
  stuffWhichMightThrowException();
}

If an exception occurs anywhere in the try block, execution will immediately jump to the end of the try-block, where we'll have our handler. The handler is written as a block called a catch-block (at least we call it that now):

try{
  stuffWhichMightThrowException();
}catch(ExceptionType e){
  // code that deals with the exception known in our handler as "e"
}

As you see, the catch-block starts with a pair of parentheses which declare a parameter of some exception type (which should match the exception we try to catch) and a parameter name for this exception, here e. As with any parameters, you can name it whatever you want, but we stuck with e for brevity. Inside the block of the handler, we can now use the exception object referred to by e in order to further investigate what has happened.

So, now it is time to talk a little about what an exception object (like the one we caught) is and what we can do with it! Like any object, it has a toString() method, which can be used for a textual representation of the exception, for instance if we want to log what happened to some error logfile. Additionally, exceptions have a few more methods, two of which we'll describe next. The getMessage() method, returns a String which may have a message for us. Perhaps the thrower created the exception, passing a message along to the constructor of the exception. If so, we can get the message by calling this method. Another interesting method which exceptions have, is the printStackTrace() method. This is a void method, which uses the standard error stream (in Java represented by System.err) to print the full stacktrace which lead up to the exception. This is also very useful for logging the root cause of the problem. Again, we'd like to remind you that stacktraces probably is better targeted towards programmers trying to understand and fix the problem, than towards the users of the program. It's good then, that it prints to standard error, which can be redirected and hidden from the user.

OK, so now, we have at least a few methods we could use on the exception we catch. We could then complete our handler for the construction of our PrintWriter to at least catch any exceptions and turn them into something more user friendly, as an error message. Note that this doesn't fix the problem at all, it just stops the exception from propagating up, potentially all the way to main, which might lack a handler too, which would make the program crash with a stacktrace!

static void writeFile(){
  try{
    PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));
  }catch(IOException ex){
    System.err.println("Couldn't open file for writing: " +
                        ex.getMessage());
  }
}

On a side note, please notice that the try-block and catch-block both are written using indentation, consistent with the indentation for the method itself. We should use indentation wherever we are using blocks.

What would the output of this error handler look like, if an exception was thrown and caught?

$ javac WritingFile.java && java WritingFile
Couldn't open file for writing: OutFile.txt (Permission denied)

In the example output above, we see in action one of the things which might go wrong when trying to open a file for writing. In this case, we were not allowed to open the file. Perhaps it was owned by some other user.

What would a stacktrace have looked like? An example stacktrace for the same problem (if left unhandled) could have looked something like this:

java.io.FileNotFoundException: OutFile.txt (Permission denied)
	at java.io.FileOutputStream.open(Native Method)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:221)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:110)
	at java.io.FileWriter.<init>(FileWriter.java:63)
	at WritingFile.writeFile(WritingFile.java:11)
	at WritingFile.main(WritingFile.java:6)

What would the syntax for our example method have been if we decided instead not to handle the IOException ourselves? According to the rules, we were allowed to declare that our method too throws an IOException, instead of handling it. We would use the same syntax as the constructor for FileWriter, actually!

static void writeFile() throws IOException{
  PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));
}

So the syntax in this case goes throws IOException. What would the consequence of this decision be for the caller of our method? Let's for simplicity say that it is main which calls our method (as shown in the stacktrace ;-) ). According to the rules, it is now main which has the decision to either handle it or declare that it too throws an exception. Now, declaring that the main method throws an exception isn't very productive, because there is no one who cares and handles it. Declaring that main throws exception is sort of careless behavior, and on par with having a handler which only re-throws the exact same exception. So, we decide here that the author of main knows how to behave, and chooses to write a handler. The handler in main can use the same syntax as our handler did in the example above.

Now, the consequence of our decision to declare that our method throws IOException put some restrictions on the author of the main method. Whether this is a good thing or bad thing is up for debate. It is important that you understand, however, that these decisions on who to put the responsibility for handling exceptions, will be a topic for the project programmers meetings. Some will argue that the method which causes the exception should deal with it, so that they don't have to write handlers all over the place, just to call your method which should work most of the time just fine. Others will argue that it is a good thing that every caller is aware of the problems your method might run into. And, to make stuff worse for people trying to learn Java, others still will argue that there is a middle ground, a compromise we can use here.

This leads us back to the strategy we discussed in the previous section, where we caught an exception and re-threw it as some other exception (more precisely, re-threw it as an unchecked exception).

A very simple example of this middle ground goes something like this. First, the main method (or some method very close to main) should have a handler for all bad things that can happen but aren't handled elsewhere. This includes (at least some) runtime exceptions. Anything which might be thrown somewhere in the system could then be handled in a central place, where the appropriate measures can be taken. If it is convenient to let code call our method without the "burden" of writing handlers, our method could be written without a throws clause. But what about the IOException for the permission denied error, then? It must be handled by our method. Those are the rules. What our method could do then, is to catch the IOException and in the handler, re-throw it as some kind of runtime exception (which will only be caught by the central exception handler which could be in main, for instance).

Now, the people who'd complain about having to handle IOExceptions from your method would be pleased, since they don't have to do anything. And the people who are particular about knowing about problems your method might run into are pleased, because they get their handler in the main, and the handler there can be written to catch also the runtime exception you are throwing when you catch an IOException.

The following, oversimplified of course, example shows how your method could be re-written to catch the IOException and wrap it inside a RuntimeException together with a message:

static void writeFile(){
  try{
    PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt"));
  }catch(IOException ex){
    throw new RuntimeException("Problem writing: "+ex.getMessage(), ex);
  }
}

The global handler in main (also very simplified, of course) could look something like this:

public static void main(String[] args){
  try{
    writeFile();
  }catch(Exception e){
    System.err.println("Something went wrong: " + e.getMessage());
    System.exit(1);
  }
}

The error message, if the global handler in main was put to work would then look like the following:

$ javac WritingFile.java && java WritingFile
Something went wrong: Problem writing: OutFile.txt (Permission denied)

Note that you now also got the basic syntax for throwing an exception! The keyword used was throw and the thing you throw is a reference to an exception object. The code in the example, threw a brand new RuntimeException, created on the fly.

Is it reasonable to throw runtime exceptions, since we do not get the help from the compiler to warn people that they might be thrown? Yes, of course it is. Why, of course? Because we don't keep secrets from the users of our objects and classes. If a public method might throw a runtime exception by the code we write, then we document that and make sure everyone who will call our method knows about this. Then it is up to them to ignore it or make sure there a handler further up the call chain.

An example of when it makes perfect sense to throw a runtime exception (and document it) is if we have a rule for some class, which says that some parameter is not allowed to be a null reference. Let's say that we have a constructor for a class Member (to create objects representing members of some association for a member system) which takes two parameters, name and email (both as String references). The rule says that the name parameter can never be null, or a NullPointerException will immediately be thrown by the constructor. We do this to prevent the system from ever creating a Member object whose name is null. The email, on the other hand, is not so important. Perhaps not every member has an email address even, so we allow our objects to have a null email.

The reasoning here, is that it is better that the system detects an attempt to create a Member with a null name and treat it as a programmer error, even if it leads to a program crash (or global handler terminating the program or so), then to hope that no code will do this by mistake.

The constructor could then look something like the below, if we accept that name and email are also instance variables of type String in the class:

public Member(String name, String email){
  // Name is NOT allowed to be null!!!
  if(name == null){
    throw new NullPointerException("Name cannot be null");
  }
  this.name  = name;
  this.email = email; // we can live with null emails ;-)
}

Of course it might seem a little drastic to throw a runtime exception from a constructor, but the fact is that this is not so uncommon at all. The only thing we have to keep in mind, is that this must of course be documented (and probably communicated to the programmers if they tend to forget stuff or read the documentation sparsely).

A quick search in the API source code for openjdk-7 shows that a NullPointerException is thrown at 550 places in the java top level package classes (classes in a package which starts with "java.").

If you want an alternative to the if-statement which is checking for null, there is a nice alternative in the Objects class, in the requireNonNull(T) method. It has two versions, one which takes an argument and throws a NullPointerException if it is null, and one that takes an argument and a String message and throws a NullPointerException if the first argument is null, and it sets the message passed as argument as the message of the NullPointerException. It can be used like this:

public Member(String name, String email){
  Objects.requireNonNull(name, "Name cannot be null");
  this.name  = name;
  this.email = email;
}

Any attempt to create a new Member with null as the parameter for name will result in a NullPointerException which left unhandled would result in a stacktrace similar to this:

Exception in thread "main" java.lang.NullPointerException: Name cannot be null
	at java.util.Objects.requireNonNull(Objects.java:228)
	at Member.<init>(NonNullExample.java:12)
	at NonNullExample.main(NonNullExample.java:5)

Finally in this section, we'd like to give you some advise. First, avoid to write a handler just be cause you "have to" and do nothing in the catch-block. This may sound self-explanatory, but it is sadly quite common practice not only among examples in literature and courses (often for brevity) but also among real programmers in the wild. It may solve a compiler error, but it will probably not handle the exception if you opt to do nothing when catching it. Second, try to avoid too broad Exception types in the catch-block. If you were to catch(Throwable t) you would catch every possible type of problem, regardless of whether it is an Error, Exception, RuntimeException or checked exception. It is probably not likely that you have a solution which fits every class of exception. So try to be precise and name as specific type of exception to catch as possible. It is legal to have several catch-blocks after each other, each catching a different family of exceptions. That seems like a good idea, if your try-block may set off many types of exceptions.

Videos

  1. Code and syntax for exceptions part 1 (eng) (sv) (download presentation)
  2. Code and syntax for exceptions part 2 (eng) (sv) (same PDF as above)
  3. Code and syntax for exceptions part 3 (eng) (sv) (same PDF as above)
  4. Code and syntax for exceptions part 4 (eng) (sv) (same PDF as above)

Exercises

1 - What could go wrong when trying to write to a file

Name three things which could go wrong when we open a file which we later want to write to. What do you think the corresponding exceptions are called? (This is not something you need to memorize for the exam - if this book is used in a course. Rather, we will encourage you to think about cases which can go wrong, and investigate the API to get hints about how it works!)

Hint: The FileOutputStream class might have some hints, regarding files - look at the constructors

2 - The JavaReader

In this exercise, we'll have a small application which can read and print the contents of a java source code file. The main uses the class JavaReader (provided as source code) to create an object and call the readAndPrint() method on this object.

However, the readAndPrint() method calls some API method which throw IOExceptions, so we must fix this in order to be able to compile!

This is the source tree for the code in this example:

.
`-- org
    `-- corporation
        |-- files
        |   `-- JavaReader.java
        `-- main
            `-- Main.java

By now, it should be fairly clear to you that the application consists of two classes from two packages:

  • org.corporation.files.JavaReader
  • org.corporation.main.Main

It's your first task to create the classes as source code files in the correct directory structure. Start by creating the directories:

$ mkdir -p org/corporation/files
$ mkdir -p org/corporation/main

Next create the sourcode files (for instance by opening atom giving it the argument org/corporation/files/JavaReader.java for the first file, then creating the other file in a similar fashion in org/corporation.files/main/Main.java ). These are code listings of the classes:

1 package org.corporation.main;
2 import org.corporation.files.JavaReader;
3 
4 public class Main{
5   public static void main(String[] args){
6     JavaReader reader = new JavaReader("org/corporation/files/wrong.java");
7     reader.readAndPrint();
8   }
9 }

And:

JavaReader class diagram
 1 package org.corporation.files;
 2 import java.io.IOException;
 3 import java.io.BufferedReader;
 4 import java.nio.file.Files;
 5 import java.nio.file.Path;
 6 import java.nio.charset.Charset;
 7 import java.nio.file.FileSystems;
 8 
 9 public class JavaReader{
10   private String file;
11   public JavaReader(String file){
12     this.file = file;
13   }
14   public void readAndPrint(){
15     Path path = FileSystems.getDefault().getPath(file);
16     BufferedReader reader =
17       Files.newBufferedReader(path,Charset.forName("US-ASCII"));
18     String line = null;
19     while ((line = reader.readLine()) != null) {
20       System.out.println(line);
21     }
22   }
23 }

Note: if we choose US-ASCII for "charset", we limit ourselves to only characters in the ascii table, including the latin letters of the English language. If we want to be able to read a Java file, it would actually be smarter to use "UTF-8" as "charset", which uses characters from a much larger table. UTF-8 is also what Java accepts as encoding for source code files. Try both charsets in combination with non-us-ascii characters to see what happens (if you want)

So, the main method creates an instance of org.corporation.files.JavaReader giving the constructor one argument "org/corporation/files/wrong.java". This is meant to create a JavaReader which knows the name and relative path of a Java-file.

Next, main tries to call readAndPrint() on the object just created. But the readAndPrint() won't be compiled, since the method uses calls which may throw an IOException. So the readAndPrint() must be changed somehow.

Compile the org/corporation/main/Main.java application.

$ javac org/corporation/main/Main.java

The compiler will say:

./org/corporation/files/JavaReader.java:19: error: unreported exception IOException; must be caught or declared to be thrown
      Files.newBufferedReader(path,Charset.forName("US-ASCII"));
                             ^
./org/corporation/files/JavaReader.java:21: error: unreported exception IOException; must be caught or declared to be thrown
    while ((line = reader.readLine()) != null) {
                                  ^
2 errors

Change the readAndPrint() method in org/corporation/files/JavaReader.java so that it declares that it throws IOException. Recompile.

What you just did, was to pass on the responsibility for the IOException to the caller of readAndPrint(), which is the main method. But the main method doesn't have a try-block with a handler for IOException. So the compiler will now complain:

org/corporation/main/Main.java:6: error: unreported exception IOException; must be caught or declared to be thrown
    reader.readAndPrint();
                       ^
1 error

Just as we suspected. Next, add a try-block in main, with a catch-block which catches an IOException. You will need to import java.io.IOException in the Main class for this to work. The following is a hint for how to write the try-catch handler in the main method:

try{
  // call the method here
}catch(IOException e){
  // Print an error message, print e (or ask e for the message it might have using e.getMessage() )
  // See the slides and video for examples
}

Compile again and confirm that compilation works.

Now run the program:

$ java org.corporation.main.Main

Note that there's an error message (because the code can't find the file called "org/corporation/files/wrong.java" (because there is no such file). If your handler in Main printed the reference to the exception, e, this is an example result:

Something went wrong: java.nio.file.NoSuchFileException: org/corporation/files/wrong.java

3 - The JavaReader - with chained (wrapped) RuntimeException

Now, we shall change the JavaReader's readAndPrint() method again, so that it doesn't say throws IOException. Instead, we will write a handler for IOException in the method itself, and throw a RuntimeException with the IOException inside it, together with a message.

Start by removing the throws IOException from readAndPrint(). Next, surround the code inside the method with a try-block which ends with a catch-block catching IOException. Call the IOException you catch e. Inside the catch-block, throw a new RuntimeException with the following two parameters in the call to the constructor: "Error reading file" and e. Something like:

...
...
catch(IOException e){
  throw new RuntimeException("Error reading file", e);
}

Next, remove the import statement in main which imports the java.io.IOException class. And change the catch-block so that it catches a RuntimeException instead (or even Exception if you want to catch all kinds of exceptions). In the catch-block, make the following printout to standard error:

   System.err.println("Critical failure: " + e.getMessage());
   System.err.println("Cause: " + e.getCause());

Compile your classes and run again. What was the output this time?

4 - Fix the file name

Now, change the filename in the constructor call to JavaReader to take the argument of a correct file which exists:

JavaReader reader = new JavaReader("org/corporation/files/JavaReader.java");

Re-compile and run again. What was the output from the program?

Solutions

Expand using link to the right to see the full content.

1 - What could go wrong when trying to write to a file

  • The file might exist but actually be a directory - not good ;-) - FileNotFoundException
  • The file cannot be created by us (filesystem/OS says NO!) - SecurityException
  • The file exists, but we cannot open it for some reason - SecurityException

Actually, the constructor for FileOutputStream(File f) will additionally throw a NullPointerException if the reference for the file object used in the constructor which takes a File reference is null. But this is a programmer error, and not a problem with opening files in general!

2 - The JavaReader

Suggested source for JavaReader:

package org.corporation.files;
import java.io.IOException;
import java.io.BufferedReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;

public class JavaReader{
  private String file;
  public JavaReader(String file){
    this.file = file;
  }
  public void readAndPrint() throws IOException{
    Path path = FileSystems.getDefault().getPath(file);
    BufferedReader reader =
      Files.newBufferedReader(path,Charset.forName("US-ASCII"));
    String line = null;
    while ((line = reader.readLine()) != null) {
      System.out.println(line);
    }
  }
}

The suggested code for Main is (note the import statement of IOException):

package org.corporation.main;
import java.io.IOException;
import org.corporation.files.JavaReader;
public class Main{
  public static void main(String[] args){
    JavaReader reader = new JavaReader("org/corporation/files/wrong.java");
    try{
      reader.readAndPrint();
    }catch(IOException e){
      System.err.println("Something went wrong: " + e);
    }
  }
}

3 - The JavaReader - with chained (wrapped) RuntimeException

Suggested new version of JavaReader:

package org.corporation.files;
import java.io.IOException;
import java.io.BufferedReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;

public class JavaReader{
  private String file;
  public JavaReader(String file){
    this.file = file;
  }
  public void readAndPrint(){
    try{
      Path path = FileSystems.getDefault().getPath(file);
      BufferedReader reader =
	Files.newBufferedReader(path,Charset.forName("US-ASCII"));
      String line = null;
      while ((line = reader.readLine()) != null) {
	System.out.println(line);
      }
    }catch(IOException e){
      throw new RuntimeException("Error reading file", e);
    }
  }
}

Suggested new version of Main:

package org.corporation.main;
import org.corporation.files.JavaReader;
public class Main{
  public static void main(String[] args){
    JavaReader reader = new JavaReader("org/corporation/files/wrong.java");
    try{
      reader.readAndPrint();
    }catch(Exception e){
      System.err.println("Critical failure: " + e.getMessage());
      System.err.println("Cause: " + e.getCause());
    }
  }
}

Note that main doesn't have to import java.io.IOException any more. It handles any exception (or if you chose to catch RuntimeException, any runtime exception) instead. We have less coupling between Main and JavaReader, but we can still catch critical exceptions.

The output will now be:

$ javac org/corporation/main/Main.java && java org.corporation.main.Main
Critical failure: Error reading file
Cause: java.nio.file.NoSuchFileException: org/corporation/files/wrong.java

4 -fix the file name

The output should be the source code for org/corporation/files/JavaReader.java !

Creating your own Exception using extends

As always, start by creating a new directory for these exercises. Don't do all your exercises in e.g. your home directory or some other inappropriate place. Always start from scratch with a new directory.

Description

It is possible to create our own custom exception classes using inheritance. We have basically two options when doing this, extend RuntimeException and create an unchecked exception, or extending Exception and create a checked exception. The same reasoning applies to this choice as the one from the previous sections of this chapter. Checked exceptions force the caller of a method to handle-or-declare, while runtime exceptions are unchecked by the compiler (and might thus come more as a surprise for the caller). Review previous sections for a discussion about this.

Simply put, if you think the caller can recover from the situation which caused the exception, then you may use checked exceptions in order to make sure the caller prepares for this event. If you are pretty sure that the caller can't fix the situation anyway, it is possible to go with unchecked exceptions, and hope that there is a global handler which at least lets the application shut down gracefully if it is a really critical problem.

One reason for creating our own custom exceptions, is that we can wrap and hide the actual exception our class might run into. We can still throw an exception when something unwanted happens, but we can wrap it up in an exception which belongs to our package. This way, the client code (callers of our methods) doesn't have to know what the cause exactly is, and this has many benefits. First of all, we hide implementation details from the caller by using only our own exceptions. Second, the caller doesn't have to import the package where the original exception comes from, which is great, because if we change our mind later, then the caller is depending on this decision and might have to import some new exception from some new API, which is bad. Third, we can actually afford to be more specific about the problem if we use our own exceptions. Writing our own exceptions lets us name them in terms of the problem domain of our application, and not the domain of the API which caused the original exception.

Let's say, for instance, that we write a class which fetches objects and offers a method which returns a list of some objects to the caller. If these objects originally comes from a database, we can hide this fact from the callers, so that we can change our mind later. Also, there are quite a few exceptions which can be thrown when working with a database. Here are a few: BatchUpdateException, RowSetWarning, SerialException, SQLClientInfoException, SQLNonTransientException, SQLRecoverableException, SQLTransientException, SQLWarning, SyncFactoryException, SyncProviderException. Then there are of course also subclasses to these exceptions. Now, the caller of our method simply wants a list of objects, and the caller might accept that this at rare occasions doesn't work. But our bet is that the caller isn't so much interested in learning about the various exceptions connected to databases. We can here create our own exceptions with better names which fit better into the terminology of our application.

In order to extend Exception we simply use the extends keyword, as you would when extending any class. It is good if we provide at least two constructors for flexibility of our Exception class, and we can mimic two constructors from the superclass.

We suggest at least the following two constructors for our custom exception (Let's call the exception class OrderSecurityException):

OrderSecurityException class diagram
public class OrderSecurityException extends Exception{
  public OrderSecurityException(String message){
    super(message);
  }
  public OrderSecurityException(String message, Throwable cause){
    super(message, cause);
  }
}

As you see, we call the constructors of the superclass Exception from our constructors, which passes along the parameters to the respective constructor of Exception.

The second constructors allow us to wrap some other Exception, if we'd want to do that.

We have shown you the syntax for wrapping exceptions in previous sections of this chapter and there is no point in repeating it here.

This section is mostly about syntax, so we will not go any deeper into the various choices we have leading up to design decisions for our custom exceptions. This will be covered in books about more advanced programming issues since this is an introductory Java book. This should come as a welcome surprise for you, because within the scope of this book, all you need to know about how to extend Exception (or RuntimeException) is stuff you already have learned from the chapter on inheritance!

The wrapping of another exception is also very uncomplicated business. You just create an exception using the operator new, and give a message parameter (as a String) as the first argument and a reference to the exception object to wrap as the second argument. Invoking a constructor and passing arguments shouldn't be new information either! We have created numerous objects this was so far in the course.

Videos

  1. Writing your own part 1 (eng) (sv) (download presentation)
  2. Writing your own part 2 (eng) (included in part 1 in Swedish video)

Exercises

1 - Create a checked exception

Create a new class called InsufficientBalanceException which extends Exception. Implement at least one constructor which takes a reference to a String as an argument, and calls the superclass (Exception) constructor, passing the String on.

Example constructor:

public InsufficientBalanceException(String message){
  super(message);
}

Change the withdraw method of the SavingsAccount class below, so that it declares that it throws InsufficientBalanceException if the balance doesn't cover the attempted withdrawal:

public class SavingsAccount{
  private double balance;
  public SavingsAccount(){

  }
  public SavingsAccount(double initialBalance){
    this.balance = initialBalance;
  }

  public void withdraw(double amount){
    if( (balance-amount) >=0.0 ){
      balance -= amount;
    }else{
      ; // throw a new InsufficientBalanceException
        // with a message including an error message and
        // the current balance, and the amount requested
    }
  }
  public void deposit(double amount){
    balance += amount;
  }
  public double balance(){
    return balance;
  }
  @Override
  public String toString(){
    return String.format("Balance: %.2f", balance);
  }
}

You should now have two source code files in the directory where you are working, SavingsAccount.java and InsufficientBalanceException.java . Add a third source code file with a main class which tests the SavingsAccount class. In main, create a SavingsAccount with an initial balance of 100.0. Withdraw 50.00 and print the account to the standard out. Withdraw 50.01 and print the account to standard out. If you couldn't withdraw the second time, your code works.

In the catch clause of the main's try-catch statement, do something like this:

System.err.println("Couldn't withdraw: " + e.getMessage());

An example output from a testrun could look something like this:

$ javac TestSavingsAccount.java && java TestSavingsAccount
Before withdrawal: Balance: 100.00
Withdraw 50.00
After withdrawal: Balance: 50.00
Withdraw 50.01
Couldn't withdraw: Current balance: 50.0 requested withdrawal amount: 50.01
Final state of a: Balance: 50.00

2 - Change the constructor

SavingsAccount class diagram with two exceptions

Change the constructor of SavingsAccount so that if someone tries to create a new SavingsAccount with a negative balance, the constructor throws IllegalArgumentException with a message about what was wrong.

As the last statement of the test class' main method, try to call the constructor of SavingsAccount with a value of -1. The test output should now end with the following stacktrace (but with file names and line numbers matching your situation):

Exception in thread "main" java.lang.IllegalArgumentException: Negative amount: -1.0
	at SavingsAccount.<init>(SavingsAccount.java:8)
	at TestSavingsAccount.main(TestSavingsAccount.java:17)

IllegalArgument is a runtime exception and you don't have to handle-or-declare the fact that one of the constructors might throw such an exception. We can argue that trying to create an account with a negative initial balance is a programmer error, so a runtime exception should be the way to go here.

3 - Optional exercise

Rather than throwing an IllegalArgumentException, the constructor could throw a custom runtime exception. Create a class NegativeAmountException which extends RuntimeException. Make sure you have at least one constructor which can take a String argument with the message, and which calls the superclass constructor passing on this message String.

Change the constructor of SavingsAccount so that it throws this new type of runtime exception instead. Also, change deposit so that it too throws this new NegativeAmountException if someone tries to deposit a negative amount. Do the same with withdraw, if someone calles withdraw with a negative argument, throw a NegativeAmountException.

No throws declarations should be necessary, since we are throwing a runtime exception here.

Add code to the test class which tries to

  1. create an account with a negative initial amount
  2. deposit a negative amount
  3. withdraw a negative amount

Think about what the effect would be if we allow negative amounts in all three places. Can you explain the effect each situation would have if we allowed negative amounts?

Hint: You need try-catch around each test case which might throw an exception, if you want to try them all

The output could look something like this:

Before withdrawal: Balance: 100.00
Withdraw 50.00
After withdrawal: Balance: 50.00
Withdraw 50.01
Couldn't withdraw: Current balance: 50.0 requested withdrawal amount: 50.01
Creating account with a negative amount
Negative amount: -1.0
Deposit of a negative amount
Can't deposit a negative amount: -1.0
Withdrawal of a negative amount
Can't withdraw a negative amount: -1.0
Final state of a: Balance: 50.00

Solutions

Expand using link to the right to see the full content.

Suggested solutions to both ex. 1 and 2 - Create a checked exception, change constructor

SavingsAccount:

public class SavingsAccount{
  private double balance;
  public SavingsAccount(){

  }
  public SavingsAccount(double initialBalance){
    if(initialBalance<0.0){
      throw new IllegalArgumentException("Negative amount: " + initialBalance);
    }
    this.balance = initialBalance;
  }

  public void withdraw(double amount) throws InsufficientBalanceException{
    if( (balance-amount) >=0.0 ){
      balance -= amount;
    }else{
      throw new InsufficientBalanceException("Current balance: " +
                                             balance +
                                             " requested withdrawal amount: " +
                                             amount);
    }
  }
  public void deposit(double amount){
    balance += amount;
  }
  public double balance(){
    return balance;
  }
  @Override
  public String toString(){
    return String.format("Balance: %.2f", balance);
  }
}

InsufficientBalanceException:

public class InsufficientBalanceException extends Exception{
  public InsufficientBalanceException(){
    super();
  }
  public InsufficientBalanceException(String message){
    super(message);
  }
}

TestSavingsAccount:

public class TestSavingsAccount{
  public static void main(String[] args){
    SavingsAccount a = new SavingsAccount(100.0);
    try{
      System.out.println("Before withdrawal: " + a);
      System.out.println("Withdraw 50.00");
      a.withdraw(50.00);
      System.out.println("After withdrawal: " + a);
      System.out.println("Withdraw 50.01");
      a.withdraw(50.01);
      System.out.println("After withdrawal: " + a);
    }catch(InsufficientBalanceException e){
      System.err.println("Couldn't withdraw: " + e.getMessage());
    }
    System.out.println("Final state of a: " + a);

    new SavingsAccount(-1);
  }
}

Suggested solution to 3 - optional exercise

NegativeAmountException:

public class NegativeAmountException extends RuntimeException{
  public NegativeAmountException(){
    super(); // would be implicit, but we'll write it to remind you                                                                              
  }
  public NegativeAmountException(String message){
    super(message);
  }
}

SavingsAccount:

public class SavingsAccount{
  private double balance;
  public SavingsAccount(){
    
  }
  public SavingsAccount(double initialBalance){
    if(initialBalance<0.0){
      throw new NegativeAmountException("Negative amount: " + initialBalance);
    }
    this.balance = initialBalance;
  }

  public void withdraw(double amount) throws InsufficientBalanceException{
    if(amount < 0.0){
      throw new NegativeAmountException("Can't withdraw a negative amount: " + amount);
    }
    if( (balance-amount) >=0.0 ){
      balance -= amount;
    }else{
      throw new InsufficientBalanceException("Current balance: " +
                                             balance +
                                             " requested withdrawal amount: " +
                                             amount);
    }
  }
  public void deposit(double amount){
    if(amount < 0.0){
      throw new NegativeAmountException("Can't deposit a negative amount: " + amount);
    }
    balance += amount;
  }
  public double balance(){
    return balance;
  }
  @Override
  public String toString(){
    return String.format("Balance: %.2f", balance); 
  }
}

TestSavingsAccount:

public class TestSavingsAccount{
  public static void main(String[] args){
    SavingsAccount a = new SavingsAccount(100.0);
    try{
      System.out.println("Before withdrawal: " + a);
      System.out.println("Withdraw 50.00");
      a.withdraw(50.00);
      System.out.println("After withdrawal: " + a);
      System.out.println("Withdraw 50.01");
      a.withdraw(50.01);
      System.out.println("After withdrawal: " + a);
    }catch(InsufficientBalanceException e){
      System.err.println("Couldn't withdraw: " + e.getMessage());
    }
    try{
      System.out.println("Creating account with a negative amount");
      new SavingsAccount(-1);
    }catch(Exception e){
      System.err.println(e.getMessage());
    }
    try{
      System.out.println("Deposit of a negative amount");
      a.deposit(-1);
    }catch(Exception e){
      System.err.println(e.getMessage());
    }
    try{
      System.out.println("Withdrawal of a negative amount");
      a.withdraw(-1);
    }catch(Exception e){
      System.err.println(e.getMessage());
    }
    System.out.println("Final state of a: " + a);
  }
}

The source code for all solutions to "writing your own" including the final version with the optional exercise can be downloaded from github.

Some rules and syntax

Description

Finally (no pun intended), we'll give you some additional rules and syntax for working with exceptions. There's a whole lot more to learn if you are really interested in the links section at the end of the page as usual.

You can have more than one catch-block, each catching a different family of exceptions. This is encouraged, because if there can be more than one unrelated exception in the try-block, there is probably different ways to handle them. So rather than catching a broad superclass type exception, you are encouraged to write a catch-block for each type of exception which might occur in the try-block (if more than one).

try{
  // some code which might throw IOException
  // some code which might throw NumberFormatException
}catch(IOException ioe){
  // Handle I/O problem
}catch(NumberFormatException nfe){
  // Handle the fact that some text couldn’t be converted to a number
}finally{
  // If you need to, you can always add a finally clause
}

Sometimes, however, you have the same solution to more than one type of exception. Rather than repeating yourself, you can in such cases catch a list of exception types, separated by pipe (the vertical bar | ):

try{
  //Some code which can throw an SQLException
  //Some code which can throw an IOException
}catch(SQLException|IOException e){
  // Code which can handle both cases
  // (n.b. they should have the same solution if you do this!)
}

There are some rules when it comes to inheritance and overriding methods. If you extend a class and override a method from the superclass, then you cannot add exceptions to the method if none are declared to be thrown in the superclass version. And you cannot say that your overriding version of the method throws an exception which is broader than the exceptions that the superclass version throws. So you cannot declare that the overriding method throws an exception which is a superclass to the exception the superclass version throws.

The reason for this rule is quite simple and logical. Client code can have a reference to a superclass type and call some method. It must then know that the exceptions which are declared to be thrown by this superclass is all that it needs to write a handler for (or declare that it throws). It is quite common that the client code has a reference to some superclass and doesn't know what class the actual object the reference refers to is. So the compiler enforces that the object won't throw any broader exception than those of the method as declared in the superclass.

The last thing we'd like to tell you is a construct called try-with-resources. Please refer to the section above where we talked about the finally clause. One typical use for the finally clause is to close resources opened in the try-block. Closing a resource in the finally block, guarantees that it will be closed whether or not any exception was thrown.

The try-with-resources construct takes advantage of two interfaces, AutoCloseable and its subinterface Closeable. The AutoCloseable interface declares one single method, close(). Many of the classes dealing with resources (files, streams, connections) implement the AutoCloseable interface (or Closeable). Those who does, thus guarantees that they have a close() method which will close the resource.

The try-with-resources construct lets us skip the finally-block for such objects who have a close() method. The syntax is to put a pair of parentheses after the try and initialize the resource inside it. This will guarantee that the close() method is called, regardless of any exceptions (just like the case with the finally block). Here's a functioning program which uses this construct. The program reads the first line of text in its own source code and prints it to the standard out. It opens the source code file (the resource) and the try-with-resources construct guarantees that the file is closed, whether an exception happened or not.

import java.io.*;
public class Resources{
  public static void main(String[] args){
	String firstLine = getFirstLine();
	System.out.println("First line of this file: " + firstLine);
  }
  static String getFirstLine(){
	try( BufferedReader in = new BufferedReader
	      (new FileReader("Resources.java")) ){
	  return in.readLine(); // use the resource
	}catch(IOException e){
	  System.err.println("Couldn't open Resources.java");
	  return null;
	}
  }
}

Running the program will produce the following:

$ javac Resources.java && java Resources
First line of this file: import java.io.*;

Videos

  1. Some rules and syntax (eng) (sv) (download presentation)

Exercises

No exercises here. This section is just for completeness for the students who wants to know more about the syntax and rules for exceptions in Java. We will not expect you to know all these rules on an exam (if this book is used as a course), so you don't have to practice this unless you really want to. But you'll have to create your own exercises, then.

Solutions

Expand using link to the right to see the full content.

  1. Bladiblah

Links

Source code

  • Code from presentations and solutions (github)

External links

Books this chapter is a part of

Programming with Java book

Book TOC | previous chapter | next chapter