Exceptions, Assertions, Logging
What we’ll cover
- Throwable Class
- Basic Exception Handling
- Better Exception Handling
- Finally Keyword
- Assertions
- Logging
Throwable Class
- Throwable Hierarchy
- Errors
- Checked Exceptions
- Unchecked Exceptions
Throwable Hierarchy
Errors
- The
Errorhierarchy describes internal errors and resource exhaustion situations. - Do not advertise internal java errors; Any code can potentially throw an
Error. IOError,StackOverflowError, andOutOfMemoryErrorare a few of the commonly encounteredErrorsIOError- serious I/O error has occurredStackOverflowError- application recurses too deeply.OutOfMemoryError- JVM cannot allocate an object because it is out of memory
Unchecked Exceptions
- Any exception which derives from
ErrororRuntimeExceptionclass. - An Exception subclassing
RuntimeExceptionis considered to be the programmer’s fault.ArrayIndexOutOfBoundExceptioncan be avoided by testing array index against array boundsNullPointerExceptioncan be avoided by testing for null values.
Checked Exceptions
- An Exception subclassing
IOExceptionis potentially not the programmer’s fault.FileNotFoundExceptioncan be thrown when trying to read from a remote file that a person incidentally removes.SQLExceptioncan be thrown as a result of a faulty network connection.
Basic Exception Handling
- What is Exception Handling?
- Unhandled Exception Example
- Handled Exception Example
What is Exception Handling?
- For exceptional situations, Java uses a form of error trapping called, exception handling.
- Exception handling enables the author of the code to record and handle errors in their program.
Unchecked Unhandled Exception Example; Compile Error
import java.io.*;
class FilePrinter {
private final BufferedReader reader;
public FilePrinter(String fileDirectory) {
// What if the file does not exist?
this.reader = new BufferedReader(new FileReader(fileDirectory));
}
public void printFile() {
String line = null;
do {
// What if the System fails to read in the next line?
// (For example if the file was suddenly closed, modified, or deleted)
line = reader.readLine();
System.out.println(line);
} while (line != null);
}
}
Exception Handling; Signature Throw Clause
import java.io.*;
class FilePrinter {
private final BufferedReader reader;
public FilePrinter(String fileDirectory) throws FileNotFoundException {
this.reader = new BufferedReader(new FileReader(fileDirectory));
}
public void printFile() throws IOException {
String line = null;
do {
line = reader.readLine();
System.out.println(line);
} while (line != null);
}
}
Exception Handling; Try / Catch
import java.io.*;
class FilePrinter {
private final BufferedReader reader;
public FilePrinter(String fileDirectory) throws FileNotFoundException {
this.reader = new BufferedReader(new FileReader(fileDirectory));
}
public void printFile() throws IOException {
String line = null;
do {
line = reader.readLine();
System.out.println(line);
} while (line != null);
}
public void tryPrintFile() {
try {
printFile();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
}
Better Exception Handling
- Multi-Exception Handling
- Dynamic Exception Handling
- Uniform Exception Handling (Good)
- Uniform Exception Handling (Bad)
- How to throw an Exception
- Recursion
andException Handling
Multi-Exception Handling
- Consider the case where multiple exceptions may be thrown.
- For example, in our
FilePrinterclass, the- constructor throws a
FileNotFoundException printFile()throws anIOException
- constructor throws a
- What if we wanted to create a
FilePrinterobject, then print its contents?
Multi-Exception Handling Examples
public class FilePrinterTest {
private static final String invalidFileName = "";
@Test(expected = FileNotFoundException.class)
public void testInstantiation() throws FileNotFoundException {
FilePrinter fpt = new FilePrinter(invalidFileName);
}
// Attempt to instantiate FilePrinter with invalid name
// Attempt to invoke method on unininstatiated FilePrinter object
@Test(expected = NullPointerException.class)
public void testNullPointer() throws NullPointerException {
FilePrinter fpt = null;
try {
fpt = new FilePrinter(invalidFileName);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
fpt.tryPrintFile();
}
@Test(expected = NullPointerException.class)
public void testMultiThrowSignature() throws NullPointerException, FileNotFoundException {
testNullPointer();
testInstantiation();
}
}
Dynamic Exception Handling; Expanded
public class FilePrinterTest {
private static final String invalidFileName = "";
public void testInstantiateAndPrint() {
FilePrinter fpt = null;
try {
fpt = new FilePrinter(invalidFileName);
} catch(FileNotFoundException fnfe) {
throw new RuntimeException(fnfe);
}
try {
fpt.printFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Dynamic Exception Handling; Compressed
public class FilePrinterTest {
private static final String invalidFileName = "";
public void testInstantiateAndPrint() {
FilePrinter fpt = null;
try {
fpt = new FilePrinter(invalidFileName);
fpt.printFile();
} catch(FileNotFoundException fnfe) {
// handle FileNotFoundException
throw new RuntimeException(fnfe);
} catch(IOEXception ioe) {
// handle IOException
throw new RuntimeException(ioe);
}
}
}
Uniform Handling Of Exceptions (Good)
public class FilePrinterTest {
private static final String invalidFileName = "";
public void testInstantiateAndPrint() {
FilePrinter fpt = null;
try {
fpt = new FilePrinter(invalidFileName);
fpt.printFile();
// bit-wise operator supported by java 1.7+
} catch(FileNotFoundException | IOException exception) {
// handle all exceptions the same way
throw new RuntimeException(exception);
}
}
}
- Each expected exception in this class is explicitly named.
- The handling of each of them is uniform.
Uniform Handling Of Exceptions (Bad)
public class FilePrinterTest {
private static final String invalidFileName = "";
public void testInstantiateAndPrint() {
FilePrinter fpt = null;
try {
fpt = new FilePrinter(invalidFileName);
fpt.printFile();
} catch(Exception e) {
// handle all exceptions the same way
throw new RuntimeException(e);
} catch(IllegalArgumentException iae) {
throw new RuntimeException(iae);
}
}
public void parseIntegerInput(String s) {
try {
Long.parseLong(s);
} catch(NumberFormatException e) {
throw new IllegalArgumentException(e);
}
}
}
Recursion and Exception Handling
- DON’T DO IT!
- Recursion and Exception Handling do not go together
- Exceptions keep track of all pending method calls
- By nature, recursion pends method calls
nlevels deep, wherenis the recursive depth of the method call. - Combining recursion and exception handling can result in very strange
StackTraces
Finally Keyword
- Purpose
- Conditions under which
finallyblock is executed - Syntax
- Decoupling
finallyclause fromtry/catchclauses
Purpose
- When code throws an exception, it stops processing the remaining code in the scope, then exits the method.
- If the method has aqcuired some local resource, then this can become an issue; The program will cease execution, and hold the resource indefinitely.
- The finally clause executes whether or not an exception was code.
Conditions under which finally block is executed
- If no exception are thrown.
- If exception outside
tryblock is thrown. - If an exception is thrown in a
catchclause. - The program skips the
finallyclause, if thecatchclause does not throw an exception.
Decoupling finally clause from try/catch clauses
class BookExample {
public void example1() {
InputStream in = ... ;
try {
try {
// code that may throw exception
} finally {
in.close();
}
} catch(IOException ioe) {
/// handle exception some way
}
}
}
Assertions
- Assertions are commonly used idiom of defensive programming.
- Java has a keyword
assert, which takes two forms:assert condition;assert condition : expression;
assertevaluates a condition, then throws anAssertionErrorif it is false. The second argument expression is a message String.
Toggling Assert Statements
- By default, assertions are disabled; If an assert statement is passed
false, no exception is thrown. - Assertions can be enabled by running the program with the
-eaoption. java -ea MyProjectenables for entire projectjava -ea:MyClass -ea:com.zipcodewilmington.MyProjectenables forMyClass
When To Use
- Assertion failures are intended to be fatal, unrecoverable errors
- Assertion checks are turned on only during development and testing
- As an additional check against uncanny method returns.
Logging
- It’s common to use
System.out.printlnto check against troublesome code. - Once the issue is resolved, these statements are usually removed, or commented out.
- Later, if the issue persists, the print statements are re-inserted.
- The Logging API is designed to overcome this issue.
Principal advantages of Logging API
- It’s easy to (un)suppress all log records, or just those below a certain level.
- Suppressed logs are inexpensive; The penalty for leaving them in your code is minimal.
- Log records can be directed to different handlers; Console display, writing to file / database, etc.
- Log records can be formatted; For example, plaint ext, or XML
- Logging configuration is controlled by configuration file; Applications can replace this mechanism
The 7 Logging Levels
- By default, loggers will log all messages of
INFOor higher to console. SEVEREWARNINGINFOCONFIGFINEFINERFINEST
Syntax
public class LogDemo {
// it is advised that you name your logger the same as your Main Application package
Logger logger = Logger.getLogger("com.github.curriculeon.MainApplication");
public void logTest() {
logger.setLevel(Level.SEVERE); // log severe
logger.setLevel(Level.WARNING); // log severe, warning
logger.setLevel(Level.INFO); // log severe, warning, info
logger.setLevel(Level.CONFIG); // log severe, warning, info, config
logger.setLevel(Level.FINE); // log severe, warning, info, config, fine
logger.setLevel(Level.FINER); // log severe, warning, info, config, fine, finer
logger.setLevel(Level.FINEST); // log severe, warning, info, config, fine, finer, finest
}
}
