1) All exception types must extend the class Throwable or one of its subclass. There’re two kinds of exceptions in Java: checked exceptions and unchecked exceptions. The Error and RuntimeException class and their subclass are unchecked exceptions, and they are the only exceptions that you do not need to list in the throws clause, and the exception type hierarchy is like this:
Checked exceptions represent conditions that, although exceptional, can reasonably be expected to occur, and if they do occur must be dealt with in some way. Making these exceptions checked documents the existence of the exception and ensures that the caller of a method deals with the exception in some wayor at least consciously chooses to ignore it.
Unchecked runtime exceptions represent conditions that, generally speaking, reflect errors in your program's logic and cannot be reasonably recovered from at run time.
The Error class, through its subclasses, defines a range of errors that indicate something has failed in the virtual machine itself (VirtualMachineError), or in the virtual machine's attempt to execute your application (LinkageError). These are also unchecked because they are beyond the applications ability to control, or handle. Application code should rarely, if ever, throw these error exceptions directly.
Nearly all exceptions you create should extend Exception, making them checked exceptions. These new checked exceptions represent the exceptional conditions that can arise in your library or application.
2) You throw exceptions using the throw statement:
throw expression;
where expression must evaluate to a value or variable that is assignable to Throwable or in simple terms, a reference to a Throwable object.
Once an exception occurs, actions after the point at which the exception occurred do not take place. If evaluation of a left-operand causes an exception then no part of the right-operand is evaluated; if evaluation of a left argument expression results in an exception, then no argument to the right is evaluated. The next action to occur will be in either a finally block, or a catch block that catches the exception.
3) You should list all the checked exceptions that a methods could throw in the throws clause. The language requires such a declaration because programmers invoking a method need to know the exceptions it can throw just as much as they need to know its normal behavior. The checked exceptions that a method throws are as important as the type of value it returns.
Example:
public void replaceValue(String name, Object newValue) throws NoSuchAttributeException { Attr attr = find(name); // look up the attr if (attr == null) // it isn't found throw new NoSuchAttributeException(name); attr.setValue(newValue); }
The contract defined by the throws clause is strictly enforcedyou can throw only a type of checked exception that has been declared in the throws clause.
Because checked exceptions must be declared in a throws clause, it follows that any code fragment outside a method, or constructor, with a throws clause cannot throw a checked exception. This means that static initializers and static initialization blocks cannot throw checked exceptions, either directly or by invoking a method that throws such an exception. Non-static initializers and non-static initialization blocks are considered to be part of the constructor for a class, and so they are allowed to throw checked exceptions only if all the constructors of the class declare those checked exceptions.
You should be explicit in your throws clause, listing all the exceptions you know that you throw, even when you could encompass several exceptions under some superclass they all share. This is good self-documentation.
The rule for overriding and implementing method is: (1) not allowed to declare more checked exceptions in the throws clause than the inherited method does; (2) subtypes of the declared exceptions can be thrown.
4) If a finally clause is present with a try, its code is executed after all other processing in the try is complete. This happens no matter how completion was achieved, whether normally, through an exception, or through a control flow statement such as return or break.
The finally clause of a try statement provides a mechanism for executing a section of code whether or not an exception is thrown. Usually, the finally clause is used to clean up internal state or to release non-object resources, such as open files stored in local variables.
public boolean searchFor(String file, String word) throws StreamException { Stream input = null; try { input = new Stream(file); while (!input.eof()) if (input.next().equals(word)) return true; return false; // not found } finally { if (input != null) input.close(); } }
If the new fails, input will never be changed from its initial null value. If the new succeeds, input will reference the object that represents the open file. When the finally clause is executed, the input stream is closed only if it has been open. Whether or not the operations on the stream generate an exception, the contents of the finally clause ensure that the file is closed, thereby conserving the limited resource of simultaneous open files. The searchFor method declares that it throws StreamException so that any exceptions generated are passed through to the invoking code after cleanup, including any StreamException thrown by the invocation of close.
A finally clause can also be used to clean up for break, continue, and return, which is one reason you will sometimes see a try clause with no catch clauses. When any control transfer statement is executed, all relevant finally clauses are executed. There is no way to leave a try block without executing its finally clause.
Example:
public class FinallyTest { static int getValue() { try{ return 0; } finally { return 1; } } public static void main(String[] args) { System.out.println(getValue()); } }
Output:
1
5) Exceptions sometimes could cause other exceptions:
public double[] getDataSet(String setName) throws BadDataSetException { String file = setName + ".dset"; FileInputStream in = null; try { in = new FileInputStream(file); return readDataSet(in); } catch (IOException e) { throw new BadDataSetException(); } finally { try { if (in != null) in.close(); } catch (IOException e) { ; // ignore: we either read the data OK // or we're throwing BadDataSetException } } } // ... definition of readDataSet ...
The initCause method, defined in Throwable, sets one exception's cause to be the exception object passed as a parameter, and return the Throwable object. For example, the previous example can have its IOException catch clause rewritten as:
} catch (IOException e) { BadDataSetException bdse = new BadDataSetException(); bdse.initCause(e); throw bdse; } finally { // ... }
or more simplicit:
throw (BadDataSetException) new BadDataSetException().initCause(e);
6) Exceptions are used to handle “unexpected error conditions”, so don’t abuse exceptions as a way to report expected situations.
7) An assertion is used to check an invariant, a condition that should always be true. If the assertion is found to be false then an exception is thrown. The syntax is :
assert eval-expr [: detail-expr];
where eval-expr is either a boolean or Boolean expression and detail-expr is an optional expression that will be passed to the AssertionError constructor to help describe the problem. The detail expression is optional; if present, it provides information to a person who looks at the statement or thrown exception. If the detail is a Throwable it will become the cause of the AssertionError; that is, it will be the value returned by the error's getCause method. Otherwise, the detail will be converted to a string and become the detail message for the error.
8) By default, assertion evaluation is turned off. You can turn on all assertion evaluation in a virtual machine, or on a package, class, or class loader basis. Because they can be on or off, you must be careful to avoid side effects that affect non-assertion code.
There’re two ways to turn on the assertion: (1) pass command-line options to the virtual machine; (2) use methods provided by the class loader. Here we will only introduce the former one.
-enableassertions/-ea[descriptor]
Enables (turns on) assertion evaluation as defined by the descriptor. If there is no descriptor, assertions are enabled for all classes except those loaded by the system class.
-disableassertions/-da[descriptor]
Disables (turns off) assertion evaluation for all classes as defined by the descriptor. If there is no descriptor, assertions are disabled for all classes.
The descriptor allows you to specify particular packages or classes that will be affected by the option. An absent descriptor means the option applies to all non-system classes (that is, classes loaded by any class loader but the system class loader). Packages are defined by name, followed by ... which means to apply the option to all subpackages. For example, the option
-enableassertions:com.acme...
Multiple options are evaluated in order from the command line. Class-specific options take precedence over package ones, and a more specific package option takes precedence over a less specific one. For example, given
-da:com.acme.Plotter -ea:com.acme... -da:com.acme.products -ea:com.acme.products.Rocket
assertions will be enabled for all classes and subpackages of com.acme, except the class com.acme.Plotter, and disabled for all classes and subpackages of com.acme.products, except for the class com.acme.products.Rocket.
In Eclipse, go to Run Menu-> Run Configurations… �C> (x)=Arguments �C> VM arguments, and type the corresponding options.
Example:
package section8; public class AssertTest { public static void main(String[] args) { int i = 0; assert i == 1 : "JavaBeta"; System.out.println(i); } }
Options: -ea:section8...
Console Output:
Exception in thread "main" java.lang.AssertionError: JavaBeta
at section8.AssertTest.main(AssertTest.java:7)