JMU JMU - Department of Computer Science
Help Tools
Lab: Experimenting with Exception Handling


Instructions: Answer the following questions one at a time. After answering each question, check your answer (by clicking on the check-mark icon if it is available) before proceeding to the next question.

1. Some Advice: For this lab, you may want to both think about what will happen when you compile and execute the applications, and actually compile and execute them.
2. Throwing and Re-throwing Exceptions: This part of the lab will help you better understand how to throw and re-throw exceptions.
  1. Implement the following method.
        /**
         * Calculates an array of percentages from an array of values.
         *
         * Specifically, this method calculates the total of all of the
         * elements in the given array and divides each element into the 
         * total to determine the percentages.
         *
         * @param  values  The array of non-negative values
         * @return         The array of percentages.
         */
        public static double[] toPercentages(double[] values)
        {
        }
        


        public static double[] toPercentages(double[] values) 
        {
            double    total;
            double[]  result;
          
            // Calculate the total
            total = 0;
            for (int i=0; i<values.length; i++)
            {
                total += values[i];
            }
    
            // Allocate memory for the result
            result = new double[values.length];
            
            // Calculate the percentages
            for (int i=0; i<values.length; i++)
            {
                result[i] = values[i] / total;
            }
            
            return result;        
        }
    
    Expand
  2. Desk check the toPercentages() method, looking for trigger conditions that will cause it to fail.

    What are the four obvious trigger conditions?


    1. The comments say that values should not contain negative values, but nothing is done to ensure that is the case. Hence, values might contain negative elements.

    2 and 3. Whenever division is used, it is important to check for situations in which the divisor might be 0. In this case, there are two situations that might give rise to a divisor of 0. First, the total might be 0 (which, if the values are all non-negative, will only be true if all of the values are 0). Second, values.length might be 0.

    4. value might be null.

    Expand
  3. What exception will be thrown when the following application is executed? What line will cause the exception to be thrown? Why?
        private static double[]     sales;
    
        public static void main(String[] args)
        {
            double[]   percentages;
            
            percentages = toPercentages(sales);
        }
    


    A NullPointerException will be thown by the first for loop in toPercentages() because sales was not initialized. Hence, both it and values will be null, causing a NullPointerException to be thrown when an attempt is made to access values.length.
    Expand
  4. Modify the declaration of the toPercentages() method so that it specifies the NullPointerException.

    What is the declaration now?


        public static double[] toPercentages(double[] values) throws NullPointerException
    
    Expand
  5. Modify the first for loop in the toPercentages() method so that it throws an IllegalArgumentException if any element of values is negative.

    What statements are in the body of the loop now?


                if (values[i] < 0.0) 
                {
                    throw new IllegalArgumentException("Element " + i + "is < 0");
                }
                
                total += values[i];
    
    Expand
  6. Why don't you need to include an else clause in the if statement?


    Because the throw statement returns control to the caller (in essentially the same way as a return statement, though to a different place).
    Expand
  7. What will happen if more than one element is negative? In particular, how many exceptions will be thrown?


    Because the throw statement returns control to the caller, only one IllegalArgumentException will be thrown, when the first negative element is encountered.
    Expand
  8. Modify the declaration of the toPercentages() method so that it now also includes IllegalArgumentException.

    What is the declaration now?


        public static double[] toPercentages(double[] values) 
            throws IllegalArgumentException, NullPointerException
    
    Expand
  9. Add a statement after the first for loop, but before the statement that allocates memory for result, that throws an IllegalArgumentException if total is 0.

    What statement did you add?


            if (total <= 0.0)
            {
                throw new IllegalArgumentException("Total is 0");
            }
    
    Expand
  10. The toPercentages(double[]) method is now implemented as follows.
        public static double[] toPercentages(double[] values) 
            throws IllegalArgumentException, NullPointerException
        {
            double    total;
            double[]  result;
          
            // Calculate the total
            total = 0;
            for (int i=0; i<values.length; i++) // throws NullPointerException
            {
                if (values[i] < 0.0) 
                {
                    throw new IllegalArgumentException("Element " + i + " < 0");
                }
                
                total += values[i];
            }
    
            if (total <= 0.0)
            {
                throw new IllegalArgumentException("Total is 0");
            }
    
            // Allocate memory for the result
            result = new double[values.length];
            
            // Calculate the percentages
            for (int i=0; i<values.length; i++)
            {
                result[i] = values[i] / total;
            }
            
            return result;        
        }
    
    Why is the statement that allocates memory for result where it is rather than earlier (for example right after the declaration or even in the declaration statement)?


    Because there is no reason to allocate memory for result until after it is clear that it will be needed. Exceptions might be thrown in three different places, thereby returning control to the caller. The memory shouldn't be allocated until after that.
    Expand
3. Catching Exceptions: This part of the lab will help you better understand how to catch exceptions.
  1. What is printed by the following application?
    Example1.java
            public class Example1
    {
        public static void main(String[] args)
        {
            int     denominator, numerator, ratio;
    
    
            numerator   = 5;
            denominator = 2;
    
            ratio = numerator / denominator;
            System.out.println("The answer is: "+ratio);
    
            System.out.println("Done."); // Don't move this line
        }
    }
            


    The answer is: 2
    Done.
    
    Expand
  2. Change the value of denominator to 0. What runtime error message is now generated by this application?


    Exception in thread "main" java.lang.ArithmeticException: / by zero
            at Example1.main(Example1.java:11)
    
    Expand
  3. Why is this error message generated at run-time (rather than at compile-time)?


    The compiler can not find defects that result from variables being assigned particular values (since the assignments do not take place until run time). In this case, at run-time, denominator holds the value 0 so the division operator throws an ArithmeticException. Since the exception is not caught, the application terminates and an error message is printed to the console.
    Expand
  4. Add a try-catch statement to this application. Specifically, put only the statement that generated the exception inside of the try block and put no statments in the catch block. (Hint: You should be able to determine what exception to catch from the error message that you received during the previous step. You should also be able to determine the line number of the statemen that generated the exception if you didn't change the white space.)

    What statements are now in the body of the main() method?


            int     denominator, numerator, ratio;
    
            numerator   = 5;
            denominator = 0;
    
            try
            {
                ratio = numerator / denominator;
            }
            catch (ArithmeticException ae)
            {
            }
            System.out.println("The answer is: "+ratio);
    
            System.out.println("Done."); // Don't move this line
    
    Expand
  5. What error message is generated by compiling this version of the application?


    Something like:

    Example.java:17: variable ratio might not have been initialized
                System.out.println("The answer is: "+ratio);
                                                     ^
    

    though the line number will vary depending on white space.

    Expand
  6. Why is this compile-time error message generated?


    This error message is generated because ratio is initialized in the try block and this initialization will not take place if an exception is thrown before ratio can be assigned a value.

    Expand
  7. Modify the body of the main method as follows.
            int     denominator, numerator, ratio;
    
            numerator   = 5;
            denominator = 0;
    
            try
            {
                ratio = numerator / denominator;
                System.out.println("The answer is: "+ratio);
            }
            catch (ArithmeticException ae)
            {
                System.out.println("Divide by 0.");
            }
    
            System.out.println("Done."); // Don't move this line
        

    Will this version of the application compile? Why or why not?


    Yes. There still may be situations in which ratio is not assigned a value, but it is only used in another statement in situations in which it is.

    Expand
  8. What output is generated by this application?


    Divide by 0.
    Done.    
    
    Expand
  9. Does the application terminate in an orderly fashion?


    Yes. Even though there is a division by 0, the application accounted for it and so terminated in an orderly fashion.
    Expand
  10. At the end of the catch block, add a call to the printStackTrace() method of the ArithmeticException object ae. What is in the catch block now?


            catch (ArithmeticException ae)
            {
                System.out.println("Divide by 0.");
                ae.printStackTrace();
            }
    

    Expand
  11. What output is generated by the application now?


    Something like:

    Divide by 0.
    java.lang.ArithmeticException: / by zero
            at Example.main(Example.java:13)
    Done.
    

    Expand
  12. Does the application terminate in an orderly fashion?
    Yes. An ArithmeticException is thrown when the division is attempted but it is caught. After it is caught some messages are printed and then the application terminates.

    The fact that the printStackTrace() method prints a message that looks like an error message does not mean that the application does not account for all possible situations and terminate properly. (Though it would, indeed, scare a user. Hence, this method should only be used when debugging.)

    Expand
  13. Returning to the toPercentages() method in the previous section, write a fragment that calls toPercentages(), passing it the array sales. If toPercentages() returns normally, then the fragment must print each element. On the other hand, if it returns "abnormally", then the fragment must either call a method named reportInvalidSalesData() or a method named reportNonexistentSalesData(), as appropriate, passing sales in both cases.

    What statements did you add?


           try
            {
                percentages = toPercentages(sales);
                for (int i=0; i<percentages.length; i++)
                {
                    System.out.println("Percentage " + i + ": " + percentages[i]);
                }
            }
            catch (IllegalArgumentException iae)
            {
                reportInvalidSalesData(sales);
            }
            catch (NullPointerException npe)
            {
                reportNonexistentSalesData(sales);
            }
    

    Expand
4. Scope Issues: This part of the lab considers an example of exception handling within and outside of block statements.
  1. What error messages are generated by compiling the following application?
    Example2.java
            public class Example2
    {
        public static void main(String[] args)
        {
            int        ratio;
            int[]      numbers = {100,10,0,5,2,8,0,30};
    
            try
            {
                for (int i=0; i < numbers.length-1; i++)
                {
                    ratio = numbers[i] / numbers[i+1];
                    System.out.println(numbers[i]+"/"+numbers[i+1]+"="+ratio);
                }
            } 
            catch (ArithmeticException ae)
            {
                System.out.println("Couldn't calculate "+
                                   numbers[i]+"/"+numbers[i+1]);
            }
        }
    }
            


    Example2.java:19: error: cannot find symbol
                                   numbers[i]+"/"+numbers[i+1]);
                                           ^
      symbol:   variable i
      location: class Example2
    Example2.java:19: error: cannot find symbol
                                   numbers[i]+"/"+numbers[i+1]);
                                                          ^
      symbol:   variable i
      location: class Example2
    
    Expand
  2. Why are these compile-time error message generated? (Hint: Think about block statements and block scope.)


    The variable i is declared in the for statement and, so, is not recognized outside of it.
    Expand
  3. Declare i (but do not initialize it) inside of the try block, just before the for loop as follows.
                int i;            
    	    for (i=0; i < numbers.length-1; i++)
         

    What error message are generated by compiling the application now?


    Example2.java:20: error: cannot find symbol
                                   numbers[i]+"/"+numbers[i+1]);
                                           ^
      symbol:   variable i
      location: class Example2
    Example2.java:20: error: cannot find symbol
                                   numbers[i]+"/"+numbers[i+1]);
                                                          ^
      symbol:   variable i
      location: class Example2
    2 errors
    
    Expand
  4. Why are these compile-time error message generated?


    The variable i is now declared in the try block so is not be recognized in the catch block.
    Expand
  5. Move the declaration of i to the same statement that declares ratio (but leave the initialization in the for statement). What error message is generated by compiling the application now?


    Example.java:20: variable i might not have been initialized
                                   numbers[i]+"/"+numbers[i+1]);
                                           ^
    
    Expand
  6. It is not possible for i to be used before it is initialized. Why is this error messaged generated anyway? (Hint: Think about block statements.)


    The try block is treated as a single (block) statement. From the compiler's perspective, this statement may throw an exception causing the catch block to be entered before the statement is completed.
    Expand
  7. Initialize i before the try block (so it will compile). What output is generated by this application now?


    100/10=10
    Couldn't calculate 10/0
    
    Expand
  8. Why aren't all of the divisions even attempted?


    During iteration 1 (not not be confused with iteration 0) of the loop an exception is thrown. When this happens control leaves the try block and enters the catch block. Control then leaves the catch block and then leaves main which causes the application to terminate.
    Expand
  9. Fix Example2 so that it executes properly. (Hint: Move the try-catch block inside of the for block.) What is the body of the main() method now?


    	int        i, ratio;
    	int[]      numbers = {100,10,0,5,2,8,0,30};
    
    		
    	for (i=0; i < numbers.length-1; i++)
            {		
    	    try
                {
    		ratio = numbers[i] / numbers[i+1];
    		System.out.println(numbers[i]+"/"+numbers[i+1]+"="+ratio);
                }
    	    catch (ArithmeticException ae)
                }
    		System.out.println("Couldn't calculate "+
    				   numbers[i]+"/"+numbers[i+1]);
    	    }
    	}
    
    Expand
5. Inappropriate Uses of Exception Handling: This part of the lab considers inappropriate uses of exception handling and how to "fix" them.
  1. What output is generated by the following application?
    Example3.java
            public class Example3
    {
        public static void main(String[] args)
        {
            int              i;
            int[]            data = {50, 320, 97, 12, 2000};
    
    
            try 
            {
                for (i=0; i < 10; i++)
                {
                    System.out.println(data[i]);
                }
            } 
            catch (ArrayIndexOutOfBoundsException aioobe)
            {
                System.out.println("Done");
            }
        }
    }
            


    50
    320
    97
    12
    2000
    Done
    
    Expand
  2. Modify Example3 so that it loops "properly" and does not need to use a try-catch statement. (Note: The output should not change.) What is the body of the main() method now?


    	int              i;
    	int[]            data = {50, 320, 97, 12, 2000};
    
    
    	for (i=0; i < data.length; i++)
            {
    	    System.out.println(data[i]);
    	}
            System.out.println("Done");
    
    Expand
  3. Earlier, you modified the toPercentages() method so that it specifies (i.e., re-throws) NullPointerExceptions. Is this the best way to handle this situation?


    Probably not. The caller should know better than to pass a null array into a method like this. So, specifying the NullPointerException doesn't really provide valuable information to the caller.

    In addition, the toPercentages() method can handle this situation by returning a special value.

    Expand
  4. Modify the toPercentages() method so that it checks for a null parameter but handles it with a special return value.


        public static double[] toPercentages(double[] values) 
            throws IlegalArgumentException
        {
            double    total;
            double[]  result;
          
    	if (values == null)
            {
                return null;
            }
    
            // Calculate the total
            total = 0;
            for (int i=0; i<values.length; i++)
            {
                if (values[i] < 0.0) 
                {
                    throw new IllegalArgumentException("Element " + i + " < 0");
                }
                
                total += values[i];
            }
    
            if (total <= 0.0)
            {
                throw new IllegalArgumentException("Total is 0");
            }
    
            // Allocate memory for the result
            result = new double[values.length];
            
            // Calculate the percentages
            for (int i=0; i<values.length; i++)
            {
                result[i] = values[i] / total;
            }
            
            return result;        
        }
    
    Expand
  5. Why is it possible to return the special value that was used in the answer to the previous question?


    We can return null here because double[] is a reference type.
    Expand
  6. Is this an example of excessive return statements?


    Most people don't think so. This is often called "early return" and most people consider it appropriate way to handle situations in which inappropriate parameters are passed to a method. They argue that it is clearer than using an if statement with an else clause and that it reduces the amount of indenting in the "important part" of the method.
    Expand
6. Some Other Exceptions: This part of the lab will give you some experience with some other exceptions, where they arise, and how they can be used.
  1. What functionality does a StringTokenizer java.util.StringTokenizer object provide?


    It can be used to break a String into component parts, called tokens.
    Expand
  2. What are the three formal parameters of the explicit value constructor in the StringTokenizer class?


    The String that is going to be tokenized, the delimiters to use while tokenizing, and whether or not the delimiters should be returned as tokens.
    Expand
  3. What output will be generated by the following application if it is passed a single command-line argument containing the String "5.3+9.2"?
    Example4.java
            import java.util.*;
    
    public class Example4
    {
        public static void main(String[] args)
        {
           double                 leftOperand, result, rightOperand;
           String                 leftString, operator, rightString;
           StringTokenizer        tokenizer;
    
    
           tokenizer = new StringTokenizer(args[0], "+", true);
    
           try
           {
              leftString   = tokenizer.nextToken();
              operator     = tokenizer.nextToken();
              rightString  = tokenizer.nextToken();
    
              leftOperand  = Double.parseDouble(leftString);
              rightOperand = Double.parseDouble(rightString);
    
              if (operator.equals("+"))
                 result = leftOperand + rightOperand;
              else
                 result = 0.0;
    
              System.out.println("Result: " + result);
           }
           catch (NoSuchElementException nsee)
           {
              System.out.println("Invalid syntax");
           }
           catch (NumberFormatException nfe)
           {
              System.out.println("One or more operands is not a number");
           }
    
    
        }
    }
            


    Result: 14.5
    
    Expand
  4. What output will be generated by this application if it is passed a single command-line argument containing the String "5.3+"?


    Invalid syntax
    
    Expand
  5. Why? In particular, what exception is thrown and why?


    A NoSuchElementException is thrown by the statement rightString = tokenizer.nextToken();
    Expand
  6. What output will be generated by this application if it is passed a single command-line argument containing the String "5.3+a"?


    One or more operands is not a number
    
    Expand
  7. Why? In particular, what exception is thrown and why?


    A NumberFormatException is thrown by the statement rightOperand = Double.parseDouble(rightString);
    Expand
Going Further


Copyright 2019