Unchecked Exceptions: Keeping Our Applications Running
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


About Exceptions

We know that all exceptions are checked exceptions, aside from those identified by Error and RuntimeException and their subclasses. If code includes methods with checked exceptions, that code won't compile until the exceptions are handled through try/catch clauses. A method that causes a checked exception has to specify that it throws the exception.

In the previous lesson, we were able to compile and run our application code, so we know our code didn't contain methods with checked exceptions. Does that mean our code is free from errors and exceptions? Sadly no. As we saw in the last lesson, our code contained one unchecked exception that had to be fixed. Unchecked exceptions usually occur because a user does something unexpected at runtime. In this lesson, we'll look at common subclasses of RuntimeExceptions.

Run-Time Exceptions

Subclasses of RuntimeExceptions appear in our code often, but sometimes they're hard to locate. These are the most commonly used subclasses of RuntimeExceptions:

NullPointerException

A null pointer exception occurs when your code tries to access an instance of an object that has not been properly instantiated. Declaring a variable to be of a certain type is not the same as instantiating it. If your variable is not of a primitive data type, it has been declared as a type of Object (remember that every object inherits from Object and is a subclass). If such an object has not been instantiated, then it doesn't point to anything in memory, so it's a null pointer.

Null pointer problems may occur when a user calls a method incorrectly. If an argument is null, the method might throw a NullPointerException, which is an unchecked exception. Let's look at such an exception in the first of five examples we'll use in this lesson:

Example 1

In the java4_Lesson1 project, SalesGUI package, edit Main.java as shown in blue and red:

CODE TO TYPE: Main
package salesGUI;

public class Main {
    // declare a class variable that is set to null by default
    public static SalesApp newApp; 

    public static void main(String[] args) {   
        // comment the next line out so it looks like we forgot to instantiate it
        //SalesApp newApp = new SalesApp();            
        SalesUserInterface appFrame = new SalesUserInterface(newApp);      
    }
}

Save and run it. Look in the Console for the exception:

In this code, we passed a variable newApp that was null for SalesApp. It is not always that easy to find exceptions, particularly when we instantiate the objects in one place and access them in another.

Edit Main.java as shown (we're reverting to the previous version, so you can use the Undo key combination [Ctrl+Z] to undo the typing you did earlier):

CODE TO EDIT: Main
package salesGUI;

public class Main {
    // Remove this and the next line
    public static SalesApp newApp; 
    
    public static void main(String[] args) {   // two classes to instantiate--the application and its GUI
        // Remove this line and the double-slash from the beginning of the next line 
        // SalesApp newApp = new SalesApp();
        SalesUserInterface appFrame = new SalesUserInterface(newApp);  // tell the interface who its app is	
    }
}

Save and run it again to make sure that all's well. Make sure to exit the running application afterward, so it's ready for the next test.

Example 2

Open the InputPanel.java class. In the constructor, comment out the line where we instantiate the sales array, as shown in red:

CODE TO EDIT: InputPanel
package salesGUI;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
public class InputPanel extends JPanel implements ActionListener {
    JPanel topPanel, middlePanel, bottomPanel, leftPanel, rightPanel;
    JLabel[] jlSales;                        
    JButton done; 
    SalesApp app;
    JLabel prompt, doneLabel, jlSalesBar; 
    JTextField[] jtfSales; 
    JTextField jtfSalesBar; 
    int numPeople; 
    int [] sales;
    int goal;

    public InputPanel(SalesApp container, int numPeople, int gridX){
        this.app = container;
        this.numPeople = numPeople;
        // sales = new int[numPeople];
    
        this.setLayout(new BorderLayout());
        topPanel = new JPanel();
        topPanel.setLayout(new FlowLayout());
        middlePanel = new JPanel(new GridLayout(numPeople, gridX));
        bottomPanel = new JPanel();
        bottomPanel.setLayout(new FlowLayout());
        leftPanel = new JPanel();
        rightPanel = new JPanel();
        add("North", topPanel);
        add("Center", middlePanel);
        add("South", bottomPanel);
        add("East", rightPanel);
        add("West", leftPanel);

        jlSales = new JLabel[numPeople];
        jtfSales = new JTextField[numPeople];
        prompt = new JLabel("Give values for each salesperson:");
        topPanel.add(prompt);
        
        for (int x = 0; x < numPeople; x++)
        {
            jlSales[x] = new JLabel("Sales Person " + (x+1));
            jtfSales[x] = new JTextField("0",8);
            middlePanel.add(jlSales[x]);
            middlePanel.add(jtfSales[x]);
        } 
        jlSalesBar = new JLabel("Enter a value for the sales goal");
        bottomPanel.add(jlSalesBar);
        jtfSalesBar = new JTextField("0",8);
        bottomPanel.add(jtfSalesBar);
        doneLabel = new JLabel("Click when all are entered:");
        bottomPanel.add(doneLabel);
        done = new JButton("All Set");
        bottomPanel.add(done);
        done.addActionListener(this);
    }
    
    public void actionPerformed(ActionEvent event) {
        if (event.getSource() instanceof JButton)
        {
            if ((JButton)event.getSource() == done) 
            {
                for (int x = 0; x < numPeople; x++)
                {
                    try 
                    {
                        sales[x] = Integer.parseInt(jtfSales[x].getText());  // throws NumberFormatException
                    } 
                    catch(NumberFormatException e)
                    {   
                        String temp = JOptionPane.showInputDialog("Decimal values are not allowed.\n Please give a whole number for Sales Person " + (x+1) + ": ");
                        sales[x] = Integer.parseInt(temp);
                        jtfSales[x].setText(Integer.toString(sales[x]));      
                    }
                }
                app.setSales(sales);
                goal = Integer.parseInt(jtfSalesBar.getText());
                app.setSalesBar(goal);
            }
        }
    }
}

Save it.

Run the Main.java class. Enter a value for the number of SalesPeople and click Submit to open the InputPanel. Enter a value for a particular Salesperson, then click All Set.

Scroll to the top of the exception trace in the Console.

At or near line 78 in the InputPanel class is the line sales[x] = Integer.parseInt(jtfSales[x].getText()); (your line numbers may vary slightly, depending on how you coded your dialog box.) The error message appears because we commented out the instantiation of the sales [ ] array, so it's not there to have elements added into it. New programmers often think that because they declared the array, it exists: int [] sales; (at or near line 16 in InputPanel). When you declare a variable as an instance variable and it is an Object, Java gives it the default value of null. So in the constructor, you need the line sales = new int[numPeople]; to allow your variable a non-null value and size.

Close this application instance using File | Exit, uncomment (in other words, remove the // from) the line you commented out in InputPanel.java. Save it, and run it again from Main to make sure it works. Then, exit the application again, using File | Exit.

Example 3

Here's another potential snag in our current application:

Run the Main.java class and enter values for Sales People and for the Sales Goal, but do not click All Set. Select Options | Results in the menu. Take a look at the Console:

Your line numbers may be slightly different. Does this look familiar? It's the exception we didn't fix in the previous lessons.

We'll trace this error from the bottom up:

OBSERVE
at salesGUI.SalesUserInterface$3.actionPerformed(SalesUserInterface.java:48)
at salesGUI.OutputPanel.writeOutput(OutputPanel.java:33)
at salesGUI.SalesApp.calculateMinMax( SalesApp.java:70)

SalesUserInterface.java:48 is results.writeOutput();}});. results is an instance of OutputPanel. We are calling its method, writeOutput().

OutputPanel.java:33 is the first line in that method. There is a call to app.calculateMinMax();. app is an instance of SalesApp; we are calling its method calculateMinMax().

SalesApp.java:70 is the first line in that method. We see int minimum = sales[0];--so, why the null pointer exception? Because we don't have a sales[0]. The user hasn't clicked SetAll, so the sales [] array never received its values, and so sales[0] doesn't exist.

There are various ways to fix these problems. We'll illustrate just one. All of the menu items were made in the SalesUserInterface class, so let's look there to find out how to fix it.

Open the SalesUserInterface.java class. See the constructor, where the Results MenuItem is added at m1.add(t = new MenuItem("Results"));. Check out the ActionListener and its requirements.

The MenuItem requires the array set in order to perform the methods described in its ActionListener. Here are some possible remedies:

  • Set a flag to indicate whether the array has been set and if not, set it.
  • Ask the user to click the AllSet button.
  • Set everything to zeros so we start with a known quantity.
  • Make sure that it is set by doing it ourselves again.

For this example, let's set all of the values to whatever is currently in the JTextFields. To do that we'll need to check the inputs again and then set the array. Also, we'll need another method that does almost the same thing as actionPerformed() in InputPanel. To promote modularity of code, edit InputPanel. There are two major changes: actionPerformed() is edited and much of its contents go into a new method named setAllInputs():

CODE TO EDIT: InputPanel
package salesGUI;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
public class InputPanel extends JPanel implements ActionListener {
    JPanel topPanel, middlePanel, bottomPanel, leftPanel, rightPanel;
    JLabel[] jlSales;                        
    JButton done; 
    SalesApp app;
    JLabel prompt, doneLabel, jlSalesBar; 
    JTextField[] jtfSales; 
    JTextField jtfSalesBar; 
    int numPeople; 
    int [] sales;
    int goal;

    public InputPanel(SalesApp container, int numPeople, int gridX) {
        this.app = container;
        this.numPeople = numPeople;
        sales = new int[numPeople];

        this.setLayout(new BorderLayout());
        topPanel = new JPanel();
        topPanel.setLayout(new FlowLayout());
        middlePanel = new JPanel(new GridLayout(numPeople, gridX));
        bottomPanel = new JPanel();
        bottomPanel.setLayout(new FlowLayout());
        leftPanel = new JPanel();
        rightPanel = new JPanel();
        add("North", topPanel);
        add("Center", middlePanel);
        add("South", bottomPanel);
        add("East", rightPanel);
        add("West", leftPanel);
        
        jlSales = new JLabel[numPeople];
        jtfSales = new JTextField[numPeople];
        prompt = new JLabel("Give values for each salesperson:");
        topPanel.add(prompt);
        
        for (int x = 0; x < numPeople; x++)
        {
            jlSales[x] = new JLabel("Sales Person " + (x+1));
            jtfSales[x] = new JTextField("0",8);
            middlePanel.add(jlSales[x]);
            middlePanel.add(jtfSales[x]);
        } 
        jlSalesBar = new JLabel("Enter a value for the sales goal");
        bottomPanel.add(jlSalesBar);
        jtfSalesBar = new JTextField("0",8);
        bottomPanel.add(jtfSalesBar);
        doneLabel = new JLabel("Click when all are entered:");
        bottomPanel.add(doneLabel);
        done = new JButton("All Set");
        bottomPanel.add(done);
        done.addActionListener(this);
    }
 
    public void actionPerformed(ActionEvent event) {
        if (event.getSource() instanceof JButton)
        {
            if ((JButton)event.getSource() == done) 
            {
                setAllInputs();    // all of the code that was here is 
                                   //now in the method named setAllInputs
            }
        }
    }
    
    public void setAllInputs(){
        for (int x = 0; x < numPeople; x++)
        {
            try 
            {
                sales[x] = Integer.parseInt(jtfSales[x].getText());
            } 
            catch(NumberFormatException e)
            {   
                String messageLine1 = "Input must be whole numbers.\n ";
                String messageLine2 = "Your decimal value " + jtfSales[x].getText() + " for Sales Person " + (x+1) +" will be truncated.\n ";
                String messageLine3 = "You may enter a different integer and click AllSet if truncation is unacceptable.";

                JOptionPane.showMessageDialog(this, messageLine1+messageLine2+messageLine3,"Input Error", JOptionPane.ERROR_MESSAGE);

                sales[x]= (int)Double.parseDouble(jtfSales[x].getText());
                jtfSales[x].setText(Integer.toString(sales[x]));      
            }
        }

        app.setSales(sales);
        goal = Integer.parseInt(jtfSalesBar.getText());  // so don't have to be sure they hit enter
        app.setSalesBar(goal);  
    }
}

Save it and run Main.java. Once you've confirmed that it still works correctly, fix the menu choices. Edit SalesUserInterface as shown in blue:

CODE TO EDIT: SalesUserInterface
package salesGUI;

java.awt.BorderLayout;
import java.awt.Dimension; 
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class SalesUserInterface extends JFrame {
    SalesApp app;
    JMenuBar mb;
    JMenu m, m1;
    JMenuItem q, r, s, t;
    InputPanel inputPanel;
    JLabel peopleLabel;
    JTextField peopleField;
    JButton jbNumPeople, done;
    int numPeople;
    OutputPanel results;
    boolean processed = false;
    
    public SalesUserInterface(SalesApp myApp) {
        app = myApp;
        app.setMyUserInterface(this);
        setLayout(new BorderLayout());
        setPreferredSize(new Dimension(600, 600));
        mb = new JMenuBar();
        setJMenuBar(mb);
        m = new JMenu("File");
        m1 = new JMenu ("Options"); 
        mb.add(m);
        mb.add(m1); 
        m.add(q = new JMenuItem("Exit"));
        q.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
            
        m1.add(t= new MenuItem("Results"));                             
        t.addActionListener(new ActionListener() {                      
            public void actionPerformed(ActionEvent e){
                inputPanel.setAllInputs();  // added method call to make sure all is set
                if (processed)                                
                {
                    remove(results);                                      
                }
                results = new OutputPanel(app);                           
                add("South", results);                                    
                processed = true;
                results.writeOutput();
        }});  
        
        InitPanel specifyNumber = new InitPanel();     // a panel to set up how many salespeople
        add("North", specifyNumber);
        pack();                                        // put it all together
        setVisible(true);                              // make it show up
    }
 
    private class InitPanel extends JPanel {
        public InitPanel() {
            peopleLabel = new JLabel("Enter the number of sales people");
            add(peopleLabel);
            peopleField = new JTextField(5);
            add(peopleField);
            jbNumPeople = new JButton("Submit");
            add(jbNumPeople);
            jbNumPeople.addActionListener(new NumSalesPeopleListener());
        }
    }
    
    private class NumSalesPeopleListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            if (inputPanel != null)
            {
                remove(inputPanel);
                app = new SalesApp();
            }
            numPeople = Integer.parseInt(peopleField.getText());
            inputPanel = new InputPanel(app, numPeople, 2);
            add("Center", inputPanel);
            SalesUserInterface.this.validate();
        }
    }
}

Save it and Run Main.java. Enter the values, but do not click All Set. Select Options | Results from the menu.

Example 4

Be sure your constructors do not have a return type. If a method has a return type, then it is not a constructor. Constructors do not have return types; by default they return an instance of themselves.

In some cases, you may think you've called a constructor to create an instance of something, but you really haven't. In this next example, our code won't give us a NullPointerException, because it never creates an instance.

Edit SalesUserInterface as shown in blue:

CODE TO EDIT: SalesUserInterface
package salesGUI;
            
java.awt.BorderLayout;
import java.awt.Dimension; 
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
            
public class SalesUserInterface extends JFrame {
    SalesApp app;
    JMenuBar mb;
    JMenu m, m1;
    JMenuItem q, r, s, t;
    InputPanel inputPanel;
    JLabel peopleLabel;
    JTextField peopleField;
    JButton jbNumPeople, done;
    int numPeople;
    OutputPanel results;
    boolean processed = false;
    
    public SalesUserInterface(SalesApp myApp) {
        System.out.println("Did I get made?");
        app = new SalesApp();                        // who am I an interface for?
        app.setMyUserInterface(this);
        setLayout(new BorderLayout());
        setPreferredSize(new Dimension(600, 600));
        mb = new JMenuBar();
        setJMenuBar(mb);
        m = new JMenu("File");
        m1 = new JMenu ("Options"); 
        mb.add(m);
        mb.add(m1); 
        m.add(q = new JMenuItem("Exit"));
        q.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        
        m1.add(t=new JMenuItem("Results")); 
        t.addActionListener(new ActionListener() {  
            public void actionPerformed(ActionEvent e) {  
                inputPanel.setAllInputs();  // added method call to make sure all is set
                if (processed) 
                { 
                    remove(results); 
                } 
                results = new OutputPanel(app); 
                add("South", results); 
                processed = true; 
                results.writeOutput();
            }
        }); 

        InitPanel specifyNumber = new InitPanel(); 
        add("North", specifyNumber);
        pack();
        setVisible(true);   
    }
            
    private class InitPanel extends JPanel {
        public InitPanel() {
            peopleLabel = new JLabel("Enter the number of sales people");
            add(peopleLabel);
            peopleField = new JTextField(5);
            add(peopleField);
            jbNumPeople = new JButton("Submit");
            add(jbNumPeople);
            jbNumPeople.addActionListener(new NumSalesPeopleListener());
        }
    }
    
    private class NumSalesPeopleListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            if (inputPanel != null)
            {
                remove(inputPanel);
                app = new SalesApp();
            }
            numPeople = Integer.parseInt(peopleField.getText());
            inputPanel = new InputPanel(app, numPeople, 2);
            add("Center", inputPanel);
            SalesUserInterface.this.validate();
        }
    }
}

Edit Main as shown in blue:

CODE TO EDIT: Main
package salesGUI;
           
public class Main {
    public static void main(String[] args) {
        SalesApp newApp = new SalesApp();
        SalesUserInterface appFrame = new SalesUserInterface();
        // so we can see if things are set as expected:
        System.out.println("I think I made it and am back");
        appFrame.app.setMyUserInterface(appFrame);
    }
}

Save and run the application from Main. Check the Console to make sure that both println comments appear. Make sure that the rest of the application works as expected. Now, give the SalesUserInterface class's constructor (found around line 21) a void return type. Add void to the SalesUserInterface() constructor as shown:

CODE TO EDIT: SalesUserInterface
package salesGUI;
    
java.awt.BorderLayout;
import java.awt.Dimension; 
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
    
public class SalesUserInterface extends JFrame {
    SalesApp app;
    JMenuBar mb;
    JMenu m, m1;
    JMenuItem q, r, s, t;
    InputPanel inputPanel;
    JLabel peopleLabel;
    JTextField peopleField;
    JButton jbNumPeople, done;
    int numPeople;
    OutputPanel results;
    boolean processed = false;
    
    public void SalesUserInterface(SalesApp myApp){
        System.out.println("Did I get made?");
        app = new SalesApp();
        app.setMyUserInterface(this);
        setLayout(new BorderLayout());
        setPreferredSize(new Dimension(600, 600));
        mb = new JMenuBar();
        setJMenuBar(mb);
        m = new JMenu("File");
        m1 = new JMenu ("Options"); 
        mb.add(m);
        mb.add(m1); 
        m.add(q = new JMenuItem("Exit"));
        q.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        
        m1.add(t=new JMenuItem("Results")); 
        t.addActionListener(new ActionListener() {  
            public void actionPerformed(ActionEvent e) {  
                inputPanel.setAllInputs();  // added method call to make sure all is set
                if (processed) 
                { 
                    remove(results); 
                } 
                results = new OutputPanel(app); 
                add("South", results); 
                processed = true; 
                results.writeOutput();
            }
        }); 

        InitPanel specifyNumber = new InitPanel();
        add("North", specifyNumber);
        pack();
        setVisible(true);
    }
    
    private class InitPanel extends JPanel {
        public InitPanel() {
            peopleLabel = new JLabel("Enter the number of sales people");
            add(peopleLabel);
            peopleField = new JTextField(5);
            add(peopleField);
            jbNumPeople = new JButton("Submit");
            add(jbNumPeople);
            jbNumPeople.addActionListener(new NumSalesPeopleListener());
        }
    }
    
    private class NumSalesPeopleListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            if (inputPanel != null)
            {
                remove(inputPanel);
                app = new SalesApp();
            }
            numPeople = Integer.parseInt(peopleField.getText());
            inputPanel = new InputPanel(app, numPeople, 2);
            add("Center", inputPanel);
            SalesUserInterface.this.validate();
        }
    }
}

Make this change to the Main as well:

CODE TO EDIT: Main
package salesGUI;

public class Main {

    public static void main(String[] args) { 
        SalesApp newApp = new SalesApp();
        // Remove the passed newApp parameter
        SalesUserInterface appFrame = new SalesUserInterface();
        System.out.println("I think I made it and am back");
        appFrame.app.setMyUserInterface(appFrame);
    }
}

Save both classes, and run the Main class. Nothing opens and we see this in the console:

We did not get the System.out.println Did I get made? from our SalesUserInterface constructor. And we didn't get a NullPointerException until the line after the instantiation in Main. The line of code intended to make an instance for SalesUserInterface ran, but nothing happened. Why?

Because void was given as a return type, the SalesUserInterface class didn't really have a constructor, so Java just let the class inherit the constructor from its super. The line SalesUserInterface appFrame = new SalesUserInterface(); ran as expected, but its Constructor was only run from JFrame. As a result of inheritance, we did not get a NullPointerException--yet. We called access to the application in the Main, but it should have been called in the Constructor. Since Java never made the instance in the proper Constructor, most of the variables we thought were set weren't.

NoteThis is a difficult error to find, so make sure you never give a constructor declaration a return type.

Example 5

Edit SalesApp as shown in blue and red:

CODE TO EDIT: SalesApp
package salesGUI;

public class SalesApp {
    private int [] sales;
    private int salesBar; 
    private int totalSales;
    // Comment out the next line and add the following line. 
    // private double average; 
    private double average = totalSales/sales.length; 
    private int minIndex = 0;
    private int maxIndex = 0;
    SalesUserInterface myUserInterface;
    
    public void setMyUserInterface(SalesUserInterface myGUI){
        myUserInterface = myGUI;
    }
      
    public void setSales(int[] sales) {
        this.sales = sales;
        for (int i = 0; i < sales.length; i++)
            System.out.println("sales [i] = " + sales[i]);
        setTotalSales();
    }
    
    public void setTotalSales() {
        totalSales = 0;
        for (int x = 0; x < sales.length; x++)
            totalSales += sales[x];
        setAverage();
    }
    
    public void setAverage() {
        if (sales.length != 0)
            average = (double)(totalSales / sales.length);
        System.out.println("totalSales is " + totalSales + " and sales.length is "
                + sales.length + ", making average "
                + ((double) totalSales / sales.length));
    }
    
    public void setSalesBar(int goal) {
        salesBar = goal;
    }
    
    public int[] getSales() {
        return sales;
    }
    
    public double getAverage() {
        if (sales.length != 0)
            return ((double) totalSales / sales.length);
        else
            return average;
    }
    
    public int getBar() {
        return salesBar;
    }
    
    public int getTotalSales() {
        return totalSales;
    }
    
    public int getMin() {
        return minIndex;
    }
    
    public int getMax() {
        return maxIndex;
    }
    
    public void calculateMinMax() {
        int minimum = sales[0];
        int maximum = sales[0];
        for (int x = 0; x < sales.length; x++) {
            if (sales[x] > maximum) {
                maximum = sales[x];
                maxIndex = x;
            }
            else if (sales[x] < minimum) {
                minimum = sales[x];
                minIndex = x;
            }
        }
        System.out.println("Maximum value is at index " + maxIndex
                + " (Salesperson " + (maxIndex + 1) + ") with value " + maximum);
        System.out.println("Minimum value is at index " + minIndex
                + " (Salesperson " + (minIndex + 1) + ") with value " + minimum);
        setAverage();
    }
    
    public int[] determineTopSalesPeople() {
        System.out.println("I'm here and salesBar is " + salesBar);
        int[] performance = new int [sales.length];
        for (int x = 0; x < sales.length; x++) {
            if (sales[x] > salesBar) {
                performance[x] = 1;
            }
            else if (sales[x] == salesBar) {
                  performance[x] = 0;
            }
            else {
                performance[x] = -1;
            }
        }
        return performance;
    }
}

Save it and run Main.

Can you see why there's a null pointer? You don't have a sales[] array instantiated yet.

Division By Zero

We'll keep working with the last example to explore another subclass (java.lang.ArithmeticException) of RuntimeException.

Edit SalesApp as shown in blue and red:

CODE TO EDIT: SalesApp
package salesGUI;

public class SalesApp {
    private int [] sales;
    private int salesBar; 
    private int totalSales;
    // private double average; 
    // Comment out or remove the next line:
    // private double average = totalSales/sales.length;
    private int numSalesPeople;                     
    private double average = totalSales/numSalesPeople;                        
    private int minIndex = 0;
    private int maxIndex = 0;
    SalesUserInterface myUserInterface;
    
    public void setMyUserInterface(SalesUserInterface myGUI){
        myUserInterface = myGUI;
    }
      
    public void setSales(int[] sales) {
        this.sales = sales;
        for (int i = 0; i < sales.length; i++)
            System.out.println("sales [i] = " + sales[i]);
        setTotalSales();
    }
    
    public void setTotalSales() {
        totalSales = 0;
        for (int x = 0; x < sales.length; x++)
            totalSales += sales[x];
        setAverage();
    }
    
    public void setAverage() {
        if (sales.length != 0)
            average = (double)(totalSales / sales.length);
        System.out.println("totalSales is " + totalSales + " and sales.length is "
                + sales.length + ", making average "
                + ((double) totalSales / sales.length));
    }
    
    public void setSalesBar(int goal) {
        salesBar = goal;
    }
    
    public int[] getSales() {
        return sales;
    }
    
    public double getAverage() {
        if (sales.length != 0)
            return ((double) totalSales / sales.length);
        else
            return average;
    }
    
    public int getBar() {
        return salesBar;
    }
    
    public int getTotalSales() {
        return totalSales;
    }
    
    public int getMin() {
        return minIndex;
    }
    
    public int getMax() {
        return maxIndex;
    }
    
    public void calculateMinMax() {
        int minimum = sales[0];
        int maximum = sales[0];
        for (int x = 0; x < sales.length; x++) {
            if (sales[x] > maximum) {
                maximum = sales[x];
                maxIndex = x;
            }
            else if (sales[x] < minimum) {
                minimum = sales[x];
                minIndex = x;
            }
        }
        System.out.println("Maximum value is at index " + maxIndex
                + " (Salesperson " + (maxIndex + 1) + ") with value " + maximum);
        System.out.println("Minimum value is at index " + minIndex
                + " (Salesperson " + (minIndex + 1) + ") with value " + minimum);
        setAverage();
    }
    
    public int[] determineTopSalesPeople() {
        System.out.println("I'm here and salesBar is " + salesBar);
        int[] performance = new int [sales.length];
        for (int x = 0; x < sales.length; x++) {
            if (sales[x] > salesBar) {
                performance[x] = 1;
            }
            else if (sales[x] == salesBar) {
                  performance[x] = 0;
            }
            else {
                performance[x] = -1;
            }
        }
        return performance;
    }
}

Save it and run Main.

It's difficult for the compiler to catch these kinds of exceptions before runtime. When the compiler scans the code for proper syntax, it doesn't know in advance whether a value for numSalesPeople has been set. The compiler won't know whether the value is 0 at the time of compilation.

Change SalesApp back, as shown in blue:

CODE TO EDIT: SalesApp
package salesGUI;

public class SalesApp {
    private int[] sales;                          
    private int salesBar;                           
    private int totalSales;                      
    private double average;                       
    private int minIndex=0;
    private int maxIndex=0;
    SalesUserInterface myUserInterface;                    

    public void setMyUserInterface(SalesUserInterface myGUI){
        myUserInterface = myGUI;
    }
      
    public void setSales(int[] sales) {
        this.sales = sales;
        for (int i = 0; i < sales.length; i++)
            System.out.println("sales [i] = " + sales[i]);
        setTotalSales();
    }
    
    public void setTotalSales() {
        totalSales = 0;
        for (int x = 0; x < sales.length; x++)
            totalSales += sales[x];
        setAverage();
    }
    
    public void setAverage() {
        if (sales.length != 0)
            average = (double)(totalSales / sales.length);
        System.out.println("totalSales is " + totalSales + " and sales.length is "
                + sales.length + ", making average "
                + ((double) totalSales / sales.length));
    }
    
    public void setSalesBar(int goal) {
        salesBar = goal;
    }
    
    public int[] getSales() {
        return sales;
    }
    
    public double getAverage() {
        if (sales.length != 0)
            return ((double) totalSales / sales.length);
        else
            return average;
    }
    
    public int getBar() {
        return salesBar;
    }
    
    public int getTotalSales() {
        return totalSales;
    }
    
    public int getMin() {
        return minIndex;
    }
    
    public int getMax() {
        return maxIndex;
    }
    
    public void calculateMinMax() {
        int minimum = sales[0];
        int maximum = sales[0];
        for (int x = 0; x < sales.length; x++) {
            if (sales[x] > maximum) {
                maximum = sales[x];
                maxIndex = x;
            }
            else if (sales[x] < minimum) {
                minimum = sales[x];
                minIndex = x;
            }
        }
        System.out.println("Maximum value is at index " + maxIndex
                + " (Salesperson " + (maxIndex + 1) + ") with value " + maximum);
        System.out.println("Minimum value is at index " + minIndex
                + " (Salesperson " + (minIndex + 1) + ") with value " + minimum);
        setAverage();
    }
    
    public int[] determineTopSalesPeople() {
        System.out.println("I'm here and salesBar is " + salesBar);
        int[] performance = new int [sales.length];
        for (int x = 0; x < sales.length; x++) {
            if (sales[x] > salesBar) {
                performance[x] = 1;
            }
            else if (sales[x] == salesBar) {
                  performance[x] = 0;
            }
            else {
                performance[x] = -1;
            }
        }
        return performance;
    }
}

Change Main back, as shown in blue:

Code to Edit: Main
package salesGUI;
         
public class Main {
    public static void main(String[] args) {
         
        SalesApp newApp = new SalesApp();
        SalesUserInterface appFrame = new SalesUserInterface(newApp);
        System.out.println("I think I made it and am back");
        appFrame.app.setMyUserInterface(appFrame);
    }
}   

Save and run it to make sure it works as before.

Array Out of Bounds

Let's check out another subclass (java.lang.ArrayIndexOutOfBoundsException) of RuntimeException that can cause runtime exceptions.

You can go out of bounds pretty easily using for loops. Take a look. Create a new class as shown:

Type SalesPeople as shown in blue:

CODE TO TYPE: SalesPeople
package salesGUI;
    
public class SalesPeople {
    public static void main(String[] args) {
        String[] salesPeople;             
        salesPeople = new String[4];     
    
        salesPeople[0] = "John"; 
        salesPeople[1] = "Paul"; 
        salesPeople[2] = "George"; 
        salesPeople[3] = "Ringo";
    
        for (int i=0;  i <= salesPeople.length; i++)
            System.out.println("Element at index " + i + " : " + salesPeople[i]);
        System.out.println("Size of the salesPeople array is " + salesPeople.length);
    }
}

Save and run it.

The attribute length of an array is equal to the number of elements in the array, but the indices start at 0, so the last index is length - 1. If we try to loop to the value of sales[length], we will get a java.lang.ArrayIndexOutOfBoundsException.

Array Out of Bounds Example 2

Look at Main.java in our java4_Lesson1 project under the sales1 package:

OBSERVE: Main
package sales1;
                
public class Main { 

    public static void main(String[] args){
        if (args.length > 0)
        {
            int argIn = Integer.parseInt(args[0]);             // user inputs argument at command line so use it
            SalesReport mySalesInfo = new SalesReport(argIn);  // pass input as parameter to constructor
            mySalesInfo.testMe();                              // start the application
        }
        else
        {                                                      // no input from user so ask in constructor
            SalesReport mySalesInfo = new SalesReport();       // instantiate the class with constructor with no parameters - will prompt for input
            mySalesInfo.testMe();                              // start the application
        }
    }
}               

Because we have included if (args.length > 0), the user can enter arguments either at the command line or via a GUI prompt. In an earlier version of this Main.java class, we tried to determine whether the user had entered an argument at the command line by using the conditional statement: if (args[0] != null). This conditional statement actually looks for args[0], but might not find an args array at all. In that case, args[0] would already be out of bounds. Let's try it again. Edit the conditional statement as shown in blue:

CODE TO EDIT: Main
package sales1;
                
public class Main { 

    public static void main(String[] args){
        if (args[0] != null) 
        {
            int argIn = Integer.parseInt(args[0]);
            SalesReport mySalesInfo = new SalesReport(argIn);
            mySalesInfo.testMe();
        }
        else
        {  
            SalesReport mySalesInfo = new SalesReport();  
            mySalesInfo.testMe();                        
        }
    }
}

Save and run it. You'll see a message in the Console: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0.

It is generally time-consuming and inefficient for Java to use try and catch blocks when an appropriate if statement works well enough. But let's try it anyway just to illustrate the idea of catching an ArrayIndexOutOfBoundsException. Edit the Main in sales1 as shown in blue below:

CODE TO EDIT: Main
package sales1;
                
public class Main { 

    public static void main(String[] args){

        try  
        {
            int argIn = Integer.parseInt(args[0]);             // if no args[0], will throw ArrayIndexOutOfBoundsException
            SalesReport mySalesInfo = new SalesReport(argIn);
            mySalesInfo.testMe();
        }
        catch (ArrayIndexOutOfBoundsException exception)
        {
            SalesReport mySalesInfo = new SalesReport();
            mySalesInfo.testMe();	
        }
    }
}

Save and run it.

TipBecause try/catch clauses are more labor intensive (for Java and for you) than conditional statements, exception handling should be just that: an exception to the norm.

Errors

Errors are conditions that happen outside of the application; for example, OutOfMemoryError. They are usually the result of a major programming mistake. For example, a recursive program (a program with a method that calls itself) might not have a stop statement, which means an infinite loop would be created. This could then generate a StackOverflowError.

Try this code:

CODE TO TYPE: InfiniteLoop

public class InfiniteLoop {
	
    public int tryMe(int x){
        // System.out.println(x); // Run first without this line--then uncomment just to see how many times it ran before having the overflow
        return tryMe(x+1);
    }

    public static void main(String [] args){
        InfiniteLoop silly = new InfiniteLoop();
        silly.tryMe(1);
    }
}

Save and run it. This is what you don't want your customers to see!

Unfortunately, Errors can also occur when we do distributed computing--that is, when we go outside of the environment of our own machine. When a dynamic linking failure or other hard failure (that is, a failure that needs to be fixed by a programmer) occurs in the Java virtual machine, the virtual machine throws an Error. According to the Oracle Tutorial's section on Errors in The Catch or Specify Requirement:

"The second kind of exception is the error. These are exceptional conditions that are external to the application, and that the application usually cannot anticipate or recover from. For example, suppose that an application successfully opens a file for input, but is unable to read the file because of a hardware or system malfunction. The unsuccessful read will throw java.io.IOError. An application might choose to catch this exception, in order to notify the user of the problem — but it also might make sense for the program to print a stack trace and exit.

Errors are not subject to the Catch or Specify Requirement. Errors are those exceptions indicated by Error and its subclasses."

We've already seen that the API is a valuable source of Interface, Class, and Exception information; now we'll see how it helps us tackle Errors.

Go to the java.lang package in the API. Scroll down to the Error Summary. Read about a few of them. Both Exceptions and Errors inherit from the class Throwable...interesting.

Simple programs typically do not catch or throw Errors, but checked exceptions are subject to the Catch or Specify Requirement. We'll discuss that requirement in detail in the next lesson.

Programming Responsibly

In this lesson, we've seen examples of subclasses of runtime exceptions and how to prevent them by careful coding. Since programmers share code with one another so often, that's pretty important.

Although Java requires that methods catch or specify checked exceptions, methods that we write do not have to catch or specify unchecked exceptions (such as runtime exceptions). And because catching or specifying an exception requires more work, programmers are occasionally tempted to write code that throws only runtime exceptions and therefore doesn't have to catch or specify them. This is exception abuse, and is not recommended. For more information, see the Oracle Tutorial Unchecked Exceptions - The Controversy.

The more you check your code to prevent unchecked exceptions from occurring at runtime, the better for all concerned. Don't make Duke angry.