CPSC 170 Lab 11
Exceptions
As usual, create a lab11 subdirectory for today's lab, open this
document in Mozilla, and start emacs.
- File ExceptionTest.java contains
a very simple program in which the main method calls another method
that divides by 0.
-
Save this file to your directory, open it in emacs, and study it.
Then compile and run it, and look at the exception that it generates --
this should be more or less what you expect. Note that the statement in
main after the call to silly is not printed.
- Now modify method silly
so that it catches the exception; you will need to put a try
around the code that generates the exception, and a catch afterwards
that catches it. Don't catch just any exception; catch the kind it
actually generated. In the body of the catch, just print the exception.
- Compile and run your program. It should print the exception, but now
it should also print the message in main after the call to silly. This
is because since the exception is caught, execution continues
normally after the catch -- the message printed by the catch is just
informative. But
notice that it doesn't tell you what line generated the exception, which
the message did before -- it just tells what kind of exception occurred.
You can get more information with the printStackTrace() method.
Modify your catch so that instead of printing the exception, it calls
its printStackTrace() method. Compile and run your program again; now you
should get a message telling exactly where the exception occurred.
- Add code in silly so that in addition to dividing by 0, it generates
a StringIndexOutOfBoundsException. (You can think of some way to do this!)
Put the new code inside the try, but before the divide by 0.
Compile and run the program again; now what do you get? Does the message
in main still get printed? Be sure you understand why or why not!
- Move the new code to just after the divide by 0 (but still inside the
try). Now does the message in main get printed? Be sure you
understand why or why not?
-
File CountLetters.java
contains
a program that reads a word from the user and prints the number of
occurrences of each letter in the word.
Save it to your
directory and study it, then compile and run it to see how it works.
In reading the code, note that the word is converted to all upper case
first, then each letter is translated to a number in the range 0..25
(by subtracting 'A') for use as an index. No test is done to ensure that
the characters are in fact letters.
Run CountLetters and enter
a phrase, that is, more than one word with spaces or other punctuation
in between. It should throw an
ArrayIndexOutOfBoundsException, because a non-letter will generate an index
that is not between 0 and 25. It might be desirable to allow non-letter
characters in the input, but not count them.
Of course, you could explicitly test the
value of the character to see if it is between 'A' and 'Z'.
However, an alternative is to go ahead and use the translated character
as an index, and catch an ArrayIndexOutOfBoundsException if it
occurs. In addition to counting each letter, you might want to know
how many non-letter characters occur in the file.
Modify
this method to do this as follows:
- Put the code that increments the counts in the array inside a try
statement. Think carefully about exactly where the try should go --
remember that you want the loop to continue even if an
ArrayIndexOutOfBoundsException
is thrown.
- Add a catch that catches an ArrayIndexOutOfBoundsException
and increments a
nonLetter counter (which you will have to declare). Do not
print anything in the catch.
- When the counts are printed, add another print statement
that gives the number of nonletter
characters.
- When we implemented the stack, queue, and bag collections, we struggled
(a little) with what to do when pop/dequeue/remove is called on an
empty collection. We decided to return null, but this really is not
a satisfactory solution, as the collection itself might contain
null objects.
A better solution is to throw an exception when this
occurs, and let the calling method decide what to do with it.
File ArrayStack.java contains code for
the array implementation of the Stack class. Copy this
to your directory, along with
StackADT.java.
- File TestStack.java contains a
test program that creates a stack, pushes some things on it,
pops it until it is empty, then pops it once more.
Save it to your directory and run it and see what happens.
- Find the EmptyStackException class in the java documentation. Is this
a RuntimeException? Remember that the rules are special for
RuntimeExceptions -- they do not have to appear in the method header and
do not have to be acknowledged by the calling routine.
Modify the ArrayStack class
so that pop() throws a EmptyStackException if the stack is empty.
Add it to the header for this method as well; this is always a good
idea, even for RuntimeExceptions, as it makes it immediately obvious to
the user how the empty case is being handled.
- Modify your test program so that it catches the exception that is
thrown and prints the stack trace. Test it with your revised ArrayStack class.
- This ArrayStack class does not expand the array, so it eventually
gets full. Modify the TestStack so that it pushes "six" after pushing "five"
and run it again.
Nothing changes! This is because push is written to do nothing if the
stack is already full, but again this is a poor solution -- it would be
better if it threw an exception to alert the calling routine that something
is amiss. But there is no FullStackException
already defined, so you'll need to define your own.
Write a class FullStackException that extends RuntimeException.
Be sure to provide both a default constructor and a constructor that
takes a String. In the latter case, you will need to call the
corresponding constructor of the RuntimeException class explicitly. No
other methods are required.
- Modify the ArrayStack class so that it throws a FullStackException
if push is called when the stack is full. Pass the item being pushed
to the exception object so that the user can see which item caused
problems when the exception is printed. Now compile and run TestStack
again; you should get an error. Modify it so that it catches and
prints (the stack trace for) a FullStackException
as well as an EmptyStackException.
- Note that the iterator method in ArrayStack just returns null; I
simply decided not to implement it. But instead of returning null,
it should throw an UnsupportedOperationException. Modify it to do this, then
modify TestStack so that the last thing it does is try to get
an iterator from the stack. Don't catch the exception it generates.
- Write a program that prompts the user for a filename, then
opens a BufferedReader to the file and copies it, a line at a time,
to the standard output. If the user enters the name of a file that
does not exist, ask for another name until you get one that refers to a valid
file. Some things to consider:
- You can create a BufferedReader from a FileReader; consult the Java
documentation to see how to construct a FileReader.
- The FileReader constructor throws a FileNotFoundException -- this is how
you will know if the file does not exist.
- The BufferedReader class
has a String readLine() method that returns the
next line in the file, or null when it encounters EOF. The readLine()
method throws an IOException, which signals anything that might go wrong
during reading (e.g., the input stream becoming unavailable). This rarely
occurs (on our system, anyway), and I don't know of any way to force it
to happen, so it's hard to test.
There is probably no
need to catch this exception, as you cannot recover from it and you
don't have anything to continue with after reading the file, so you
can just have your main method throw it.
What to turn in
Turn in
hardcopy of ExceptionTest.java, CountLetters.java, ArrayStack.java,
FullStackException.java, TestStack.java, and your last (IO) program.
Tar your lab11 directory
and send it to me with cpsc170 lab11 in the subject line.