The course is part of this learning path
This training course introduces you to Java exceptions and how you should go about handling, managing, and recovering from them through the use of appropriate Java exception handling.
- Understand what exceptions are and when and how they happen
- How to handle and recover from exception
- Implement exception handling
- Understand the try catch finally statement
- A basic understanding of software development
- A basic understanding of the software development life cycle
- Software Engineers interested in learning Java to develop applications
- Software Architects interested in learning Java to design applications
- Anyone interested in basic Java application development and associated tooling
- Anyone interested in understanding the basics of the Java SDK
- Okay, welcome back, in this lecture we'll introduce you to Java exceptions and exception handling. We'll cover items such as, defining a try/catch block that allows methods that throw exceptions to be called, correctly implementing try/catch blocks for methods that throw multiple exceptions, and naming the exception supertype that is not checked for by the compiler. So, exactly what is an exception? There are many types of errors that can crop up in production systems. Java provides a standard way of handling them. System errors, device errors, data inconsistencies, program errors, logic errors, etc, can all be handled with exceptions. Exceptions in Java are very efficient as there is no error checking if all goes well, except right at the point where the error could happen. The return up the calling stack does not require error checking on the return from each method. When an exception is thrown and caught, then and only then does the code have to deal them. Exception Architecture. Methods raise exceptions instead of returning error codes. An exception object is created and thrown. Subtypes of an exception can contain data. A method that may throw and exception must be called within a try/catch block. This forces error conditions to be recognized and handled appropriately. When an error does happen, an exception object is created, and this object is thrown back to some point in the code, where it is caught. Catching it, as mentioned, requires a try/catch block to be implemented. Throwing an exception is effectively a non-local go-to, execution jumps to the closest catch block. The illustration shown here shows the try-catch-finally statement keywords, and how they work, which we'll explore in the next few slides. The darker line illustrates an occurrence of an exception, the lighter line illustrates the normal flow of control. All errors and exceptions inherit from the Throwable class. An error is an abnormal condition which is typically unrecoverable. This typically includes hardware problems and, as an error, will usually cause the program to crash. On the other hand, an exception is an abnormal condition which can at least be handled. Handling the exception may just be gracefully shutting down the program. The diagram shown here illustrates the exception type hierarchy, as used within the Java SDK, again, all exceptions types have the object type at the root, descending downwards through the exception type hierarchy, the exception types become more and more specialized. The try block. A try block is used to enclose a block of code that may throw an exception. The syntax for catching exceptions is perfectly logical. Within the try block is the normal code, if no exception are thrown then this code executes normally, however, if an exception is thrown, then the remaining statements are skipped, and the catch block is executed. The non-local go-to generated when the exception is thrown starts execution at the catch block. In the above example, creating the file reader could generate a host of errors that will throw exceptions. The catch block. Exceptions are handled by the catch block. The catch block can be used to catch multiple exceptions. The getMessage method of Exception prints information. The printStackTrace method shows the origin of exception. The catch block looks very similar to a method call, the argument is the exception that was thrown. This is a genuine bona fide Java object. Execution continues into the catch block, just as it would within a method. The finally block. Execution may leave a try block by completing successfully and falling through the end brace, executing a return statement, or throwing an exception. The finally block guarantees that a block of code is always executed, unless the try block it is associated with exits via the System.exit method. This is often used for cleaning up resources, regardless of outcome. Once the try block has executed normally, or a catch block has completed execution, the finally method will always be called. Again, the only time a finally block will not execute is if the try block it is associated with exits via the System.exit method. You must be extremely careful not to allow a finally block to throw an exception itself. If code in your finally block can thrown an exception, you must catch it within the finally block. Multiple catch blocks. Several catch blocks can be defined, dealing with individual exceptions. The try-catch-finally syntax is described in the slide above. Note that you can have any number of catch statements. Also, remember that exceptions are objects, and as such, a catch statement can catch an exception, or any of its subclasses. Generalized versus specialized exceptions. A catch block can specify a supertype exception.
A catch block sequence must start with subtypes. All exceptions are either java.lang.exception, or, more specialized versions of it. If you have many catch blocks for different exceptions, as shown above, how are they treated? The rule is as follows: the catch blocks will be examined in top-down sequential order until a type match is found, and that will be the catch block that is executed. Be careful not to put "exception" as the top parameter for the catch block, as all exceptions are subclasses of "exception", and therefore it would always be called. As a rule of thumb, make sure that the exceptions are ordered in the most specialized to the most general, "exception" will always be the most general. If you override a method that throws exceptions, you do not need to throw all of the exceptions which were declared in the overridden method, however, you cannot add exceptions to the overriding method, which were not in the original overridden method. It is also allowable for the overridden method to throw no exceptions, remember, again, exceptions are objects, so, the overriding method can throw exceptions which are subclasses of the exceptions in the overridden method. You can use the "throw" keyword to throw your own exceptions, these exceptions may be standard system exceptions, or, specialized ones which you have defined and created. The main thing to remember when throwing exceptions is that they are throwable objects. This means that you must instantiate an exception object prior to throwing it. Unchecked exceptions, for example subclasses of java.lang.RuntimeException, do not need the "throws" clause on the method. Unchecked exceptions. Not all exceptions need to be caught. All subtypes of RuntimeException are considered unchecked exceptions. Methods are not required to handle or declare unchecked exceptions. As we have already seen, some operations that are performed on the Java API throw exceptions that we are not forced to deal with. When accessing data outside of the bounds of an array, this will throw an array index out of bounds exception, while an invalid cast results in an ClassCastException. All this time the compiler did not inform us about these exceptions while they might occur at runtime. All subtypes of the RuntimeException class are therefore considered unchecked exceptions, and do not have to be dealt with in a method, and do not need to be declared in the method signature. However, the exceptions can be caught using a catch block, just like the checked exceptions. All subtypes of the RuntimeException class are considered unchecked exceptions, and again, do not have to be dealt with in a method, and do not need to be declared in the method signature. However, the exceptions can be caught using a catch block, just like the checked exceptions. Let's now review checked versus unchecked exceptions. Starting with unchecked exceptions, they represent program defects, all are subtypes of RuntimeException, Methods are not required to handle or declare, Examples of unchecked exceptions include: IllegalArgumentException, and the NullPointerException. Checked exceptions on the other hand, represent defects that are outside immediate control of the application. Methods are required to handle and catch the exception, or declare that it could be thrown, and, application-specific errors are part of this category. Again, it is expected that unchecked exceptions would be fixed by the programmer. Checked exceptions are expected to be caused by something external to the application, like user input. It is perfectly acceptable to handle unchecked exceptions, and is expected if it would be caused by the user instead of the logic within the program. Checked exceptions can be caught at the location where they are thrown, or declared in the method signature to travel up to the caller. Unchecked exceptions do not need to be handled. Consider the following questions to test yourself on the content we have just reviewed. The answers to the above questions are: one, exceptions force the application to handle error conditions; two, checked and unchecked exceptions; three, runtime exceptions can be caught, but it is not required by the compiler. Okay, that completes this lecture, go ahead and close it, and we'll see you shortly in the next one.
About the Author
Jeremy is the DevOps Content Lead at Cloud Academy where he specializes in developing technical training documentation for DevOps.
He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 20+ years. In recent times, Jeremy has been focused on DevOps, Cloud, Security, and Machine Learning.
Jeremy holds professional certifications for both the AWS and GCP cloud platforms.