First Part
Learning objectives
- Understanding the difference between checked and unchecked exception
- Defining the use of the try, catch, finally, throw and throws keywords
- Implementing your own exception
Exceptions:
Exceptions are “problems” that occurs during the execution of a program. When an exception occurs, the normal flow of the program is interrupted, and if the exception is unhandled, the program will terminate.
Some exception can be caused by user error, others by programming error or even by failure of some physical resource.
Some examples of sources of exceptions are:
- A user enters an invalid data
- A file that needs to be opened cannot be found
There are two categories of exceptions: Checked and Unchecked exceptions.
Checked exceptions:
A checked exception is an exception that cannot be ignored and needs to be explicitly handled by the programmer, we will see how later. These exceptions are checked at compile time.
A common example of a checked exception is the FileNotFoundException. When using a FileReader ,(We will explore FileReader further in another lab; they essentially allow you to read the content of a file) if the file specified in the constructor of the FileReader does not exist, then FileNotFoundException occurs.
import java.io.File;
import java.io.FileReader;
public class FilenotFound_Demo {
public static void main(String args[]) {
readFile();
}
public static void readFile(){
File file = new File("E://file.txt");
FileReader fr = new FileReader(file);
}
}
If you try to compile this code, you will get this message:
FilenotFound_Demo.java:11: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
FileReader fr = new FileReader(file);
^
1 error
Unchecked exceptions:
Unchecked exceptions are exceptions that do not need to be explicitly handled by programmers. They include bugs, logical error and improper use of an API.
Here is a hierarchy exception:
The Java exceptions under Error and RuntimeException are exceptions that are not checked at compile time.
An example of unchecked exception you might be familiar with is the ArrayIndexOutOfBoundsException that occurs when you try to access the index of an array that is not within the boundary of the array.
public class ArrayOutOfBound_Demo {
public static void main(String args[]) {
createArray();
}
public static void createArray(){
int[] myArray=new int[2];
myArray[2]=1;
}
}
If you try to compile this code it will do so without errors. However, when you try to run the code, you will get this message:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
at ArrayOutOfBound_Demo.createArray(ArrayOutOfBound_Demo.java:8)
at ArrayOutOfBound_Demo.main(ArrayOutOfBound_Demo.java:4)
Throw and Throws:
If a method does not handle a checked exception, it needs to declare at the end of its definition that it throws that exception. You can also throw an exception, either by creating a new exception or by throwing an exception that was just caught, by using the throw keyword.
It is important that you remember:
- Keyword throws: in the method declaration, indicates what checked exception can be thrown by that method, and, optionally, some or all of the unchecked exceptions that can be thrown by that method.
- Keyword throw: use to throw an exception, whether checked or unchecked
Throwing an exception means that the exception is send to the caller of this method.
A method can have multiple throws; they need to be separated with comas like this:
public void myMethod() throws ExceptionA, ExceptionB{
//some code
}
Try, Catch and Finally:
Any code that is likely to cause an exception should be surrounded by a try block. A try block should be followed by a catch block. If an exception occurs during the execution of the try block and the exception is thrown, the exception may then be caught by a catch block associated to that exception.
A catch statement involves declaring the type of exception that we are trying to catch. If the exception that we are trying to catch is thrown, the code inside the catch block will be executed. Catch blocks always follow a try block, there can be one or multiple catch blocks but only one will be executed.
Important observation: only the first catch statement encountered that is valid will be executed, it is therefore important to always put the catch statements in order from the most specific to the least.
A finally statement is a block of code that will always be executed after a try-catch whether or not an exception was thrown and or caught.
In this example, we try to run the method throwingMethod, which throws IOException. When the exception is thrown, is it caught in the main method by the catch statement. Since the IOException is a checked exception, the method signature for throwingMethod has to finish with the keyword throws and the type of exception (IOException)
import java.io.IOException;
public class TryCatchThrow_Demo {
public static void main(String args[]) {
try{
System.out.println("start the try block");
throwingMethod();
}
catch(IOException e){
System.out.println("The exception is caught");
}
}
public static void throwingMethod() throws IOException{
//some code...
System.out.println("Running the method throwingMethod");
throw new IOException();
}
}
When running this code you will see:
start the try block
Running the method throwingMethod
The exception is caught
Now let's update the ArrayOutOfBound_Demo shown before, this new version has been modified to handle the exception.You can see that we do not need to use the throws keyword because it is an unchecked exception. We also have 2 catch statements, one to handle a specific exception ArrayIndexOutOfBoundsException and the other to catch and react in a default manner to any other exceptions that might be thrown. catch(Exception e) will catch every exception as they are all child of Exception. We also have a finally statement that will always be executed.
Note: the most specific exception has to be placed first
public class ArrayOutOfBound2_Demo {
public static void main(String args[]) {
try{
System.out.println("start the try block");
createArray();
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("The specific exception is caught");
}
catch(Exception e){
System.out.println("the generic Exception is caught");
}
finally{
System.out.println("Executing the finally block");
}
}
public static void createArray(){
int[] myArray=new int[2];
myArray[2]=1;
}
}
When running this code you will see:
start the try block
The specific exception is caught
Executing the finally block
Try to inverse the order of the catch statements to see what happens!
Exercise 1
Files to submit:
Complete the following code so that it compiles and that you catch each type of exception thrown by the method throwException and print out the type of exception as well as the exceptionNumber that was randomly generated and then return the name of the exception. The default value to be returned is "NoException" (if none of the specified exceptions have been thrown).
You can use the starter code from Exercise1.java. This file will be part of your submission.
import java.io.IOException;
public class Exercise1 {
public static void throwException(int exceptionNumber) throws Exception{
if(exceptionNumber==1){
throw new Exception();
}
if(exceptionNumber==2){
throw new ArrayIndexOutOfBoundsException();
}
if(exceptionNumber==3){
throw new IOException();
}
if(exceptionNumber==4){
throw new IllegalArgumentException();
}
if(exceptionNumber==5){
throw new NullPointerException();
}
}
/*
Returns the name of the exception thrown by the method throwException.
Method that handles the exceptions thrown by the throwException method.
@param exceptionNumber
@return The exception name or "NoException" if no exception was caught.
*/
public static String catchException(int exceptionNumber){
try{
throwException(exceptionNumber);
}
// YOUR CODE HERE
return "NoException";
}
public static void main(String[] args) {
int exceptionNumber=(int)(Math.random()*5) + 1;
String exceptionName = catchException(exceptionNumber);
System.out.println("Exception number: " + exceptionNumber);
System.out.println("Exception name: " + exceptionName);
}
}
Running the code a few times should give a result similar to this:
>java Exercise1
The exception type is: ArrayIndexOutOfBoundsException, the exceptionNumber is: 2
Exception number: 2
Exception name: ArrayIndexOutOfBoundsException
>java Exercise1
The exception type is: NullPointerException, the exceptionNumber is: 5
Exception number: 5
Exception name: NullPointerException
>java Exercise1
The exception type is: ArrayIndexOutOfBoundsException, the exceptionNumber is: 2
Exception number: 2
Exception name: ArrayIndexOutOfBoundsException
>java Exercise1
The exception type is: Exception, the exceptionNumber is: 1
Exception number: 1
Exception name: Exception
Creating exceptions:
Exceptions are classes; they contain methods and fields just like any other class. All exception needs to be a child of Exception. To create a checked exception you need to extend the class Exception, for unchecked exception you need to extend the class RuntimeException.
For example, we might want to throw an exception if a given number is negative. We would then create the class NegativeNumberException.
public class NegativeNumberException extends IllegalArgumentException{
private int number;
public NegativeNumberException(int number){
super("Cannot us a negative number: "+ number);
this.number=number;
}
public int getNumber(){
return number;
}
}
Note : The call to the super constructor will allow the default method getMethod to return the String given as parameter. There are many other method inherited from the class Exception including toString
That new exception we created can now be used like a regular exception:
public class NegativeNumberException_Demo{
public static void main(String args[]) {
try{
printSquareRoot(4);
printSquareRoot(-1);
}
catch(NegativeNumberException e){
System.out.println(e.getMessage());
System.out.println("the number " + e.getNumber() + " is invalid");
}
}
public static void printSquareRoot(int x) throws NegativeNumberException{
if(x<0){
throw new NegativeNumberException(x);
}
System.out.println("the square root of " + x + " is "+Math.sqrt(x));
}
}
When running this code you will see:
the square root of 4 is 2.0
Cannot us a negative number: -1
the number -1 is invalid
Exercise 2:
Files to submit:
- Account.java
- NotEnoughMoneyException.java
For this exercise, you will need to create a class named Account, this class represents a bank account. This class has a constructor that sets the variable balance to 0. It also has the methods: deposit that takes a double and add that number to the balance, withdraw that takes a double and removes that amount from the balance (both method should print the new balance), and getBalance (the getter method for balance). However, if the amount to withdraw is higher than the balance, you need to throw the NotEnoughMoneyException. You will need to create the NotEnoughMoneyException, it extends IllegalStateException. It has a method getAmount that returns the amount that was requested, getBalance that returns the balance at the time the exception was created and getMissingAmount that returns the amount of money needed to withdraw the requested amount. The exception should also invoke it’s super constructor so that the method getMessage indicates that the amount cannot be withdrawn.
For this exercise, you have to submit the files: Account.java and NotEnoughMoneyException.java.
public class Exercise2{
public static void main(String args[]) {
try{
Account myAccount=new Account();
myAccount.deposit(600);
myAccount.witdraw(300);
myAccount.witdraw(400);
}
catch(NotEnoughMoneyException e){
System.out.println(e.getMessage());
System.out.println("You are missing " + e.getMissingAmount() + "$");
}
}
}
When running the code in Exercise2.java you should get an output similar to this:
new balance=600.0$
new balance=300.0$
you have not enought money to witdraw 400.0$
You are missing 100.0$