I. Nonlinear Control of Flow
- Somewhere, for any executing program, there is a special
references that says what statement is about to be executed
next: the program
counter.
- Programmers (people in general?) think best about
sequences – simple plans, always go to the next step.
- Loops are a little trickier to think about, because they
make the pointer skip around (although in a very determined
way.)
- In the beginning, before loops & subroutines, there
was GOTO
- use a line number of the code
- more sophisticated, use a label
- Used to create iteration, subroutines.
- Assembly code is still like this.
- Example:
mysum = 0
myiterator = 0
LOOP:
if myiterator == 5
goto END
mysum += myiterator
myiterator += 1
goto LOOP
END:
print "mysum = "
mysum
- Once loops & function calls had been invented,
non-linear control was considered inelegant / bad style.
- Generally, this is true. People have more trouble
reading code if it doesn't go in a sequence.
- Loops are self-documenting
- the best ones (for in C, do
in lisp) tend to tell you what their criteria &
index are in the declaration.
- they all clearly demarcate where they begin and end
(with brackets and/or indentation)
- Function/method calls are also self-documenting, they
are abstract & their names should tell you what they
do.
- However, this has always been a little controversial.
- Sometimes making a loop do exactly what you want is just
so messy that jumping out of it might be better.
- But in general, programmers who use GOTO use it because
they haven't thought hard enough about the alternatives,
so GOTO is widely condemned.
- I am sure someone
in this department will think I shouldn't even have
mentioned the notion to you!
- But Apple clearly still uses GOTO... or haven't you
heard of the Apple
GOTO #fail?
- Java has a number of ways of going non-linear
- break & continue,
- only work in loops
- will accept labels as arguments.
- return: can happen anywhere in a method
call, skips the rest of the code.
- error/exception handling (today's main topic).
- thread handling (we'll do this tomorrow).
- Quickly, here's what break & continue do:
- break ends a loop & transfers the flow of control to
whatever comes next.
- continue returns to the top of a loop, skipping any
remaining code in the loop.
- Either command can take an argument
- lets you disambiguate which loop you want to break or
continue if you are in nested loops.
- Labels are similar as above, you just stick a name in
with a colon after it, but it must be right before a
loop starts.
- Here's a page from Sun with some examples:
II. Signals & Errors
- One reason that you might want to go non-linear is if
something else interrupted your program. Examples:
- The machine is being shut down.
- Someone wants to chat to you (this is now usually
handled by the OS/windowing system).
- You want the program to do something every five minutes
(like save it's current state in a file on the hard disk.)
- Someone killed your window with a mouse.
- Here's a list of signals from the "man kill" page
on unix:
Some of the
more commonly used signals:
1 HUP (hang up)
2 INT (interrupt)
3 QUIT (quit)
6 ABRT (abort)
9 KILL (non-catchable,
non-ignorable kill)
14
ALRM (alarm clock)
15
TERM (software termination signal)
- Don't usually want the program to just die exactly when
the event occurs!
- Probably want to save state.
- Always want to:
- close files --- make sure disk is safe,
- kill children / spawned processes (e.g. dependent
sub-windows, like "find").
- May want to go back to what you were doing
after you've handled the situation.
- These problems can always happen, not controversial that
you should be able to handle them.
- Common metaphor in many programming languages is catching
& throwing.
- Something "throws" an exception when an event happens.
- Something else needs to catch it!
III. Throwables
- One reason Java is cool, is that it actually made the
thing you throw into an object.
- In previous languages e.g. C you "throwing an exception"
was sort of an expression (also said "raising an
exception").
- All that got passed around was integers, which had
meanings you could look up (called signals or flags: show
% man kill).
- In Java, things that can throw are subclasses of java.lang.Throwable
- Things that are Throwable are either Exceptions
or Errors.
- Errors tend to be
things you can't do much about, so people don't tend to
check for them in the code
- Either things that come from outside that you can't fix,
or things that are really sort of glorified syntax
errors.
- Examples (from 1.4.1)
AbstractMethodError
ClassCircularityError ClassFormatError IllegalAccessError
IncompatibleClassChangeError InstantiationError
InternalError NoClassDefFoundError NoSuchFieldError NoSuchMethodError
OutOfMemoryError
StackOverflowError ThreadDeath UnknownError
UnsupportedClassVersionError VerifyError
VirtualMachineError
- Sun now has these nested in hierarchies, see e.g. VM
Error http://docs.oracle.com/javase/7/docs/api/java/lang/Error.html
- Exceptions are
things you might want to do something about.
- http://docs.oracle.com/javase/7/docs/api/java/lang/Exception.html
- Exceptions come in two types:
- checked:
you
are obligated to do something about them or the compiler
won't let you compile.
- unchecked:
you
can look for them if you like, it's up to you.
- What's the difference?
- Some kinds of bad things will happen for exogenous
reasons the programmer can't control, but can anticipate.
- e.g. not being able to write to a disk --- could be
full.
- These are checked exceptions.
- Some kinds of bad things will only happen if a
programmer makes a mistake.
- e.g. asking for an illegal index for an array.
- These are unchecked exceptions.
- All unchecked exceptions are a subclass of RuntimeExceptions
(which is a subclass of Exceptions.)
- Examples: ArithmeticException,
ArrayStoreException,
BufferOverflowException, BufferUnderflowException,
CannotRedoException, CannotUndoException, ClassCastException,
CMMException, ConcurrentModificationException,
DOMException, EmptyStackException,
IllegalArgumentException, IllegalMonitorStateException,
IllegalPathStateException, IllegalStateException,
ImagingOpException, IndexOutOfBoundsException,
MissingResourceException, NegativeArraySizeException,
NoSuchElementException, NullPointerException,
ProfileDataException, ProviderException,
RasterFormatException, SecurityException, SystemException,
UndeclaredThrowableException, UnmodifiableSetException,
UnsupportedOperationException
- Arguable whether you should check for unchecked exceptions:
- Do you expect other programmers who don't read your
comments carefully to use your code?
- Do you think you might make a mistake
while you are programming?
- Do you think the user might make a mistake that might
break your code?
- Rather than checking for everything, often specific
checks are inserted during development / debugging, or
based on personal experience of the individual programmer.
- With checked
exceptions, you can actually get away with very
little.
- The least you can do is to declare that the method
throws the exception, thus passing the problem on to the
next calling method.
- If nothing ever actually checks it, then when the
exception gets triggered you will crash the virtual
machine.
- The compiler makes sure you know this can happen
(generally considered unprofessional to crash the VM.)
- More about this below.
IV. Catching Throwables
- The actual catching (and throwing) is done in syntax, not
with objects.
- Keywords:
try {}
catch {}*
finally {}.
- Any checked exception must be performed within a try
clause.
- Any try clause must be followed by one or more catch
clauses, each for a particular error.
- Order matters -- the first applicable catch phrase is
applied.
- So you can (should?) finish with a very general one
- e.g. here's a piece of a program which is letting the
user type an array index to access. Whatever they
typed in is in "myNumString", which is currently just a
string. This code tries to convert it into an
integer.
try {
newIx = Integer.decode(myNumString).intValue();
} catch (NumberFormatException nfe) {
System.out.println("please only enter integers!");
newIx = 0; // give newIx some value that won't stop the program
} catch (Exception ex) {
System.out.println("Wow, I didn't expect a " + ex);
ex.printStackTrace(); newIx = 0;
}
- Exceptions and
NumberFormatExceptions
are both classes.
- nfe & ex are both objects!
- ex won't be
instantiated if nfe
is: only one catch clause runs.
- Have a look at the Java Doc to see what you can do with
these objects.
- Printing their stack trace is very useful, it tells you
what the call history was.
- Other ways to handle exceptions:
- Stop everything: System.exit(1); Generally
considered bad form!
- Throw a new exception of a type of your own making
(see below.)
- Finally clauses
are optional, run after ALL the other clauses.
- Allows you to have something that happens regardless of
whether any of the catches is called.
- You can see one of these in the program
I hopefully showed you running in class, which is just
below.
- Even runs after return, continue or break are called!!
- Note that this
program has many examples of bad form in it.
But it is just a hack to demonstrate some ideas.
- You shouldn't be afraid to hack, especially when you are
trying to understand something for the first time.
- But you should
know the difference between hacking and programming.
- You should only submit programs for your coursework, not
hacks!
- This is the same program we looked at the declarations
for in Lecture 6,
this time we're looking at a different part of it: error
handling. And we're watching it run in eclipse!
/**
* @author joanna
*
* Illustrates how to catch exceptions, as well as command line I/O.
*
*/
public class BeBadIndex {// bunch of stuff skipped, see lecture 6 version
public static void main(String[] args) {
ArrayList myStuff = new ArrayList();
myStuff.add(new Integer(5)); // will be the 0th element.
myStuff.add(new Integer(7));
myStuff.add(new Integer(13));
myStuff.add(new Integer(2));
int newIx = -1;
try {
BufferedReader stdin =
new BufferedReader(new InputStreamReader(System.in));
String myNumString;
System.out.print("Welcome to my program. The prompt looks like >." +
"\n >> ");
while ((myNumString=stdin.readLine()) != null) {
try { // this is the bit you saw earlier...
newIx = Integer.decode(myNumString).intValue();
} catch (NumberFormatException nfe) {
System.out.println("er, you were meant to put in integers! Maybe I need more documentation.");
newIx = 0; // give newIx a value that won't crash the program
} catch (Exception ex) {
System.out.println("Wow, I didn't expect a " + ex);
} finally { // this prints out whether there's an error or not!
System.out.println("I have " + newIx + " as my index.");
System.out.flush();
}
try {
System.out.println("The value at " + newIx +
" is " + myStuff.get(newIx));
} catch (IndexOutOfBoundsException iobe) {
System.out.println("We're sorry, " +
"there is no such array element.");
}
System.out.print("\n >> ");
} // while reading
} catch (IOException ioe) {
ioe.printStackTrace();
System.exit(-3);
}
}
}
V. Throwing Throwables
- Syntax is pretty obvious (note -- java hashes won't throw
an exception for this, that's why we might have to if we
don't want null hash keys!):
if (key == null) {
throw new NullPointerException("null key in someBadPlaceForNulls");
} else {
return myHash.get(key)
}
- Notice you have to create a new instance of the throwable
object.
- String offers some diagnostics, will print if the object
is printed.
- Should also declare this in documentation to warn
programmers who use your class! @throws will wind up
in the java doc.
@throws NullPointerException if the key is null
- If the exception is checked, then you must
declare it in the method header:
public void saveToFile(String myFileName)
throws IOException
- Sometimes you just rethrow (propagate) an error
you've caught:
- Satisfies the obligation for checked error.
- Allow calling function to handle.
- This is done automatically, implicitly with unchecked
errors that aren't handled otherwise.
- Generally prefer the implicit handling of unchecked
errors, makes code easier to read.
- Sometimes you may want to catch one error & throw
another,
- e.g. to translate from a database layer of a program up
to the GUI interface.
- Can define new exception classes.
- Allows for more information / diagnostics /
documentation.
- But can be confusing -- proliferation of names.
- Be sure to subclass off of something appropriate!
VI. Summary
- Program execution can jump around.
- In Java (and most modern languages) the way it can jump is
severely limited & controlled in order to make code
easier to understand.
- Java has exceptionally good error handling
(that's the generic term, though you usually actually handle
what Java calls "exceptions").