II. Nonlinear Control of Flow
- Somewhere, for any executing program, there is a special
references that says what statement is about to be executed next.
- Programmers think best about sequences --- simple plans, always
go to the next step.
- Loops are a little tricker 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, they tend to tell you what their
criteria & index are in the test.
- Function/method calls are also self-documenting, they are
abstract & their names should tell you want 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 it is widely
condemned.
- I am sure someone in
this department will think I shouldn't even have mentioned the notion
to you!
- 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:
III. 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.
- Don't 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!
IV. Throwables
- 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:
AbstractMethodError
ClassCircularityError
ClassFormatError
IllegalAccessError
IncompatibleClassChangeError
InstantiationError
InternalError
NoClassDefFoundError
NoSuchFieldError NoSuchMethodError
OutOfMemoryError
StackOverflowError
ThreadDeath
UnknownError
UnsupportedClassVersionError
VerifyError
VirtualMachineError
- Show on laptop file:///home/joanna/s1studio_jdk/j2sdk1.4.1_02/docs/api/index.html
- Exceptions are things you might want to do something about.
- 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?
- Often inserted during development / debugging, or based on
personal experience.
- 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.
V. 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 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!");
ewIx = 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 exeptions:
- 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.
- 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!
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
/**
* @author joanna
*
* Illustrates how to catch exceptions, as well as command line I/O.
*
*/
public class BeBadIndex {
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").
- The Blue J chapter on this topic is very, very good, you should
read it!