Exceptions are a type of error that a Java application might produce if there is a problem. Now one potential issue with this is that Java has both Exceptions and Errors, so lets discuss those real quick.
Error: indicates serious problem that a reasonable application should not try to catch. These tend to deal with issues with the Java Run Time Environment (JRE) itself.
Exception: indicates a problem condition that a reasonable application might try to catch.
Both an Exception and an Error are objects that will be “thrown”. Thrown as in “generated” and our application can “catch” them. Because they are objects, the class Object is in their hierarchy, and they both extend a class called Throwable
.
There a multiple types of Errors and Exceptions such as NullPointerException and VirtualMachineError. The UML class diagram might look like:
Since Exceptions are the ones which we can reasonably attempt to catch, we will focus on those. The others deal with the JRE which is outside of our normal scope.
The Idea of Exceptions
the basic idea of an exception is that you will run into problems when you are programming, things that are outside of your control. For example, you might go to load a file, and the file is corrupted.
Or maybe you have information and you need to divide two numbers, but one of the numbers that people give you is a zero. You cannot control what data is input, and if you don’t test every potential value, you can run into issues.
The exception is generated instead of crashing the entire application, and can allow the developer to safely exit the application, continue on with working, or make some other decision.
To be able to do that, we must know how this process works. There are several key words which are used to generate these results – they include, try, catch, finally, throw, and throws.
Here is an example of a Java Exception being thrown:
public static void main(String args[]) {
double result - 10 / 0;
}
Here is another, and different type of exception.
public static void main(String args[]) {
String name = null;
System.out.println(name.length());
}
In both of these examples, the code is syntactically correct. However there are errors that will appear at run time. Now some compilers will try to catch some of these errors before you run them, however, it may not always know what will happen, and not every potential error can be caught.
The idea of the compiler catching these is a “checked exception“, and the intention is to reduce the number of run time errors by simulating running the app. However, we cannot control what a user enters, or a network resource becoming unavailable after we’ve started using it, etc. These are called unchecked exceptions.
For those that we attempt to protect the run time from, we use the try/catch block.
Exception Information
Exceptions provide information that we can use, usually in debugging, logging, or in presenting a lot of confusing data to an end user. As an object, they have methods which return data for us, such as the getMessage() method, and the printStackTrace() method. Now these are not the only methods, but they are the ones most commonly used.
getMessage()
returns a string with information about the error message. This tells you what the error message is, although it’s not always the best in describing the error. Keep that in mind if you need to create your own exceptions – it needs to be understandable by people other than yourself!
printStackTrace()
returns a string with information about every method in the stack from the time the error occurred to the time it was finally caught. This can be very helpful for determining where the error occurred.
Try, Catch
The Try/Catch block is one of the absolute core components you might run into.
try
is a keyword which lets Java know that you are going to try something that is potentially dangerous. Now, you may not have to use this block, or you might need to, depending upon what it is that you are going to do. For example, when doing a lot of, but not all, IO operations, you will be required by Java to wrap those commands in a try
.
The try
block is within a set of braces. And it is always followed by a catch
block.
The catch block is where you catch the exceptions that your code might generate. Within parentheses you will specify what exception you are expecting to potentially catch. Since all exceptions extend from Exception, you can simply catch that, catch a specific exception, or if your code requires it, catch multiple exceptions due to the potential of multiple types of errors.
You cannot have a try
without a catch
, and vise versa.
The most common example you’ll see is below with a single exception being caught. In this case, we go with Exception
, and as the parent of all Exceptions, the exception being caught can by its polymorphic nature.
try {
// block of code to monitor for errors
// the code you think can raise an exception
}
catch (Exception exOb) {
// exception handler for Exception
}
If you want to test for multiple types of potential Exceptions you would use the following sample block of code. Notice in the code block that the more specific Exception is listed first. Java will go through the list of exceptions, until it finds the one that it needs. Having a generic “catch all” isn’t a bad thing, but tends to be unnecessary.
try {
// block of code to monitor for errors
// the code you think can raise an exception
}
catch (ExceptionSpecific exOb) {
// exception handler for specific type of Exception
}
catch (Exception exOb) {
// exception handler for Exception
}
Now you might ask, why have multiple catches – well if you have multiple potential exceptions, you can use it to handle them differently. Providing better error messages, or handling the process in a different manner allowing some to fail safely, while others to attempt to recover from them.
And finally
The finally block is an optional block. While there can be multiple catch blocks, there can be only zero, or one, finally block. This block always runs, whether an Exception is thrown or not.
The idea is to do anything that needs to be done regardless of what has previously happened. For example, closing a file (or other data stream) that you open.
try {
// block of code to monitor for errors
// the code you think can raise an exception
}
catch (Exception exOb) {
// exception handler for Exception
}
finally {
// this optional block can be very useful
}
Throw and Throws Keywords
If you have a checked exception, and you do not catch it, you must throw it by using the throws
keyword. This is done after your function header, but before the function body.
When an error occurs, you can throw
it manually, or have it bubble up through the stack.
// ...
throw new RemoteException();
// ...
Understanding the Call Stack
The call stack is not unique to Java. Rather, it is used to identify the methods which are called in order. As a new method (function) is called, it is added to the call stack, and when it exits, it is popped off of the stack, and you know which method to return to. Now this stack object has a lot of extra information in it that we don’t need to worry about at this time.
When we look at the methods within Exception object and see printStackTrace
, it is letting us know what series of methods were called to get to this error. As you will often see, you might have called on method, but there are dozens of methods which are called by APIs, Java libraries, etc.
If you read through this, you can often find where the error actually occurred and either pass it on to the library developer to fix (if that is the needed case) or let you know where to go to fix your code.
Generally line numbers are provided to help make debugging easier.
Important Notes About Scope
Remember, in Java, like other languages, items created within a certain block, can only be seen within a block. So it might be tempting to create a variable/object within the try, however, then it cannot be seen inside the finally block.
So often you will need to create variables outside of the try block, and instantiate them inside of the try block, so you can properly use them everywhere you need to.
Why not Try/Catch Everything
Now one thing I see people ask about periodically, and I know I asked it when I first started to use try/catch blocks, was why not use them around every method, to keep yourself extra safe.
Well, the answer is, that the process of “trying” each line of code to run, slows the performance of your application. So you should focus on using try/catch only where you really need it, not on everything.
In Introduction to Exceptions in Java was originally found on Access 2 Learn
2 Comments
Comments are closed.