"TableTest" contains the main method for this program.

This example illustrates a number of aspects of the Java programming language.

Firstly, it shows how package definitions operate -- the classes which define
a table have been placed in a separate "table" package.

Secondly, various exception classes have been defined to indicate problems
that may occur when accessing the table -- e.g. trying to retrieve data from
it using an undefined key.

The exceptions directly extend "java.lang.Exception", meaning that they are
so-called 'checked' exceptions and have to be listed in the 'throws' clause
of any method that may raise them.  In this case we have defined methods in 
the "TryTable" class which print an error message if they catch any of these
exceptions.

Other examples use 'unchecked' exceptions which do not have to be listed in
the 'throws' clauses.  An unchecked exception is usually defined by extending
"java.lang.Error" instead of "java.lang.Exception".  Unchecked exceptions
are commonly used for error conditions from which recovery would be impossible.

Beware of writing code that simply masks exceptions, e.g.

   try { 
     o.something();
   } catch (Exception) {
   }

In most cases an exception handler would be expected to either (i) terminate
the program, (ii) perform some kind of clean up or recovery, or (iii) 
propagate a different exception.  There are few cases where doing nothing
is correct.

