File - Start your own free website

advertisement
UNIT IV
GENERIC PROGRAMMING
It would be nice if we could write a single sort method that could sort the elements in an Integer array, a
String array or an array of any type that supports ordering. Java Generic methods and generic classes enable
programmers to specify, with a single method declaration, a set of related methods or, with a single class
declaration, a set of related types, respectively. Generics also provide compile-time type safety that allows
programmers to catch invalid types at compile time. Using Java Generic concept we might write a generic
method for sorting an array of objects, then invoke the generic method with Integer arrays, Double arrays,
String arrays and so on, to sort the array elements. Support for the generics, or "containers-of-type-T", subset
of generic programming were added to the Java programming language in 2004 as part of J2SE 5.0. In Java,
generics are checked at compile time for type correctness. The generic type information is then removed via a
process
called type erasure, and is unavailable at runtime. For example, a List<String> is converted to the raw type
List. The compiler inserts type casts to convert the elements to the String type when they are retrieved from
the list.
Why Generic Programming?
Generic programming means to write code that can be reused for objects of many different types.
Here is a typical issue that the designers of the standard class library had to grapple with. The ArrayList class
has a method addAll to add all elements of another collection. A programmer may want to add all elements
from an ArrayList<Manager> to an ArrayList<Employee>. But, of course, doing it the other way around
should not be legal. How do you allow one call and disallow the other? The Java language designers invented
an ingenious new concept, the wildcard type, to solve this problem. Wildcard
types are rather abstract, but they allow a library builder to make methods as flexible as possible.
The term generics means parameterized types
Motivation for Generics:
Overloaded methods are often used to perform similar operations on different types of data. To motivate
generic methods, let’s begin with an example that contains three overloaded printArray methods These
methods print the string representations of the elements of an Integer array, a Double array and a Character
array, respectively. Note that we could have used arrays of primitive types int, double and char in this
example. We chose to use arrays of typeInteger, Double and Character to set up our generic method example,
because only reference types can be used with generic methods and classes.
public class OverloadedMethods
for ( Character element : inputArray )
{
System.out.printf( "%s ", element );
// method printArray to print Integer array
System.out.println();
public static void printArray( Integer[] inputArray
}
)
// end method printArray
{
public static void main( String args[] )
// display array elements
{
for ( Integer element : inputArray )
// create arrays of Integer, Double and Character
System.out.printf( "%s ", element );
Integer[] integerArray = { 1, 2, 3, 4, 5, 6 };
System.out.println();
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5,
}
6.6, 7.7 };
// end method printArray
Character[] characterArray = { 'H', 'E', 'L', 'L', 'O'
// method printArray to print Double array
};
public static void printArray( Double[] inputArray
System.out.println( "Array integerArray contains:"
)
);
{
printArray( integerArray );
// display array elements
// pass an Integer array System.out.println(
for ( Double element : inputArray )
"\nArray doubleArray contains:" );
System.out.printf( "%s ", element );
printArray( doubleArray );
System.out.println();
// pass a Double array
}
System.out.println(
"\nArray
characterArray
// end method printArray
contains:" );
// method printArray to print Character array
printArray( characterArray );
public static void printArray( Character[]
// pass a Character array
inputArray )
}
{
// end main
// display array elements
} // end class
1
OverloadedMethods
Array integerArray contains: 1 2 3 4 5 6
Array doubleArray contains: 1.1 2.2 3.3 4.4 5.5 6.6 7.7
Array characterArray contains: H E L L O
Generic class definitions
Here is an example of a generic class:
public class Pair<T, S>
{
{
return second;
public Pair(T f, S s)
}
{
public String toString()
first = f;
{
second = s;
return "(" + first.toString() + ", " +
}
second.toString() + ")";
public T getFirst()
}
{
private T first;
return first;
private S second;
}
}
public S getSecond()
This generic class can be used in the following way:
Pair<String, String> grade440 = new Pair<String, String>("mike", "A");
Pair<String, Integer> marks440 = new Pair<String, Integer>("mike", 100); System.out.println("grade:" +
grade440.toString());
System.out.println("marks:" + marks440.toString());
Definition of a Simple Generic Class
A generic class is a class with one or more type variables. In this chapter, we use a simple Pair class as an
example. This class allows us to focus on generics without being distracted by data storage details. Here is the
code for the generic Pair class:
public class Pair<T>
{
public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
private T first;
private T second;
}
The Pair class introduces a type variable T, enclosed in angle brackets < >, after the class name. A generic
class can have more than one type variable. For example, we could have defined the Pair class with separate
types for the first and second field:
public class Pair<T, U> { . . . } The type variables are used throughout the class definition to specify method
return types and the types of fields and local variables. For example: private T first; // uses type variable
You instantiate the generic type by substituting types for the type variables, such as
Pair<String>
You can think of the result as an ordinary class with constructors
Pair<String>()
String getSecond()
Pair<String>(String, String)
void setFirst(String)
and methods
void setSecond(String)
String getFirst()
In other words, the generic class acts as a factory for ordinary classes.
The program in Listing 12–1 puts the Pair class to work. The static minmax method traverses an array and
simultaneously computes the minimum and maximum value. It uses a Pair object to return both results. Recall
that the compareTo method compares two strings, returning 0 if the strings are identical, a negative integer if
the first string comes before the second in dictionary order, and a positive integer otherwise.
Listing 12–1 PairTest1.java
1. /**
4. */
2. * @version 1.00 2004-05-10
5. public class PairTest1
3. * @author Cay Horstmann
6. {
2
7. public static void main(String[] args)
21. * @return a pair with the min and max value,
8. {
or null if a is null or empty
9. String[] words = { "Mary", "had", "a", "little",
22. */
"lamb" };
23. public static Pair<String> minmax(String[] a)
10. Pair<String> mm = ArrayAlg.minmax(words);
24. {
11. System.out.println("min = " + mm.getFirst());
25. if (a == null || a.length == 0) return null;
12.
System.out.println("max
=
"
+
26. String min = a[0];
mm.getSecond());
27. String max = a[0];
13. }
28. for (int i = 1; i < a.length; i++)
14. }
29. {
15.
30. if (min.compareTo(a[i]) > 0) min = a[i];
16. class ArrayAlg
31. if (max.compareTo(a[i]) < 0) max = a[i];
17. {
32. }
18. /**
33. return new Pair<String>(min, max);
19. * Gets the minimum and maximum of an array
34. }
of strings.
35. }
20. * @param a an array of strings
1. T is a type parameter that will be replaced by a real type.
2. T is the name of a type parameter.
3. This name is used as a placeholder for the actual type that will be passed to
4. Gen when an object is created.
Generics are a facility of generic programming that was added to the Java programming language. Generic
programming is a style of computer programming in which algorithms are written in terms of to-be-specifiedlater types that are then instantiated when needed for specific types provided as parameters.
class GenericClass<T> {
// Create a Gen reference for Integers.
T ob;
GenericClass<Integer>
iOb
=
new
GenericClass(T o) {
GenericClass<Integer>(88);
ob = o;
iOb.showType();
}
// no cast is needed.
T getob() {
int v = iOb.getob();
return ob;
System.out.println("value: " + v);
}
// Create a Gen object for Strings.
void showType() {
GenericClass<String>
strOb
=
new
System.out.println("Type of T is " +
GenericClass<String>("Generics Test");
ob.getClass().getName());
strOb.showType();
}
String str = strOb.getob();
}
System.out.println("value: " + str);
public class MainClass {
}
public static void main(String args[]) {
}
Type of T is java.lang.Integer
value: 88
Type of T is java.lang.String
value: Generics Test
Generic Methods
In the preceding section, you have seen how to define a generic class. You can also define a single method
with type parameters.
class ArrayAlg
return a[a.length / 2];
{
}
public static <T> T getMiddle(T[] a)
}
{
This method is defined inside an ordinary class, not inside a generic class. However, it is a generic method, as
you can see from the angle brackets and the type variable. Note that the type variables are inserted after the
modifiers (public static, in our case) and
before the return type. You can define generic methods both inside ordinary classes and inside generic classes.
When you call a generic method, you can place the actual types, enclosed in angle brackets, before the method
name:
String[] names = { "John", "Q.", "Public" };
String middle = ArrayAlg.<String>getMiddle(names);
3
In this case (and indeed in most cases), you can omit the <String> type parameter from the method call. The
compiler has enough information to infer the method that you want. It matches the type of names (that is,
String[]) against the generic type T[] and
deduces that T must be String. That is, you can simply call
String middle = ArrayAlg.getMiddle(names);
Generic Methods
Genericity is not limited to classes and interfaces, you can define generic
methods. Static methods, nonstatic methods, and constructors can all be parameterized in almost the same
way as for classes and interfaces, but the syntax is a bit different. Generic methods are also invoked in the
same way as non-generic methods. Before we see an example of a generics method, consider the
following segment of code that prints out all the elements in a collection:
public void
printCollection(Collection c)
{
Iterator i = c.iterator();
for(int k = 0;k<c.size();k++)
{
System.out.println(i.next());
}
}
Using generics, this can be re-written as follows.
Note that the Collection<?> is the collection of an unknown type.
void printCollection(Collection<?> c)
{
for(Object o:c)
{
System.out.println(o);
}
}
Example:
public class GenericMethodTest
{
// generic method printArray
public static < E > void printArray( E[] inputArray )
{
// display array elements
for ( E element : inputArray )
System.out.printf( "%s ", element );
System.out.println();
} // end method printArray
public static void main( String args[] )
{
// create arrays of Integer, Double and Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 }; Character[]
charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "Array integerArray contains:" );
printArray( integerArray );
// pass an Integer array
System.out.println( "\nArray doubleArray contains:" );
printArray( doubleArray );
// pass a Double array
System.out.println( "\nArray characterArray contains:" );
printArray( characterArray );
// pass a Character array
}
// end main
}
// end class GenericMethodTest
Array integerArray contains: 1 2 3 4 5 6
Array doubleArray contains: 1.1 2.2 3.3 4.4 5.5 6.6 7.7
Array characterArray contains: H E L L O
Bounds for Type Variables
Sometimes, a class or a method needs to place restrictions on type variables. Here is a typical example. We
want to compute the smallest element of an array:
class ArrayAlg
T smallest = a[0];
{
for (int i = 1; i < a.length; i++)
public static <T> T min(T[] a) // almost correct
if (smallest.compareTo(a[i]) > 0) smallest = a[i];
{
return smallest;
if (a == null || a.length == 0) return null;
}
}
4
But there is a problem. Look inside the code of min method. The variable smallest has type T, which means
that it could be an object of an arbitrary class. How do we know that the class to which T belongs has a
compareTo method? The solution is to restrict T to a class that implements the Comparable interface—a
standard interface with a single method, compareTo.To achieve this by giving a bound for the type variable
T:
public static <T extends Comparable> T min(T[] a) . . .
Actually, the Comparable interface is itself a generic type. For now, we will ignore that complexity and the
warnings that the compiler generates. “Wildcard Types” on page 632 discusses how to properly use type
parameters with the Comparable interface.
Now, the generic min method can only be called with arrays of classes that implement the Comparable
interface, such as String, Date, and so on. Calling min with a Rectangle array is a compile-time error because
the Rectangle class does not implement Comparable.
You may wonder why you use the extends keyword rather than the implements keyword in this situation—
after all, Comparable is an interface. The notation
<T extends BoundingType>
expresses that T should be a subtype of the bounding type. Both T and the bounding type can be either a class
or an interface. The extends keyword was chosen because it is a reasonable approximation of the subtype
concept, and the Java designers did not want to add a new keyword (such as sub) to the language.
A type variable or wildcard can have multiple bounds. For example:
T extends Comparable & Serializable
The bounding types are separated by ampersands (&) because commas are used to separate type variables. As
with Java inheritance, you can have as many interface supertypes as you like, but at most one of the bounds
can be a class. If you have a class as a bound, it must be the first one in the bounds list. In the next sample
program (Listing 12–2), we rewrite the minmax method to be generic. The method computes the minimum
and maximum of a generic array, returning a Pair<T>.
Listing 12–2 PairTest2.java
1. import java.util.*;
2.
22. }
3. /**
23.
4. * @version 1.00 2004-05-10
24. class ArrayAlg
5. * @author Cay Horstmann
25. {
6. */
26. /**
7. public class PairTest2
27. Gets the minimum and maximum of an array
8. {
of objects of type T.
9. public static void main(String[] args)
28. @param a an array of objects of type T
10. {
29. @return a pair with the min and max value, or
11. GregorianCalendar[] birthdays =
null if a is
12. {
30. null or empty
13.
new
GregorianCalendar(1906,
31. */
Calendar.DECEMBER, 9), // G. Hopper
32. public static <T extends Comparable> Pair<T>
14.
new
GregorianCalendar(1815,
minmax(T[] a)
Calendar.DECEMBER, 10), // A. Lovelace
33. {
15.
new
GregorianCalendar(1903,
34. if (a == null || a.length == 0) return null;
Calendar.DECEMBER, 3), // J. von Neumann
35. T min = a[0];
16. new GregorianCalendar(1910, Calendar.JUNE,
36. T max = a[0];
22), // K. Zuse
37. for (int i = 1; i < a.length; i++)
17. };
38. {
18.
Pair<GregorianCalendar>
mm
=
39. if (min.compareTo(a[i]) > 0) min = a[i];
ArrayAlg.minmax(birthdays);
40. if (max.compareTo(a[i]) < 0) max = a[i];
19.
System.out.println("min
=
"
+
41. }
mm.getFirst().getTime());
42. return new Pair<T>(min, max);
20.
System.out.println("max
=
"
+
43. }
mm.getSecond().getTime());
44. }
21. }
There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type
parameter. For example, a method that operates on numbers might only want to accept instances of
Number or its subclasses. This is what bounded type parameters are for.
To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword,
followed by its upper bound, which in this example is Number. Note that, in this context, extends is
5
used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).
public class Box<T>
{
private T t;
public void add(T t)
{
this.t = t;
}
public T get()
{
return t;
}
public <U extends Number> void inspect(U u)
{
System.out.println("T: " + t.getClass().getName());
System.out.println("U: " + u.getClass().getName());
}
public static void main(String[] args)
{
Box<Integer> integerBox = new Box<Integer>();
integerBox.add(new Integer(10));
integerBox.inspect("some text");
// error: this is still String!
}
}
By modifying our generic method to include this bounded type parameter, compilation will now fail,
since our invocation of inspect still includes a String:
Box.java:21: <U>inspect(U) in Box<java.lang.Integer> cannot be applied to
(java.lang.String)
integerBox.inspect("10");
^
1 error
To specify additional interfaces that must be implemented, use the & character, as in:
<U extends Number & MyInterface>
Generic Code and the Virtual Machine
The virtual machine does not have objects of generic types—all objects belong to ordinary classes. An earlier
version of the generics implementation was even able to compile a program that uses generics into class files
that executed on 1.0 virtual machines! This backward compatibility was only abandoned fairly late in the
development for Java generics. If you use the Sun compiler to compile code that uses Java generics, the
resulting class files will not execute on pre-5.0 virtual machines.
Whenever you define a generic type, a corresponding raw type is automatically provided.
The name of the raw type is simply the name of the generic type, with the type
parameters removed. The type variables are erased and replaced by their bounding
types (or Object for variables without bounds.)
For example, the raw type for Pair<T> looks like this:
public class Pair
public Object getSecond() { return second; }
{
public void setFirst(Object newValue) { first =
public Pair(Object first, Object second)
newValue; }
{
public void setSecond(Object newValue) { second
this.first = first;
= newValue; }
this.second = second;
private Object first;
}
private Object second;
public Object getFirst() { return first; }
}
Because T is an unbounded type variable, it is simply replaced by Object. The result is an ordinary class, just
as you might have implemented it before generics were added to the Java programming language. Your
programs may contain different kinds of Pair, such as Pair<String> or Pair<Gregorian- Calendar>, but erasure
turns them all into raw Pair types. The raw type replaces type variables with the first bound, or Object if no
bounds are given. For example, the type variable in the class Pair<T> has no explicit bounds, hence the raw
type replaces T with Object. Suppose we declare a slightly different type:
6
public class Interval<T extends Comparable & Serializable> implements Serializable
{
public Interval(T first, T second)
{
if (first.compareTo(second) <= 0) { lower = first; upper = second; }
else { lower = second; upper = first; }
}
...
private T lower;
private T upper;
}
The raw type Interval looks like this:
public class Interval implements Serializable
{
public Interval(Comparable first, Comparable second) { . . . }
...
private Comparable lower;
private Comparable upper;
}
Translating Generic Expressions
When you program a call to a generic method, the compiler inserts casts when the return type has been erased.
For example, consider the sequence of statements
Pair<Employee> buddies = . . .;
Employee buddy = buddies.getFirst();
The erasure of getFirst has return type Object. The compiler automatically inserts the cast to Employee. That
is, the compiler translates the method call into two virtual machine instructions:
• A call to the raw method Pair.getFirst
• A cast of the returned Object to the Employee type
Casts are also inserted when you access a generic field. Suppose the first and second fields of the Pair class
were public. (Not a good programming style, perhaps, but it is legal Java.) Then the expression
Employee buddy = buddies.first;
also has a cast inserted in the resulting byte codes.
Translating Generic Methods
Type erasure also happens for generic methods. Programmers usually think of a generic method such as
public static <T extends Comparable> T min(T[] a)
as a whole family of methods, but after erasure, only a single method is left:
public static Comparable min(Comparable[] a)
Note that the type parameter T has been erased, leaving only its bounding type Comparable. Erasure of
method brings up a couple of complexities. Consider this example:
class DateInterval extends Pair<Date>
{
public void setSecond(Date second)
{
if (second.compareTo(getFirst()) >= 0)
super.setSecond(second);
}
...
}
A date interval is a pair of Date objects, and we’ll want to override the methods to ensure that the second
value is never smaller than the first. This class is erased to
class DateInterval extends Pair // after erasure
{
public void setSecond(Date second) { . . . }
...
}
Perhaps surprisingly, there is another setSecond method, inherited from Pair, namely,
public void setSecond(Object second).
7
This is clearly a different method because it has a parameter of a different type—Object instead of Date. But it
shouldn’t be different. Consider this sequence of statements:
DateInterval interval = new DateInterval(. . .);
Pair<Date> pair = interval; // OK--assignment to superclass
pair.setSecond(aDate);
Our expectation is that the call to setSecond is polymorphic and that the appropriate method is called. Because
pair refers to a DateInterval object, that should be DateInterval. setSecond. The problem is that the type
erasure interferes with polymorphism. To fix
this problem, the compiler generates a bridge method in the DateInterval class:
public void setSecond(Object second) { setSecond((Date) second); }
To see why this works, let us carefully follow the execution of the statement pair.setSecond(aDate) The
variable pair has declared type Pair<Date>, and that type only has a single method called setSecond, namely
setSecond(Object). The virtual machine calls that method on the object to which pair refers. That object is of
type DateInterval. Therefore, the method DateInterval.setSecond(Object) is called. That method is the
synthesized bridge method. It calls DateInterval.setSecond(Date), which is what we want. Bridge methods can
get even stranger. Suppose the DateInterval method also overrides the getSecond method:
class DateInterval extends Pair<Date>
{
public Date getSecond() { return (Date) super.getSecond().clone(); }
...
}
In the erased type, there are two getSecond methods:
Date getSecond() // defined in DateInterval
Object getSecond() // defined in Pair
You could not write Java code like that—it would be illegal to have two methods with the same parameter
types—here, no parameters. However, in the virtual machine, the parameter types and the return type specify
a method. Therefore, the compiler can produce bytecodes for two methods that differ only in their return type,
and the virtual machine will handle this situation correctly.
Restrictions and Limitations
In the following sections, we discuss a number of restrictions that you need to consider when working with
Java generics. Most of these restrictions are a consequence of type erasure.
Type Parameters Cannot Be Instantiated with Primitive Types
You cannot substitute a primitive type for a type parameter. Thus, there is no Pair<double>, only
Pair<Double>. The reason is, of course, type erasure. After erasure, the Pair class has fields of type Object,
and you can’t use them to store double values. This is an annoyance, to be sure, but it is consistent with the
separate status of primitive types in the Java language. It is not a fatal flaw—there are only eight primitive
types, and you can always handle them with separate classes and methods when wrapper
types are not an acceptable substitute.
Runtime Type Inquiry Only Works with Raw Types
Objects in the virtual machine always have a specific nongeneric type. Therefore, all type inquiries yield only
the raw type. For example,
if (a instanceof Pair<String>) // same as a instanceof Pair
really only tests whether a is a Pair of any type. The same is true for the test
if (a instanceof Pair<T>) // T is ignored
or the cast
Pair<String> p = (Pair<String>) a; // WARNING--can only test that a is a Pair
To remind you of the risk, you will get a compiler warning whenever you use instanceof or cast expressions
that involve generic types. In the same spirit, the getClass method always returns the raw type. For example:
Pair<String> stringPair = . . .;
Pair<Employee> employeePair = . . .;
if (stringPair.getClass() == employeePair.getClass()) // they are equal
The comparison yields true because both calls to getClass return Pair.class.
You Cannot Throw or Catch Instances of a Generic Class
You can neither throw nor catch objects of a generic class. In fact, it is not even legal for a generic class to
extend Throwable. For example, the following definition will not compile:
public class Problem<T> extends Exception { /* . . . */ } // ERROR--can't extend Throwable
You cannot use a type variable in a catch clause. For example, the following method will not compile:
8
public static <T extends Throwable> void
}
doWork(Class<T> t)
catch (T e) // ERROR--can't catch type variable
{
{
try
Logger.global.info(...)
{
}
do work
}
However, it is ok to use type variables in exception specifications. The following
method is legal:
public static <T extends Throwable> void
catch (Throwable realCause)
doWork(T t) throws T // OK
{
{
t.initCause(realCause);
try
throw t;
{
}
do work
}
}
Arrays of Parameterized Types Are Not Legal
You cannot declare arrays of parameterized types, such as
Pair<String>[] table = new Pair<String>[10]; // ERROR
What’s wrong with that? After erasure, the type of table is Pair[]. You can convert it to Object[]:
Object[] objarray = table;
An array remembers its component type and throws an ArrayStoreException if you try to store an element of
the wrong type:
objarray[0] = "Hello"; // ERROR--component type is Pair
But erasure renders this mechanism ineffective for generic types. The assignment
objarray[0] = new Pair<Employee>();
would pass the array store check but still result in a type error. For this reason, arrays of parameterized types
are outlawed.
You Cannot Instantiate Type Variables
You cannot use type variables in expression such as new T(...), new T[...], or T.class. For example, the
following Pair<T> constructor is illegal:
public Pair() { first = new T(); second = new T(); } // ERROR
Type erasure would change T to Object, and surely you don’t want to call new Object(). As a workaround,
you can construct generic objects through reflection, by calling the
Class.newInstance method.
Unfortunately, the details are a bit complex. You cannot call
first = T.class.newInstance(); // ERROR
The expression T.class is not legal. Instead, you must design the API so that you are handed a Class object,
like this:
public static <T> Pair<T> makePair(Class<T> cl)
{
try { return new Pair<T>(cl.newInstance(), cl.newInstance()) }
catch (Exception ex) { return null; }
}
This method could be called as follows:
Pair<String> p = Pair.makePair(String.class);
Note that the Class class is itself generic. For example, String.class is an instance (indeed, the sole instance) of
Class<String>. Therefore, the makePair method can infer the type of the pair that it is making. You cannot
construct a generic array:
public static <T extends Comparable> T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // ERROR
Type erasure would cause this method to always construct an array Object[2]. If the array is only used as a
private instance field of a class, you can declare the array as Object[] and use casts when retrieving elements.
For example, the ArrayList class could be
implemented as follows:
public class ArrayList<E>
{
private Object[] elements;
@SuppressWarnings("unchecked") public E get(int n) { return (E) elements[n]; }
public void set(int n, E e) { elements[n] = e; } // no cast needed
...
}
9
The actual implementation is not quite as clean:
public class ArrayList<E>
{
private E[] elements;
public ArrayList() { elements = (E[]) new Object[10]; }
...
}
Here, the cast E[] is an outright lie, but type erasure makes it undetectable. This technique does not work for
our minmax method since we are returning a T[] array, and a runtime error results if we lie about its type.
Suppose we implement
public static <T extends Comparable> T[] minmax(T[] a)
{
Object[] mm = new Object[2];
. . .;
return (T[]) mm; // compiles with warning
}
The call
String[] ss = minmax("Tom", "Dick", "Harry");
compiles without any warning. A ClassCastException occurs when the Object[] reference is assigned to the
String[] variable. In this situation, you can use reflection and call Array.newInstance:
public static <T extends Comparable> T[] minmax(T[] a)
{
T[] mm = (T[]) Array.newInstance(a.getClass().getComponentType(), 2);
...
}
The toArray method of the ArrayList class is not so lucky. It needs to produce a T[] array, but it doesn’t have
the component type. Therefore, there are two variants:
Object[] toArray()
T[] toArray(T[] result)
The second method receives an array parameter. If the array is large enough, it is used. Otherwise, a new array
of sufficient size is created, using the component type of result.
Type Variables Are Not Valid in Static Contexts of Generic Classes
You cannot reference type variables in static fields or methods. For example, the following clever idea won’t
work:
public class Singleton<T>
{
public static T getSingleInstance() // ERROR
{
if (singleInstance == null) construct new instance of T
return singleInstance;
}
private static T singleInstance; // ERROR
}
If this could be done, then a program could declare a Singleton<Random> to share a random number
generator and a Singleton<JFileChooser> to share a file chooser dialog. But it can’t work. After type erasure
there is only one Singleton class, and only one singleInstance field. For that reason, static fields and methods
with type variables are simply outlawed.
Beware of Clashes After Erasure
It is illegal to create conditions that cause clashes when generic types are erased. Here is an example. Suppose
we add an equals method to the Pair class, like this:
public class Pair<T>
{
public boolean equals(T value) { return first.equals(value) && second.equals(value); }
...
}
Consider a Pair<String>. Conceptually, it has two equals methods:
boolean equals(String) // defined in Pair<T>
boolean equals(Object) // inherited from Object
10
But the intuition leads us astray. The erasure of the method
boolean equals(T)
is
boolean equals(Object)
which clashes with the Object.equals method. The remedy is, of course, to rename the offending method. The
generics specification cites another rule: “To support translation by erasure, we impose the restriction that a
class or type variable may not at the same time be a subtype of two interface types which are different
parameterizations of the same interface.” For example, the following is illegal:
class Calendar implements Comparable<Calendar> { . . . }
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>
{ . . . } // ERROR
GregorianCalendar would then implement both Comparable<Calendar> and Comparable<GregorianCalendar>, which are different parameterizations of the same interface. It is not obvious what this restriction
has to do with type erasure. After all, the nongeneric
Version
class Calendar implements Comparable { . . . }
class GregorianCalendar extends Calendar implements Comparable { . . . }
is legal. The reason is far more subtle. There would be a conflict with the synthesized bridge methods. A class
that implements Comparable<X> gets a bridge method
public int compareTo(Object other) { return compareTo((X) other); }
You cannot have two such methods for different types X.
Inheritance Rules for Generic Types
When you work with generic classes, you need to learn a few rules about inheritance and subtypes. Let’s start
with a situation that many programmers find unintuitive. Consider a class and a subclass, such as Employee
and Manager. Is Pair<Manager> a subclass of Pair<Employee>? Perhaps surprisingly, the answer is “no.” For
example, the following code will not compile:
Manager[] topHonchos = . . .;
Pair<Employee> result = ArrayAlg.minmax(topHonchos); // ERROR
The minmax method returns a Pair<Manager>, not a Pair<Employee>, and it is illegal to assign one to the
other. In general, there is no relationship between Pair<S> and Pair<T>, no matter how S and T are related
(see Figure 12–1).
This seems like a cruel restriction, but it is necessary for type safety. Suppose we were allowed to convert a
Pair<Manager> to a Pair<Employee>. Consider this code:
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<Employee> employeeBuddies = managerBuddies; // illegal, but suppose it wasn't
employeeBuddies.setFirst(lowlyEmployee);
Clearly, the last statement is legal. But employeeBuddies and managerBuddies refer to the same object. We
now managed to pair up the CFO with a lowly employee, which should not be possible for a Pair<Manager>.
You can always convert a parameterized type to a raw type. For example, Pair<Employee> is a subtype of the
raw type Pair. This conversion is necessary for interfacing with legacy code.
Can you convert to the raw type and then cause
a type error? Unfortunately, you can. Consider
this example:
Pair<Manager> managerBuddies = new
Pair<Manager>(ceo, cfo);
Pair rawBuddies = managerBuddies; // OK
rawBuddies.setFirst(new File(". . .")); // only a
compile-time warning
This sounds scary. However, keep in mind that you
are no worse off than you were with older versions
of Java. The security of the virtual machine is not at
stake. When the foreign object is retrieved with
getFirst and assigned to a Manager variable, a
ClassCastException is thrown, just as in the good
old days.
11
You merely lose the added safety that generic
programming normally provides. Finally, generic
classes can extend or implement other generic
classes. In this regard, they are no different from
ordinary
classes.For
example,
the
class
ArrayList<T> implements the interface List<T>.
That means, an ArrayList<Manager> can be
converted to a List<Manager>. However, as you
just saw, an ArrayList<Manager> is not an
ArrayList<Employee> or List<Employee>. Figure
12–2 shows these relationships.
Wildcard Types
It was known for some time among researchers of type systems that a rigid system of generic types is quite
unpleasant to use. The Java designers invented an ingenious (but nevertheless safe) “escape hatch”: the
wildcard type. For example, the wildcard type
Pair<? extends Employee>
denotes any generic Pair type whose type parameter is a subclass of Employee, such as Pair<Manager>, but
not Pair<String>.
Let’s say you want to write a method that prints out pairs of employees, like this:
public static void printBuddies(Pair<Employee> p)
{
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() + " and " + second.getName() + " are buddies.";
}
As you saw in the preceding section, you cannot pass a Pair<Manager> to that method, which is rather
limiting. But the solution is simple—use a wildcard type:
public static void printBuddies(Pair<? extends Employee> p)
The type Pair<Manager> is a subtype of Pair<? extends Employee> (see Figure 12–3). Can we use wildcards
to corrupt a Pair<Manager> through a Pair<? extends Employee> reference?
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<? extends Employee> wildcardBuddies = managerBuddies; // OK
wildcardBuddies.setFirst(lowlyEmployee); // compile-time error
No corruption is possible. The call to setFirst is a type error. To see why, let us have a closer look at the type
Pair<? extends Employee>. Its methods look like this:
? extends Employee getFirst()
void setFirst(? extends Employee)
This makes it impossible to call the setFirst method.
The compiler only knows that it needs some subtype of
Employee, but it doesn’t know which type. It refuses to
pass any specific type—after all, ? might not match it.
We don’t have this problem with getFirst: It is perfectly
legal to assign the return value of getFirst to an
Employee reference. This is the key idea behind
bounded wildcards. We now have a way of
distinguishing between the safe accessor methods and
the unsafe mutator methods.
Supertype Bounds for Wildcards
Wildcard bounds are similar to type variable bounds, but they have an added capability— you can specify a
supertype bound, like this:
? super Manager
12
This wildcard is restricted to all supertypes of Manager. (It was a stroke of good luck that the existing super
keyword describes the relationship so accurately.) Why would you want to do this? A wildcard with a
supertype bound gives you the opposite behavior of the wildcards described in the section “Wildcard Types”
on page 632. You can supply parameters to methods, but you can’t use the return values. For example, Pair<?
super Manager> has methods
void setFirst(? super Manager)
? super Manager getFirst()
The compiler doesn’t know the exact type of the setFirst method but can call it with any object of type
Manager, Employee, or Object, but not a subtype such as Executive. However, if you call getFirst, there is no
guarantee about the type of the returned object. You can only assign it to an Object.
Here is a typical example. We have an array of managers and want to put the manager with the lowest and
highest bonus into a Pair object. What kind of Pair? A Pair<Employee> should be fair game or, for that
matter, a Pair<Object> (see Figure 12–4). The following method will accept any appropriate Pair:
public static void minmaxBonus(Manager[] a,
{
Pair<? super Manager> result)
if (min.getBonus() > a[i].getBonus()) min = a[i];
{
if (max.getBonus() < a[i].getBonus()) max = a[i];
if (a == null || a.length == 0) return;
}
Manager min = a[0];
result.setFirst(min);
Manager max = a[0];
result.setSecond(max);
for (int i = 1; i < a.length; i++)
}
Intuitively speaking, wildcards with supertype bounds let you write to a generic object, wildcards with
subtype bounds let you read from a generic objects. Here is another use for supertype bounds. The
Comparable interface is itself a generic type. It is declared as follows:
public interface Comparable<T>
{
public int compareTo(T other);
}
Here, the type variable indicates the type of the other parameter. For example, the String class implements
Comparable<String>, and its compareTo method is declared as public int compareTo(String other) This is
nice—the explicit parameter has the correct type. Before Java SE 5.0, other was an Object, and a cast was
necessary in the implementation of the method. Because Comparable is a generic type, perhaps we should
have done a better job with the min method of the ArrayAlg class? We could have declared it as
public static <T extends Comparable<T>> T min(T[] a) This looks more thorough than just using T extends
Comparable, and it would work fine for many classes. For example, if you compute the minimum of a String
array, then T is the type String, and String is a subtype of Comparable<String>. But we run into a problem
when processing an array of GregorianCalendar objects. As it happens, GregorianCalendar is a subclass of
Calendar, and Calendar implements Comparable<Calendar>. Thus, GregorianCalendar implements
Comparable<Calendar> but not Comparable<GregorianCalendar>.
In a situation such as this one, supertypes come to the rescue:
public static <T extends Comparable<? super T>> T min(T[] a) . . .
Now the compareTo method has the form int compareTo(? super T)
Maybe it is declared to take an object of type T, or—for example, when
T is GregorianCalendar— a supertype of T. At any rate, it is safe to
pass an object of type T to the compareTo method. To the uninitiated, a
declaration such as <T extends Comparable<? super T>> is bound to
look intimidating. This is unfortunate, because the intent of this
declaration is to help application programmers by removing
unnecessary restrictions on the call parameters. Application
programmers with no interest in generics will probably learn quickly to
gloss over these declarations and just take for granted that library
programmers will do the right thing. If you are a library programmer,
you’ll need to get used to wildcards, or your users will curse you and
throw random casts at their code until it compiles
Unbounded Wildcards
You can even use wildcards with no bounds at all, for example, Pair<?>. At first glance, this looks identical to
the raw Pair type. Actually, the types are very different. The type Pair<?> has methods such as
? getFirst()
void setFirst(?)
13
The return value of getFirst can only be assigned to an Object. The setFirst method can never be called, not
even with an Object. That’s the essential difference between Pair<?> and Pair: you can call the setObject
method of the raw Pair class with any Object.
Why would you ever want such a wimpy type? It is useful for very simple operations. For example, the
following method tests whether a pair contains a given object. It never needs the actual type.
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
}
You could have avoided the wildcard type by turning contains into a generic method: public static <T>
boolean hasNulls(Pair<T> p)
However, the version with the wildcard type seems easier to read.
Wildcard Capture
Let us write a method that swaps the elements of a pair:
public static void swap(Pair<?> p)
A wildcard is not a type variable, so we can’t write code that uses ? as a type. In other words, the following
would be illegal:
? t = p.getFirst(); // ERROR
p.setFirst(p.getSecond());
p.setSecond(t);
That’s a problem because we need to temporarily hold the first element when we do the swapping.
Fortunately, there is an interesting solution to this problem. We can write a helper method, swapHelper, like
this:
public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
Note that swapHelper is a generic method, whereas swap is not—it has a fixed parameter of type Pair<?>.
Now we can call swapHelper from swap:
public static void swap(Pair<?> p) { swapHelper(p); }
In this case, the parameter T of the swapHelper method captures the wildcard. It isn’t known what type the
wildcard denotes, but it is a definite type, and the definition of <T>swapHelper makes perfect sense when T
denotes that type. Of course, in this case, we were not compelled to use a wildcard. We could have directly
implemented <T> void swap(Pair<T> p) as a generic method without wildcards. However, consider this
example in which a wildcard type occurs naturally in the middle of a computation:
public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
{
minmaxBonus(a, result);
PairAlg.swapHelper(result); // OK--swapHelper captures wildcard type
}
Here, the wildcard capture mechanism cannot be avoided. Wildcard capture is only legal in very limited
circumstances. The compiler must be able to guarantee that the wildcard represents a single, definite type. For
example, the T in ArrayList<Pair<T>> can never capture the wildcard in ArrayList<Pair<?>>. The array list
might hold two Pair<?>, each of which has a different type for ?.
Reflection and Generics
The Class class is now generic. For example, String.class is actually an object (in fact, the sole object) of the
class Class<String>.
The type parameter is useful because it allows the methods of Class<T> to be more specific about their return
types. The following methods of Class<T> take advantage of the type parameter:
T newInstance()
T cast(Object obj)
T[] getEnumConstants()
Class<? super T> getSuperclass()
Constructor<T> getConstructor(Class... parameterTypes)
Constructor<T> getDeclaredConstructor(Class... parameterTypes)
14
The newInstance method returns an instance of the class, obtained from the default constructor. Its return type
can now be declared to be T, the same type as the class that is being described by Class<T>. That saves a cast.
The cast method returns the given object, now declared as type T if its type is indeed a subtype of T.
Otherwise, it throws a BadCastException. The getEnumConstants method returns null if this class is not an
enum class or an array of the enumeration values, which are known to be of type T. Finally, the
getConstructor and getDeclaredConstructor methods return a Constructor<T> object.
The Constructor class has also been made generic so that its newInstance method has the correct return type.
Using Class<T> Parameters for Type Matching
It is sometimes useful to match the type variable of a Class<T> parameter in a generic method. Here is the
canonical example:
public static <T> Pair<T> makePair(Class<T> c) throws InstantiationException,
IllegalAccessException
{
return new Pair<T>(c.newInstance(), c.newInstance());
}
If you call makePair(Employee.class) then Employee.class is an object of type Class<Employee>. The type
parameter T of the makePair
method matches Employee, and the compiler can infer that the method returns a Pair<Employee>.
Generic Type Information in the Virtual Machine
One of the notable features of Java generics is the erasure of generic types in the virtual machine. Perhaps
surprisingly, the erased classes still retain some faint memory of their generic origin. For example, the raw
Pair class knows that it originated from the generic class Pair<T>, even though an object of type Pair can’t tell
whether it was constructed as a Pair<String> or Pair<Employee>.
In order to express generic type declarations, Java SE 5.0 introduced a new interface Type in the
java.lang.reflect package. The interface has the following subtypes:
• The Class class, describing concrete types
• The TypeVariable interface, describing type variables (such as T extends Comparable<?
super T>)
• The WildcardType interface, describing wildcards (such as ? super T)
• The ParameterizedType interface, describing generic class or interface types (such as
Comparable<? super T>)
• The GenericArrayType interface, describing generic arrays (such as T[])
Figure 12–5 shows the inheritance hierarchy. Note that the last four subtypes are interfaces— the virtual
machine instantiates suitable classes that implement these interfaces.
Exceptions:
Exceptions are such anomalous conditions (or typically an event) which changes the normal flow of execution
of a program. Exceptions are used for signaling erroneous (exceptional) conditions which occur during the run
time processing. Exceptions may occur in any programming language. Occurrence of any kind of exception in
java applications may result in an abrupt termination of the JVM or simply the JVM crashes which leaves the
user unaware of the causes of such anomalous conditions. However Java provides mechanisms to handle such
situations through its superb exception handling mechanism. The Java programming language uses Exception
classes to handle such erroneous conditions and exceptional events. Suppose an error occurs while a Java
program is running. The error might be caused by a file containing wrong information, a flaky network
15
connection, or (we hate to mention it) use of an invalid array index or an attempt to use an object reference
that hasn’t yet been assigned to an object. Users expect that programs will act sensibly when errors happen. If
an operation cannot be completed because of an error, the program ought to either
• Return to a safe state and enable the user to execute other commands; or
• Allow the user to save all work and terminate the program gracefully.
This may not be easy to do, because the code that detects (or even causes) the error condition is usually far
removed from the code that can roll back the data to a safe state or the code that can save the user’s work and
exit cheerfully. The mission of
exception handling is to transfer control from where the error occurred to an error handler that can deal with
the situation. To handle exceptional situations in your program, you must take into account the errors and
problems that may occur
• User input errors. In addition to the inevitable typos, some users like to blaze their own trail instead of
following directions. Suppose, for example, that a user asks to connect to a URL that is syntactically wrong.
Your code should check the syntax, but
suppose it does not. Then the network layer will complain.
• Device errors. Hardware does not always do what you want it to. The printer may be turned off. A web page
may be temporarily unavailable. Devices will often fail in the middle of a task. For example, a printer may run
out of paper during printing.
• Physical limitations. Disks can fill up; you can run out of available memory.
• Code errors. A method may not perform correctly. For example, it could deliver
wrong answers or use other methods incorrectly. Computing an invalid array index, trying to find a
nonexistent entry in a hash table, and trying to pop an empty stack are all examples of a code error
The traditional reaction to an error in a method is to return a special error code that the calling method
analyzes. For example, methods that read information back from files often return a –1 end-of-file value
marker rather than a standard character. This can be an efficient method for dealing with many exceptional
conditions. Another common return value to denote an error condition is the null reference..
Unfortunately, it is not always possible to return an error code. There may be no obvious way of
distinguishing valid and invalid data. A method returning an integer cannot simply return –1 to denote the
error—the value –1 might be a perfectly valid result.
Instead, as we mentioned back in Chapter 5, Java allows every method an alternative exit path if it is unable to
complete its task in the normal way. In this situation, the method does not return a value. Instead, it throws an
object that encapsulates the error information. Note that the method exits immediately; it does not return its
normal (or any) value. Moreover, execution does not resume at the code that called the method; instead, the
exception-handling mechanism begins its search for an exception handler that can deal with this particular
error condition. Exceptions have their own syntax and are part of a special inheritance hierarchy. We take up
the syntax first and then give a few hints on how to use this language feature effectively.
The term exception is shorthand for the phrase "exceptional event."
Definition: An exception is an event, which occurs during the execution of a program, that disrupts the
normal flow of the program's instructions. When an error occurs within a method, the method creates an
object and hands it off to the runtime system. The object, called an exception object, contains information
about the error, including its type and the state of the program when the error occurred. Creating an exception
object and handing it to the runtime system is called throwing an exception. After a method throws an
exception, the runtime system attempts to find something to handle it. The set of possible "somethings" to
handle the exception is the ordered list of methods that had been called to get to the method where the error
occurred. The list of methods is known as the call stack. Below figure shows call stack.
The runtime system searches the call stack for a method that contains a
block of code that can handle the exception. This block of code is called
an exception handler. The search begins with the method in which the
error occurred and proceeds through the call stack in the reverse order
in which the methods were called. When an appropriate handler is
found, the runtime system passes the exception to the handler. An
exception handler is considered appropriate if the type of the exception
object thrown matches the type that can be handled by the handler. The
exception handler chosen is said to catch the exception. If the runtime
system exhaustively searches all the methods on the call stack without
finding an appropriate exception handler, as shown in the next figure,
the runtime system (and, consequently, the program) terminates.
16
The Catch or Specify Requirement
Valid Java programming language code must honor the Catch or Specify Requirement. This means that code
that might throw certain exceptions must be enclosed by either of the following:
· A try statement that catches the exception. The try must provide a handler for the exception, as described in
Catching and Handling Exceptions.
· A method that specifies that it can throw the exception. The method must provide a throws clause that lists
the exception, as described in Specifying the Exceptions Thrown by a Method.
The Classification of Exceptions
In the Java programming language, an exception object is always
an instance of a class derived from Throwable. As you will soon
.
see, you can create your own exception classes if the ones built
into Java do not suit your needs. Figure 11–1 is a simplified
diagram of the exception hierarchy in Java. Notice that all
exceptions descend from Throwable, but the hierarchy immediately
splits into two branches: Error and Exception. The Error hierarchy
describes internal errors and resource exhaustion inside the Java
runtime system. You should not throw an object of this type. There
is little you can do if such an internal error occurs, beyond
notifying the user and trying to terminate the program gracefully.
These situations are quite rare. When doing Java programming,
you focus on the Exception hierarchy. The Exception hierarchy
also splits into two branches: exceptions that derive from
RuntimeException and those that do not. The general rule is this: A
RuntimeException happens because you made a programming
error. Any other exception occurs because a bad thing, such as an
I/O error, happened to your otherwise good program
Exceptions that inherit from RuntimeException include such problems as
• A bad cast
• An out-of-bounds array access • A null pointer access
Exceptions that do not inherit from RuntimeException include
• Trying to read past the end of a file
• Trying to open a malformed URL
• Trying to find a Class object for a string that does not denote an existing class
The rule “If it is a RuntimeException, it was your fault” works pretty well. You could have avoided that
ArrayIndexOutOfBoundsException by testing the array index against the array bounds. The
NullPointerException would not have happened had you checked whether the variable was null before using
it.
The Java Language Specification calls any exception that derives from the class Error or the class
RuntimeException an unchecked exception. All other exceptions are called checked exceptions. This is useful
terminology that we also adopt. The compiler checks that you provide exception handlers for all checked
exceptions.
1. Checked Exceptions - These are the exceptions which occur during the compile time of the program. The
compiler checks at the compile time that whether the program contains handlers for checked exceptions or
not. These exceptions do not extend RuntimeException class and must be handled to avoid a compile-time
error by the programmer. These exceptions extend the java.lang.Exception class These exceptional conditions
should be anticipated and recovered by an application. Furthermore Checked exceptions are required to be
caught. Remember that all the exceptions are checked exceptions unless and until those indicated by Error,
RuntimeException or their subclasses.
For example if you call the readLine() method on a BufferedReader object then the IOException may
occur or if you want to build a program that could read a file with a specific name then you would be
prompted to input a file name by the application. Then it passes the name to the constructor for
java.io.FileReader and opens the file.
However if you do not provide the name of any existing file then the constructor
throwsjava.io.FileNotFoundException which abrupt the application to succeed. Hence this exception will be
caught by a well-written application and will also prompt to correct the file name. Here is the list of checked
exceptions.
NoSuchFieldException,
InstantiationException
,IllegalAccessException,
ClassNotFoundException , NoSuchMethodException, CloneNotSupportedException, InterruptedException
2. Unchecked Exceptions - Unchecked exceptions are the exceptions which occur during the runtime of the
program. Unchecked exceptions are internal to the application and extend the java.lang.RuntimeException
that is inherited from java.lang.Exceptionclass. These exceptions cannot be anticipated and recovered like
programming bugs, such as logic errors or improper use of an API.
These type of exceptions are also calledRuntime exceptions that are usually caused by data errors, like
arithmetic overflow, divide by zero etc. Lets take the same file name example as described earlier. In that
17
example the file name is passed to the constructor for FileReader. However, the constructor will
throwNullPointerException if a logic error causes a null to be passed to the constructor. Well in this case the
exception could be caught by the application but it would rather try to eliminate the bug due to which the
exception has occurred.
You must have encountered the most common exception in your program i.e. the
ArithmeticException. I am sure you must be familiar with the reason of its occurrence that is when something
tries to divide by zero. Similarly when an instance data member or method of a reference variable is to be
accessed that hasn't yet referenced an object throws NullPointerException. Here is the list of unchecked
exceptions.
IndexOutOfBoundsException ArrayIndexOutOfBoundsException
ClassCastException
ArithmeticException NullPointerException IllegalStateException
SecurityException
3. Error - The errors in java are external to the application. These are the exceptional conditions that could not
be usually anticipated by the application and also could not be recovered from. Error exceptions belong to
Error and its subclasses are not subject to the catch or Specify requirement. Suppose a file is successfully
opened by an application for input but due to some system malfunction could not be able to read that file then
the java.io.IOError would be thrown.
This error will cause the program to terminate but if an application wants then the error might be
caught. An Error indicates serious problems that a reasonable application should not try to catch. Most such
errors are abnormal conditions. Hence we conclude that Errors and runtime exceptions are together called as
unchecked exceptions.
Declaring Checked Exceptions
A Java method can throw an exception if it encounters a situation it cannot handle. The idea is simple: a
method will not only tell the Java compiler what values it can return, it is also going to tell the compiler what
can go wrong. For example, code that attempts to read from a file knows that the file might not exist or that it
might be empty. The code that tries to process the information in a file therefore will need to notify the
compiler that it can throw some sort of IOException. The place in which you advertise that your method can
throw an exception is the header of the method; the header changes to reflect the checked exceptions the
method can throw. For example, here is the declaration of one of the constructors of the FileInputStream class from the standard library.
public FileInputStream(String name) throws
FileNotFoundException
The declaration says that this constructor produces a FileInputStream object from a String parameter but that
it also can go wrong in a special way—by throwing a FileNotFoundException. If this sad state should come to
pass, the constructor call will not initialize a new FileInputStream object but instead will throw an object of
the FileNotFoundException class. If it does, then the runtime system will begin to search for an exception
handler that knows how to deal with FileNotFoundException objects. When you write your own methods, you
don’t have to advertise every possible throwable object that your method might actually throw. To understand
when (and what) you have to advertise in the throws clause of the methods you write, keep in mind that an
exception is thrown in any of the following four situations:
• You call a method that throws a checked exception, for example, the FileInputStream constructor.
• You detect an error and throw a checked exception with the throw statement
• You make a programming error, such as a[-1] = 0 that gives rise to an unchecked exception such as
an ArrayIndexOutOfBoundsException.
• An internal error occurs in the virtual machine or runtime library.
As with Java methods that are part of the supplied classes, you declare that your method may throw an
exception with an exception specification in the method header.
class MyAnimation
{. . .
public Image loadImage(String s) throws IOException
{. . .}}
If a method might throw more than one checked exception type, you must list all exception classes in the
header. Separate them by a comma as in the following example:
class MyAnimation
{. . .
public Image loadImage(String s) throws EOFException, MalformedURLException
{. . .}}
However, you do not need to advertise internal Java errors, that is, exceptions inheriting from Error. Any code
could potentially throw those exceptions, and they are entirely beyond your control. Similarly, you should not
advertise unchecked exceptions inheriting from RuntimeException.
18
class MyAnimation
{. . .
void drawImage(int i) throws ArrayIndexOutOfBoundsException // bad style
{. . .}}
These runtime errors are completely under your control. If you are so concerned about array index errors, you
should spend the time needed to fix them instead of advertising the possibility that they can happen. In
summary, a method must declare all the checked exceptions that it might throw. Unchecked exceptions are
either beyond your control (Error) or result from conditions
that you should not have allowed in the first place (RuntimeException). If your method fails to faithfully
declare all checked exceptions, the compiler will issue an error message. Of course, as you have already seen
in quite a few examples, instead of declaring the exception, you can also catch it. Then the exception won’t be
thrown out of the method, and no throws specification is necessary. You see later in this chapter how to decide
whether to catch an exception or to enable someone else to catch it.
When a method in a class declares that it throws an exception that is an instance of a particular class,
then it may throw an exception of that class or of any of its subclasses. For example, the FileInputStream
constructor could have declared that it throws an IOException. In that case, you would not have known what
kind of IOException. It could be a plain IOException or an object of one of the various subclasses, such as
FileNotFoundException.
How to Throw an Exception
Let us suppose something terrible has happened in your code. You have a method, readData, that is reading in
a file whose header promised
Content-length: 1024
but you get an end of file after 733 characters. You decide this situation is so abnormal that you want to throw
an exception.
You need to decide what exception type to throw. Some kind of IOException would be a good choice.
Perusing the Java API documentation, you find an EOFException with the description “Signals that an EOF
has been reached unexpectedly during input.” Perfect. Here is how you throw it:
throw new EOFException();
or, if you prefer,
EOFException e = new EOFException();
if (!in.hasNext()) // EOF encountered
throw e;
{
Here is how it all fits together:
if (n < len)
String
readData(Scanner
in)
throws
throw new EOFException();
}
EOFException
{
...
...
}
while (. . .)
return s;
{
}
The EOFException has a second constructor that takes a string argument. You can put this to good use by
describing the exceptional condition more carefully.
String gripe = "Content-length: " + len + ", Received: " + n;
throw new EOFException(gripe);
As you can see, throwing an exception is easy if one of the existing exception classes works for you. In this
case:
1. Find an appropriate exception class. 2. Make an object of that class. 3. Throw it.
Once a method throws an exception, the method does not return to its caller. This means that you do not have
to worry about cooking up a default return value or an error code.
Creating Exception Classes
Your code may run into a problem that is not adequately described by any of the standard exception classes.
In this case, it is easy enough to create your own exception class. Just derive it from Exception or from a child
class of Exception such as IOException. It is customary to give both a default constructor and a constructor
that contains a detailed message. (The toString method of the Throwable superclass prints that detailed
message, which is handy for debugging.)
class FileFormatException extends IOException
{
{
super(gripe);
public FileFormatException() {}
}
public FileFormatException(String gripe)
}
Now you are ready to throw your very own exception type.
19
String readData(BufferedReader in) throws FileFormatException
{
throw new FileFormatException();
...
}
while (. . .)
...
{
}
if (ch == -1) // EOF encountered
return s;
{
}
if (n < len)
Catching Exceptions
You now know how to throw an exception. It is pretty easy. You throw it and you forget it. Of course, some
code has to catch the exception. Catching exceptions requires more planning. If an exception occurs that is not
caught anywhere, the program will terminate and print a message to the console, giving the type of the
exception and a stack trace. Graphics programs (both applets and applications) catch exceptions, print stack
trace messages, and then go back to the user interface processing loop. (When you are debugging a
graphically based program, it is a good idea to keep the console available on the screen and not minimized.)
To catch an exception, you set up a try/catch block. The simplest form of the try block is as follows:
try
}
{
catch (ExceptionType e)
code
{
more code
handler for this type
more code
}
If any of the code inside the try block throws an exception of the class specified in the catch clause, then
1. The program skips the remainder of the code in the try block.
2. The program executes the handler code inside the catch clause.
If none of the code inside the try block throws an exception, then the program skips the
catch clause.
If any of the code in a method throws an exception of a type other than the one named
in the catch clause, this method exits immediately. (Hopefully, one of its callers has
already coded a catch clause for that type.)
To show this at work, we show some fairly typical code for reading in data:
public void read(String filename)
process input
{
}
}
try
{
catch (IOException exception)
InputStream in = new FileInputStream(filename);
{
int b;
exception.printStackTrace();
while ((b = in.read()) != -1)
}
{
}
Notice that most of the code in the try clause is straightforward: it reads and processes bytes until we
encounter the end of the file. As you can see by looking at the Java API, there is the possibility that the read
method will throw an IOException. In that case, we skip out of the entire while loop, enter the catch clause
and generate a stack trace. For a toy program, that seems like a reasonable way to deal with this exception.
What other choice do you have? Often, the best choice is to do nothing at all and simply pass the exception on
to the caller. If an error occurs in the read method, let the caller of the read method worry about
it! If we take that approach, then we have to advertise the fact that the method may throw an IOException.
public void read(String filename) throws
while ((b = in.read()) != -1)
{
IOException
{
process input
InputStream in = new FileInputStream(filename);
}
int b;
}
Remember, the compiler strictly enforces the throws specifiers. If you call a method that throws a checked
exception, you must either handle it or pass it on. Which of the two is better? As a general rule, you should
catch those exceptions that you know how to handle and propagate those that you do not know how to handle.
When you propagate an exception, you must add a throws specifier to alert the caller that an exception may be
thrown.
Look at the Java API documentation to see what methods throw which exceptions. Then decide whether you
should handle them or add them to the throws list. There is nothing embarrassing about the latter choice. It is
20
better to direct an exception to a competent handler than to squelch it. Please keep in mind that there is one
exception to this rule, as we mentioned earlier. If
you are writing a method that overrides a superclass method that throws no exceptions (such as
paintComponent in JComponent), then you must catch each checked exception in the method’s code. You are
not allowed to add more throws specifiers to a subclass method than are present in the superclass method.
Catching Multiple Exceptions
You can catch multiple exception types in a try block and handle each type differently. You use a separate
catch clause for each type as in the following example:
try
catch (UnknownHostException e2)
{
{
code that might throw exceptions
emergency action for unknown hosts
}
}
catch (MalformedURLException e1)
catch (IOException e3)
{
{
emergency action for malformed URLs
emergency action for all other I/O problems
}
}
The exception object (e1, e2, e3) may contain information about the nature of the exception. To find out more
about the object, try
e3.getMessage()
to get the detailed error message (if there is one), or
e3.getClass().getName()
to get the actual type of the exception object.
Rethrowing and Chaining Exceptions
You can throw an exception in a catch clause. Typically, you do this because you want to change the
exception type. If you build a subsystem that other programmers use, it makes a lot of sense to use an
exception type that indicates a failure of the subsystem.
An example of such an exception type is the ServletException. The code that executes a servlet may not want
to know in minute detail what went wrong, but it definitely wants to know that the servlet was at fault. Here is
how you can catch an exception and rethrow it:
try
{
{
throw new ServletException("database error: " +
access the database
e.getMessage());
}
}
catch (SQLException e)
Here, the ServletException is constructed with the message text of the exception. As of Java SE 1.4, you can
do better than that and set the original exception as the “cause” of the new exception:
try
Throwable se = new ServletException("database
{
error");
access the database
se.initCause(e);
}
throw se;
catch (SQLException e)
}
{
When the exception is caught, the original exception can be retrieved:
Throwable e = se.getCause();
This wrapping technique is highly recommended. It allows you to throw high-level exceptions in subsystems
without losing the details of the original failure.
The finally Clause
When your code throws an exception, it stops processing the remaining code in your method and exits the
method. This is a problem if the method has acquired some local resource that only it knows about and if that
resource must be cleaned up. One solution is to catch and rethrow all exceptions. But this solution is tedious
because you need to clean up the resource allocation in two places, in the normal code and in the exception
code. Java has a better solution, the finally clause. Here we show you how to properly dispose of a Graphics
object. If you do any database programming in Java, you will need to use the same techniques to close
connections to the database. As you will see in Chapter 4 of Volume II, it is very important to close all
database connections properly, even when exceptions occur. The code in the finally clause executes whether
or not an exception was caught. In the following example, the program will dispose of the graphics context
under all circumstances:
21
Graphics g = image.getGraphics();
show error dialog
try
// 4
{
}
// 1
finally
code that might throw exceptions
{
// 2
// 5
}
g.dispose();
catch (IOException e)
}
{
// 6
// 3
Let us look at the three possible situations in which the program will execute the finally clause.
1. The code throws no exceptions. In this event, the program first executes all the code in the try block. Then,
it executes the code in the finally clause. Afterwards, execution continues with the first statement after the
finally clause. In other words, execution passes through points 1, 2, 5, and 6.
2. The code throws an exception that is caught in a catch clause, in our case, an IOException. For this, the
program executes all code in the try block, up to the point at which the exception was thrown. The remaining
code in the try block is skipped. The program then executes the code in the matching catch clause, then the
code in the finally clause.
If the catch clause does not throw an exception, the program executes the first line after the finally clause. In
this scenario, execution passes through points 1, 3, 4, 5, and 6. If the catch clause throws an exception, then
the exception is thrown back to the
caller of this method, and execution passes through points 1, 3, and 5 only.
3. The code throws an exception that is not caught in any catch clause. For this, the program executes all code
in the try block until the exception is thrown. The remaining code in the try block is skipped. Then, the code
in the finally clause is executed, and the exception is thrown back to the caller of this method. Execution
passes through points 1 and 5 only. You can use the finally clause without a catch clause. For example,
consider the following
try statement:
InputStream in = ...;
finally
try
{
{
in.close();
code that might throw exceptions
}
}
The in.close() statement in the finally clause is executed whether or not an exception is encountered in the try
block. Of course, if an exception is encountered, it is rethrown and must be caught in another catch clause. In
fact, as explained in the following tip, we think it is a very good idea to use the finally clause in this way
whenever you need to close a resource.
Sometimes the finally clause gives you grief, namely if the cleanup method can also throw an exception. A
typical case is closing a stream. Suppose you want to make sure that you close a stream when an exception
hits in the stream processing code.
InputStream in = ...;
try
finally
{
{
code that might throw exceptions
in.close();
}
}
Now suppose that the code in the try block throws some exception other than an IOException that is of
interest to the caller of the code. The finally block executes, and the close method is called. That method can
itself throw an IOException! When it does, then the original exception is lost and the IOException is thrown
instead. That is very much against the spirit of exception handling.
It is always a good idea—unfortunately not one that the designers of the InputStream class chose to follow—
to throw no exceptions in cleanup operations such as dispose, close, and so on, that you expect users to call in
finally blocks.
Throwing Exceptions
If a method needs to be able to throw an exception, it has to declare the exception(s) thrown in the method
signature, and then include a throw-statement in the method. Here is an example: public void divide(int
numberToDivide, int numberToDivideBy) throws BadNumberException
{
if(numberToDivideBy == 0)
22
{
throw new BadNumberException("Cannot divide by 0");
}
return numberToDivide / numberToDivideBy;
}
When an exception is thrown the method stops execution right after the "throw" statement. Any statements
following the "throw" statement are not executed. In the example above the "return numberToDivide /
numberToDivideBy;" statement is not executed if a BadNumberException is thrown. The program resumes
execution when the exception is caught somewhere by a "catch" block. Catching exceptions is explained later.
You can throw any type of exception from your code, as long as your method signature declares it. You can
also make up your own exceptions. Exceptions are regular Java classes that extends java.lang.Exception, or
any of the other built-in exception classes. If a method declares that it throws an exception A, then it is also
legal to throw subclasses of A.
This section describes how to use the three exception handler components — the try, catch, and finally blocks
— to write an exception handler
Catching Exceptions
You associate exception handlers with a try block by providing one or more catch blocks
directly after the try block. No code can be between the end of the try block and the
beginning of the first catch block.
try {
} catch (ExceptionType name) {
} catch (ExceptionType name) { }
Each catch block is an exception handler and handles the type of exception indicated by its argument. The
argument type, ExceptionType, declares the type of exception that the handler can handle and must be the
name of a class that inherits from the Throwable class. The handler can refer to the exception with name. The
catch block contains code that is executed if and when the exception handler is invoked. The runtime system
invokes the exception handler when the handler is the first one in the call stack whose ExceptionType matches
the type of the exception thrown. The system considers it a match if the thrown object can legally be assigned
to the exception handler's argument. If a method calls another method that throws checked exceptions, the
calling method is forced to either pass the exception on, or catch it. Catching the exception is done using a trycatch block. Here is an example:
public void callDivide()
catch (BadNumberException e)
{
{ //do something clever with the exception
try
System.out.println(e.getMessage());
{
}
int result = divide(2,1);
System.out.println("Division attempt done");
System.out.println(result);
}
}
The BadNumberException parameter e inside the catch-clause points to the exception thrown from the divide
method, if an exception is thrown. If no exeception is thrown by any of the methods called or statements
executed inside the try-block, the catch-block is simply ignored. It will not be executed. If an exception is
thrown inside the try-block, for instance from the divide method, the program flow of the calling method,
callDivide, is interrupted just like the program flow inside divide. The program flow resumes at a catch-block
in the call stack that can catch the thrown exception. In the example above the "System.out.println(result);"
statement will not get executed if an exception is thrown fromt the divide method. Instead program execution
will resume inside the "catch (BadNumberException e) { }" block. If an exception is thrown inside the catchblock and that exception is not caught, the catch-block is interrupted just like the try-block would have been.
When the catch block is finished the program continues with any statements following the catch block. In the
example above the "System.out.println("Division attempt done");" statement will always get executed.
Propagating Exceptions
You don't have to catch exceptions thrown from other methods. If you cannot do anything about the exception
where the method throwing it is called, you can just let the method propagate the exception up the call stack to
the method that called this method. If you do so the method calling the method that throws the exception must
also declare to throw the exception. Here is how the callDivide() method would look in that case.
public void callDivide() throws BadNumberException
{
int result = divide(2,1);
System.out.println(result);
}
23
Notice how the try-catch block is gone, and the callDivide method now declares that it can throw a
BadNumberException. The program execution is still interrupted if an exception is thrown from the divide
method. Thus the "System.out.println(result);" method will not get executed if an exception is thrown from
the divide method. But now the program execution is not resumed inside the callDivide method. The
exception is propagated to the method that calls callDivide. Program execution doesn't resume until a catchblock somewhere in the call stack catches the exception. All methods in the call stack between the method
throwing the exception and the method catching it have their execution stopped at the point in the code where
the exception is thrown or propagated.
Example: Catching IOException's
If an exception is thrown during a sequence of statements inside a try-catch block, the sequence of statements
is interrupted and the flow of control will skip directly to the catch-block. This code can be interrupted by
exceptions in several places:
public void openFile()
reader.close();
{
System.out.println("--- File End ---");
Try
}
{ // constructor may throw FileNotFoundException
catch (FileNotFoundException e)
FileReader reader = new FileReader("someFile");
{ //do something clever with the exception
int i=0;
}
while(i != -1)
catch (IOException e)
{ //reader.read() may throw IOException
{ //do something clever with the exception
i = reader.read();
}
System.out.println((char) i );
}
}
If the reader.read() method call throws an IOException, the following System.out.println((char) i ); is not
executed. Neither is the last reader.close() or the System.out.println("--- File End ---"); statements. Instead the
program skips directly to the catch(IOException e){ ... } catch clause. If the new FileReader("someFile");
constructor call throws an exception, none of the code inside the try-block is executed.
Example: Propagating IOException's
This code is a version of the previous method that throws the exceptions instead of catching them:
public void openFile() throws IOException
i = reader.read();
{
System.out.println((char) i );
FileReader reader = new FileReader("someFile");
}
int i=0;
reader.close();
while(i != -1)
System.out.println("--- File End ---");
{
}
If an exception is thrown from the reader.read() method then program execution is halted, and the exception is
passed up the call stack to the method that called openFile(). If the calling method has a try-catch block, the
exception will be caught there. If the calling method also just throws the method on, the calling method is also
interrupted at the openFile() method call, and the exception passed on up the call stack. The exception is
propagated up the call stack like this until some method catches the exception, or the Java Virtual Machine
does.
Finally
The finally block always executes when the try block exits. This ensures that the finally block is executed
even if an unexpected exception occurs. But finally is useful for more than just exception handling — it
allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break.
Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.
NOTE: If the JVM exits while the try or catch code is being executed, then the finally block may not execute.
Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not
execute even though the application as a whole continues. Important: The finally block is a key tool for
preventing resource leaks. When closing a file or otherwise recovering resources, place the code in a finally
block to ensure that resource is always recovered
You can attach a finally-clause to a try-catch block. The code inside the finally clause will always be
executed, even if an exception is thrown from within the try or catch block. If your code has a return statement
inside the try or catch block, the code inside the finally-block will get executed before returning from the
method. Here is how a finally clause looks:
public void openFile()
try
{
{
FileReader reader = null;
reader = new FileReader("someFile");
24
int i=0;
{
while(i != -1)
try
{
{
i = reader.read();
reader.close();
System.out.println((char) i );
}
}
catch (IOException e)
}
{ //do something clever with the exception
catch (IOException e)
}
{ //do something clever with the exception
}
}
System.out.println("--- File End ---");
finally
}
{
}
if(reader != null)
No matter whether an exception is thrown or not inside the try or catch block the code inside the finally-block
is executed. The example above shows how the file reader is always closed, regardless of the program flow
inside the try or catch block.
Note: If an exception is thrown inside a finally block, and it is not caught, then that finally block is interrupted
just like the try-block and catch-block is. That is why the previous example had the reader.close() method call
in the finally block wrapped in a try-catch block:
} finally {
catch (IOException e) {
if(reader != null)
//do something clever with the exception
{
}
try {
}
reader.close();
System.out.println("--- File End ---");
}
}
That way the System.out.println("--- File End ---"); method call will always be executed. If no unchecked
exceptions are thrown that is. More about checked and unchecked in a later chapter. You don't need both a
catch and a finally block. You can have one of them or both of them with a try-block, but not none of them.
This code doesn't catch the exception but lets it propagate up the call stack. Due to the finally block the code
still closes the filer reader even if an exception is thrown. Notice how the catch block is gone.
public void openFile() throws IOException
} finally {
{
if(reader != null)
FileReader reader = null;
{ try { reader.close();
try {
} catch (IOException e)
reader = new FileReader("someFile");
{ //do something clever with the exception
int i=0;
}
while(i != -1)
}
{
System.out.println("--- File End ---");
i = reader.read();
}
System.out.println((char) i );
}
}
Tips for Using Exceptions
There is a certain amount of controversy about the proper use of exceptions. Some programmers believe that
all checked exceptions are a nuisance, others can’t seem to throw enough of them. We think that exceptions
(even checked exceptions) have their place, and offer you these tips for their proper use.
1. Exception handling is not supposed to replace a simple test. As an example of this, we wrote some code
that tries 10,000,000 times to pop an empty stack. It first does this by finding out whether the stack is empty.
if (!s.empty()) s.pop();
Next, we tell it to pop the stack no matter what. Then, we catch the EmptyStackException that tells us that we
should not have done that.
try()
catch (EmptyStackException e)
{
{
s.pop();
}
}
Test
Throw/Catch
646 milliseconds
21,739 milliseconds
2. Do not micromanage exceptions. Many programmers wrap every statement in a separate try block.
25
OutputStream out;
}
Stack s;
try
for (i = 0; i < 100; i++)
{
{
out.writeInt(n);
try
}
{
catch (IOException e)
n = s.pop();
{
}
// problem writing to file
catch (EmptyStackException s)
}
{
}
// stack was empty
This approach blows up your code dramatically. Think about the task that you want the code to accomplish.
Here we want to pop 100 numbers off a stack and save them to a file. (Never mind why—it is just a toy
example.) There is nothing we can do if a problem rears its ugly head. If the stack is empty, it will not become
occupied. If the file contains an error, the error will not magically go away. It therefore makes sense to wrap
the entire task in a try block. If any one operation fails, you can then abandon the task.
try
catch (IOException e)
{
{
for (i = 0; i < 100; i++)
// problem writing to file
{
}
n = s.pop();
catch (EmptyStackException s)
out.writeInt(n);
{
}
// stack was empty
}
}
This code looks much cleaner. It fulfills one of the promises of exception handling, to separate normal
processing from error handling.
3. Make good use of the exception hierarchy. Don’t just throw a RuntimeException. Find an appropriate
subclass or create your own. Don’t just catch Throwable. It makes your code hard to read and maintain.
Respect the difference between checked and unchecked exceptions. Checked exceptions are inherently
burdensome—don’t throw them for logic errors. (For example, the reflection library gets this wrong. Callers
often need to catch exceptions that they know can never happen.) Do not hesitate to turn an exception into
another exception that is more appropriate. For example, when you parse an integer in a file, catch the
NumberFormatException and turn it into a subclass of IOException or MySubsystemException.
4. Do not squelch exceptions. In Java, there is the tremendous temptation to shut up exceptions. You write a
method that calls a method that might throw an exception once a century. The compiler whines because you
have not declared the exception in the throws list of your method. You do not want to put it in the throws list
because then the compiler will whine about all the methods that call your method. So you just shut it up:
public Image loadImage(String s)
}
{
catch (Exception e)
try
{} // so there
{
}
code that threatens to throw checked exceptions
Now your code will compile without a hitch. It will run fine, except when an exception occurs. Then, the
exception will be silently ignored. If you believe that exceptions are at all important, you should make some
effort to handle them right.
5. When you detect an error, “tough love” works better than indulgence. Some programmers worry about
throwing exceptions when they detect errors. Maybe it would be better to return a dummy value rather than
throw an exception when a method is called with invalid parameters. For example, should Stack.pop return
null rather than throw an exception when a stack is empty? We think it is better to throw a
EmptyStackException at the point of failure than to have a NullPointerException occur at later time.
6. Propagating exceptions is not a sign of shame.
Many programmers feel compelled to catch all exceptions that are thrown. If they call a method that throws an
exception, such as the FileInputStream constructor or the readLine method, they instinctively catch the
exception that may be generated. Often, it is actually better to propagate the exception instead of catching it:
public void readStuff(String filename) throws IOException // not a sign of shame!
{
InputStream in = new FileInputStream(filename);
. . .}
26
Higher-level methods are often better equipped to inform the user of errors or to abandon unsuccessful
commands.
Advantages of Exceptions
1: Separating Error-Handling Code from "Regular" Code
2: Propagating Errors Up the Call Stack
3: Grouping and Differentiating Error Types
Analyzing Stack Trace Elements
A stack trace is a listing of all pending method calls at a particular point in the execution of a program. You
have almost certainly seen stack trace listings—they are displayed whenever a Java program terminates with
an uncaught exception. Before Java SE 1.4, you could access the text description of a stack trace by calling
the printStackTrace method of the Throwable class. Now you can call the getStackTrace method to get an
array of StackTraceElement objects that you can analyze in your program. For eg:
Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement frame : frames)
analyze frame
The StackTraceElement class has methods to obtain the file name and line number, as well as the class and
method name, of the executing line of code. The toString method yields a formatted string containing all of
this information. Java SE 5.0 added the static Thread.getAllStackTraces method that yields the stack traces of
all threads. Here is how you use that method:
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
for (Thread t : map.keySet())
{
StackTraceElement[] frames = map.get(t);
analyze frames
}
Listing 11–1 prints the stack trace of a recursive factorial function. For example, if you compute factorial(3),
the printout is
factorial(3):
StackTraceTest.factorial(StackTraceTest.java:18)
StackTraceTest.factorial(StackTraceTest.java:18)
StackTraceTest.factorial(StackTraceTest.java:24)
StackTraceTest.main(StackTraceTest.java:34)
StackTraceTest.factorial(StackTraceTest.java:24)
factorial(2):
StackTraceTest.main(StackTraceTest.java:34)
StackTraceTest.factorial(StackTraceTest.java:18)
return 1
StackTraceTest.factorial(StackTraceTest.java:24)
return 2
StackTraceTest.main(StackTraceTest.java:34)
return 6
factorial(1):
Listing 11–1 StackTraceTest.java
1. import java.util.*;
3. /*** A program that displays a trace feature of a recursive method call.
5. * @version 1.01 2004-05-10
6. * @author Cay Horstmann*/
8. public class StackTraceTest
9. {/**
11. * Computes the factorial of a number
12. * @param n a nonnegative integer
13. * @return n! = 1 * 2 * . . . * n*/
15. public static int factorial(int n)
16. {
17. System.out.println("factorial(" + n + "):");
18. Throwable t = new Throwable();
19. StackTraceElement[] frames = t.getStackTrace();
20. for (StackTraceElement f : frames)
21. System.out.println(f);
22. int r;
23. if (n <= 1) r = 1;
24. else r = n * factorial(n - 1);
25. System.out.println("return " + r);
27
26. return r;
27. }
29. public static void main(String[] args)
30. {
31. Scanner in = new Scanner(System.in);
32. System.out.print("Enter n: ");
33. int n = in.nextInt();
34. factorial(n);
35. }
36. }
Now let's suppose that the higher-level exception handler wants to dump the stack trace in its own format.
Definition: A stack trace provides information on the execution history of the current thread and lists the
names of the classes and methods that were called at the point when the exception occurred. A stack trace is a
useful debugging tool that you'll normally take advantage of when an exception has been thrown. The
following code shows how to call the getStackTrace method on the exception object.
catch (Exception cause) {
StackTraceElement elements[] = cause.getStackTrace();
for (int i = 0, n = elements.length; i < n; i++) {
System.err.println(elements[i].getFileName() + ":"
+ elements[i].getLineNumber()
+ ">> "
+ elements[i].getMethodName() + "()");
}
}
An element in a stack trace, as returned by Throwable.getStackTrace(). Each element represents a single stack
frame. All stack frames except for the one at the top of the stack represent a method invocation. The frame at
the top of the stack represents the the execution point at which the stack trace was generated. Typically, this is
the point at which the throwable corresponding to the stack trace was created.
public final class StackTraceElement
extends Object
implements Serializable
Using Assertions
Assertions are a commonly used idiom for defensive programming. Suppose you are convinced that a
particular property is fulfilled, and you rely on that property in your code. For example, you may be
computing
double y = Math.sqrt(x);
You are certain that x is not negative. Perhaps it is the result of another computation that can’t have a negative
result, or it is a parameter of a method that requires its callers to supply only positive inputs. Still, you want to
double-check rather than having confusing “not a number” floating-point values creep into your computation.
You could, of course, throw an exception:
if (x < 0) throw new IllegalArgumentException("x < 0");
But this code stays in the program, even after testing is complete. If you have lots of checks of this kind, the
program runs quite a bit slower than it should. The assertion mechanism allows you to put in checks during
testing and to have them automatically removed in the production code.
As of Java SE 1.4, the Java language has a keyword assert. There are two forms: assert condition;
And
assert condition : expression;
Both statements evaluate the condition and throw an AssertionError if it is false. In the second statement, the
expression is passed to the constructor of the AssertionError object and turned into a message string. To assert
that x is nonnegative, you can simply use the statement
assert x >= 0;
Or you can pass the actual value of x into the AssertionError object, so that it gets displayed later.
assert x >= 0 : x;
Assertion Enabling and Disabling
By default, assertions are disabled. You enable them by running the program with the -enableassertions or -ea
option:
java -enableassertions MyApp
28
Note that you do not have to recompile your program to enable or disable assertions. Enabling or disabling
assertions is a function of the class loader. When assertions are disabled, the class loader strips out the
assertion code so that it won’t slow execution. You can even turn on assertions in specific classes or in entire
packages. For example:
java -ea:MyClass -ea:com.mycompany.mylib... MyApp
This command turns on assertions for the class MyClass and all classes in the com.mycompany.mylib package
and its subpackages. The option -ea... turns on assertions in all classes of the default package.
You can also disable assertions in certain classes and packages with the –disableassertions or -da option:
java -ea:... -da:MyClass MyApp
Some classes are not loaded by a class loader but directly by the virtual machine. You can use these switches
to selectively enable or disable assertions in those classes. However, the -ea and -da switches that enable or
disable all assertions do not apply to the “system classes” without class loaders. Use the enablesystemassertions/-esa switch to enable assertions in system classes.
It is also possible to programmatically control the assertion status of class loaders.
Using Assertions for Parameter Checking
The Java language gives you three mechanisms to deal with system failures:
• Throwing an exception
• Logging
• Using assertions
When should you choose assertions? Keep these points in mind:
• Assertion failures are intended to be fatal, unrecoverable errors.
• Assertion checks are turned on only during development and testing. (This is sometimes jokingly described
as “wearing a life jacket when you are close to shore, and throwing it overboard once you are in the middle of
the ocean.”)
Therefore, you would not use assertions for signaling recoverable conditions to another part of the program or
for communicating problems to the program user. Assertions should only be used to locate internal program
errors during testing. Let’s look at a common scenario—the checking of method parameters. Should you use
assertions to check for illegal index values or null references? To answer that question, you have to look at the
documentation of the method. Suppose you implement a sorting
method. /**
Sorts the specified range of the specified array into ascending numerical order.
The range to be sorted extends from fromIndex, inclusive, to toIndex, exclusive.
@param a the array to be sorted.
@param fromIndex the index of the first element (inclusive) to be sorted.
@param toIndex the index of the last element (exclusive) to be sorted.
@throws IllegalArgumentException if fromIndex > toIndex
@throws ArrayIndexOutOfBoundsException if fromIndex < 0 or toIndex > a.length
*/
static void sort(int[] a, int fromIndex, int toIndex)
The documentation states that the method throws an exception if the index values are incorrect. That behavior
is part of the contract that the method makes with its callers. If you implement the method, you have to respect
that contract and throw the indicated exceptions. It would not be appropriate to use assertions instead. Should
you assert that a is not null? That is not appropriate either. The method documentation is silent on the
behavior of the method when a is null. The callers have the right to
assume that the method will return successfully in that case and not throw an assertion error.
However, suppose the method contract had been slightly different:
@param a the array to be sorted. (Must not be null)
Now the callers of the method have been put on notice that it is illegal to call the method with a null array.
Then the method may start with the assertion
assert a != null;
Computer scientists call this kind of contract a precondition. The original method had no preconditions on its
parameters—it promised a well-defined behavior in all cases. The revised method has a single precondition:
that a is not null. If the caller fails to fulfill the precondition, then all bets are off and the method can do
anything it wants. In fact, with the assertion in place, the method has a rather unpredictable behavior when it
is called illegally. It sometimes throws an assertion error, and sometimes a null pointer exception, depending
on how its class loader is configured.
Using Assertions for Documenting Assumptions
29
Many programmers use comments to document their underlying assumptions. Consider this example from
http://java.sun.com/javase/6/docs/technotes/guides/language/assert.html:
if (i % 3 == 0)
...
else if (i % 3 == 1)
...
else // (i % 3 == 2)
...
In this case, it makes a lot of sense to use an assertion instead.
if (i % 3 == 0)
...
else if (i % 3 == 1)
...
else
{
assert i % 3 == 2;
...
}
Of course, it would make even more sense to think through the issue a bit more thoroughly. What are the
possible values of i % 3? If i is positive, the remainders must be 0, 1, or 2. If i is negative, then the remainders
can be −1 or −2. Thus, the real assumption is that i is not negative. A better assertion would be
assert i >= 0;
before the if statement. At any rate, this example shows a good use of assertions as a self-check for the
programmer.
As you can see, assertions are a tactical tool for testing and debugging. In contrast, logging is a strategic tool
for the entire life cycle of a program.
An assertion is a statement in the JavaTM programming language that enables you to test your assumptions
about your program. For example, if you write a method that calculates the speed of a particle, you might
assert that the calculated speed is less than the speed of light. Each assertion contains a boolean expression
that you believe will be true when the assertion executes. If it is not true, the system will throw an error. By
verifying that the boolean expression is indeed true, the assertion confirms your assumptions about the
behavior of your program, increasing your confidence that the program is free of errors.
Introduction
The assertion statement has two forms. The first, simpler form is:
assert Expression1 ;
where Expression1 is a boolean expression. When the system runs the assertion, it evaluates Expression1 and
if it is false throws an AssertionError with no detail message. The second form of the assertion statement is:
assert Expression1 : Expression2 ;
where:
Expression1 is a boolean expression.
Expression2 is an expression that has a value. (It cannot be an invocation of a method that is declared
void.)
Use this version of the assert statement to provide a detail message for the AssertionError. The system passes
the value of Expression2 to the appropriate AssertionError constructor, which uses the string representation of
the value as the error's detail message. The purpose of the detail message is to capture and communicate the
details of the assertion failure. The message should allow you to diagnose and ultimately fix the error that led
the assertion to fail. Note that the detail message is not a user-level error message, so it is generally
unnecessary to make these messages understandable in isolation, or to internationalize them.
The detail message is meant to be interpreted in the context of a full stack trace, in conjunction with
the source code containing the failed assertion. Like all uncaught exceptions, assertion failures are generally
labeled in the stack trace with the file and line number from which they were thrown. The second form of the
assertion statement should be used in preference to the first only when the program has some additional
information that might help diagnose the failure.
For example, if Expression1 involves the relationship between two variables x and y, the second form
should be used. Under these circumstances, a reasonable candidate for Expression2 would be "x: " + x + ", y:
" + y. In some cases Expression1 may be expensive to evaluate. For example, suppose you write a method to
find the minimum element in an unsorted list, and you add an assertion to verify that the selected element is
30
indeed the minimum. The work done by the assert will be at least as expensive as the work done by the
method itself. To ensure that assertions are not a performance liability in deployed applications, assertions can
be enabled or disabled when the program is started, and are disabled by default. Disabling assertions
eliminates their performance penalty entirely. Once disabled, they are essentially equivalent to empty
statements in semantics and performance. See Enabling and Disabling Assertions for more information. The
addition of the assert keyword to the Java programming language has implications for existing code. See
Compatibility With Existing Programs for more information.
Putting Assertions Into Your Code
There are many situations where it is good to use assertions. This section covers some of them:
· Internal Invariants
· Control-Flow Invariants
· Preconditions, Postconditions, and Class Invariants
There are also a few situations where you should not use them:
· Do not use assertions for argument checking in public methods. Argument checking is typically part of the
published specifications (or contract) of a method, and these specifications must be obeyed whether assertions
are enabled or disabled. Another problem with using assertions for argument checking is that erroneous
arguments should result in an appropriate runtime exception (such as IllegalArgumentException,
IndexOutOfBoundsException, or NullPointerException). An assertion failure will not throw an appropriate
exception.
· Do not use assertions to do any work that your application requires for correct operation. Because assertions
may be disabled, programs must not assume that the boolean expression contained in an assertion will be
evaluated. Violating this rule has dire consequences. For example, suppose you wanted to remove all of the
null elements from a list names, and knew that the list contained one or more nulls. It would be wrong to do
this:
// Broken! - action is contained in assertion
assert names.remove(null);
The program would work fine when asserts were enabled, but would fail when they were disabled, as it would
no longer remove the null elements from the list. The correct idiom is to perform the action before the
assertion and then assert that the action succeeded:
// Fixed - action precedes assertion
boolean nullsRemoved = names.remove(null);
assert nullsRemoved; // Runs whether or not asserts are enabled As a rule, the expressions contained in
assertions should be free of side effects: evaluating the expression should not affect any state that is visible
after the evaluation is complete. One exception to this rule is that assertions can modify state that is used only
from within other assertions. An idiom that makes use of this exception is presented later in this document.
Internal Invariants
Before assertions were available, many programmers used comments to indicate their assumptions concerning
a program's behavior. For example, you might have written something like this to explain your assumption
about an else clause in a multiway if-statement:
if (i % 3 == 0) {
...
} else if (i % 3 == 1) {
...
} else { // We know (i % 3 == 2)
...
}
You should now use an assertion whenever you would have written a comment that asserts an invariant.
For example, you should rewrite the previous if-statement like this:
if (i % 3 == 0) {
...
} else if (i % 3 == 1) {
...
} else {
assert i % 3 == 2 : i;
...
}
Note, incidentally, that the assertion in the above example may fail if i is negative, as the % operator is not a
true modulus operator, but computes the remainder, which may be negative. Another good candidate for an
31
assertion is a switch statement with no default case. The absence of a default case typically indicates that a
programmer believes that one of the cases will always be executed. The assumption that a particular variable
will have one of a small number of values is an invariant that should be checked with an assertion. For
example, suppose the following switch statement appears in a program that handles playing cards:
switch(suit) {
case Suit.HEARTS:
case Suit.CLUBS:
...
...
break;
break;
case Suit.SPADES:
case Suit.DIAMONDS:
...
...
}
break;
It probably indicates an assumption that the suit variable will have one of only four values. To test this
assumption, you should add the following default case:
default:
assert false : suit;
If the suit variable takes on another value and assertions are enabled, the assert will fail and an AssertionError
will be thrown.
An acceptable alternative is:
default:
throw new AssertionError(suit);
This alternative offers protection even if assertions are disabled, but the extra protection adds no cost: the
throw statement won't execute unless the program has failed. Moreover, the alternative is legal under some
circumstances where the assert statement is not. If the enclosing method returns a value, each case in the
switch statement contains a return statement, and no return statement follows the switch statement, then it
would cause a syntax error to add a default case with an assertion. (The method would return without a value
if no case matched and assertions were disabled.)
Control-Flow Invariants
The previous example not only tests an invariant, it also checks an assumption about the application's flow of
control. The author of the original switch statement probably assumed not only that the suit variable would
always have one of four values, but also that one of the four cases would always be executed. It points out
another general area where you should use assertions: place an assertion at any location you assume will
not be reached. The assertions statement to use is:
assert false;
For example, suppose you have a method that looks like this:
void foo() {
for (...) {
if (...)
return;
}
// Execution should never reach this point!!!
}
Replace the final comment so that the code now reads:
void foo() {
for (...) {
if (...)
return;
}
assert false; // Execution should never reach this point!
}
Note: Use this technique with discretion. If a statement is unreachable as defined in the Java Language
Specification ( JLS 14.20), you will get a compile time error if you try to assert that it is not reached. Again,
an acceptable alternative is simply to throw an AssertionError. Preconditions, Postconditions, and Class
Invariants
While the assert construct is not a full-blown design-by-contract facility, it can help support an informal
design-by-contract style of programming. This section shows you how to use asserts
for:
· Preconditions — what must be true when a method is invoked.
o Lock-Status Preconditions — preconditions concerning whether or not a given
32
lock is held.
· Postconditions — what must be true after a method completes successfully.
· Class invariants — what must be true about each instance of a class.
Preconditions
By convention, preconditions on public methods are enforced by explicit checks that throw particular,
specified exceptions. For example:
/**
* Sets the refresh rate.
*
* @param rate refresh rate, in frames per second.
* @throws IllegalArgumentException if rate <= 0 or
* rate > MAX_REFRESH_RATE.
*/
public void setRefreshRate(int rate) {
// Enforce specified precondition in public method
if (rate <= 0 || rate > MAX_REFRESH_RATE)
throw new IllegalArgumentException("Illegal rate: " + rate);
setRefreshInterval(1000/rate);
}
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the
parameters of a public method. An assert is inappropriate because the method guarantees that it will always
enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the
assert construct does not throw an exception of the specified type. It can throw only an AssertionError. You
can, however, use an assertion to test a nonpublic method's precondition that you believe will be true no
matter what a client does with the class. For example, an assertion is appropriate in the following "helper
method" that is invoked by the previous method:
/**
* Sets the refresh interval (which must correspond to a legal frame
rate).
*
* @param interval refresh interval in milliseconds.
*/
private void setRefreshInterval(int interval) {
// Confirm adherence to precondition in nonpublic method
assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;
... // Set the refresh interval
}
Note, the above assertion will fail if MAX_REFRESH_RATE is greater than 1000 and the client selects a
refresh rate greater than 1000. This would, in fact, indicate a bug in the library!
Lock-Status Preconditions
Classes designed for multithreaded use often have non-public methods with preconditions relating to whether
or not some lock is held. For example, it is not uncommon to see something like this:
private Object[] a;
public synchronized int find(Object key) {
return find(key, a, 0, a.length);
}
// Recursive helper method - always called with a lock on this object
private int find(Object key, Object[] arr, int start, int len) {
...
}
A static method called holdsLock has been added to the Thread class to test whether the current thread holds
the lock on a specified object. This method can be used in combination with an assert statement to supplement
a comment describing a lock-status precondition, as shown in the following example:
// Recursive helper method - always called with a lock on this.
private int find(Object key, Object[] arr, int start, int len) {
assert Thread.holdsLock(this); // lock-status assertion
...
} Note that it is also possible to write a lock-status assertion asserting that a given lock isn't held.
33
Postconditions
You can test postcondition with assertions in both public and nonpublic methods. For example, the following
public method uses an assert statement to check a post condition:
/**
* Returns a BigInteger whose value is (this-1 mod m).
*
* @param m the modulus.
* @return this-1 mod m.
* @throws ArithmeticException m <= 0, or this BigInteger
* has no multiplicative inverse mod m (that is, this BigInteger
* is not relatively prime to m).
*/
public BigInteger modInverse(BigInteger m) {
if (m.signum <= 0)
throw new ArithmeticException("Modulus not positive: " + m);
... // Do the computation
assert this.multiply(result).mod(m).equals(ONE) : this;
return result;
}
Occasionally it is necessary to save some data prior to performing a computation in order to check a
postcondition. You can do this with two assert statements and a simple inner class that saves the state of one
or more variables so they can be checked (or rechecked) after the computation. For example, suppose you
have a piece of code that looks like this:
void foo(int[] array) {
// Manipulate array
...
// At this point, array will contain exactly the ints that it did
// prior to manipulation, in the same order.
}
Class Invariants
A class invariants is a type of internal invariant that applies to every instance of a class at all times, except
when an instance is in transition from one consistent state to another. A class invariant can specify the
relationships among multiple attributes, and should be true before and after any method completes. For
example, suppose you implement a balanced tree data structure of some sort. A class invariant might be that
the tree is balanced and properly ordered.
The assertion mechanism does not enforce any particular style for checking invariants. It is sometimes
convenient, though, to combine the expressions that check required constraints into a single internal method
that can be called by assertions. Continuing the balanced tree example, it might be appropriate to implement a
private method that checked that the tree was indeed balanced as per the dictates of the data structure:
// Returns true if this tree is properly balanced
private boolean balanced() {
...
}
Because this method checks a constraint that should be true before and after any method completes, each
public method and constructor should contain the following line immediately prior to its return:
assert balanced();
It is generally unnecessary to place similar checks at the head of each public method unless the data structure
is implemented by native methods. In this case, it is possible that a memory corruption bug could corrupt a
"native peer" data structure in between method invocations. A failure of the assertion at the head of such a
method would indicate that such memory corruption had occurred. Similarly, it may be advisable to include
class invariant checks at the heads of methods in classes whose state is modifiable by other classes.
Logging
Every Java programmer is familiar with the process of inserting calls to System.out.println into troublesome
code to gain insight into program behavior. Of course, once you have figured out the cause of trouble, you
remove the print statements, only to put them back in when the next problem surfaces. The logging API is
designed to overcome this problem.
Here are the principal advantages of the API:
34
• It is easy to suppress all log records or just those below a certain level, and just as easy to turn them back on.
• Suppressed logs are very cheap, so that there is only a minimal penalty for leaving the logging code in your
application.
• Log records can be directed to different handlers, for display in the console, for storage in a file, and so on.
• Both loggers and handlers can filter records. Filters discard boring log entries, using any criteria supplied by
the filter implementor.
• Log records can be formatted in different ways, for example, in plain text or XML.
• Applications can use multiple loggers, with hierarchical names such as com.mycompany.myapp, similar to
package names.
• By default, the logging configuration is controlled by a configuration file. Applications can replace this
mechanism if desired.
Basic Logging
Let’s get started with the simplest possible case. The logging system manages a default logger Logger.global
that you can use instead of System.out. Use the info method to log an information message:
Logger.global.info("File->Open menu item selected");
By default, the record is printed like this:
May 10, 2004 10:12:15 PM LoggingImageViewer fileOpen INFO: File->Open menu item selected (Note that
the time and the names of the calling class and method are automatically included.) But if you call
Logger.global.setLevel(Level.OFF); at an appropriate place (such as the beginning of main), then all logging
is suppressed.
Advanced Logging
Now that you have seen “logging for dummies,” let’s go on to industrial-strength logging. In a professional
application, you wouldn’t want to log all records to a single global logger. Instead, you can define your own
loggers. When you request a logger with a given name for the first time, it is created.
Logger myLogger = Logger.getLogger("com.mycompany.myapp");
Subsequent calls to the same name yield the same logger object. Similar to package names, logger names are
hierarchical. In fact, they are more hierarchical than packages. There is no semantic relationship between a
package and its parent, but logger parents and children share certain properties. For example, if you set the log
level on the logger "com.mycompany", then the child loggers inherit that level. There are seven logging
levels:
• SEVERE
• INFO
• FINE
• FINER
• WARNING
• CONFIG
• FINEST
By default, the top three levels are actually logged. You can set a different level, for example,
logger.setLevel(Level.FINE);
Now all levels of FINE and higher are logged.You can also use Level.ALL to turn on logging for all levels or
Level.OFF to turn all logging off. There are logging methods for all levels, such as
logger.warning(message);
logger.fine(message);
and so on. Alternatively, you can use the log method and supply the level, such as logger.log(Level.FINE,
message);
The default log record shows the name of the class and method that contain the logging call, as inferred from
the call stack. However, if the virtual machine optimizes execution, accurate call information may not be
available. You can use the logp method to give the precise location of the calling class and method. The
method signature is
void logp(Level l, String className, String methodName, String message)
There are convenience methods for tracing execution flow:
void entering(String className, String methodName)
void entering(String className, String methodName, Object param)
void entering(String className, String methodName, Object[] params)
void exiting(String className, String methodName)
void exiting(String className, String methodName, Object result)
For example:
int read(String file, String pattern)
{
logger.entering("com.mycompany.mylib.Reader", "read",
new Object[] { file, pattern });
...
logger.exiting("com.mycompany.mylib.Reader", "read", count);
35
return count;
}
These calls generate log records of level FINER that start with the strings ENTRY and RETURN.
A common use for logging is to log unexpected exceptions. Two convenience methods include a description
of the exception in the log record.
void throwing(String className, String methodName, Throwable t)
void log(Level l, String message, Throwable t)
Typical uses are
if (. . .)
{
{
...
IOException exception = new IOException(". . .");
}
logger.throwing("com.mycompany.mylib.Reader",
catch (IOException e)
"read", exception);
{
throw exception;
Logger.getLogger("com.mycompany.myapp").log(
}
Level.WARNING, "Reading image", e);
and
}
try
The throwing call logs a record with level FINER and a message that starts with THROW.
Changing the Log Manager Configuration
You can change various properties of the logging system by editing a configuration file. The default
configuration file is located at
jre/lib/logging.properties To use another file, set the java.util.logging.config.file property to the file location
by starting your application with java -Djava.util.logging.config.file=configFile MainClass
To change the default logging level, edit the configuration file and modify the line .level=INFO. You can
specify the logging levels for your own loggers by adding lines such as com.mycompany.myapp.level=FINE.
That is, append the .level suffix to the logger name. As you see later in this section, the loggers don’t actually
send the messages to the console— that is the job of the handlers. Handlers also have levels. To see FINE
messages on the console, you also need to set java.util.logging.ConsoleHandler.level=FINE
Localization
You may want to localize logging messages so that they are readable for international users.
Internationalization of applications is the topic of Chapter 5 of Volume II. Briefly, here are the points to keep
in mind when localizing logging messages. Localized applications contain locale-specific information in
resource bundles. A resource bundle consists of a set of mappings for various locales (such as United States or
Germany). For example, a resource bundle may map the string "reading- File" into strings "Reading file" in
English or "Achtung! Datei wird eingelesen" in German. A program may contain multiple resource bundles,
perhaps one for menus and another for log messages. Each resource bundle has a name (such as
"com.mycompany. logmessages"). To add mappings to a resource bundle, you supply a file for each locale.
English message mappings are in a file com/mycompany/logmessages_en.properties, and German message
mappings are in a file com/mycompany/logmessages_de.properties. (The en, de codes are the language
codes.) You place the files together with the class files of your application, so that the ResourceBundle class
will automatically locate them. These files are plain text files, consisting of entries such as
readingFile=Achtung! Datei wird eingelesen
renamingFile=Datei wird umbenannt
...
When requesting a logger, you can specify a resource bundle:
Logger logger = Logger.getLogger(loggerName, "com.mycompany.logmessages");
Then you specify the resource bundle key, not the actual message string, for the log message.
logger.info("readingFile");
You often need to include arguments into localized messages. Then the message should contain placeholders
{0}, {1}, and so on. For example, to include the file name with a log message, include the placeholder like
this:
Reading file {0}.
Achtung! Datei {0} wird eingelesen.
You then pass values into the placeholders by calling one of the following methods:
logger.log(Level.INFO, "readingFile", fileName);
logger.log(Level.INFO, "renamingFile", new Object[] { oldName, newName });
Handlers
36
By default, loggers send records to a ConsoleHandler that prints them to the System.err stream. Specifically,
the logger sends the record to the parent handler, and the ultimate ancestor (with name "") has a
ConsoleHandler. Like loggers, handlers have a logging level. For a record to be logged, its logging level must
be above the threshold of both the logger and the handler. The log manager configuration file sets the logging
level of the default console handler as java.util.logging.ConsoleHandler.level=INFO
To log records with level FINE, change both the default logger level and the handler level in the
configuration. Alternatively, you can bypass the configuration file altogether and install your own handler.
Logger logger = Logger.getLogger("com.mycompany.myapp");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(false);
Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);
By default, a logger sends records both to its own handlers and the handlers of the parent. Our logger is a child
of the primordial logger (with name "") that sends all records with level INFO or higher to the console. But we
don’t want to see those records twice. For that reason, we set the useParentHandlers property to false. To send
log records elsewhere, add another handler. The logging API provides two useful handlers for this purpose, a
FileHandler and a SocketHandler. The SocketHandler sends records to a specified host and port. Of greater
interest is the FileHandler that collects records in a file.You can simply send records to a default file handler,
like this:
FileHandler handler = new FileHandler();
logger.addHandler(handler);
The records are sent to a file javan.log in the user’s home directory, where n is a number to make the file
unique. If a user’s system has no concept of the user’s home directory (for example, in Windows 95/98/Me),
then the file is stored in a default location such as C:\Windows. By default, the records are formatted in XML.
A typical log record has the form
<record>
<date>2002-02-04T07:45:15</date>
<millis>1012837515710</millis>
<sequence>1</sequence>
<logger>com.mycompany.myapp</logger>
<level>INFO</level>
<class>com.mycompany.mylib.Reader</class>
<method>read</method>
<thread>10</thread>
<message>Reading file corejava.gif</message>
</record>
You can modify the default behavior of the file handler by setting various parameters in the log manager
configuration (see Table 11–2), or by using another constructor. You probably don’t want to use the default
log file name. Therefore, you should use
another pattern, such as %h/myapp.log. (See Table 11–3 for an explanation of the pattern variables.) If
multiple applications (or multiple copies of the same application) use the same log file, then you should turn
the “append” flag on. Alternatively, use %u in the file name pattern so that each application creates a unique
copy of the log. It is also a good idea to turn file rotation on. Log files are kept in a rotation sequence, such as
myapp.log.0, myapp.log.1, myapp.log.2, and so on. Whenever a file exceeds the size
limit, the oldest log is deleted, the other files are renamed, and a new file with generation number 0 is created.
You can also define your own handlers by extending the Handler or the StreamHandler class. We define such
a handler in the example program at the end of this section. That handler displays the records in a window (see
Figure 11–2). The handler extends the StreamHandler class and installs a stream whose write methods display
the stream output in a text area.
class WindowHandler extends StreamHandler
{
{
public void write(int b) {} // not called
public WindowHandler()
public void write(byte[] b, int off, int len)
{
{
...
output.append(new String(b, off, len));
final JTextArea output = new JTextArea();
}
setOutputStream(new
});
OutputStream()
}
37
...
}
There is just one problem with this approach—the handler buffers
the records and only writes them to the stream when the buffer is
full. Therefore, we override the publish method to flush the buffer
after each record:
class WindowHandler extends StreamHandler
{
...
public void publish(LogRecord record)
{
super.publish(record);
flush();
}
}
If you want to write more exotic stream handlers, extend the Handler
class and define the publish, flush, and close methods.
Logging:
The JDK contains the "Java Logging API". Via a logger you can save text to a central place to report on
errors, provide additional information about your program, etc. This logging API allows to configure how
messages are written by which class with which priority Overview of Control Flow Applications make
logging calls on Logger objects. Loggers are organized in a hierarchical namespace and child Loggers may
inherit some logging properties from their parents in the namespace. Applications make logging calls on
Logger objects. These Logger objects allocate LogRecord objects which are passed to Handler objects for
publication. Both Loggers and Handlers may use logging Levels and (optionally) Filters to decide if they are
interested in a particular LogRecord. When it is necessary to publish a LogRecord externally, a Handler can
(optionally) use a Formatter to localize and format the message before publishing it to an I/O stream.
Each Logger keeps track of a set of output Handlers. By default all Loggers also send their output to their
parent Logger. But Loggers may also be configured to ignore Handlers higher up the tree. Some Handlers may
direct output to other Handlers. For example, the MemoryHandler maintains an internal ring buffer of
LogRecords and on trigger events it publishes its LogRecords through a target Handler. In such cases, any
formatting is done by the last Handler in the chain.
The APIs are structured so that calls on the Logger APIs can be cheap when logging is disabled. If logging is
disabled for a given log level, then the Logger can make a cheap comparison test and return. If logging is
enabled for a given log level, the Logger is still careful to minimize costs before passing the LogRecord into
the Handlers. In particular, localization and formatting (which are relatively expensive) are deferred until the
Handler requests them. For example, a MemoryHandler can maintain a circular buffer of LogRecords without
having to pay formatting costs.
Log Levels
Each log message has an associated log Level. The Level gives a rough guide to the importance and urgency
of a log message. Log level objects encapsulate an integer value, with higher values indicating higher
priorities. The Level class defines seven standard log levels, ranging from FINEST (the lowest priority, with
the lowest value) to SEVERE (the highest priority, with the highest value).
Loggers
As stated earlier, client code sends log requests to Logger objects. Each logger keeps track of a log level that it
is interested in, and discards log requests that are below this level. Loggers are normally named entities, using
38
dot-separated names such as "java.awt". The namespace is hierarchical and is managed by the LogManager.
The namespace should typically be aligned with the Java packaging namespace, but is not required to follow it
slavishly. For example, a Logger called "java.awt" might handle logging requests for classes in the java.awt
package, but it might also handle logging for classes in sun.awt that support the client-visible abstractions
defined in the java.awt package.
In addition to named Loggers, it is also possible to create anonymous Loggers that don't appear in the shared
namespace. See section 1.14. Loggers keep track of their parent loggers in the logging namespace. A logger's
parent is its nearest extant ancestor in the logging namespace. The root Logger (named "") has no parent.
Anonymous loggers are all given the root logger as their parent. Loggers may inherit various attributes from
their parents in the logger namespace. In particular, a logger may inherit:
obtained by walking up the parent tree and using the first non-null Level.
up the tree.
name defined for its parent, and so on recursively up the tree.
Logging Methods
The Logger class provides a large set of convenience methods for generating log messages. For convenience,
there are methods for each logging level, named after the logging level name. Thus rather than calling
"logger.log(Constants.WARNING,..." a developer can simply call the convenience method
"logger.warning(..." There are two different styles of logging methods, to meet the needs of different
communities of users. First, there are methods that take an explicit source class name and source method
name. These methods are intended for developers who want to be able to quickly locate the source of any
given logging message. An example of this style is: void warning(String sourceClass, String sourceMethod,
String msg); Second, there are a set of methods that do not take explicit source class or source method names.
These are intended for developers who want easy-to-use logging and do not require detailed source
information. void warning(String msg); For this second set of methods, the Logging framework will make a
"best effort" to determine which class and method called into the logging framework and will add this
information into the LogRecord. However, it is important to realize that this automatically inferred
information may only be approximate. The latest generation of virtual machines perform extensive
optimizations when JITing and may entirely remove stack frames, making it impossible to reliably locate the
calling class and method. Handlers
StreamHandler: A simple handler for writing formatted records to
an OutputStream.
ConsoleHandler: A simple handler for writing formatted records to System.err
FileHandler: A handler that writes formatted log records either to a single file, or to a set of rotating log
files.
SocketHandler: A handler that writes formatted log records to remote TCP ports.
MemoryHandler: A handler that buffers log records in memory.
It is fairly straightforward to develop new Handlers. Developers requiring specific functionality can either
develop a Handler from scratch or subclass one of the provided Handlers.
Formatters
SimpleFormatter: Writes brief "human-readable" summaries
of log records.
XMLFormatter: Writes detailed XML-structured information.
As with Handlers, it is fairly straightforward to develop new Formatters.
The LogManager
hierarchical namespace of named Loggers.
et of logging control properties read from the configuration file.
method. This is created during LogManager initialization, based on a system property. This property allows
container applications (such as EJB containers) to substitute their own subclass of LogManager in place of the
default class.
Create a logger
39
The package java.util.logging provides the logging capabilities via the class Logger. To create a logger in your
Java coding.
import java.util.logging.Logger;
private final static Logger LOGGER = Logger.getLogger(MyClass.class .getName());
Level The log levels define the severity of a message. The class Level is used to define which messages
In addition to that you have also the levels OFF and ALL to turn the logging of or to log everything. For
example the following will set the logger to level info which means all messages with severe, warning and
info will be logged. LOGGER.setLevel(Level.INFO);
An Example
To help motivate the use of the logging API, we will use a simple program, based on the idea of a "Hello
World" program.
public class HelloWorld
public HelloWorld(String message)
{
{
public static void main(String[] args)
theMessage = message;
{
}
HelloWorld hello = new HelloWorld("Hello
public void sayHello()
world!");
{
hello.sayHello();
System.err.println(theMessage);
}
}
private String theMessage;
}
We will expand upon this simple program in order to demonstrate and motivate the logging API. The first
thing we will do is actually generate a logging message. This is straightforward; we need to create a Logger
object, which represents theHelloWorld class, like this:
package logging.example2;
import java.util.logging.Logger;
/** * A simple Java Hello World program, in the tradition of * Kernighan and Ritchie. */
public class HelloWorld
private String theMessage;
{
public HelloWorld(String message)
private
static
Logger
theLogger
=
{
Logger.getLogger(HelloWorld.class.getName());
theMessage = message;
public static void main(String[] args)
}
{
public void sayHello()
HelloWorld hello = new HelloWorld("Hello
{
world!");
System.err.println(theMessage);
hello.sayHello();
}
}
}
This generates a Logger, which we can use to generate logging messages. Since you typically use one logger
per class, we can make it a static field of the HelloWorld class. We use the fully qualified name of the
HelloWorld class as the name of theLogger. We'll look at the reasons for this in more detail later. Now, let's
actually use the Logger to try to generate a logging message:
import java.util.logging.Logger;
public class HelloWorld
public HelloWorld(String message)
{
{
private
static
Logger
theLogger
=
theMessage = message;
Logger.getLogger(HelloWorld.class.getName());
}
public static void main(String[] args)
public void sayHello()
{
{
HelloWorld hello = new HelloWorld("Hello
theLogger.finest("Hello logging!");
world!");
System.err.println(theMessage);
hello.sayHello();
}
}
}
private String theMessage;
The logging API has a configuration file and by default, it provides a handler that will send logging messages,
in the form of aLogRecord to the console (System.err). So we would expect to see the string "Hello logging!";
appear before our message of "Hello world!". But if you run this program you only see "Hello world!":
40
% java logging.example3.HelloWorld Hello world What's going on here? The answer lies in the notion of
logging levels.
Assertions An assertion has a Boolean expression that, if evaluated as false, indicates a bug in the code. This
mechanism provides a way to detect when a program starts falling into an inconsistent state. Assertions are
excellent for documenting assumptions and invariants about a class. Here is a simple example of assertion:
BankAccount acct = null;
// ...
// Get a BankAccount object
// ...
// Check to ensure we have one
assert acct != null;
This asserts that acct is not null. If acct is null, an AssertionError is thrown. Any line that executes after the
assert statement can safely assume that acct is not null. Using assertions helps developers write code that is
more correct, more readable, and easier to maintain. Thus, assertions improve the odds that the behavior of a
class matches the expectations of its clients. Note that assertions can be compiled out. In languages such as
C/C++, this means using the preprocessor. In C/C++, you can use assertions through the assert macro, which
has the following definition in ANSI C:
void assert(int expression)
The program will be aborted if the expression evaluates to false, and it has no effect if the expression
evaluates to true. When testing and debugging is completed, assertions do not have to be removed from the
program. However, note that the program will be larger in size and therefore slower to load. When assertions
are no longer needed, the line
#define NDEBUG
is inserted at the beginning of the program. This causes the C/C++ preprocessor to ignore all assertions,
instead of deleting them manually. In other words, this is a requirement for performance reasons. You should
write assertions into software in a form that can be optionally compiled. Thus, assertions should be executed
with the code only when you are debugging your program -- that is, when assertions will really help flush out
errors. You can think of assertions as a uniform mechanism that replaces the use of ad hoc conditional tests.
Implementing Assertions in Java Technology
J2SE 1.3 and earlier versions have no built-in support for assertions.
They can, however, be provided as an ad hoc solution. Here is an example of how you would roll your own
assertion class. Here we have an assert method that checks whether a Boolean expression is true or false. If the
expression evaluates to true, then there is no effect. But if it evaluates to false, the assert method prints the
stack trace and the program aborts. In this sample implementation, a second argument for a string is used so
that the cause of error can be printed. Note that in the assert method, I am checking whether the value of
NDEBUG is on (true) or off (false). If NDEBUG sets to true, then the assertion is to be executed. Otherwise,
it would have no effect. The user of this class is able to set assertions on or off by toggling the value of
NDEBUG. Code Sample 1 shows my implementation.
public class Assertion
public static void assert(boolean expression, String
{
why)
public static boolean NDEBUG = true;
{
private static void printStack(String why)
if (NDEBUG && !expression)
{
{
Throwable t = new Throwable(why);
printStack(why);
t.printStackTrace();
}
System.exit(1);
}
}
}
Note: In order for Code Sample 1 to compile, use -source 1.3 because assert is a keyword as of J2SE 1.4.
Otherwise, you will get the following error message:
C:\CLASSES>javac Assertion.java
Assertion.java:11: as of release 1.4, 'assert' is a keyword, and may not be used as an identifier
(try -source 1.3 or lower to use 'assert' as an identifier)
public static ^ void assert(boolean expression, String why) {
1 error
41
Code Sample 2 demonstrates how to use the Assertion class. In this example, an integer representing the
user's age is read. If the age is greater than or equal to 18, the assertion evaluates to true, and it will have no
effect on the program execution. But if the age is less than 18, the assertion evaluates to false. The program
then aborts, displays the message You are too young to vote, and shows the stack trace.
It is important to note that in this example assertions are used to validate user input and that no invariant is
being tested or verified. This is merely to demonstrate the use of assertions.
import java.util.Scanner;
int age = reader.nextInt();
import java.io.IOException;
//Assertion.NDEBUG=false;
public class AssertionTest1
Assertion.assert(age>=18, "You are too young to
{
vote");
public static void main(String argv[]) throws
// use age
IOException
System.out.println("You are eligible to vote");
{
}
Scanner reader = new Scanner(System.in);
}
System.out.print("Enter your age: ");
import java.io.IOException;
public class AssertionTest2
case 'S': System.out.println("Single");
{
break;
public static void main(String argv[]) throws
case 'm': case 'M': System.out.println("Married");
IOException
break;
{
case 'd': case 'D': System.out.println("Divorced");
System.out.print("Enter your marital status: ");
break;
int c = System.in.read();
default: Assertion.assert(!true, "Invalid Option");
//Assertion.NDEBUG=false;
break;
switch ((char) c)
}
{
}
case 's':
}
In the three examples, if you do not want assertions to be executed as part of the code, uncomment the line
Assert.NDEBUG = false;
Using Assertions
Use the assert statement to insert assertions at particular points in the code. The assert statement can have one
of two forms:
assert booleanExpression;
assert booleanExpression : errorMessage;
The errorMessage is an optional string message that would be shown when an assertion fails. As an example, I
will modify Code Sample 3 to use the assert statement instead of my Assertion class, as shown in Code
Sample 4.
import java.io.*;
break;
public class AssertionTest3
case 'm': case 'M': System.out.println("Married");
{
break;
public static void main(String argv[]) throws
case 'd': case 'D': System.out.println("Divorced");
IOException
break;
{
default: assert !true : "Invalid Option";
System.out.print("Enter your marital status: ");
break;
int c = System.in.read();
}
switch ((char) c)
}
{c
}
ase 's': case 'S': System.out.println("Single");
prompt> javac -source 1.4 AssertionTest3.java
If you try to compile your assertion-enabled classes without using the -source 1.4 option, you will get a
compiler error saying that assert is a new keyword as of release 1.4.
If you now run the program using the command
prompt> java AssertionTest3
and you enter a valid character, it will work fine. However, if you enter an invalid character, nothing will
happen. This is because, by default, assertions are disabled at runtime. To enable assertions, use the switch enableassertion (or -ea) as follows:
prompt> java -ea AssertionTest3
42
prompt> java -enableassertion AssertionTest3
Following is a sample run:
C:\CLASSES>java -ea AssertionTest3
Enter your marital status: w
Exception
in
thread
"main"
java.lang.AssertionError:
Invalid
Option
at
AssertionTest3.main(AssertionTest3.java:15)
When an assertion fails, it means that the application has entered an incorrect state. Possible behaviors may
include suspending the program or allowing it to continue to run. A good behavior, however, might be to
terminate the application, because it may start functioning inappropriately after a failure. In this case, when an
assertion fails, an AssertionError is thrown.
Note: By default, assertions are disabled, so you must not assume that the Boolean expression contained in an
assertion will be evaluated. Therefore, your expressions must be free of side effects.
The switch -disableassertion (or -da) can be used to disable assertions. This, however, is most useful when
you wish to disable assertions on classes from specific packages. For example, to run the program MyClass
with assertions disabled in class Hello, you can use the following command:
prompt> java -da:com.javacourses.tests.Hello MyClass
And to disable assertions in a specific package and any subpackages it may have, you can use the following
command:
prompt> java -da:com.javacourses.tests... MyClass
Note that the three-dot ellipsis (...) is part of the syntax.
Switches can be combined. For example, to run a program with assertions enabled in the
com.javacourses.tests package (and any subpackages) and disabled in the class com.javacourses.ui.phone, you
can use the following command:
prompt> java -ea:com.javacourses.tests... -da:com.javacourses.ui.phone MyClass
Note that when switches are combined and applied to packages, they are applied to all classes, including
system classes (which do not have class loaders). But if you use them with no arguments (-ea or -da), they do
not apply to system classes. In other words, if you use the command
prompt> java -ea MyClass then assertions are enabled in all classes except system classes. If you wish to turn
assertions on or off in system classes, use the switches -enablesystemassertions (or -esa) and disablesystemassertions (or -dsa).
Using Assertions for Design by Contract
The assertion facility can help you in supporting an informal design-by-contract style of programming. We
will now see examples of using assertions for preconditions, postconditions, and class invariants. The
examples are snippets of code from an integer stack, which provides operations such as push to add an item on
the stack and pop to retrieve an item from the stack.
Preconditions
In order to retrieve an item from the stack, the stack must not be empty. The condition that the stack must not
be empty is a precondition. This precondition can be programmed using assertions as follows:
public int pop()
{
// precondition
assert !isEmpty() : "Stack is empty";
return stack[--num];
}
Note: Because assertions might be disabled in some cases, precondition checking can still be performed by
checks inside methods that result in exceptions such asIllegalArgumentException or NullPointerException.
Postconditions
In order to push an item on the stack, the stack must not be full. This is a precondition. To add an item on the
stack, we assign the element to be added to the next index in the stack as follows:
stack[num++] = element;
However, if you make a mistake and you write this statement as
stack[num] = element;
then you have a bug. In this case, we need to ensure that invoking the push operation is working correctly. So
the postcondition here is to ensure that the new index in the stack is the old index plus one. Also, we need to
make sure that the element has been added on the stack. The following snippet of code shows the push
operation with a precondition and a postcondition.
public void push(int element)
{
43
// precondition
assert num<capacity : "stack is full";
int oldNum = num;
stack[num] = element;
// postcondition
assert num == oldNum+1 && stack[num-1] == element : "problem with counter";
}
Note that if a method has multiple return statements, then postconditions should be evaluated before each of
these return statements.
Class Invariants
A class invariant is a predicate that must be true before and after any method completes. In the stack example,
the invariant would be that the number of elements in the stack is greater than or equal to zero and the number
of elements should not exceed the maximum capacity of the class. These conditions, for example, can be
coded as follows:
private boolean inv()
{
return (num >= 0 && num < capacity);
}
To check that the stack should satisfy this predicate at all times, each public method and constructor should
contain the assertion assert inv(); right before each return.
44
Download