CPSC 170 Lab 11

As usual, create a lab11 subdirectory for today's lab, open this document in Mozilla, and start emacs.

  1. File ExceptionTest.java contains a very simple program in which the main method calls another method that divides by 0.
    1. 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.

    2. 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.

    3. 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.

    4. 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!

    5. 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?

  2. 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:

    1. 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.

    2. 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.

    3. When the counts are printed, add another print statement that gives the number of nonletter characters.

  3. 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.

    1. 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.

    2. 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.

    3. 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.

    4. 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.

    5. 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.

    6. 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.

  4. 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:

    1. You can create a BufferedReader from a FileReader; consult the Java documentation to see how to construct a FileReader.

    2. The FileReader constructor throws a FileNotFoundException -- this is how you will know if the file does not exist.

    3. 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.