Java Programming 4: Java Application Building Lesso n 1: Applicat io n Building Review Preview Impro ving Yo ur Co de Design Pattern: Mo del/View/Co ntro ller Separating Applicatio n fro m User Interface Co ming Attractio ns: The View Lesso n 2: Swing: A Ve ry Brie f Ove rvie w AWT vs. Swing Hello Wo rld in AWT and Swing Changing Appearance JApplets, JFrames, and Threads Mo re Info rmatio n o n Applets Mo re Info rmatio n o n JApplets Even Mo re Swing Lesso n 3: Graphical Use r Int e rf ace s Views JFrames and JPanels JFrames: The To p-Level View Lesso n 4: Graphical Use r Int e rf ace s, co nt inue d Making the Output Panel Lesso n 5: Erro r Che cking and Exce pt io n Handling Being Prepared fo r Users Crashes Expect the Unexpected Handling Exceptio ns Finding the Pro blem Fixing the Pro blem Try/Catch Clauses Anticipating Exceptio ns Making It Right: Dialo g Bo xes Types o f Exceptio ns Checked Exceptio ns Unchecked Exceptio ns The Other Pro blem Lesso n 6 : Unche cke d Exce pt io ns: Ke e ping Our Applicat io ns Running Abo ut Exceptio ns Run-Time Exceptio ns NullPo interExceptio n Example 1

Example 2 Example 3 Example 4 Example 5 Divisio n By Zero Array Out o f Bo unds Array Out o f Bo unds Example 2 Erro rs Pro gramming Respo nsibly Lesso n 7: Che cke d Exce pt io ns: Cat ching Pro ble m s Degrading Gracefully I/O Exceptio ns Exceptio n Types Using Try and Catch Fo rwarding the Exceptio n Determining What To Do With Exceptio ns Thro wing Exceptio ns Example Passing Exceptio ns to Other Metho ds Catching Exceptio ns fro m Other Metho ds One Mo re Time Exceptio n Hierarchy Using Finally in a Try/Catch Blo ck Additio nal Info rmatio n Lesso n 8 : T hre ads: Int ro duct io n Multi-Tasking Threads Subclassing the Thread Class Manipulating Threads Threads in Applets Implementing the Runnable Interface One Mo re Time Lesso n 9 : T hre ads: Co ncurre nt Pro gram m ing Behind the Scenes Multi-threaded Applicatio ns The Life o f a Thread What's Happening in the Backgro und? Garbage Co llectio n Thread States Thread.State Mo re o n Multi-Threaded Applicatio ns Design Pattern: Pro ducer/Co nsumer Using Threads in an Applicatio n

The Pro ducer The Co nsumer The Mo nito r The GUI View Lesso n 10 : T hre ads: Synchro nizat io n Race Co nditio ns Fixing a Race Co nditio n Ano ther Race Co nditio n Additio nal Reso urces Using Threads in a Game Creating the Typing Game Preview: The Classes The Bo mb Pro ducing Wo rds Co nsuming Keys Mo nito ring Wo rds The User Interface We Lo ve Threads Real Games Blackjack Lesso n 11: Dat abase s: Co nne ct ivit y t hro ugh J ava The JDBC API What is JDBC? What Do es JDBC Help Us Do ? Co nnecting to the Database Access to a Database The Driver Verifying the Co nnectio n The Facto ry Design Pattern Testing Our Co nnectio n The Main 1: Pro viding Parameters In Co de 2: Giving Parameters in Eclipse Sending SQL Statements User Access and Input Clo sing Our Co nnectio ns Additio nal Reso urces Lesso n 12: Dat abase s and J ava: Pro ce ssing Inf o rm at io n Getting Results Databases and SQL ResultSet Getting Info rmatio n Abo ut Info rmatio n Metadata

Creating a Table Clo sing Pro perly Seeing Table Co ntents SQL Co mmands Lo gging In Lesso n 13: Dat abase Applicat io n Wit h GUI Refining the Applicatio n Impro ving the Appearance Co pying an Existing Class Creating New Classes Creating the View Creating Co ntro llers fo r the View Additio nal Reso urces Lesso n 14: Do cum e nt at io n: J avado c, Do c Co m m e nt s, and Anno t at io n Do cumenting Yo ur Wo rk Javado c and API Pages DatabaseManager Creating Javado cs Do cumenting and Tagging the Applicatio n SimplePho neBo o k Pho neBo o kFrame ListingsTableMo del AddListingListener AddListingDialo g Pho neDo cumentListener Pho neFo cusListener The Package Do cumentatio n Additio nal Reso urces What's Next?

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Application Building Welco me to the J ava Applicat io n Building series o f Java co urses. This series will fo cus o n develo ping applicatio ns using the many to o ls available in Java. As in all OST co urses, the emphasis will be o n interactive instructio n.

Course Objectives When yo u co mplete this co urse, yo u will be able to : enhance Graphical User Interfaces in Java using views, frames, panels, and Swing. implement erro r checking, exceptio n handling, and try/catch clauses to minimize bugs. catch unchecked exceptio ns and prepare fo r pro blems thro ugh graceful degradatio n. create and manipulate threads fo r co ncurrent pro gramming. co nnect with databases using the JDBC API, facto ry design patterns, and view co ntro llers. do cument and tag co de using Javado c and API pages. In this co urse, yo u will achieve an understanding o f the structure and purpo ses fo r many o f the classes in the Java API. In-depth experience with user-interfaces, event and exceptio n handling, database co nnectivity, multiple threads and synchro nizatio n will pro vide yo u with a to o lkit fo r bo th implementing applicatio ns as well as understanding so urce co de o f o thers. Pro grams designed in the co urse using Java Threads, Client/Server So ckets and Database Co nnectivity pro vide a so lid basis fo r applicatio n building. Fro m beginning to end, yo u will learn by do ing yo ur o wn Java pro jects, within o ur Eclipse Learning Sandbo x we affectio nately call "Ellipse". These pro jects will add to yo ur po rtfo lio and pro vide needed experience. Besides a bro wser and internet co nnectio n, all so ftware is pro vided o nline by the O'Reilly Scho o l o f Techno lo gy.

Review In this series, we assume that yo u have a general fo undatio n o f Java and o bject-o riented pro gramming kno wledge, so basic pro gramming skills wo n't be co vered in this co urse. We'll wo rk no w to gro w and refine yo ur existing Java pro gramming skills. If yo u are unable to fo llo w the co de that we use fo r illustratio ns and examples in this co urse, we reco mmend that yo u take OST's first Java series o f co urses to gain tho se basic pro gramming skills; then yo u'll be able to reap the full benefits o f the materials presented here. If yo u are new to OST co urses, read this o verview befo re yo u go further. In the previo us co urse, we went o ver these co ncepts: Co de flexibility Package declaratio n and usage Separatio n o f classes to mo del the Mo del/View/Co ntro ller (MVC) design pattern Interfaces Casting Declaratio n and use o f inner classes We'll apply these ideas and mo re as we impro ve o ur Sales Repo rt applicatio n.

Preview In the first new versio n o f o ur applicatio n, we'll bring in Layo ut Manage rs to pro vide a better user interface. Except fo r the vario us Layo ut Manage rs and Pane ls, we'll also be using familiar co de and techniques. In upco ming lesso ns, we will learn additio nal techniques that will enable us to : pro vide exceptio n handling. make better GUIs. allo w applicatio n deplo yment using jars and executables. add input fro m o ther so urces (such as databases and "o ffsite" machines). allo w multiple peo ple to use the same applicatio ns at the same time (threads).

add o ther useful features to o ur applicatio ns. Ready? Alright then!

Improving Your Code Let's get to wo rk using the same applicatio n we develo ped in the previo us series. The applicatio n pro mpts users fo r the number o f salespeo ple and their sales perfo rmance figures, and then displays the to p perfo rmer. (If yo u to o k the earlier co urse, t ype the co de in and run it.) Create a new java4 _Le sso n1 pro ject (it may help to take a lo o k at the o verview). No w, create a new m ain class in this pro ject as sho wn:

Type m ain as sho wn in blue :

CODE TO TYPE: Main // **************************************************************** // Main.java // // Instantiates and starts the SalesReport class // // **************************************************************** package sales1; public class Main { public static void main(String[] args){ if (args.length > 0) { int argIn = Integer.parseInt(args[0]); SalesReport mySalesInfo = new SalesReport(argIn); mySalesInfo.testMe(); } else { SalesReport mySalesInfo = new SalesReport(); // instantiate the class mySalesInfo.testMe(); // start the application } } } This class instantiates and starts o ur applicatio n. There will be erro rs in Main, because we still haven't created the class that it instantiates. In java4_Lesso n1, create the Sale sRe po rt class as sho wn:

Type Sale sRe po rt as sho wn in blue belo w:

CODE TO TYPE: SalesRepo rt package sales1; import java.util.Scanner; public class SalesReport{ int SALESPEOPLE; int sum; int sales[]; Scanner scan = new Scanner(System.in); public SalesReport(){ System.out.print("Enter the number of salespersons: "); this.SALESPEOPLE = scan.nextInt(); this.sales = new int[SALESPEOPLE]; } public SalesReport(int howMany){ this.SALESPEOPLE = howMany; this.sales = new int[SALESPEOPLE]; } public void testMe(){ getSalesInput(); provideSalesOutput(); findMax(); } public void getSalesInput(){ Scanner scan = new Scanner(System.in); for (int i=0; i < sales.length; i++) { System.out.print("Enter sales for salesperson " + (i+1) + ": "); sales[i] = scan.nextInt(); } } public void provideSalesOutput(){ System.out.println("\nSalesperson Sales"); System.out.println("--------------------"); sum = 0; for (int i=0; i < sales.length; i++) { System.out.println(" " + (i+1) + " sum = sum + sales[i]; } System.out.println("\nTotal sales: " + sum); }

" + sales[i]);

public void findMax(){ int max = sales[0]; // this way we are assured that value for the initial max is in the collection int who = 0; // and the initial index is the first so we visit all for (int i=0; i < sales.length; i++) { if (max < sales[i]) { max = sales[i]; who = i; } } System.out.println("\nSalesPerson " + (who+1) + " had the highest sale with $" + max ); } }

Save and

Run it.

Since Sale sRe po rt do es no t extend Apple t , it is no t an Applet, and since Sale sRe po rt do es no t have a m ain() metho d, Java is no t sure what to do . We created the Main class to instantiate and start this applicatio n, so we sho uld go to that class to run it.

Note

In Eclipse, if yo u cho o se Run As, but neither J ava Apple t no r J ava Applicat io n appear as o ptio ns, click in the Editor Window. This lets Eclipse kno w that yo u are running the .java file.

Click o n the Main.java class. (Sale sRe po rt is no lo nger unkno wn, because we have defined it no w.) Save and Run it. The Co nso le o pens and is ready fo r yo u to pro vide input:

Click in the co nso le windo w, type 2, and press Ent e r. Yo u're asked fo r sales numbers fo r salesperso ns 1 and 2-enter any number fo r each and press Ent e r. Trace the co de fro m the instantiatio n in Main(). The co de wo rks fine, but we can impro ve it. Previo usly, we wro te co de to find the average and minimum sales, and to allo w the user to set a number as a go al and determine which salespeo ple reached this go al. The new versio ns o f the Sale sRe po rt applicatio n we'll write in this co urse will call upo n many o f the added po tentials we learned earlier.

Design Pattern: Model/View/Controller A co mmo n and well-kno wn design pattern in o bject-o riented pro gramming invo lves separating the vario us co mpo nents o f an applicatio n based o n functio n. In keeping with o bject-o riented principles o f mo dularity and the MVC design pattern, we'll separate the co mpo nents in o ur co nstructio n. First, we will make the applicatio n, o r Model class. The mo del class ho lds the co de that defines a particular applicatio n. The applicatio n functio nality required fo r Sale sRe po rt was the ability to determine the average sales, the minimum and maximum sales, and which salespeo ple surpassed a go al set by the user.

Separating Application from User Interface

Let's put o ur new versio n o f the applicatio n in a different package. In the java4_Lesso n1 pro ject, add a new class as sho wn:

Type Sale sApp as sho wn in blue : CODE TO TYPE: SalesApp package salesGUI; public class SalesApp { //array to hold sales of each salesperson private int[] sales; //variable for sales goal (to be established by user) private int salesBar; private int totalSales; //why not average = totalSales/sales.length; here? private double average; private int minIndex = 0; private int maxIndex = 0; SalesUserInterface myUserInterface; }

Save it. OBSERVE: private int[] sales; private int salesBar; private int totalSales; private double average; private int minIndex = 0; private int maxIndex = 0; SalesUserInterface myUserInterface; Here we set up variables fo r the m o de l o f o ur Sales Repo rt Applicatio n. We created the sales array sale s to ho ld each salesperso n's sales figures. The sale sBar variable will ho ld o ur sales go al. The t o t alSale s and ave rage variables will keep the to tals and average sales. The m inInde x and m axInde x will ho ld the lo catio ns in the sale s array fo r the minimum sales and maximum sales. Sale sUse rInt e rf ace m yUse rInt e rf ace is a reference to the GUI fo r the SalesUserInterface applicatio n that we'll be making in Lesso n 3. We'll use it to send info rmatio n to and fro m the GUI. No w add the rest o f the setters fo r the private variables. Add the blue co de as sho wn:

CODE TO EDIT: SalesApp package salesGUI; public class SalesApp { //array to hold sales of each salesperson private int[] sales; //variable for sales goal (to be established by user) private int salesBar; //sales of all sales people together private int totalSales; //why not average = totalSales/sales.length; here? 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++) // checking to see if it's working System.out.println(sales[i]); // data consistency setTotalSales(); } public void setTotalSales() { totalSales = 0; for (int x = 0; x < sales.length; x++) totalSales += sales[x]; setAverage(); // data consistency } 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; } }

Save it.

OBSERVE: Setters public void setMyUserInterface(SalesUserInterface myGUI)){ myUserInterface = myGUI; } public void setSales(int[] sales) { this.sales = sales; for (int i = 0; i < sales.length; i++) // just checking to see if working System.out.println(sales[i]); // data consistency setTotalSales(); } public void setTotalSales() { totalSales = 0; for (int x = 0; x < sales.length; x++) totalSales += sales[x]; setAverage(); // data consistency } 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; }

The five m e t ho ds abo ve are setters fo r the variables sale s, t o t alSale s, and ave rage . They are chained to gether; if we call se t Sale s(), it calls se t T o t alSale s(), which in turn calls se t Ave rage (). This ensures that when we set the sales, the to talSales and average are up to date and co nsistent with the current sales array data. Finally, we set the salesBar variable with se t Sale Bar(int go al). The goal will be an integer that is set by the end user when we build o ur User Interface in a future lesso n. No w, add the getters. Add the co de sho wn in blue :

CODE TO EDIT: SalesApp package salesGUI; public class SalesApp { //array to hold sales of each salesperson private int[] sales; //variable for sales goal (to be established by user) private int salesBar; private int totalSales; //why not average = totalSales/sales.length; here? 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++) // just checking to see if working System.out.println(sales[i]); // data consistency setTotalSales(); } public void setTotalSales() { totalSales = 0; for (int x = 0; x < sales.length; x++) totalSales += sales[x]; setAverage(); // data consistency } 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) // cast so does not truncate int division 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; } }

Save it. Let's take a clo ser lo o k at the ge t Ave rage () getter: OBSERVE public double getAverage() { if (sales.length != 0) return ((double) totalSales / sales.length); else return average; } If the sales array length (the user-entered number o f salesperso ns) is no t 0 , ge t Ave rage () calculates the average befo re returning its value; o therwise, it returns the value o f the ave rage variable. No w create a metho d that calculates the minimum and maximum sales, using co mpariso ns. Add the co de sho wn in blue :

CODE TO EDIT: SalesApp package salesGUI; public class SalesApp { //array to hold sales of each salesperson private int[] sales; //variable for sales goal (to be established by user) private int salesBar; private int totalSales; //why not average = totalSales/sales.length; here? 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++) // just checking to see if working System.out.println(sales[i]); // data consistency setTotalSales(); } public void setTotalSales() { totalSales = 0; for (int x = 0; x < sales.length; x++) totalSales += sales[x]; setAverage(); // data consistency } 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) // cast so does not truncate int division 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]; // loop through the sales array to see each sales amount for (int x = 0; x < sales.length; x++) { //Check for max sale if (sales[x] > maximum) { maximum = sales[x]; maxIndex = x; } else if (sales[x] < minimum) //Check for min sale { 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(); } }

Save it. The calculateMinMax() metho d public void calculateMinMax() { int minimum = sales[0]; int maximum = sales[0]; // loop through the sales array to see each sales amount for (int x = 0; x < sales.length; x++) { //Check for max sale if (sales[x] > maximum) { maximum = sales[x]; maxIndex = x; } else if (sales[x] < minimum) //Check for min sale { 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(); } The calculat e MaxMin() sets the index o f the maximum (m axInde x) and minimum (m inInde x) values in the sales array. We set lo cal variables m inim um and m axim um to the value in the sales[0 ] element as a starting po int, then lo o p thro ugh the array. If the value in a particular index is greater than the previo us maximum, we set maximum to that value. If the value in a particular index is no t greater than the previo us maximum, we check to see if the value is less than the previo us minimum, and if so , we set minimum to the

new value. We also keep track o f the lo catio n o f the indexes that co ntain the current minimum (m inInde x) and maximum (m axInde x) values in the array. Okay, no w we'll add a metho d to determine who the to p sales peo ple are, so we can praise them and then give them even mo re wo rk! Edit yo ur co de as sho wn in blue :

CODE TO EDIT: SalesApp package salesGUI; public class SalesApp { //array to hold sales of each salesperson private int[] sales; //variable for sales goal (to be established by user) private int salesBar; private int totalSales; //why not average = totalSales/sales.length; here? 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++) // just checking to see if working System.out.println(sales[i]); // data consistency setTotalSales(); } public void setTotalSales() { totalSales = 0; for (int x = 0; x < sales.length; x++) totalSales += sales[x]; setAverage(); // data consistency } 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) // cast so does not truncate int division 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]; // loop through the sales array to see each sales amount for (int x = 0; x < sales.length; x++) { //Check for max sale if (sales[x] > maximum) { maximum = sales[x]; maxIndex = x; } else if (sales[x] < minimum) //Check for min sale { 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(); } //method returns performance array to indicate success at reaching goal public int[] determineTopSalesPeople() { // System.out prints to console to be sure we got here--debugging tool System.out.println("I'm here and salesBar is " + salesBar); // an array with values of -1, 0, 1 to indicate success at reaching goal int[] performance = new int[sales.length]; // Loop through the sales array and see who sold more than the sales bar 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. The metho d de t e rm ine T o pSale sPe o ple () will return the to p-perfo rming salespeo ple in the sales array. It returns an integer array, asso ciated with the sales array. If a salesperso n's perfo rmance is belo w the salesBar, then we place a -1 in the co rrespo nding slo t o f the integer array. If perfo rmance is equal to the salesBar, then we place a 0 in that slo t. And if a salesperso n's perfo rmance is abo ve the salesBar, then we place a +1 in that slo t.

Coming Attractions: T he View The "View" is exactly that: the View o r GUI used to interact with the Mo del. Eventually we'll create the Graphical User Interface (GUI) using Swing co mpo nents, so in the next lesso n we'll co ver so me Swing basics. Using javax.swing package is similar to using java.awt co mpo nents. In fact, Swing co mpo nents usually inherit fro m the awt co mpo nents:

Duke will help yo u wo rk o ut the design.

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Swing: A Very Brief Overview AWT vs. Swing This lesso n will give yo u a brief o verview o f the Swing package, and in particular, co mpare the AWT package to the Swing package. The similarities between the co mpo nents o f these two packages will allo w yo u to assimilate the new material and ultimately inco rpo rate mo re o ptio ns to co ntro l the appearance o f yo ur GUI. Acco rding to the freejavaguide.co m page o n Java Swing: Free Java Tuto rials: Java Swing is a GUI toolkit for Java. Swing is one part of the Java Foundation Classes (JFC). Swing includes graphical user interface (GUI) widgets such as text boxes, buttons, split-panes, and tables. Swing widgets provide more sophisticated GUI components than the earlier Abstract Window Toolkit. Since they are written in pure Java, they run the same on all platforms, unlike the [first] AWT which is tied to the underlying platform's windowing system. Swing supports pluggable look and feel – not by using the native platform's facilities, but by roughly emulating them. This means you can get any supported look and feel on any platform. The disadvantage of lightweight components is possibly slower execution. The advantage is uniform behavior on all platforms.

Note

Please no te, where po ssible, we have updated o ur links to po int to the new Oracle site fo r Java. Oracle bo ught Sun Micro systems so me time ago . So me o f Oracle's links po int to lo catio ns that no lo nger exist. We have no co nto l o ver that. We are so rry fo r any inco nvenience. If yo u are directed to the java.sun.co m do main fro m o ur co urse, it is because we co uld no t find a co rrespo nding o racle .co m URL fo r that particular reso urce. Oracle has indicated that they want to shut do wn java.sun.co m ; ho wever, they have, at least fo r the time being, delayed that decisio n, partly due to o utcry fro m the Java co mmunity.

HelloWorld in AWT and Swing To illustrate the similarities between AWT and Swing, we'll use a Hello Wo rld Fram e and J Fram e . Later we'll use a Hello Wo rld J Apple t , which makes use o f T hre ads. Create a new java4 _Le sso n2 pro ject. If yo u're given the o ptio n to "Open Asso ciated Perspective", click No . In yo ur new pro ject, create a He llo AppAWT class as sho wn:

Type He llo AppAWT as sho wn in blue : CODE TO TYPE: Hello AppAWT package compare; import java.awt.*; import java.awt.event.*; public class HelloAppAWT extends Frame { public HelloAppAWT() { addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); }}); add(new Label("Hello, world!")); pack(); } public static void main(String[] args) { new HelloAppAWT().setVisible(true); } }

Save and run it. No w we'll write the small applicatio n in Swing. In the co mments, yo u can see ho w it differs fro m AWT. In the java4_Lesso n2 pro ject, add the He llo AppSwing class as sho wn:

Type He llo AppSwing as sho wn in blue belo w:

CODE TO TYPE: Hello AppSwing package compare; import javax.swing.*; public class HelloAppSwing extends JFrame { public HelloAppSwing() { setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); add(new JLabel("Hello, world!")); pack(); } public static void main(String[] args) { new HelloAppSwing().setVisible(true); } } Save and run it.

Let's take a lo o k at the im po rt in o ur Swing example. The first line impo rts o nly the main Swing package: im po rt javax.swing.* This is the o nly package that yo ur applicatio n needs. Ho wever, if yo ur applicatio n had any List e ne rs (fo r user input), yo ur Swing pro gram might have also needed to impo rt the AWT packages java.awt .* and java.awt .e ve nt .*. These packages are o ften required because Swing co mpo nents use the AWT infrastructure, including the AWT event mo del as well. They use the same Listeners and Listener API Tables.

Changing Appearance Even tho ugh the differences in appearance are o ften subtle, yo u'll still want to co ntro l what yo ur GUI's lo o k like. Yo u can use any o f these fo ur platfo rm types:

The Swing tuto rial has an example o f a mo re decked o ut GUI. It is replicated exactly here so that yo u can see the

co mments and the co pyright no tice as well (yo u do n't need to type the co pyright no tice tho ugh). In the java4_Lesso n2 pro ject, add a He llo Wo rldSwing class as sho wn:

Type He llo Wo rldSwing as sho wn in blue belo w:

CODE TO TYPE: Hello Wo rldSwing package compare; import javax.swing.*; public class HelloWorldSwing { /** * Create the GUI and show it. For thread safety, this method should be invoked fr om the * event-dispatching thread. */ private static void createAndShowGUI() { //Make sure we have nice window decorations. JFrame.setDefaultLookAndFeelDecorated(true); //Create and set up the window. JFrame frame = new JFrame("HelloWorldSwing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Add the "Hello World" label. JLabel label = new JLabel("Hello World"); frame.getContentPane().add(label); //Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application's GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } } Save and run it. Yo u might need to resize the windo w. No w, THAT lo o ks different. Go to the javax.swing.J Fram e class in the API. Lo o k at the se t De f ault Lo o kAndFe e lDe co rat e d() metho d. Click o n it to see the detailed descriptio n, and read the discussio n o f the Lo o kAndFe e l. To learn mo re abo ut Lo o kAndFeel fo r J Fram e s, see Ho w to Make Frames (Main Windo ws), Using Swing Co mpo nents, and also the page o n Pluggable Lo o k and Feel.

JApplets, JFrames, and T hreads So , what was go ing o n with the m ain() metho d and javax.swing.SwingUt ilit ie s.invo ke Lat e r() call with the Runnable interface parameter? In o ur first examples with the AWT and Swing applicatio ns, we did no t use a Runnable interface to access ano ther thread fro m o ur m ain() metho d. This can lead to race co nditio ns in the class's co nst ruct o rs and/o r init () metho ds. Because o f these differences between the Swing and the AWT packages, Oracle suggests making J Fram e s fo r applicatio ns and J Apple t s differently.

More Information on Applets Oracle's Swing Tuto rial link, Ho w to Make Applets, is really useful:

Because so much o f the Swing material uses the AWT infrastructure and event mo del, the tuto rial first po ints to a tuto rial fo r Getting Started with Applets. Back o n the Ho w to Make Applets page, we have the link, Features Pro vided by JApplet, which sho ws us ho w to add co mpo nents to the Co ntent Pane. Go ahead and read this who le page to beco me familiar with the to o ls and co ncepts there.

The Co nt e nt Pane is a Co nt aine r that wo rks similarly to the way do uble-buffering do es when we paint o n the Graphics area in applets. In the same way that the se t De f ault Clo se Ope rat io n(Windo wCo nst ant s.DISPOSE_ON_CLOSE) handles the Windo w listener fo r yo u, the Co nt e nt Pane will take care o f do uble-buffering fo r yo u and help graphics run mo re smo o thly. Applets aren't much different fro m applicatio ns. The main difference between them is in the ways they are started and in the way yo u pro duce a GUI fo r an applicatio n. In o rder to have a GUI fo r an applicatio n, yo u need to instantiate the Frame (o r JFrame).

Let's co mpare the two . Go back to the Ho w to Make Applets page, then to the sectio n Threads in Applets. It has an example o f an init () metho d that lo o ks a lo t like the m ain() metho d o f their J Fram e applicatio n. No w go to javax.swing.SwingUt ilit ie s and read o ver the full descriptio n o f the invo ke Lat e r(Runnable do Run) metho d.

More Information on JApplets We'll demo nstrate JApplets using the example metho ds init () and cre at e GUI()) fro m the Swing tuto rial Ho w to Make Applets. In the java4_Lesso n2 pro ject, add a SwingApple t De m o class as sho wn:

Type SwingApple t De m o as sho wn in blue belo w:

CODE TO TYPE: SwingAppletDemo package compare; import javax.swing.*; // Change from javax.swing.JApplet import java.awt.*; public class SwingAppletDemo extends JApplet { public void init() { //Execute a job on the event-dispatching thread: //creating this applet's GUI. try { javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { createGUI(); } }); } catch (Exception e) { System.err.println("createGUI didn't finish successfully"); } } private void createGUI() { JLabel label = new JLabel("You are successfully running a Swing applet!" ); label.setHorizontalAlignment(JLabel.CENTER); label.setBorder(BorderFactory.createMatteBorder(1,1,1,1,Color.black)); getContentPane().add(label, BorderLayout.CENTER); } } Save and run it (yo u might need to resize the Applet windo w).

Our example JApplet co de sho ws two metho ds: init () and cre at e GUI(). These metho ds are similar to the applicatio n's m ain() and cre at e AndSho wGUI() metho ds; starting JApplets is very similar to starting applicatio ns. But the JApplet's init () metho d with its javax.swing.SwingUt ilit ie s.invo ke AndWait () call, is different fro m the applicatio n's javax.swing.SwingUt ilit ie s.invo ke Lat e r() call. The invo keLater metho d is no t appro priate fo r so me JApplets because it co uld allo w init () to return befo re initializatio n is co mplete. This co uld cause applet pro blems that are difficult to debug (such as co nstructo rs that mistakenly have a return type). Take a lo o k at the class LabelDemo .java (fro m Ho w to Use Labels), which extends JPanel. m ain() invo kes cre at e AndSho wGUI(), which instantiates a JFrame, then adds a Labe lDe m o (which is a JPanel), then gives the frame the Co ntentPane o f this JPanel. LabelDemo 's co nstructo r adds all o f the co mpo nents to the JPanel.

Here's an example that's a bit mo re co mplex: Ico nDemo App (fro m Ico n Demo ), and an applicatio n. While we're at it, here's the Table o f Examples fo r a tuto rial o n Swing Co mpo nents.

Even More Swing Explo re o n yo ur o wn. Have fun--Swing has so me aweso me lo o ks and capabilities! Here are a few mo re reso urces fo r yo u: The Oracle Swing tuto rial includes Creating a GUI with JFC/Swing. The Swing Seco nd Editio n Bo o k has a link to free versio n o f the first editio n. O'Reilly Media has published several bo o ks o n Swing including Java Swing, Seco nd Editio n, by Marc Lo y, Ro bert Eckstein, Dave Wo o d, James Ellio tt, and Brian Co le. Oracle pro vides Java Lo o k and Feel Design Guidelines. Our perpetual so urce o f Java kno wledge, the API includes do cumentatio n o n the javax.swing package and sub-packages. Oracle has a list o f Training and Tuto rials: Graphical User Interfaces and Printing. Finally, check o ut the Swing Set Demo . Yo u can test it fro m this web page o r, if yo u do wnlo aded Java and the demo s o n to yo ur o wn machine, yo u have Swing Set Demo in the java directo ry. Play aro und with this demo , all it takes is a few mo use clicks!

We still have a lo t mo re Swing co ming up in the next lesso n. See yo u there!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Graphical User Interfaces Views In this lesso n, we'll create a user interface and learn abo ut the Layo ut Manage r. Mo st o f the co nstructs in this co de were demo nstrated in the previo us Java co urse series, so yo u'll pro bably reco gnize them. But using the Pane l and the vario us Layo utManagers fo r GUI Frame's Pane ls will be new. To learn mo re abo ut layo ut managers, check o ut the java.awt.Layo utManager in the API. If yo u want to dig deeper still into layo ut managers, visit the Visual Guide to Layo ut Managers Tuto rial. When it runs to co mpletio n, o ur applicatio n will have three separate JPanels (InitPanel, InputPanel, and OutputPanel); o ne fo r each stage o f the run. In the picture belo w, they are separated by re d lines so yo u can differentiate between them:

JFrames and JPanels A J Fram e (similar to a Fram e in AWT) is a Windo w fo r the user. We kno w that JFrame is a Swing Co mpo nent, because it is preceded by J . All Swing co mpo nent names are preceded by a J in o rder to avo id co nfusing them with AWT Co mpo nents. We can add menus and Pane ls (JMenus and JPanels) to suit o ur needs. In fact, bo th J Fram e and J Pane l inherit fro m Co nt aine r, so we can add o ther Co m po ne nt s to bo th o f them.

We'll pro vide the user with a to p-level J Fram e and add vario us J Pane ls to it.

JFrames: T he T op-Level View Let's create the J Fram e (Windo w) that will ho ld the JPanels and o ther co mpo nents. Then we'll add a Menu Bar (JMenuBar) and a Menu item "File" (JMenuItem) with the menu o ptio n "Exit." Then we'll capture the click event, so we can clo se the windo w when we test it. If we didn't do this, we'd have to use the Co nso le to end the pro gram. Swing co mpo nents are referred to as "light weight" (as o ppo sed to AWT co mpo nents which are "heavy weight"), meaning that the co mpo nents use the o perating system to create co mpo nents like Checkbo xes and Cho ices. Swing co mpo nents are created and drawn by the Swing library rather than relying o n the o perating system to draw them. This gives Java applicatio ns and applets a unifo rm lo o k and feel acro ss multiple o perating systems. And when using Swing, the lo o k and feel o f yo ur applicatio ns can be changed by altering a few lines o f co de. Okay, time to get busy! In the java4_Lesso n1 pro ject, create a new Sale sUse rInt e rf ace class as sho wn:

Go to the Sale sUse rInt e rf ace edito r windo w and edit it as sho wn in blue :

CODE TO TYPE: SalesUserInterface package salesGUI; import import import import import

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

public class SalesUserInterface extends JFrame{ SalesApp app; JMenuBar mb; JMenu m; JMenuItem q, r, s, t; 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"); mb.add(m); m.add(q = new JMenuItem("Exit")); q.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { System.exit(0); } }); pack(); setVisible(true); } }

Save it. It wo n't run, because we haven't created a Main class yet. We'll do that, but first let's take a clo ser lo o k at the co de we do have:

OBSERVE: SalesUserInterface package salesGUI; import import import import import

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

public class SalesUserInterface >extends JFrame>{ SalesApp app; JMenuBar mb; JMenu m; JMenuItem q, r, s, t; public SalesUserInterface(SalesAPP myApp) { app = myApp; app.setMyUserInterface(this); setLayoutManager(new BorderLayout()); setPreferredSize(new Dimension(600, 600)); mb = new JMenuBar(); setJMenuBar(mb); m = new JMenu("File"); mb.add(m); m.add(q = new JMenuItem("Exit")); q.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { System.exit(0); } }); pack(); setVisible(true); } } So let's go o ver o ur co de piece by piece. We impo rted javax.swing.*, java.awt .e ve nt , the java.Bo rde rLayo ut , and java.awt .Dim e nsio n. The J Fram e co ntainer is extended fro m Swing. Our SalesUserInterface class extends J Fram e fro m Swing. We impo rted java.awt .e ve nt to allo w us to capture events. We impo rted java.Bo rde rLayo ut as o ur cho sen Layo ut Manager. Finally, we impo rted java.awt .Dim e nsio n in o rder to size the windo w in this instance. Next, we set up the variables fo r this applicatio n to use. The app variable is a reference to the Sale sApp class that we created earlier. The o ther variables are o f type JMenuBar, JMenu, and JMenuOptio n, all o f which are Swing co mpo nents we'll use fo r o ur menu bar. Again, the "J" is used here to differentiate Swing co mpo nents fro m AWT co mpo nents. The co nstructo r fo r this class accepts a Sale sAPP o bject as a parameter. This enables the SalesApp o bject to make co mputatio ns fro m this GUI. We call app.se t MyUse rInt e rf ace (t his), which passes the SalesUserInterface o bject to the SaleApp instance. (This may be a bit co nfusing right no w, because we haven't used this handle yet. Do n't wo rry, we'll get there. Patience grassho pper.) Next, we call the JFrame metho d se t Layo ut Manage r(ne w Bo rde rLayo ut ()), in which we create a new Bo rde rLayo ut ()--we'll use the Bo rderLayo ut manager. Well, we aren't actually using it just yet, but it will be used to lay o ut the JPanels when we add them. Fo r no w, let's get the windo w up with the File menu and Exit o ptio n. In the dark re d co de abo ve, we instantiate a J Me nuBar called m b, and then set the menu bar o n the SalesUserInterface JFrame with se t J Me nuBar(m b);. Then we add the JMenu "File" to the menu bar, and add the "Exit" JMenuItem to that. We add an Act io nList e r to the Exit Menu Item to catch the click event. To do that, we use the ano nymo us inner class technique. Then we call Syst e m .e xit (0 ); in the implemented interface metho d Actio nPerfo rmed() to kill the Applicatio n pro cess.

Finally, we call pack(); and se t Visible (t rue ); to sho w the GUI. The metho d pack() is actually inherited fro m Windo w, and causes the windo w to be set to its preferred size. se t Visible () makes the windo w visible o n the screen. These two metho ds sho uld always be called when using a JFrame. Okay, let's make a Main Class and get this applicatio n running! Start a new Main Class file in the same lo catio n as yo ur Sale sUse rInt e rf ace class. Type the blue co de as sho wn: CODE TO TYPE: Main package salesGUI; public class Main { public static void main(String[] args) { SalesApp newApp = new SalesApp(); SalesUserInterface appFrame = new SalesUserInterface(newApp); } } Save and run it. Select File | Exit :

OBSERVE: Breaking Do wn the Main package salesGUI; public class Main { public static void main(String[] args) { SalesAPP newApp = new SalesApp(); SalesUserInterface appFrame = new SalesUserInterface(newApp); } } Here we instantiate a Sale sApp o bject we call ne wApp. Then we instantiate a SalesUserInterface o bject and pass the ne wApp o bject to it as a parameter. In the SalesUserInterface o bject, we get that ne wApp o bject and it beco mes app, which will ultimately make calculatio ns fo r us. No w, add the first JPanel: Init Pane l. We'll add this class as an Inner Class to SalesUserInterface. We do that fo r two reaso ns: This class is specific to the SalesUserInterface and we do n't plan to reuse it. It will make it easier to access aspects o f the SalesUserInterface later in this lesso n. Add the blue co de to SalesUserInterface:

CODE TO EDIT: Inner Class InitPanel in SalesUserInterface package salesGUI; import import import import import

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

public class SalesUserInterface extends JFrame{ SalesApp app; JMenuBar mb; JMenu m; JMenuItem q, r, s, t; JLabel peopleLabel; JTextField peopleField; JButton jbNumPeople, done; 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"); mb.add(m); m.add(q = new JMenuItem("Exit")); q.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { System.exit(0); } }); 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); } } } Save this SalesUserInterface and run the Main.java. Yo u'll see the "Enter the Number o f Sales Peo ple" JLabel, an input JTextfield, and a Submit JButto n. Of co urse, it do esn't do anything just yet.

OBSERVE: InitPanel package salesGUI; import import import import

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

public class SalesUserInterface extends JFrame { SalesApp app; JMenuBar mb; JMenu m; JMenuItem q, r, s, t; JLabel peopleLabel; JTextField peopleField; JButton jbNumPeople, done; public SalesUserInterface(SalesAPP myApp) { app = myApp; app.setMyUserInterface(this); setLayout(new BorderLayout()); setPreferredSize(new Dimension(600, 600)); mb = new MenuBar(); setMenuBar(mb); m = new Menu("File"); mb.add(m); m.add(q = new JMenuItem("Exit")); q.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { System.exit(0); } }); InitPanel specifyNumber = new InitPanel(); add("North", specifyNumber); pack(); setVisible(true); } public 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); } } }

We created a new class called Init Pane l, which e xt e nds J Pane l. We added a JLabel, JTextfield, and JButton. We used the add() metho d fro m JPanel to add each o f tho se instantiated o bjects to o ur JPanel. Then we added the variables used in InitPanel to the glo bal sco pe o f SalesUserInterface. Yo u'll see why we did that later in this lesso n. OBSERVE InitPanel specifyNumber = new InitPanel(); add("North", specifyNumber); Just fo r practice, try changing "No rth" to "East" o r "So uth" and running the pro gram again to o bserve the effect it has. We want o ur GUI to lo o k like this:

So next, we need to create the input panel to add to o ur JFrame. We'll make a class called Input Pane l, and implement it as inputPanel o n o ur SalesUserInterface. Let's make it a separate class (we might want to reuse it so meday). In this particular class, we'll extend JPanel and layer mo re JPanels o nto it. Here's a graphical representatio n o f what we'll be adding to o ur InputPanel:

In the java4_Lesso n1 pro ject, create an Input Pane l class as sho wn:

Type Input Pane l as sho wn in blue :

CODE TO TYPE: InputPanel package salesGUI; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class InputPanel extends JPanel { JPanel topPanel; SalesApp app; JLabel prompt; public InputPanel(SalesApp container) { this.app = container; this.setLayout(new BorderLayout()); topPanel = new JPanel(); topPanel.setLayout(new FlowLayout()); add("North", topPanel); prompt = new JLabel("Give values for each salesperson:"); topPanel.add(prompt); } }

Save it. We can't view it o n o ur SalesUserInterface yet, because we haven't added this InputPanel to o ur SalesUserInterface JFrame. Let's do that no w! In yo ur SalesUserInterface.java file, add the co de sho wn 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; JMenuItem q, r, s, t; JLabel peopleLabel; JTextField peopleField; JButton jbNumPeople, done; 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"); mb.add(m); m.add(q = new JMenuItem("Exit")); q.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { System.exit(0); } }); InitPanel specifyNumber = new InitPanel(); add("North", specifyNumber); InputPanel inputPanel = new InputPanel(app); add("Center",inputPanel); pack(); setVisible(true); } public 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); } } } Save this SalesUserInterface and run the Main. Yo u'll see the pro mpt Give value s f o r e ach sale spe rso n. That's o ur t o pPane l! So far, we've added the to pPanel, a JPanel, into InputPanel. We still need to make the middle panel to do all o f the wo rk in InputPanel. We'll add so me co de to prepare o ur InputPanel to accept the number o f sales peo ple entered fro m the InitPanel as well.

CODE TO EDIT: InputPanel package salesGUI; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class InputPanel extends JPanel { JPanel topPanel, middlePanel, bottomPanel, leftPanel, rightPanel; SalesApp app; JLabel prompt, doneLabel, jlSalesBar; JLabel[] jlSales; JButton done; JTextField[] jtfSales; JTextField jtfSalesBar; int numPeople; public InputPanel(SalesApp container, int numPeople, int gridX) { this.app = container; this.numPeople = 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); } }

Save it. We've added lo ts o f new co de here. Let's just get it wo rking so we can actually see it in actio n first, then we'll go o ver it in detail. In o rder fo r the butto n in the InitPanel to wo rk, we'll create a new private inner class in SalesUserInterface.java that will serve as the butto n's listener. Let's call it Num Pe o ple Sale sList e ne r. In SalesUserInterface.java, add the co de sho wn 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; 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"); mb.add(m); m.add(q = new JMenuItem("Exit")); q.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { System.exit(0); } }); InitPanel specifyNumber = new InitPanel(); add("North", specifyNumber); // REMOVE the next two lines. InputPanel inputPanel = new InputPanel(app); add("Center",inputPanel); 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(); } } } Save it and run Main.java. When yo ur applicatio n appears, type in a number and press Subm it . Yo u'll see the input fields fo r yo ur salespeo ple. No w let's go o ver this, bit by bit. First we'll lo o k o ver the co de we added to Input Pane l:

OBSERVE: InputPanel package salesGUI; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class InputPanel extends JPanel { JPanel topPanel, middlePanel, bottomPanel, leftPanel, rightPanel; SalesAPP app; JLabel prompt, doneLabel, jlSalesBar; JLabel[] jlSales; JButton done; JTextField[] jtfSales; JTextField jtfSalesBar; int numPeople; public InputPanel(SalesApp container,int numPeople , int gridX) { this.app = container; this.numPeople = 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]; add("North", topPanel); 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); } } The co de in blue is already familiar, so we'll turn o ur attentio n to the new stuff. When we call this Input Pane l() co nstructo r in SalesUserInterface, we will supply the parameters num Pe o ple and gridX. Tho se parameters are used in the GridLayo ut (ro ws, co ls) co nstructo r (go ahead and lo o k up GridLayo ut in the API). num Pe o ple is the number o f ro ws that o ur grid will have and gridX is the to tal number o f co lum ns o ur grid will have. The co de in re d is a f o r statement that is indexed by x up to num Pe o ple . We create an array o f bo th J Labe l and J T e xt Fie ld co mpo nents, then add them to the m iddle Pane l. When we add co mpo nents using add,

and J T e xt Fie ld co mpo nents, then add them to the m iddle Pane l. When we add co mpo nents using add, the GridLayo ut layo ut manager auto matically adds them fro m left-to -right and to p-to -bo tto m, and allo ws them each the same amo unt o f space. Nice. No w, lo o k at the co de we added to Sale sUse rInt e rf ace :

OBSERVE: SalesUserInterface.java 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; 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"); mb.add(m); m.add(q = new JMenuItem("Exit")); q.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { System.exit(0); } }); 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();

} } } We've added the actio nlistener Num Sale sPe o ple List e ne r to the JButto n jbNum Pe o ple . And we've added the Num Sale sPe o ple List e ne r class that implements Act io nList e ne r. In the implemented actio nPerfo rmed() metho d, we've added an if statement that checks to see if an inputPanel exists already. If we change the number o f sales peo ple, then we rebuild that existing inputPanel. We re m o ve (input Pane l) and create a new SalesApp to pass to the new InputPanel. We retrieve the num Pe o ple using the ge t T e xt () metho d in peo pleField (a JTextField o bject). No w, when we create an instance o f InputPanel, we pass it num Pe o ple (the number o f ro ws we want) and 2 (the number o f co lumns we want). Next, we add the inputPanel, po sitio n it in the " Ce nt e r" , and then call Sale sUse rInt e rf ace .t his.validat e (). We call JFrame's validat e () metho d when we rebuild and re-add the inputPanel co mpo nent. In o ur co de, we called pack() befo re we called se t Visible (). Once a panel is visible o n a JFrame, we call validat e () to get o ur applicatio n to redraw. Because Num Sale sPe o ple List e ne r is an inner class, we can use Sale sUse rInt e rf ace .t his to access the JFrame's validat e () metho d. So far, so go o d. No w let's turn o ur InputPanel into a listener and make o ur do ne JButto n grab the numbers the user enters and send them to o ur SalesApp. Edit Input Pane l.java as sho wn in blue :

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++) { sales[x] = Integer.parseInt(jtfSales[x].getText());

} app.setSales(sales); goal = Integer.parseInt(jtfSalesBar.getText()); app.setSalesBar(goal); } } } } Save and run it (run the Main.java). No w type in a number (we used 4 ) and when the input panel co mes up, enter numbers into that as well; include the sales go al. Then click All Se t as sho wn:

The System.o ut.print o utput appears in the Co nso le:

OBSERVE: Changes to InputPanel package salesGUI; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class InputPanel extends JPanel implements ActionListener { Panel topPanel, middlePanel, bottomPanel, leftPanel, rightPanel; JLabel[] jlSales; JLabel prompt, doneLabel, jlSalesBar; JTextField[] jtfSales; JTextField jtfSalesBar; JButton done; SalesApp app; 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 Panel(); topPanel.setLayout(new FlowLayout()); middlePanel = new Panel(); middlePanel.setLayout(new GridLayout(numPeople, gridX)); bottomPanel = new Panel(); bottomPanel.setLayout(new FlowLayout()); leftPanel = new Panel(); rightPanel = new Panel(); 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++) {

sales[x] = Integer.parseInt(jtfSales[x].getText()); } app.setSales(sales); goal = Integer.parseInt(jtfSalesBar.getText()); app.setSalesBar(goal); } } } } The act io nPe rf o rm e d() metho d o f this InputPanel class determines whether the so urce o f the e ve nt is a J But t o n. If it is, we use a f o r lo o p to go thro ugh the number o f peo ple and lo o k in each o f the TextFields defined by the jt f Sale s[] array. We use ge t T e xt () to get the text the user typed in and we use Int e ge r.parse Int () to co nvert that text into an integer. Then we call the se t Sale s() metho d o f the SalesApp o bject. We determine the value o f the go al and set the salesBar to that value. Okay, nice wo rk! Take a break, pat yo urself o n the back, and bask in the glo ry o f yo ur acco mplishments so far! We'll do the Output Panel fo r this user interface in the next lesso n. See yo u there! Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Graphical User Interfaces, continued Making the Output Panel No w that we've created the input panel and have o utput go ing to the co nso le, let's create a Pane l to display the o utput:

We can take advantage o f JLabel's ability to display HTML to fo rmat the o utput. Using the same java4_Lesso n1 pro ject we used in the previo us lesso n, create an Out put Pane l class as sho wn:

The o utput panel we're go ing to build will actually co nsist o f two panels: o ne in the East area and o ne in the We st area o f the layo ut. They will be used to display the o utput o f the applicatio n to the user. Type Out put Pane l as sho wn in blue :

CODE TO TYPE: OutputPanel package salesGUI; import javax.swing.*; import java.text.DecimalFormat; public class OutputPanel extends JPanel { JLabel jlSalesOutput; JPanel leftPanel, rightPanel; JLabel jlSalesBar; JTextField jtfSalesBar; JButton done; SalesApp app; int salesBar; int [] sales; public OutputPanel(SalesApp container) { app = container; sales = app.getSales(); leftPanel = new JPanel(); rightPanel = new JPanel(); add("East", rightPanel); add("West", leftPanel); jlSalesOutput = new JLabel(); rightPanel.add(jlSalesOutput); jlSalesOutput.setText(""); } } Next we'll add metho ds to fo rmat and display the results. The writ e Out put () metho d uses co ncatenatio n (+=) to build a +=t xt Out put String that co ntains all o f o ur o utput. We are using JLabel's ability to display HTML co ntent, and displaying the data as we wo uld in an HTML do cument.

Note

HTML break tags (
) are used fo r new lines in the t xt Out put St rings fo r the o utput JLabel.

CODE TO EDIT: OutputPanel package salesGUI; import java.awt.Panel; import javax.swing.*; import java.text.DecimalFormat; public class OutputPanel extends JPanel { JLabel jlSalesOutput; Panel leftPanel, rightPanel; JLabel jlSalesBar; JTextField jtfSalesBar; JButton done; SalesApp app; int salesBar; int [] sales; public OutputPanel(SalesApp container) { app = container; sales = app.getSales(); leftPanel = new Panel(); rightPanel = new Panel(); add("East", rightPanel); add("West", leftPanel); jlSalesOutput = new JLabel(); rightPanel.add(jlSalesOutput); jlSalesOutput.setText(""); } public void refreshOutput(){ jlSalesOutput.setText(""); } protected void writeOutput(){ app.calculateMinMax(); DecimalFormat df1 = new DecimalFormat("####.##"); // Build the output string like an HTML doc String txtOutput = "Sales Figures
__________________________
"; for (int x = 0; x < sales.length; x++) { txtOutput += "Sales Person " + (x + 1) + ": $" + sales[x] + "
"; } txtOutput += "
The lowest sales belongs to sales person " + (app.getMin() + 1) + " with $" + sales[app.getMin()] + "
"; txtOutput += "The highest sales belongs to sales person " + (app.getMax() + 1) + " with $" + sales[app.getMax()] + "
"; txtOutput += "
The total sales were: $ " + app.getTotalSales() + "
"; txtOutput += "The average sales was: $ " + df1.format(app.getAverage()) + "

"; txtOutput += createSalesBarInfo(); txtOutput += ""; jlSalesOutput.setText(txtOutput); validate(); repaint(); } protected String createSalesBarInfo(){ String salesBarOutput = ""; int overSalesBar = 0; int [] performance = app.determineTopSalesPeople();

int [] sales = app.getSales(); for (int x = 0; x < sales.length; x++) { if (performance[x] ==1) { overSalesBar++; salesBarOutput += "Sales person " + (x + 1) + " sold more than the sales goal with sales of "+ sales[x]+ "
" ; } else if (performance[x] ==0) { salesBarOutput += "Sales person " + (x + 1) + " exactly reached the sales goal with sales of "+ sales[x]+ "
" ; } } if (overSalesBar ==1) salesBarOutput += "Only " + overSalesBar + " sales person sold more than the sales goal of " + app.getBar() + "

"; else salesBarOutput += overSalesBar + " sales people sold more than the sales goal of " + app.getBar() + "

"; return salesBarOutput; } } Here we use JLabel's ability to display HTML to display the sales to tals o f each salesperso n, and which were greater than, lo wer than, o r equal to the sales bar. Fo r mo re info rmatio n o n the javax.swing.JLabel, lo o k at the API, as well as Ho w to Use Labels in the Java Tuto rial o n Using Swing Components. Save the Out put Pane l class. Click o n Input Pane l.java. If yo u haven't do ne so ,

save it no w--any erro rs yo u have sho uld go away.

Click o n Sale sUse rInt e rf ace .java; it sho uld be free o f erro rs as well. No w let's add the Results o ptio n and OutputPanel. Edit SalesUserInterface as sho wn in blue belo w:

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 JMenuItem("Results")); t.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (processed) { remove(results); } results = new OutputPanel(app); add("South", results); processed = true; results.writeOutput();} }); InitPanel specifyNumber = new InitPanel(); add("North", specifyNumber); //InputPanel inputPanel = new InputPanel(app, numPeople, 2); //add("Center", inputPanel); 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(); } } }

Save it and run the Main class. Enter a number o f salespeo ple, their sales numbers and the go al, and then click All Se t . Select Opt io ns | Re sult s, and yo u sho uld see so mething like this:

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Error Checking and Exception Handling Being Prepared for Users Creating applicatio ns invo lves three basic tasks: writing the co de that perfo rms the desired functio n (creating the model), pro viding a clear and easy to use interface (o r view), and making sure that users do n't break the applicatio n.

Crashes In the java4 _Le sso n1 pro ject, go to the sale sGUI package, o pen the Main.java class, and give it these values:

Click All Se t . Yo u'll see a lo t o f re d in the Co nso le:

Run it. No w

This isn't a pro blem fo r pro grammers--we can see the co nso le, so we can see the Exce pt io n to o . But it's a pro blem fo r users, because the applicatio n view do esn't change at all, so they aren't even aware that they've made an erro r. They can carry o n and try the menu items, but they wo n't get their results. In the applicatio n that's currently running, select Opt io ns | Re sult s. Yo u'll no tice even mo re re d in the Co nso le:

Again, the user still do esn't see any o f this; they just kno w that the lo usy pro gram isn't wo rking! Clo se the running applicatio n using the menu item File | Exit . If an applicatio n is o pen and running, it will co ntinue to use the same .class (old co mpiled co de) that it was o pened with, even if yo u make changes to the applicatio n and save it again. If yo u edit, resave, and rerun an applicatio n, but still have the o lder (erro neo us) co de running, it can cause frustratio n. Always make sure to clo se an applicatio n pro perly, befo re editing and running a new versio n.

Expect the Unexpected

Exce pt io ns o ccur even to o ur mo st well-tho ught-o ut Java co de plans. In fact, they are so co mmo n that Java has a class named Exce pt io n. Go to the java.lang package. Scro ll do wn to the Exce pt io n Sum m ary, then to the Exce pt io n class. There are quite a few Dire ct Kno wn Subclasse s (we edited mo st o f them o ut in the image belo w tho ugh, because the list was so lo ng):

And that's just the beginning. Go back to java.lang's Exce pt io n Sum m ary. Scro ll do wn to Runt im e Exce pt io n (just o ne o f the Direct Kno wn Subclasses o f Exce pt io n). Click o n Runt im e Exce pt io n and check o ut all o f its Direct Kno wn Subclasses. Pro grams and users may behave in an infinite number o f unexpected ways. When the unexpected happens, o ur co de (with the help o f Java) will t hro w an Exce pt io n (the java.lang.Exce pt io n class extends (o r inherits fro m) the class java.lang.T hro wable ). So , what's an exceptio n? Oracle's Java tuto rial says, "An exception is an event, which o ccurs during the executio n o f a pro gram, that disrupts the no rmal flo w o f the pro gram's instructio ns." If we do n't want o ur pro grams to crash and cause o ur users to beco me frustrated, then we need to plan fo r all po tential Exce pt io ns.

Handling Exceptions Finding the Problem When Java thro ws an Exce pt io n, it tells us which type o f Exce pt io n it was and where it o ccurred. In o ur first exceptio n, Java pro vided a lo ng debugging trace o f its lo catio n. Usually, the last co uple o f lines in a trace are the mo st impo rtant fo r pro grammers. Fo r example, when the user entered the value o f 3.4 in the Input Pane l, we saw:

We can tell fro m the Exce pt io n that an input string o f 3.4 caused a java.lang.Num be rFo rm at Exce pt io n. We can also determine that the exceptio n o ccurred in the Input Pane l.java class at line number 7 6 .

Open the InputPanel.java class and display the line numbers (o n the left side bar, right -click and cho o se Sho w Line Num be rs). Go to line 7 6 . Yo u sho uld see sale s[x] = Int e ge r.parse Int (jt f Sale s[x].ge t T e xt ());. Do yo u reco gnize the pro blem? The sale s[ ] array is declared as Int e ge r. We to ld Java to expect an int , but the user gave us a decimal. A decimal is no t an int ; it's a do uble o r a f lo at . So , ho w do we remedy this?

Fixing the Problem Java pro vides a specific structure to handle Exce pt io ns. We put po tential pro blems into t ry/cat ch clauses. If certain co de co uld thro w exceptio ns, we place it in a t ry clause, and then pro vide a cat ch clause to make the appro priate co rrectio ns.

T ry/Catch Clauses Anticipating Exceptions If we want to catch exceptio ns, we need to kno w when they might o ccur. So metimes co de pro vided in the API indicates that it will thro w vario us types o f exceptio ns. We'll address tho se exceptio ns in greater detail later, but fo r no w, let's get a handle o n the general idea. If we had researched the metho ds we were using carefully in the API befo rehand, we co uld have anticipated po tential pro blems befo re writing the co de. In the API, go to java.lang.Int e ge r. Go to the parse Int (St ring s) metho d.

We co uld have anticipated that a user might enter a decimal number rather than an integer. Pro grammers need to be ready fo r all kinds o f po tentially unexpected situatio ns. So , ho w can we be prepared? Well, if the user behaves as we wo uld like them to , the co de wo rks great. But if the user do esn't, we need to cat ch the Exce pt io n. If a m e t ho d thro ws an Exce pt io n, then we sho uld instruct o ur co de to t ry that metho d's piece o f co de. If o ur pro grams do no t catch exceptio ns, then Java is fo rced to t hro w them farther. Java will keep thro wing exceptio ns until so mething catches it, o r until it gets to the "to p o f the stack" (mo re o n this in a later lesso n). At that po int, if an exceptio n has no t been caught, it will cause erro rs in the co nso le.

Making It Right: Dialog Boxes In o ur example, the pro blem is in the type o f input given by the user, so let's tell the user when so mething they've entered needs to be changed. We'll do that using a dialo g bo x. In the Input Pane l class, where the exceptio n o ccurred (aro und line 76 ), edit the act io nPe rf o rm e d() metho d as sho wn in blue :

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); //jtfSalesBar.addActionListener(new GoalButtonListener()); 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 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+message Line2+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()); e to be sure they hit enter app.setSalesBar(goal); } } } }

// so don't hav

Save the InputPanel class. Run the Main class. Enter a decimal number fo r o ne o f the values and click All Se t .

That info rmatio n helps the user and the pro grammer. We can pro vide additio nal St rings o f info rmatio n fo r the user in the dialo g bo xes to o . Or we can just fix things witho ut no tifying them at all. Cho o sing ho w to respo nd depends o n the applicatio n and the significance o f each piece o f data. There are several o ther types o f dialo g bo x o ptio ns; let's take a lo o k at ano ther o ne. Replace the cat ch clause inside the f o r lo o p as sho wn in blue :

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); //jtfSalesBar.addActionListener(new GoalButtonListener()); 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()); } catch(NumberFormatException e) { String temp = JOptionPane.showInputDialog("Decimal value s 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 the InputPanel class. Run the Main class. Enter a decimal number fo r o ne o f the values and click All Se t :

Fo r mo re o n dialo g bo xes, see the Oracle tuto rial o n Ho w to Make Dialo gs.

T ypes of Exceptions The Java pro gramming language uses e xce pt io ns to handle erro rs and o ther exceptio nal events. Exce pt io ns are unusual co nditio ns that a well-written applicatio n will anticipate and remedy. Java pro vides two main types o f exceptio ns: checked and unchecked. Che cke d e xce pt io ns can be checked at co mpile time. All exceptio ns are checked exceptio ns, except fo r tho se that are instances o f the Erro r and Runt im e Exce pt io n classes and their subclasses.

Checked Exceptions If a metho d has a checked exceptio n, Java info rms the pro grammer using the metho d. The class that uses

the metho d will no t co mpile (o r Eclipse will repo rt erro rs) and the pro grammer will no t be able to run the pro gram until the exceptio n in the co de has been handled; the pro grammer is f o rce d to handle that exceptio n. In additio n, pro grammers can o ften anticipate pro blems that co uld o ccur in a metho d they have written. The autho r o f the metho d is o bliged to warn o ther pro grammers who may use it, that such pro blems are a po ssibility. A go o d pro grammer will handle tho se pro blems within the applicatio n. Pro grammers must co nsider o ther pro grammers, as well as users when writing metho ds and applicatio ns: A metho d's autho r needs to make sure that o ther programmers who use the metho d do n't experience surprise failures. An applicatio n's autho r needs to make sure that the users o f their applicatio n do n't have surprise failures. Of co urse, the autho r o f a metho d can't always anticipate which enviro nment a pro grammer will use, o r the type o f applicatio n a pro grammer may want to create. Because o f such variables, the metho d autho r can't predict ho w each applicatio n might handle a pro blem. The best the metho d autho r can do is to info rm users o f the metho d that a pro blem might exist, and that using the metho d might thro w an Exce pt io n. Then the metho d's autho r sho uld include t hro ws in the metho d definitio n.

Unchecked Exceptions Erro rs, Runtime Exceptio ns, and their subclasses are unche cke d e xce pt io ns. The co de we wro te to retrieve user-entered sales values had the po tential to present the pro blems asso ciated with unche cke d e xce pt io ns. Its specificatio n in the API clearly stated that it thro ws a Num be rFo rm at Exce pt io n. But we were still able to co mpile and run the co de initially witho ut a try/catch clause. Why? Go to java.lang.Num be rFo rm at Exce pt io n in the API and lo o k at its class hierarchy:

OR Open Input Pane l.java in the Edito r. Go to the line that specifies the NumberFo rmatExceptio n in the catch clause. Highlight it. Right-click and cho o se Ope n T ype Hie rarchy:

A hierarchy windo w o pens in the left panel (there are actually two panels there):

Num be rFo rm at Exce pt io n is a subclass o f Runt im e Exce pt io n. All exceptio ns are checked exceptio ns, e xce pt fo r tho se that are instances o f the Erro r and Runt im e Exce pt io n classes, and their subclasses. So metimes exceptio ns arise when a pro gram is run, depending o n which variables are present at that particular time. These are called Runtime Exceptions. The exceptio ns that we have lo o ked at in this lesso n have been unchecked (Runtime Exceptions), because the co mpiler canno t anticipate what a user will enter. So , even tho ugh the metho d java.lang.Int e ge r.parse Int (St ring s) states that it might thro w an exceptio n, Java allo wed the co de to co mpile. As the Oracle Tuto rial states: Runtime exceptions represent problems that are the result of a programming problem, and as such, the API client code cannot reasonably be expected to recover from them or to handle them in any way. Such problems include arithmetic exceptions, such as dividing by zero; pointer exceptions, such as trying to access an object through a null reference; and indexing exceptions, such as attempting to access an array element through an index that is too large or too small. Because such exceptio ns can happen anywhere in a pro gram, and o ften runtime exceptio ns are no t easy to spo t, the co mpiler do esn't require pro grammers to catch runtime exceptio ns. But so o ner o r later, exceptio ns will make their presence kno wn. The Java Virtual Machine is merciless and wo n't hesitate to bro adcast the re d details o f o ur uncaught exceptio ns all o ver the co nso le.

T he Other Problem When we ran o ur applicatio n at the beginning o f this lesso n, we saw two exceptio ns:

When we fixed the first exceptio n (the Num be rFo rm at Exce pt io n) with the t ry/cat ch clause, the seco nd exceptio n disappeared. So , we're go ing to do what mo st sensible beginning pro grammers do : fo rget abo ut it. But remember to expect the unexpected. That pro blem will po p up again.

Be prepared to see mo re exceptio ns in the co ming lesso ns as we co ntinue to investigate... Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Unchecked Exceptions: Keeping Our Applications Running About Exceptions We kno w that all exceptio ns are checked exceptio ns, aside fro m tho se identified by Erro r and Runt im e Exce pt io n and their subclasses. If co de includes metho ds with checked exceptio ns, that co de wo n't co mpile until the exceptio ns are handled thro ugh t ry/cat ch clauses. A metho d that causes a checked exceptio n has to specify that it throws the exceptio n. In the previo us lesso n, we were able to co mpile and run o ur applicatio n co de, so we kno w o ur co de didn't co ntain metho ds with checked exceptio ns. Do es that mean o ur co de is free fro m erro rs and exceptio ns? Sadly no . As we saw in the last lesso n, o ur co de co ntained o ne unchecked exceptio n that had to be fixed. Unchecked exceptio ns usually o ccur because a user do es so mething unexpected at runtime. In this lesso n, we'll lo o k at co mmo n subclasses o f Runt im e Exce pt io ns.

Run-T ime Exceptions Subclasses o f Runt im e Exce pt io ns appear in o ur co de o ften, but so metimes they're hard to lo cate. These are the mo st co mmo nly used subclasses o f Runt im e Exce pt io ns: NullPo interExceptio n ArithmeticExceptio n Array Out o f Bo unds

NullPointerException A null po inter exceptio n o ccurs when yo ur co de tries to access an instance o f an o bject that has no t been pro perly instantiated. Declaring a variable to be o f a certain type is no t the same as instantiating it. If yo ur variable is no t o f a primitive data type, it has been declared as a type o f Obje ct (remember that every o bject inherits fro m Obje ct and is a subclass). If such an o bject has no t been instantiated, then it do esn't po int to anything in memo ry, so it's a null pointer. Null po inter pro blems may o ccur when a user calls a metho d inco rrectly. If an argument is null, the metho d might thro w a NullPo interExceptio n, which is an unchecked exceptio n. Let's lo o k at such an exceptio n in the first o f five examples we'll use in this lesso n:

Example 1 In the java4_Lesso n1 pro ject, SalesGUI package, edit Main.java as sho wn in blue and re d: 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 i t //SalesApp newApp = new SalesApp(); SalesUserInterface appFrame = new SalesUserInterface(newApp); } } Save and run it. Lo o k in the Co nso le fo r the exceptio n:

In this co de, we passed a variable ne wApp that was null fo r Sale sApp. It is no t always that easy to find exceptio ns, particularly when we instantiate the o bjects in o ne place and access them in ano ther. Edit Main.java as sho wn (we're reverting to the previo us versio n, so yo u can use the Undo key co mbinatio n [Ct rl+Z ] to undo the typing yo u 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--th e 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 t he interface who its app is } } Save and run it again to make sure that all's well. Make sure to exit the running applicatio n afterward, so it's ready fo r the next test.

Example 2 Open the Input Pane l.java class. In the co nstructo r, co mment o ut the line where we instantiate the sale s array, as sho wn in re d:

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 value s 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 fo r the number o f SalesPeo ple and click Subm it to o pen the InputPanel. Enter a value fo r a particular Salesperso n, then click All Se t . Scro ll to the to p o f the exceptio n trace in the Co nso le.

At o r near line 78 in the InputPanel class is the line sale s[x] = Int e ge r.parse Int (jt f Sale s[x].ge t T e xt ()); (yo ur line numbers may vary slightly, depending o n ho w yo u co ded yo ur dialo g bo x.) The erro r message appears because we co mmented o ut the instantiatio n o f the sale s [ ] array, so it's no t there to have elements added into it. New pro grammers o ften think that because they declared the array, it exists: int [] sale s; (at o r near line 16 in Input Pane l). When yo u declare a variable as an instance variable and it is an Object, Java gives it the default value o f null. So in the co nstructo r, yo u need the line sale s = ne w int [num Pe o ple ]; to allo w yo ur variable a no n-null value and size. Clo se this applicatio n instance using File | Exit , unco mment (in o ther wo rds, remo ve the // fro m) the line yo u co mmented o ut in Input Pane l.java. Save it, and run it again fro m Main to make sure it wo rks. Then, exit the applicatio n again, using File | Exit .

Example 3 Here's ano ther po tential snag in o ur current applicatio n: Run the Main.java class and enter values fo r Sales Peo ple and fo r the Sales Go al, but do not click All Se t . Select Opt io ns | Re sult s in the menu. Take a lo o k at the Co nso le:

Yo ur line numbers may be slightly different. Do es this lo o k familiar? It's the exceptio n we didn't fix in the previo us lesso ns.

We'll trace this erro r fro m the bo tto m up: OBSERVE at salesGUI.SalesUserInterface$3.actionPerformed(SalesUserInterface.java:48) at salesGUI.OutputPanel.writeOutput(OutputPanel.java:33) at salesGUI.SalesApp.calculateMinMax( SalesApp.java:70)

Sale sUse rInt e rf ace .java:4 8 is re sult s.writ e Out put ();} } );. re sult s is an instance o f Out put Pane l. We are calling its metho d, writ e Out put (). Out put Pane l.java:33 is the first line in that metho d. There is a call to app.calculat e MinMax();. app is an instance o f Sale sApp; we are calling its metho d calculat e MinMax(). Sale sApp.java:7 0 is the first line in that metho d. We see int m inim um = sale s[0 ];--so , why the null po inter exceptio n? Because we do n't have a sale s[0 ]. The user hasn't clicked Se t All, so the sale s [] array never received its values, and so sale s[0 ] do esn't exist. There are vario us ways to fix these pro blems. We'll illustrate just o ne. All o f the menu items were made in the Sale sUse rInt e rf ace class, so let's lo o k there to find o ut ho w to fix it. Open the Sale sUse rInt e rf ace .java class. See the co nstructo r, where the Results MenuItem is added at m 1.add(t = ne w Me nuIt e m (" Re sult s" ));. Check o ut the Act io nList e ne r and its requirements. The Me nuIt e m requires the array set in o rder to perfo rm the metho ds described in its Act io nList e ne r. Here are so me po ssible remedies: Set a flag to indicate whether the array has been set and if no t, set it. Ask the user to click the AllSe t butto n. Set everything to zero s so we start with a kno wn quantity. Make sure that it is set by do ing it o urselves again. Fo r this example, let's set all o f the values to whatever is currently in the J T e xt Fie lds. To do that we'll need to check the inputs again and then set the array. Also , we'll need ano ther metho d that do es almo st the same thing as act io nPe rf o rm e d() in Input Pane l. To pro mo te mo dularity o f co de, edit Input Pane l. There are two majo r changes: act io nPe rf o rm e d() is edited and much o f its co ntents go into a new metho d named se t AllInput s():

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].getTex t() + " for Sales Person " + (x+1) +" will be truncated.\n "; String messageLine3 = "You may enter a different integer and cli ck AllSet if truncation is unacceptable."; JOptionPane.showMessageDialog(this, messageLine1+messageLine2+me ssageLine3,"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()); sure they hit enter app.setSalesBar(goal); } }

// so don't have to be

Save it and run Main.java. Once yo u've co nfirmed that it still wo rks co rrectly, fix the menu cho ices. Edit Sale sUse rInt e rf ace as sho wn 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 al l is set if (processed) { remove(results); } results = new OutputPanel(app); add("South", results); processed = true; results.writeOutput(); }}); InitPanel specifyNumber = new InitPanel(); many salespeople add("North", specifyNumber); pack(); setVisible(true); }

// a panel to set up how // put it all together // 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 no t click All Se t . Select Opt io ns | Re sult s fro m the menu.

Example 4 Be sure yo ur co nstructo rs do not have a return type. If a metho d has a return type, then it is no t a co nstructo r. Co nstructo rs do no t have return types; by default they return an instance o f themselves. In so me cases, yo u may think yo u've called a co nstructo r to create an instance o f so mething, but yo u really haven't. In this next example, o ur co de wo n't give us a NullPo int e rExce pt io n, because it never creates an instance. Edit Sale sUse rInt e rf ace as sho wn 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 fo

r? 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 al l 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 sho wn 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 applicatio n fro m Main. Check the Co nso le to make sure that bo th print ln co mments appear. Make sure that the rest o f the applicatio n wo rks as expected. No w, give the Sale sUse rInt e rf ace class's constructor (fo und aro und line 21) a vo id return type. Add vo id to the Sale sUse rInt e rf ace () co nstructo r as sho wn:

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 al l 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 bo th classes, and run the Main class. No thing o pens and we see this in the co nso le:

We did no t get the Syst e m .o ut .print ln Did I ge t m ade ? fro m o ur Sale sUse rInt e rf ace co nstructo r. And we didn't get a NullPo interExceptio n until the line after the instantiatio n in Main. The line o f co de intended to make an instance fo r Sale sUse rInt e rf ace ran, but no thing happened. Why? Because vo id was given as a return type, the Sale sUse rInt e rf ace class didn't really have a co nstructo r, so Java just let the class inherit the co nstructo r fro m its supe r. The line Sale sUse rInt e rf ace appFram e = ne w Sale sUse rInt e rf ace (); ran as expected, but its Co nstructo r was o nly run fro m J Fram e . As a result o f inheritance, we did no t get a NullPo interExceptio n--yet. We called access to the applicatio n in the Main, but it should have been called in the Co nstructo r. Since Java never made the instance in the pro per Co nstructo r, mo st o f the variables we tho ught were set weren't.

Note

This is a difficult erro r to find, so make sure yo u never give a co nstructo r declaratio n a return type.

Example 5 Edit Sale sApp as sho wn in blue and re d:

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 yo u see why there's a null po inter? Yo u do n't have a sale s[] array instantiated yet.

Division By Zero We'll keep wo rking with the last example to explo re ano ther subclass (java.lang.Arit hm e t icExce pt io n) o f Runt im e Exce pt io n. Edit Sale sApp as sho wn in blue and re d:

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 fo r the co mpiler to catch these kinds o f exceptio ns befo re runtime. When the co mpiler scans the co de fo r pro per syntax, it do esn't kno w in advance whether a value fo r num Sale sPe o ple has been set. The co mpiler wo n't kno w whether the value is 0 at the time o f co mpilatio n. Change Sale sApp back, as sho wn 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 sho wn in blue : Co de 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 wo rks as befo re.

Array Out of Bounds Let's check o ut ano ther subclass (java.lang.ArrayInde xOut Of Bo undsExce pt io n) o f Runt im e Exce pt io n that can cause runtime exceptio ns. Yo u can go o ut o f bo unds pretty easily using f o r lo o ps. Take a lo o k. Create a new class as sho wn:

Type Sale sPe o ple as sho wn in blue : CODE TO TYPE: SalesPeo ple package salesGUI; public class SalesPeople { public static void main(String[] args) { String[] salesPeople; salesPeople = new String[4]; salesPeople[0] salesPeople[1] salesPeople[2] salesPeople[3]

= = = =

"John"; "Paul"; "George"; "Ringo";

for (int i=0; i 0) { int argIn = Integer.parseInt(args[0]); // user inputs ar gument at command line so use it SalesReport mySalesInfo = new SalesReport(argIn); // pass input as parameter to constructor mySalesInfo.testMe(); // start the appl ication } else { // no input from user so ask in constructor SalesReport mySalesInfo = new SalesReport(); // instantiate th e class with constructor with no parameters - will prompt for input mySalesInfo.testMe(); // start the appl ication } } } Because we have included if (args.le ngt h > 0 ), the user can enter arguments either at the co mmand line o r via a GUI pro mpt. In an earlier versio n o f this Main.java class, we tried to determine whether the user had entered an argument at the co mmand line by using the co nditio nal statement: if (args[0 ] != null). This co nditio nal statement actually lo o ks fo r args[0 ], but might no t find an args array at all. In that case, args[0 ] wo uld already be o ut o f bo unds. Let's try it again. Edit the co nditio nal statement as sho wn 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. Yo u'll see a message in the Co nso le: Exce pt io n in t hre ad " m ain" java.lang.ArrayInde xOut Of Bo undsExce pt io n: 0 . It is generally time-co nsuming and inefficient fo r Java to use t ry and cat ch blo cks when an appro priate if statement wo rks well eno ugh. But let's try it anyway just to illustrate the idea o f catching an ArrayInde xOut Of Bo undsExce pt io n. Edit the Main in sales1 as sho wn in blue belo w: CODE TO EDIT: Main package sales1; public class Main { public static void main(String[] args){ try { int argIn = Integer.parseInt(args[0]); will throw ArrayIndexOutOfBoundsException SalesReport mySalesInfo = new SalesReport(argIn); mySalesInfo.testMe(); } catch (ArrayIndexOutOfBoundsException exception) { SalesReport mySalesInfo = new SalesReport(); mySalesInfo.testMe(); } } }

// if no args[0],

Save and run it.

T ip

Because try/catch clauses are mo re labo r intensive (fo r Java and fo r yo u) than co nditio nal statements, exceptio n handling sho uld be just that: an exception to the no rm.

Errors Erro rs are co nditio ns that happen outside o f the applicatio n; fo r example, Out Of Me m o ryErro r. They are usually the result o f a majo r pro gramming mistake. Fo r example, a recursive pro gram (a pro gram with a metho d that calls itself) might no t have a sto p statement, which means an infinite lo o p wo uld be created. This co uld then generate a St ackOve rf lo wErro r. Try this co de:

CODE TO TYPE: InfiniteLo o p public class InfiniteLoop { public int tryMe(int x){ // System.out.println(x); // Run first without this line--then uncomment just t o 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 yo u don't want yo ur custo mers to see! Unfo rtunately, Erro rs can also o ccur when we do distributed co mputing--that is, when we go o utside o f the enviro nment o f o ur o wn machine. When a dynamic linking failure o r o ther hard failure (that is, a failure that needs to be fixed by a pro grammer) o ccurs in the Java virtual machine, the virtual machine thro ws an Erro r. Acco rding to the Oracle Tuto rial's sectio n o n Erro rs in The Catch o r Specify Requirement: "The seco nd kind o f exceptio n is the error. These are exceptio nal co nditio ns that are external to the applicatio n, and that the applicatio n usually canno t anticipate o r reco ver fro m. Fo r example, suppo se that an applicatio n successfully o pens a file fo r input, but is unable to read the file because o f a hardware o r system malfunctio n. The unsuccessful read will thro w java.io .IOErro r. An applicatio n might cho o se to catch this exceptio n, in o rder to no tify the user o f the pro blem — but it also might make sense fo r the pro gram to print a stack trace and exit. Erro rs are not subject to the Catch o r Specify Requirement. Erro rs are tho se exceptio ns indicated by Erro r and its subclasses." We've already seen that the API is a valuable so urce o f Interface, Class, and Exceptio n info rmatio n; no w we'll see ho w it helps us tackle Erro rs. Go to the java.lang package in the API. Scro ll do wn to the Erro r Sum m ary. Read abo ut a few o f them. Bo th Exce pt io ns and Erro rs inherit fro m the class T hro wable ...interesting. Simple pro grams typically do no t catch o r thro w Erro rs, but checked exceptio ns are subject to the Cat ch o r Spe cif y Re quire m e nt . We'll discuss that requirement in detail in the next lesso n.

Programming Responsibly In this lesso n, we've seen examples o f subclasses o f runtime exceptio ns and ho w to prevent them by careful co ding. Since pro grammers share co de with o ne ano ther so o ften, that's pretty impo rtant. Altho ugh Java requires that metho ds catch o r specify checked exceptio ns, metho ds that we write do no t have to catch o r specify unchecked exceptio ns (such as runtime exceptio ns). And because catching o r specifying an exceptio n requires mo re wo rk, pro grammers are o ccasio nally tempted to write co de that thro ws o nly runtime exceptio ns and therefo re do esn't have to catch o r specify them. This is exception abuse, and is no t reco mmended. Fo r mo re info rmatio n, see the Oracle Tuto rial Unchecked Exceptio ns - The Co ntro versy. The mo re yo u check yo ur co de to prevent unchecked exceptio ns fro m o ccurring at runtime, the better fo r all co ncerned. Do n't make Duke angry.

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Checked Exceptions: Catching Problems Degrading Gracefully In this lesso n, we'll fo cus o n checked exceptions. A well-written applicatio n will anticipate and pro vide mechanisms fo r reco very fro m these exceptio nal co nditio ns. We want yo u to understand exactly what's happening when an Exce pt io n is thro wn. We'll illustrate that pro cess so yo u'll be co nfident using, and ultimately defining yo ur o wn checked exceptio ns.

I/O Exceptions Other than Runt im e Exce pt io n and its subclasses, the mo st co mmo n exceptio ns o ccur when attempting to access files that are either no nexistent o r inaccessible. The next co urse will discuss Java's classes fo r input and o utput (I/O) in mo re explciit detail, but fo r no w, we'll use an less co mplicated example that reads a file to demo nstrate the use o f checked exceptio ns in the java.io package. Create a new java4 _Le sso n7 pro ject. If yo u're given the o ptio n to Ope n Asso ciat e d Pe rspe ct ive , click No . In this new pro ject, create a File Fe t che r class as sho wn:

Type File Fe t che r as sho wn in blue :

CODE TO TYPE: FileFetcher package exceptionTesting; import java.io.FileReader; import java.io.BufferedReader; public class FileFetcher { String aLine="";

// we will look at the file one line at a time

public void getHomework() { FileReader myFile = new FileReader("homework.txt"); ile--we will define this file next BufferedReader in = new BufferedReader(myFile); a class that lets us manipulate it aLine = in.readLine(); ile System.out.println(aLine); }

// create a Reader for a f // wrap the FileReader in // read in a line of the f // print it to the Console

public static void main(String [] args){ FileFetcher testMe = new FileFetcher(); testMe.getHomework(); } } We see two so urces o f erro rs:

Save and run it anyway. Even tho ugh Java co mplains abo ut Erro rs, click Pro ce e d. We have Unre so lve d co m pilat io n pro ble m s. (Also , since o ur pro gram co uld no t co mpile, it threw a java.lang.Erro r):

On the first line o f co de with an erro r, we invo ke the File Re ade r co nstructo r, so let's go to that part o f the API to find o ut mo re. Go to the java.io package. Scro ll do wn to the File Re ade r class in the Class Sum m ary. Lo o k at its co nstructo r File Re ade r(File f ile ):

In the Edito r, o n the next line with an erro r, we are calling the in.re adLine () metho d. in is an instance o f Buf f e re dRe ade r, so let's lo o k at its re adLine () metho d. Go back to the java.io package. Scro ll do wn to the Buf f e re dRe ade r class in the Class Sum m ary. Lo o k at its re adLine () metho d. Sure eno ugh, there's o ur exceptio n:

Exception T ypes We have seen vario us types o f exceptio ns. No t every cat ch clause will wo rk fo r every exceptio n that's thro wn.

Note

An exceptio n handler is co nsidered appro priate if the type o f the exceptio n o bject thro wn matches the type that it can handle.

Using T ry and Catch Since these are checked exceptions, Java will no t let us co mpile the co de to run it until we handle them. In this example, we aren't really handling the situatio n (in that we are no t addressing the "big picture" by, fo r example, finding replacement files), but we are handling the individual exceptio ns the co de thro ws, by pro viding t ry/cat ch clauses fo r them. In a real applicatio n, yo u wo uld do so mething mo re in these cat ch blo cks to reco ver fro m the exceptio n in a mo re meaningful way.

Edit File Fe t che r as sho wn in blue belo w: CODE TO EDIT: FileFetcher package exceptionTesting; import java.io.FileReader; import java.io.BufferedReader; public class FileFetcher { String aLine="";

// we will look at the file one line at a time

public void getHomework() { try { FileReader myFile = new FileReader("homework.txt"); // der for a file - we will define this file next System.out.println("I did get here"); in = new BufferedReader(myFile); // eReader in a class that lets us manipulate it } catch (FileNotFoundException e) { System.out.println("Can't find the file, but keep going ws for future problems!"); } try { aLine = in.readLine(); // of the file } catch(IOException e){ System.out.println("Now we have some other problem!"); } System.out.println(aLine); // the Console } public static void main(String [] args){ FileFetcher testMe = new FileFetcher(); testMe.getHomework(); } } That didn't help much:

create a Rea wrap the Fil

anyway--allo

read a line

print it to

We can fix these. The unreso lved issues can be reso lved by impo rting appro priate classes (java.io .File No t Fo undExce pt io n and java.io .IOExce pt io n). The variable in canno t be reso lved due to a sco pe issue. It is declared in o ne blo ck o f co de, a t ry clause, and then used in ano ther t ry clause. Edit File Fe t che r. Add the impo rts and declare the instance o f File Re ade r and Buf f e re dRe ade r so they can be seen by all metho ds:

CODE TO EDIT: FileFetcher package exceptionTesting; import these import import import

java.io.FileReader;

// could also import java.io.*; to get all of

java.io.BufferedReader; java.io.FileNotFoundException; java.io.IOException;

public class FileFetcher { String aLine=""; FileReader myFile; variables BufferedReader in;

// declare FileReader and BufferedReader as instance

public void getHomework() { try { myFile = new FileReader("homework.txt"); // Do NOT declare here too or scope stays within try block System.out.println("I did get here"); in = new BufferedReader(myFile); // Do NOT declare here too or scope stays within try block } catch (FileNotFoundException e) { System.out.println("Can't find the file, but keep going anyway - all ows for future problems!"); } try { aLine = in.readLine(); // read a line of th e file } catch(IOException e){ System.out.println("Now we have some other problem!"); } System.out.println(aLine); // print it to the Console } public static void main(String [] args){ FileFetcher testMe = new FileFetcher(); testMe.getHomework(); } } All o f the erro rs are go ne no w. Save and run it.

Of co urse, we can't find a file--we haven't made o ne yet. Let's make o ne no w. This time we're making a new File , no t a Java Class. Right-click o n the java4 _Le sso n7 Pro ject fo lder. Select Ne w | File . Enter the name ho m e wo rk.t xt and click Finish. No w the file structure fo r yo ur java4_Lesso n7 lo o ks like this:

Type ho m e wo rk.t xt as sho wn: CODE TO TYPE: ho mewo rk.txt I do not like doing my homework so I will try to make my parents do it. I will tell them that I am sick and see if I can get away with it. Save it and run the File Fe t che r class. Yo ur co de has this in the Co nso le no w:

Forwarding the Exception We've already used Java metho ds that thro w and handle exceptio ns . No w we'll fo rward the exceptio ns fo r o ther metho ds to handle. To see the entire text file in yo ur co nso le o utput, edit File Fe t che r as sho wn:

CODE TO EDIT: FileFetcher package exceptionTesting; import import import import

java.io.FileNotFoundException; java.io.IOException; java.io.FileReader; java.io.BufferedReader;

public class FileFetcher { FileReader myFile; BufferedReader in; String aLine =""; public void getHomework() { try { myFile = new FileReader("homework.txt");

// create a Reader for a f

ile System.out.println("I did get here"); in = new BufferedReader(myFile); // wrap the FileReader in a class that lets us manipulate it } catch (FileNotFoundException e){ System.out.println("Can't find the file, but keep going anyway--allo ws for future problems!"); } while (aLine != null){ try { aLine = in.readLine(); } catch(IOException e){ System.out.println("Now we have some other I/O problem"); } if (aLine !=null) System.out.println(aLine); // we had another read Line after the check for null } // later, we will do something more here } public static void main(String [] args){ FileFetcher testMe = new FileFetcher(); testMe.getHomework(); } } Save and run it.

Determining What T o Do With Exceptions T hrowing Exceptions Java metho ds may thro w exceptio ns at runtime. So metimes these erro rs can be avo ided thro ugh well-written co de, but o thers are unavo idable because we can't always anticipate what a user will do until runtime. Similarly, in handling checked exceptio ns, a metho d may kno w that it has an exceptio nal co nditio n, but the no t kno w ho w to handle it. The metho d wo uld need to t hro w the exceptio n, so the user o f the metho d co uld specify ho w to handle the pro blem given the current enviro nment. All co de in an o bject-o riented pro gram (like Java) is perfo rmed thro ugh the use o f metho ds, and metho ds that call o ther metho ds. So exceptio ns will always be thro wn in (o r mo re accurately, by) metho ds to so me o ther metho d that invo ked it. In fact, this is precisely the reaso n it is thro wing the exceptio n. If a metho d (say m e t ho d1()) can handle an exceptio nal co nditio n, it sho uld. Only when a metho d do esn't know what to do with an exceptio n, do es it need to thro w it to the metho d (say m e t ho d2()) that is using m e t ho d1() with the po tentially exceptio nal co nditio n.

Keep in mind that if m e t ho d1() is thro wing, then m e t ho d2() needs to cat ch--assuming m e t ho d2 is in an enviro nment where it kno ws what to do . If m e t ho d2() is not in such an enviro nment, then it needs to specify that it will thro w (fo rward) the exceptio n as well. Then the metho d that called it (say m e t ho d3()) wo uld need to cat ch it. This is the call st ack:

The thro ws co uld be passed back do wn the stack like this:

...and still be caught:

The Java runtime system searches the call stack fo r a metho d that co ntains a blo ck o f co de that can handle the exceptio n.

Example We will let o ur File Fe t che r class represent a student who is suppo sed to be writing a file named homework.txt. Our student wants to co nvince his parents that he has to o much to do and canno t do his ho mewo rk this time aro und; he is no t go ing to "handle" the ho mewo rk situatio n. He is no t go ing to handle the exceptio ns fo r retrieving his ho mewo rk file in the File Fe t che r metho d o f ge t Ho m e wo rk(). Here are the co nditio ns that represent Metho d2 fo r us: 1. The Metho d(s) where the erro r o ccurred thro w the exceptio ns. Co nstructo r File Re ade r(" ho m e wo rk.t xt " ) and re adLine () will be Metho d1(a) and Metho d1(b), respectively. 2. The File Fe t che r metho d o f ge t Ho m e wo rk() is Metho d2: Metho d witho ut an exceptio n handler. So , after all that wo rk we did to handle the pro blem within the metho d itself, no w o ur student says he is go ing to fo rward the exceptio ns. We'll see. Edit File Fe t che r as sho wn in blue and re d:

CODE TO EDIT: FileFetcher package exceptionTesting; import import import import

java.io.FileNotFoundException; java.io.IOException; java.io.FileReader; java.io.BufferedReader;

public class FileFetcher { FileReader myFile; BufferedReader in; String aLine =""; public void getHomework() throws FileNotFoundException, IOException { // try { myFile = new FileReader("homework.txt"); System.out.println("I did get here"); in = new BufferedReader(myFile); // } // catch (FileNotFoundException e){ // System.out.println("Can't find the file, but keep going anyway allows for future problems"); // } while (aLine != null){ // try { aLine = in.readLine(); // } // catch(IOException e){ // System.out.println("Now we have some other I/O problem"); // } if (aLine !=null) System.out.println(aLine); // we had a nother readLine after the check for null } } public static void main(String [] args){ FileFetcher testMe = new FileFetcher(); testMe.getHomework(); } } The o nly active pieces o f co de in the ge t Ho m e wo rk() metho d no w are the FileReader instance, the print ln statements, and the while lo o p fo r reading and printing lines fro m the ho mewo rk file. We might as well co mment o ut the m ain() metho d to o . As yo u can see by the erro r message, if the ge t Ho m e wo rk() metho d do esn't handle the exceptio ns, then the instance t e st Me canno t call the metho d, unless it handles them.

It lo o ks like o ur irrespo nsible student isn't go ing to handle anything. Instead he's letting his Mo m do it. Co mment o ut the entire m ain metho d here so there are no erro rs: CODE TO EDIT: FileFetcher package exceptionTesting; import import import import

java.io.FileNotFoundException; java.io.IOException; java.io.FileReader; java.io.BufferedReader;

public class FileFetcher { FileReader myFile; BufferedReader in; String aLine =""; public void getHomework() throws FileNotFoundException, IOException { // try { myFile = new FileReader("homework.txt"); System.out.println("I did get here"); in = new BufferedReader(myFile); // } // catch (FileNotFoundException e){ // System.out.println("Can't find the file, but keep going anyway allows for future problems"); // } while (aLine != null){ // try { aLine = in.readLine(); // } // catch(IOException e){ // System.out.println("Now we have some other I/O problem"); // } if (aLine !=null) System.out.println(aLine); // we had a nother readLine after the check for null } } //public static void main(String [] args){ // FileFetcher testMe = new FileFetcher(); // testMe.getHomework(); //} } If a metho d in a class do es no t catch the exceptio ns fro m metho d calls within itself, then that metho d needs to

thro w tho se uncaught exceptio ns o r it will no t co mpile. Thro wing exceptio ns rather than handling them is not the preferred way to write metho ds. Only do it when it's absolutely necessary. If yo ur metho d can handle the exceptio ns fro m the metho ds that it uses, it sho uld. Thro wing metho ds sho uld happen o nly when the applicatio n canno t deal with the exceptio n in its current metho d enviro nment.

Passing Exceptions to Other Methods No w, let's suppo se that o ur student's Mo m says she will help, but o nly with a single exceptio n (she cho o ses to handle File No t Fo undExce pt io n), and that Dad has to help with the o ther. In the java4_Lesso n7 pro ject, create a new Mo m class as sho wn:

Type the Mo m class, as sho wn in blue belo w:

CODE TO TYPE: Mo m package exceptionTesting; import java.io.FileNotFoundException; import java.io.IOException; public class Mom { public void getToDoHomework() throws IOException { FileFetcher testMe = new FileFetcher(); try{ testMe.getHomework(); } catch(FileNotFoundException e){ System.out.println("Mom caught the File Not Found Exception."); } } public static void main(String [] args) throws IOException { // Note: This is VERY BAD programming. Do not throw exceptions in main m ethods. Mom parent1 = new Mom(); parent1.getToDoHomework(); } } Save and run it. Yo u might get a warning that there are still erro rs--click Pro ce e d anyway. We are thro wing an exceptio n in this m ain() metho d to demo nstrate that it's a terrible thing to do , because no o ne can catch fro m m ain(). That's the reaso n Eclipse warned that yo u still had erro rs. Ho wever, the co de do es run. No exceptio n was thro wn, so there was no thing fo r the file name to catch. Go back to the File Fe t che r class and change the file name as sho wn:

CODE TO EDIT: FileFetcher package exceptionTesting; import import import import

java.io.FileNotFoundException; java.io.IOException; java.io.FileReader; java.io.BufferedReader;

public class FileFetcher { FileReader myFile; BufferedReader in; String aLine =""; public void getHomework() throws FileNotFoundException, IOException { //try { myFile = new FileReader("homework2.txt"); System.out.println("I did get here"); in = new BufferedReader(myFile); //} //catch (FileNotFoundException e){ // System.out.println("Can't find the file, but keep going anyway - allow s for future problems"); //} while (aLine != null){ // try { aLine = in.readLine(); // } // catch(IOException e){ // System.out.println("Now we have some other IO problem"); // } if (aLine !=null) System.out.println(aLine); // we had another readLin e after the check for null } } //public static void main(String [] args){ // FileFetcher testMe = new FileFetcher(); // testMe.getHomework(); //} }

Save it. Yo u haven't created a ho mewo rk2.txt file, so yo u pro bably have a little pro blem. Open the Mo m class and run it. We can see that Mo m caught the File No t Fo undExce pt io n; the Co nso le sho ws the print ln fro m the cat ch clause blo ck. No w in File Fe t che r, change the filename back to ho m e wo rk.t xt .

Catching Exceptions from Other Methods In java4_Lesso n7, create a new Dad class as sho wn:

Type the Dad class as sho wn in blue belo w:

CODE TO TYPE: Dad package exceptionTesting; import java.io.IOException; public class Dad { public void parentalCollaboration() { Mom spouse = new Mom(); try{ spouse.getToDoHomework(); } catch(IOException e){ System.out.println("Dad caught the I/O Exception."); } } public static void main(String [] args) Dad parent2 = new Dad(); parent2.parentalCollaboration(); }

{

} Save and run it. Yo u do not get any erro rs, because no w yo u've caught all Exceptio ns.

One More T ime The last example demo nstrates the way yo u can either handle exceptio ns within yo ur co de with try/catch clauses, o r have yo ur metho d thro w the exceptio n and let the metho ds' users handle them. And in o ur example that passed an exceptio n do wn the call stack, we saw this:

Co mpare that to o ur metho d with a handler:

Exception Hierarchy Let's have Mo m handle a different exceptio n. Edit the Mo m class as sho wn in blue : CODE TO EDIT: Mo m package exceptionTesting; import java.io.FileNotFoundException; import java.io.IOException; public class Mom { public void getToDoHomework() throws FileNotFoundException { FileFetcher testMe = new FileFetcher(); try{ testMe.getHomework(); } catch(IOException e){ System.out.println("Mom caught the I/O Exception."); } } public static void main(String [] args) throws FileNotFoundException { Mom parent1 = new Mom(); parent1.getToDoHomework(); } } And edit the Dad class as sho wn in blue belo w:

CODE TO EDIT: Dad package exceptionTesting; import java.io.FileNotFoundException; public class Dad { public void parentalCollaboration() { Mom spouse = new Mom(); try{ spouse.getToDoHomework(); } catch(FileNotFoundException e){ System.out.println("Dad caught the File Not Found Exception."); } } public static void main(String [] args) Dad parent2 = new Dad(); parent2.parentalCollaboration(); }

{

} Go to File Fe t che r and change the filename to ho m e wo rk2.t xt . Save all three classes: File Fe t che r, Mo m , and Dad. Run fro m the Dad class. Even tho ugh this was a File No t Fo undExce pt io n, Mo m caught it--we see the o utput fro m her catch in the Co nso le. Why? Because she was suppo sed to catch the IOExce pt io ns and Dad was suppo sed to catch the File No t Fo undExce pt io ns. Go to the java.io package. Scro ll do wn to File No t Fo undExce pt io n in the Exce pt io n Sum m ary and take a lo o k at its hierarchy:

Exceptio ns also pay attentio n to inheritance. Mo m said she wo uld catch IOExce pt io ns, and File No t Fo undExce pt io n inhe rit s fro m IOExce pt io ns, so it actually is an IOExce pt io n. If yo u want to make sure to catch the right exceptio ns, always catch the mo re specific o ne first. Exceptio ns are o ften the results o f I/O pro blems.

Using Finally in a T ry/Catch Block A given metho d can thro w mo re than o ne exceptio n. In fact, the ge t Ho m e wo rk() metho d in the File Fe t che r class threw two exceptio ns. If yo u had wanted the Mo m class to catch and handle bo th specifically, the metho d ge t T o Do Ho m e wo rk() might have been written like this:

Multiple Catches public void getToDoHomework(){ FileFetcher testMe = new FileFetcher(); try { //Begin "try" block testMe.getHomework(); } catch(FileNotFoundException e){ System.out.println("Mom caught the File Not Found Exception."); } catch(IOException e) { System.out.println("Mom caught the I/O Exception."); } finally { System.out.println("After you finish reading the file, you need to c lose the file streams."); try{ if (testMe.in != null) testMe.in.close(); if (testMe.myFile != null) testMe.myFile.close(); } catch(IOException e){} } // End "try" block } The Mo m class also added a f inally clause, to remind Dad that he sho uld clo se his files and input streams when he finishes reading them. The f inally clause always executes when the t ry blo ck exits. This guarantees that the f inally blo ck is executed even if an unexpected exceptio n o ccurs. We want to allo w a pro grammer to "clean up" after exceptio ns have o ccurred because a call fro m a t ry clause may have jumped o ut o f co de prematurely. The f inally blo ck is a key to o l fo r preventing reso urce leaks.

Additional Information There is plenty mo re to see in the Java Tuto rial Lesso n o n Exceptio ns. Check it o ut. See yo u in the next lesso n... Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Threads: Introduction Multi-T asking In the next few lesso ns, we'll explo re the ways Java allo ws us to co o rdinate multiple tasks. Pro grammers need be able to determine which o peratio ns are executed and in what o rder. The Java pro gramming language allo ws us to write vario us individual pro grams that, while running seperately and co ncurrently, appear as a single, unified and seamless o peratio n to the user. This is acco mplished by pro viding mechanisms fo r synchro nizing the co ncurrent activity o f threads.

T hreads A thread is a single sequential flo w o f co ntro l within a pro gram. The API says, "A thread is a thread o f executio n in a pro gram. The Java Virtual Machine allo ws an applicatio n to have multiple threads o f executio n running co ncurrently." Befo re we discuss the theo ry o f Threads, we'll lo o k at the to o ls Java pro vides fo r us to weave them into o ur co de. We will lo o k at Threads themselves mo re explicitly in the next lesso n. Go to the java.lang package and scro ll do wn to the T hre ad class and read its intro ductio n. A thread has these qualities: It im ple m e nt s the Runable interface. It inherits fro m Obje ct . It has a Ne st e d Class o f T hre ad.St at e . We can take advantage o f the capabilities o f T hre ads in Java by: 1. subclassing the T hre ad class. 2. implementing the Runnable interface. We'll demo nstrate bo th o f these techniques in this lesso n.

Subclassing the T hread Class Create a new java4 _Le sso n8 pro ject. If yo u're o ffered the o ptio n to Ope n Asso ciat e d Pe rspe ct ive , click No . In this pro ject, create a new Sim ple T hre ad class as sho wn:

Type Sim ple T hre ad as sho wn in blue : CODE TO TYPE: SimpleThread package demo; class SimpleThread extends Thread { public SimpleThread(String str) { super(str); } public void run() { for (int i = 0; i < 10; i++) { System.out.println(i + " " + getName()); try { sleep((int)(Math.random() * 1000)); } // end try catch (InterruptedException e) { } // end catch } // end for loop System.out.println("DONE! " + getName()); } // end run method }

There are two snippets o f co de here that we haven't seen befo re: 1. supe r(st r) 2. the try/catch clause surro unding the sle e p() metho d Let's go to the API to find o ut mo re abo ut this co de. Go to java.lang.T hre ad and check o ut the inheritance tree. Our Sim ple T hre ad class inherits fro m T hre ad. What wo uld T hre ad Co nst ruct o r inherit if a St ring was passed to it via supe r(st r)? Go to the java.lang.T hre ad Me t ho d Sum m ary and lo o k at the metho ds there. Bo th metho ds present thro w Int e rrupt e dExce pt io ns. We need to handle them using try/catch clauses. We've seen the static metho d call Mat h.rando m () and casting befo re, so this might lo o k familiar to yo u already. If no t, go to the java.lang.Mat h class and lo o k at the rando m () metho d. Save and run Sim ple T hre ad. Yo u see a menu cho ice Ope n Run Dialo g, and then a windo w:

Go ahead and cho o se Run. Hmm. So mething's wro ng. There are a number o f questio ns to co nsider: Is yo ur class an Apple t ? Do es yo ur applicat io n have a m ain() metho d? Have yo u instantiated yo ur Sim ple T hre ad to make an instance? Did Eclipse simply grab the last applicatio n yo u ran? Let's add a m ain() metho d to test. Edit Sim ple T hre ad as sho wn in blue belo w:

CODE TO EDIT: SimpleThread package demo; class SimpleThread extends Thread { public SimpleThread(String str) { super(str); } public void run() { for (int i = 0; i < 10; i++) { System.out.println(i + " " + getName()); try { sleep((int)(Math.random() * 1000)); } catch (InterruptedException e) { } } System.out.println("DONE! " + getName()); } public static void main (String [] args){ SimpleThread st = new SimpleThread("myGuy"); } } Save and run it. No thing happened. That's because you have direct co ntro l o f yo ur thread, so you must start it explicitly. Java T hre ads need to be instantiated like any o ther class. Ho wever, yo u do n't call the run() metho d explicitly; yo u begin by invo king the st art () metho d. Edit Sim ple T hre ad again. Add the co de in blue as sho wn: CODE TO EDIT: SimpleThread package demo; class SimpleThread extends Thread { public SimpleThread(String str) { super(str); } public void run() { for (int i = 0; i < 10; i++) { System.out.println(i + " " + getName()); try { sleep((int)(Math.random() * 1000)); } catch (InterruptedException e) { } } System.out.println("DONE! " + getName()); } public static void main (String [] args){ SimpleThread st = new SimpleThread("myGuy"); st.start(); } } Save and run it.

Note

Starting T hre ads is different fro m starting o ther classes and metho ds. T hre ads must have a run() metho d in o rder to o perate, but yo u start the run() metho d by invo king the instance o f the T hre ad with st art ().

Go to the java.lang.T hre ad class and lo o k at its st art () metho d. Here (and in general) we inherit the metho d st art () fro m o ur thread Sim ple T hre ad's parent T hre ad. In the instance o f the T hre ad that we're running here, we instruct it to sle e p() fo r a few milliseco nds. This sto ps it fro m running fo r at least the specified time and then allo ws it to co ntinue. Let's see what happens when we co mment o ut the t ry/cat ch blo ck with the sleep call: CODE TO EDIT: SimpleThread package demo; class SimpleThread extends Thread { public SimpleThread(String str) { super(str); } public void run() { for (int i = 0; i < 10; i++) { System.out.println(i + " " + getName()); //try { // sleep((int)(Math.random() * 1000)); //} //catch (InterruptedException e) { //} } System.out.println("DONE! " + getName()); } public static void main (String [] args){ SimpleThread st = new SimpleThread("myGuy"); st.start(); } } Save and run it. Take the co mment slashes (//) o ut, then save and run it again. Putting threads to sle e p() allo ws mo re time fo r o ther actio ns (fro m o ther threads) to take place.

Manipulating T hreads Let's experiment so me mo re. Create a new Ano t he rT hre ad class in the java4_Lesso n8 pro ject as sho wn:

Type Ano t he rT hre ad as sho wn in blue :

CODE TO TYPE: Ano therThread package demo; // Threads are in java.lang.Thread so no import is needed public class AnotherThread { public static void main(String args[]) { T t = new T(); t.start(); } } class T extends Thread { public void run() { while(true) { // forever, System.out.println("b: "); // prompt the user with a b:, wait ing for them to do something try { sleep(300); // sleep is in milliseconds } catch (InterruptedException e){} } } } We have defined the class Ano t he rT hre ad, which has a nested class named T , which extends T hre ad. The variable t in the m ain() metho d o f class Ano t he rClass sho uld co ntain a valid thread o f executio n fo r an instance o f the subclass o f T hre ad that we named T . We co ntro l this thread in the run() metho d. Once inside the run() metho d, we're able execute statements just like in any pro gram. In these examples, we are pausing fo r a specified perio d o f time. In o ur first example, the perio d o f time was rando m; in the abo ve class, it's 30 0 milliseco nds. In o ur co de it's written like this: sle e p(30 0 ). The sle e p() metho d tells a thread to pause fo r at least the specified number o f milliseco nds. The sle e p() metho d do es no t take up system reso urces while the thread sleeps. Other threads can co ntinue to wo rk. Save and run it. No rmally, threads sto p when their run() metho d is co mplete. In this thread, ho wever, we have an infinite lo o p in the run() metho d. [Ct rl+c] will sto p mo st executio n pro cesses. However, in this case (that is, within Eclipse within a thread within o ur co ntro l), yo u click the T e rm inat e bo x to sto p executio n:

Here's ano ther example that uses threads and passes parameters. In the java4_Lesso n8 pro ject, add a new T e st T hre ad class as sho wn:

Type T e st T hre ad as sho wn in blue belo w:

CODE TO TYPE: TestThread package demo; They will pause for a short time

// Define our simple threads. // and then print out their nam

es class TestThread extends Thread { private String whoAmI; private int delay; public TestThread(String s, int d) { (whoAmI) and time to sleep (delay) whoAmI = s; delay = d; }

// Our constructor to receive the name

// run--the thread method similar to m ain()--when run is finished, the thread dies. public void run() { // run is called from the start() meth od of Thread try { sleep(delay); } catch (InterruptedException e) { } System.out.println(whoAmI + " has delay time of " + delay); } } The run() metho d serves as the m ain() ro utine fo r threads. Just like m ain(), when run() finishes, so do es the thread. Applicatio ns use the m ain() metho d to retrieve their arguments fro m the args parameter (which is typically set in the co mmand line). A newly created thread must receive its arguments pro grammatically fro m the o riginating thread. That way parameters can be passed in thro ugh the co nstructo r, static variables, o r any o ther technique designed by the develo per. In the T e st T hre ad example, we pass the parameters thro ugh the co nstructo r. We need to create a class to instantiate a few instances o f this T e st T hre ad. In the java4_Lesso n8 pro ject, add a new class named Mult iT e st as sho wn:

Type Mult iT e st as sho wn in blue belo w: CODE TO TYPE: MultiTest // A simple multithread test program package demo; public class MultiTest { public static void main(String args[]) { TestThread t1, t2, t3; // Instantiate/create our test threads t1 = new TestThread("Thread1",(int)(Math.random()*1000)); t2 = new TestThread("Thread2",(int)(Math.random()*2000)); t3 = new TestThread("Thread3",(int)(Math.random()*3000)); // Start each of the threads t1.start(); t2.start(); t3.start(); // At this point we have started 3 threads! } }

Save bo th T e st T hre ad and Mult iT e st . Run Mult iT e st using its m ain() metho d. No w, try changing the maximum number o f rando m times as sho wn in blue : CODE TO EDIT: MultiTest // A simple multithread test program package demo; public class MultiTest { public static void main(String args[]) { TestThread t1, t2, t3; // Instantiate/create our test threads t1 = new TestThread("Thread1",(int)(Math.random()*3000)); t2 = new TestThread("Thread2",(int)(Math.random()*2000)); t3 = new TestThread("Thread3",(int)(Math.random()*1000)); // Start each of the threads t1.start(); t2.start(); t3.start(); // At this point we have started 3 threads! } } Remember that these are random, so the o rder in which they appear do es no t necessarily indicate increased o r decreased delays. Save and run Mult iT e st again.

T hreads in Applets Apple t s can have T hre ads to o . With a few adaptatio ns, o ur applicatio n abo ve can be turned into an Applet. In java4_Lesso n8 , create a new Mult iT e st Apple t class as sho wn:

Type MultiTest as sho wn:

CODE TO TYPE: MultiTestApplet package demo; import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class MultiTestApplet extends Applet implements ActionListener { TestThread t1, t2, t3; public void init() { Button runUs = new Button("Run Threads"); utton add(runUs); the Applet runUs.addActionListener(this); ener to the Button t1 = new TestThread("Thread1",(int)(Math.random()*1000)); e our 3 TestThreads t2 = new TestThread("Thread2",(int)(Math.random()*2000)); t3 = new TestThread("Thread3",(int)(Math.random()*3000)); } public void actionPerformed(ActionEvent e){ t1.start(); he Button will allow us to start the threads t2.start(); t3.start(); } }

// create a B // add it to // add a List // instantiat

// clicking t

Save and run it. Click o n the butto n. There's o utput in the Co nso le because we used Syst e m .o ut .print ln. Fo r no w, clo se this Applet. We'll co me back to it later when we lo o k at Thread States.

Implementing the Runnable Interface So metimes we want a class to use a T hre ad, but we do no t want that class to be a T hre ad. Java allo ws o nly single inheritance, so if a class inherits fro m Apple t it canno t inherit fro m T hre ad at the same time. We get aro und this issue using Int e rf ace s. Yo u can also create a thread by declaring a class that im ple m e nt s the Runnable interface. in the java.lang package, go to the Int e rf ace Sum m ary and cho o se Runnable . Scro ll do wn its descriptio n to see the metho ds that this interface specifies. Yo u'll actually o nly see o ne metho d in the Me t ho d Sum m ary: run(). If a class im ple m e nt s Runnable , then the class must implement the run() metho d. An instance o f the class can then be allo cated, passed as an argument when creating a T hre ad, and started. We'll go to the API to see what this means, and demo nstrate it. Go to the class java.lang.Thread and read thro ugh its co nstructo rs. Many o f them have a parameter o f type Runnable :

Let's try an example. In java4_Lesso n8 , create a new class as sho wn:

Type T hre ade dApple t as sho wn in blue :

CODE TO TYPE: ThreadedApplet package demo; import java.applet.Applet; import java.awt.Graphics; public class ThreadedApplet extends Applet implements Runnable { Thread appletThread; // the thread we make will be an instance of the class Th read String messages[] = {"Hello Thread World!" , "I'm doing fine." , "Goodbye for now!" }; int i = 0; public void paint(Graphics g) { g.drawString(messages[i], 15, 50); } public void run() { while (true){ i = (i+1) % messages.length; repaint(); try { appletThread.sleep(5000); } catch (InterruptedException e){} } } public void start() { appletThread = new Thread(this); appletThread.start(); } } This pro gram is an Apple t with a run() metho d that cycles to print different messages. When the Applet starts, it instantiates the T hre ad and then starts the Thread instance. In this example, we passed the Thread Co nstructo r an instance o f an o bject that implements Runnable (that is, the Applet itself--t his). The Thread then co mes back to the Applet to get the run() metho d that it implemented. This is especially co nvenient, because then the T hre ade dApple t 's paint (Graphics g) metho d and the implemented run() metho d can share the m e ssage s[ ] instance variable. Save and run it. Sit back and watch fo r a while. Here the ThreadedApplet class has a st art () metho d f o r t he Apple t . In this metho d, the thread is instantiated and its st art () metho d (apple t T hre ad.st art ();) is invo ked. Of co urse, we'll want a st o p fo r o ur thread to o . Fo r no w, yo u can sto p the thread and applet by quitting the applet.

One More T ime To create a thread, yo u must subclass T hre ad and define this subclass's o wn run() metho d o r yo u must pass a Runnable o bject--which means the o bject must implement a run() metho d--to the T hre ad Co nstructo r. The Runnable interface specifies the run() metho d that is required. Any class that implements this interface can pro vide the body o f a thread. By implementing the Runnable interface, yo u declare yo ur intentio n to run a separate thread. Whichever way yo u lo o k at threads, the o perative wo rd is run(). Mo re details o n Java Threads are o n the way!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Threads: Concurrent Programming Behind the Scenes In co ncurrent pro gramming, there are two basic units o f executio n: pro cess and thread. A process has a self-co ntained executio n enviro nment. Mo st co mputer users use pro cesses witho ut kno wing it. Pro cesses are usually lo cated at the level o f o perating systems so ftware, o r kernel-level entities. A pro cess generally has a co mplete, private set o f basic run-time reso urces. Specifically, each pro cess has its o wn registers, pro gram co unter, stack po inter, and memo ry space. Threads exist within a pro cess-—every pro cess has at least o ne thread. In Java, co ncurrent pro gramming is acco mplished mo stly thro ugh threads. So metimes threads are called lightweight pro cesses o r execution contexts. Bo th pro cesses and threads pro vide an executio n enviro nment (like pro cesses, threads have their o wn registers, pro gram co unters, stack po inters, etc.), but threads are clo ser to a user-level entity. We can create threads and tell them what to do using fewer reso urces than we do when we create and info rm new pro cesses.

Note

Multiple threads running at the same time and sharing reso urces have the po tential to cause pro blems. We'll go o ver so me o f these issues in this lesso n. We'll talk abo ut o thers in the next lesso n when we explo re synchro nizatio n. Fo r no w, we'll fo cus o n plain o ld multi-tasking.

Multi-threaded Applications T he Life of a T hread Let's start with an example o f multi-tasking. In the edito r, o pen yo ur java4_Lesso n8 fo lder to the de m o package and o pen the T hre ade dApple t class. Edit T hre ade dApple t as sho wn in blue belo w:

CODE TO TYPE: ThreadedApplet package demo; import java.awt.Graphics; import java.applet.Applet; public class ThreadedApplet extends Applet implements Runnable { Thread appletThread; // the thread we make will be an instance of the class Thread String messages[] = {"Hello Thread World!" , "I'm doing fine." , "Goodbye fo r now!"}; int i = 0; public void paint(Graphics g) { g.drawString(messages[i], 15, 50); } public void run() { while (true){ i = (i+1) % messages.length; repaint(); System.out.println("Hey! I'm still here"); try { appletThread.sleep(5000); } catch (InterruptedException e){} } } public void start() { appletThread = new Thread(this); appletThread.start(); } } Save and run it. Watch it run fo r a while, then in the Applet Viewer Windo w, select Apple t | St o p to sto p it. No w, watch the Co nso le fo r a minute.

Our Applet has sto pped (it's no lo nger painting), but its Thread is still running (it's still putting o utput into the Co nso le). Quit the Applet to clo se the Applet Viewer Windo w; the actio n in the Co nso le sto ps.

What's Happening in the Background? Our T hre ade dApple t illustrates that we need to take care when using multiple threads. Have yo u ever o pened a web page that seemed to slo w yo ur machine do wn--even after yo u left the page? Our example sho ws us the reaso n behind that. We st o ppe d the Applet (which happens when yo u leave a bro wser page that's running the applet), but we did no t st o p o ur Applet's thread. Remember--sto pping the thread and sto pping the applet are two distinct pro cesses. We will fix this by making o ur co de cleaner, but first let's go o ver o ne mo re backgro und item.

Garbage Collection One example o f a thread wo rking in the backgro und in Java is in garbage collection--retrieving memo ry that has been allo cated, but is no lo nger being used. Java co llects garbage using T hre ads. While yo u are running yo ur pro gram, the Java Virtual Machine has a garbage co llectio n thread cleaning up in the backgro und. Here are so me links to mo re info rmatio n o n this to pic. This JavaWo rld article explains the co ncept o f garbage co llectio n. Oracle also pro vides a useful page that explains Tuning Garbage Co llectio n.

T hread States The first Java tuto rial o n T hre ads pro vides the state transitio n diagram belo w described as "an o verview o f the interesting and co mmo n facets o f a thread's life":

Each o val in the diagram represents a po ssible state o f the thread. The arro ws represent po tential transitions amo ng the states. We will give yo u a new versio n o f o ur T hre ade dApple t and co mment in the co de when the thread is in o ne o f tho se po tential states listed. Edit T hre ade dApple t as sho wn in blue : CODE TO EDIT: ThreadedApplet package demo; import java.awt.Graphics; import java.applet.Applet; public class ThreadedApplet extends Applet implements Runnable { Thread appletThread; String messages[] = {"Hello Thread World!" , "I'm doing fine." , "Good-bye for now! "}; int i = 0; public void paint(Graphics g) { g.drawString(messages[i], 15, 50); } public void run() { while (appletThread != null) { // checks if Thread exists i = (i + 1) % messages.length; repaint(); System.out.println("Hey! I'm still here"); try { appletThread.sleep(5000); // sleep--put in Not Runnable State (TIMED_ WAITING) } catch (InterruptedException e){ } } } public void start() { // start of Applet if (appletThread == null) { appletThread = new Thread(this); // new Thread()--achieve New Thread State appletThread.start(); // start of Thread--achieve Runnable Stat e } } public void stop() { appletThread = null; State } }

// stop Applet // stop Thread by destroying--put in Dead

Save and run it. Watch a while--at least until it cycles--and then, in the Applet Viewer Windo w, select Apple t | St o p to sto p it. No w lo o k at the Co nso le fo r a bit:

This time, when o ur Applet was sto pped (no lo nger painting), its Thread was also sto pped (no new o utput in Co nso le). This is because when we sto pped the Applet, we actually killed the thread (we made it null), but we aren't do ne yet! Restart the Applet (Apple t | Re st art ). When we Restart, it calls the st art () metho d o f the Applet, which starts a new Thread. The thread begins again--bo th in the Applet's paint () metho d and in the Co nso le. No w, try to Start the Applet (Apple t | St art ). Lo o k at the bo tto m o f the Applet Viewer Windo w:

This time, the Applet was already in the start state, as was its Thread. A thread canno t be started with the st art () metho d after it has already been started (click here to learn mo re abo ut st art () fo r T hre ads). That's why we checked first to find o ut if the thread already existed in the st art () metho d fo r the applet. If it did no t exist, we wo uld've created it. If it had existed, we wo uldn't have had to do anything because it still wo uld've been there, even if the applet had been delayed fo r a while. Let's lo o k mo re clo sely at thread states and see what else is po ssible.

T hread.State In o rder to start the thread, we called a st art () metho d fo r the thread inside o f the st art () metho d fo r the applet. So why do n't we do the same thing to sto p? We wro te a st o p() metho d fo r o ur applet, so why did we kill the thread (by making it null) rather than call a thread st o p() metho d? Go to java.lang.T hre ad. Scro ll to the Me t ho d Sum m ary. Check o ut these metho ds: de st ro y(), re sum e (), st o p(), and suspe nd(). In each o f these metho ds yo u see the wo rd deprecated and a descriptio n o f the reaso n. Each new release o f Java includes new functio nality and fixes. If a metho d is deprecated, it means that the metho d sho uld no t be used in new co de because a pro blem was fo und that co uld no t be fixed, but that metho d is retained (perhaps tempo rarily) in o rder to maintain co mpatibility with o lder versio ns o f Java. There is an inherent pro blem with the o lder implementatio n, so newer versio ns o f Java sho uld no t use it. If yo u are respo nsible fo r maintaining co de that co ntains a deprecated metho d, replace that metho d if po ssible. Each deprecated metho d po ints to the Oracle tuto rial Java Thread Primitive Deprecatio n. T hre ads was altered in versio n 1.5 o f Java to alleviate pro blems with deprecated metho ds. They also want to make sure that users do n't enco unter pro blems due to the speed o f co mputers by adding Enum States fo r T hre ads. Let's take a lo o k at tho se changes. Go to java.lang.T hre ad. Scro ll to the Ne st e d Class Sum m ary. Click o n T hre ad.St at e . Scro ll to the descriptio n o f the states:

If Oracle created Enum Co nst ant s in their o wn Enum class T hre ad.St at e , then T hre ad states must be impo rtant. This particular T hre ad.St at e class is dedicated so lely to pro viding an enumeratio n o f the states and metho ds that indentify tho se states.

More on Multi-T hreaded Applications Design Pattern: Producer/Consumer The design pattern o f Producers and Consumers used in co njunctio n with threads is co mmo n in Java pro gramming. As is o ften the case, many pro ducers supply a pro duct o r reso urce, and many co nsumers take the pro duct o r reso urce. A shared resource is o ne that many co nsume. The reso urce is available at so me lo catio n (like a sto re o r wareho use), and invento ry must be maintained and tracked. Pro blems may o ccur when there is no t eno ugh supply available to meet the demand, o r if the "sto reho use" takes in mo re than it can handle. To avo id such pro blems, we need a monitor to keep invento ry o f o ur reso urce. We'll include a mo nito r when we create o ur applicatio n. Our threaded applicatio ns will have these three co mpo nents: Pro duce r: supplies the reso urce. Co nsum e r: uses the reso urce.

Mo nit o r: keeps a running invento ry o f the reso urce.

Using T hreads in an Application Our first example using threads will invo lve alphabet so up. In this example, there is a child who demands that his so up co ntain mo re than just bro th, so his parent produces alphabet letters to add to his so up. The child can then consume these letters. The design pattern we described abo ve materializes in o ur example like this: Pro duce r: the parent. Co nsum e r: the child. Mo nit o r: alphabet so up (stay with me o n this).

T he Producer Make a new java4 _Le sso n9 pro ject. If yo u're given the o ptio n to "Open Asso ciated Perspective", click No . In this pro ject, create a new Class as sho wn:

Type Pro duce r as sho wn in blue :

CODE TO TYPE: Pro ducer package prodcons; class Producer extends Thread { private Soup soup; private String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; private MyTableSetting bowlView; public Producer(MyTableSetting bowl, Soup s) { bowlView = bowl; // the producer is given the GUI that will show what is happening soup = s; // the producer is given the soup--the monitor } public void run() { String c; for (int i = 0; i < 10; i++) { // only put in 10 things so it will stop c = String.valueOf(alphabet.charAt((int)(Math.random() * 26))); // randomly pick a number to associate with an alphabet letter soup.add(c); // add it to the soup System.out.println("Added " + c + " to the soup."); // show what happened in Console bowlView.repaint(); // show it i n the bowl try { sleep((int)(Math.random() * 2000)); is not too fast to see } catch (InterruptedException e) { } } } }

Save it.

T he Consumer In java4_Lesso n9 , create a new Co nsum e r class as sho wn:

// sleep for a while so it

T ype Co nsum e r as sho wn in blue :

CODE TO TYPE: Co nsumer package prodcons; class Consumer extends Thread { private Soup soup; private MyTableSetting bowlView; public Consumer(MyTableSetting bowl, Soup s) { bowlView = bowl; the GUI that will show what is happening soup = s; the soup--the monitor } public void run() { String c; for (int i = 0; i < 10; i++) { // re are no more coming; here we know there will only be c = soup.eat(); System.out.println("Ate a letter: " + c); Console bowlView.repaint();

// the consumer is given // the consumer is given

stop thread when know the 10 // eat it from the soup // show what happened in // show it in the bowl

try { sleep((int)(Math.random() * 3000)); // have consumer sleep a little longer or sometimes we never see the alphabets! } catch (InterruptedException e) { } } } }

Save it.

T he Monitor To ensure that shared info rmatio n within the mo nito r do esn't beco me co rrupted, we'll synchronize the add and re m o ve metho ds in this class. Synchro nizatio n prevents multiple metho ds fro m accessing a shared reso urce simultaneo usly. If a metho d in a class is synchro nize d, it BLOCKs o ther T hre ads fro m accessing any o ther synchro nize d metho ds in that instance o f that class. A class with synchro nize d metho ds pro vides a lo ck and prevents what are kno wn as deadlock co nditio ns. Here's ho w a deadlo ck co nditio n might lo o k in real life. Let's say we're waiting in line in a bank. I am at the fro nt o f the line, waiting to withdraw cash. The bank is o ut o f cash, but I am willing to wait fo r so me cash to be depo sited. The bank o nly has o ne teller, who canno t handle ano ther transactio n until the current transactio n is finished. I am still waiting to receive my mo ney, so my transactio n is no t finished. Yo u are in line behind me with a millio n do llars to depo sit. Yo u can't depo sit the mo ney until I finish my transactio n, and I will no t be finished until so meo ne depo sits so me mo ney. We are in a deadlock. In o ur example, we'll use synchro nize d blo cks o f co de to prevent deadlo ck. A few o ther elements o f o ur example yo u sho uld also be aware o f: Only 6 alphabet letters may be in the so up at a given time, so we set the variable capacit y to 6 . We'll determine whether there are alphabet letters in the so up (the buffer) befo re we take any o ut. We'll determine whether the so up is full (that is, at it's capacity o f 6 letters) befo re we add any mo re.

Two impo rtant metho ds fo r T hre ads will be used during this pro cess: no t if yAll() and wait () Go to java.lang.T hre ad in the API. Scro ll do wn to the Me t ho d Sum m ary. Lo o k fo r the metho ds no t if yAll() and wait (). Remember to lo o k at all o f the metho ds fo r T hre ads--including the o nes inherited fro m Obje ct . In java4_Lesso n9 , create a new So up class as sho wn:

Type So up as sho wn in blue :

CODE TO TYPE: So up package prodcons; import java.util.*; public class Soup { private ArrayList buffer = new ArrayList(); s what is in the soup private int capacity = 6; limit to 6 alphabet pieces

// buffer hold

public synchronized String eat() { synchronized makes others BLOCKED while(buffer.isEmpty()){ cannot eat if nothing is there, so check to see if it is empty

// // //

try { wait(); // if so, we WAIT until someone puts something there } catch (InterruptedException e) {} // doing so temporarily allows other synchronized methods to run (specifically - a dd) } // we will not get out of this while until something is there to eat String toReturn = buffer.get((int)(Math.random() * buffer.size())); // get a random alphabet in the soup buffer.remove(toReturn); // remove it so no one else can eat it buffer.trimToSize(); // reduce the size of the buffer to fit how many pieces are there notifyAll(); // tell anyone WAITing that we have eaten something and are done return(toReturn); // actually return the alphabet piece to the consumer who asked to eat it } public synchronized void add(String c) { // synchronized makes others BLOCKED while (buffer.size() == capacity) { // cannot add more pieces if the buffer is full to capacity try { wait(); // if so, we WAIT - temporarily allows other synchronized methods to run - i.e., e at() } catch (InterruptedException e) {} } // we will not get out of this while until something has been eaten to make room buffer.add(c); // add another alphabet piece to the soup notifyAll(); // tell anyone WAITing that we have added something and are done } public ArrayList getContents(){ nt to be able to get the contents so we can show them in the GUI view return (buffer); multiple problems with this - we will address later } }

Save it.

T he GUI View

// we wa //

No w let's put it all to gether, using an Apple t to pro vide a nice vie w fo r o ur users. In java4_Lesso n9 , add a new MyT able Se t t ing class as sho wn:

Type MyT able Se t t ing as sho wn in blue :

CODE TO TYPE: MyTableSetting package prodcons; import java.applet.Applet; import java.util.*; import java.awt.*; public class MyTableSetting extends Applet { Soup s; bowl with the soup's alphabet pieces int bowlLength = 150; variables in case we want to change it int bowlWidth = 220; int bowlX = 60; int bowlY = 10; public void init(){ setSize(400,200); e big enough for our soup bowl s = new Soup(); p Producer p1 = new Producer(this, s); iate one producer thread - state of NEW Consumer c1 = new Consumer(this, s); iate one consumer thread - state of NEW

// we will show the soup // bowl's dimensions as

// make the applet siz // instantiate the Sou // declare and instant // declare and instant

p1.start();

// start the producer

c1.start();

// start the consumer

thread thread } public void paint(Graphics g){ // first we make the b owl and spoon int x; int y; g.setColor(Color.orange); g.fillOval(bowlX, bowlY, bowlWidth, bowlLength); // the bowl g.setColor(Color.cyan); g.fillOval(10, 25, 40, 55); // the spoon g.fillOval(25, 80, 8, 75); g.setColor(Color.black); // black outlines for the dinnerware g.drawOval(10, 25, 40, 55); g.drawOval(25, 80, 8, 75); g.drawOval(bowlX,bowlY, bowlWidth, bowlLength); ArrayList contents = s.getContents(); // get contents of the s oup for (String each: contents){ // individually add ea ch alphabet piece in the soup x = bowlX + bowlWidth/4 +(int)(Math.random()* (bowlWidth/2)); // pu t them at random places to mimic stirring y = bowlY + bowlLength/4 + (int)(Math.random()* (bowlLength/2)); Font bigFont = new Font("Helvetica", Font.BOLD, 20); g.setFont(bigFont); g.drawString(each, x, y); } } } Save and run it. Lo o k at the Co nso le and the Soup Bowl. The letters mo ve aro und because they are being "stirred" as they are eaten. Run it a few times (be sure to clo se the applets when yo u're finished so they do n't pile up). If yo u run the pro gram eno ugh times, eventually yo u'll have pro blems:

Play with the sle e p frequency in the Pro duce r and Co nsum e r to see ho w changing that number effects the way the Applet runs. And, fo r yo ur added enjo yment, here's ano ther simple illustratio n o f pro ducer/co nsumer/mo nito r co de at wo rk. Enjo y! And see yo u in the next lesso n... Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Threads: Synchronization Race Conditions In the last lesso n we made o ur first pro ducer/co nsumer design patterns pro gram. In this lesso n, we'll explo re synchro nizatio n further, expanding o n o ur earlier examples. First we'll edit o ur So up pro gram to facilitate a cleaner design. As yo u may recall, in that last lesso n we left yo u with a co nflict:

No t to wo rry. We pro grammers can always find ways to fix a pro blem! Let's go o ver the initial text in re d first: Exce pt io n in t hre ad " AWT -Eve nt Que ue -1" java.ut il.Co ncurre nt Mo dif icat io nExce pt io n Race co nditio ns usually invo lve o ne o r mo re pro cesses accessing a shared reso urce (such as a file o r variable), where multiple access is no t co ntro lled pro perly. Here's ho w a race co nditio n might lo o k in real life. Suppo se o n a given day, a husband and wife bo th decide to empty the same bank acco unt and, purely by chance, they empty the acco unt at the same time. If the two withdraw fro m the bank at the exact same time, causing the metho ds to be called at the exact same time, bo th ATMs co uld co nfirm that the acco unt has eno ugh cash and dispense it. The two threads access the acco unt database at the same time. The race co nditio n exists here because the actio ns o f checking the account and changing the account balance are no t atomic. An atomic ro utine is o ne that can't be interrupted during its executio n. In o ur banking example, if the actio ns o f checking the acco unt and changing the acco unt balance were ato mic, it wo uld be impo ssible fo r a seco nd thread to check o n the acco unt, until the first thread had finished changing the acco unt status. To avo id race co nditio ns, we synchro nize the e at () and add() metho ds. Synchro nizatio n prevents race co nditio ns by preventing a seco nd metho d fro m running befo re the first metho d is co mplete. No w let's go back to the erro r in o ur So up example. Our red text info rms us o f the lo catio n o f the erro r: at pro dco ns.MyT able Se t t ing.paint (MyT able Se t t ing.java:37 )

It lo o ks like we we did everything right--we go t the co ntents o f the buf f e r and put them into a co nt e nt s variable so we co uld go thro ugh the ArrayList to print them o ut. So what's the pro blem? Because we are accessing the variable that represents o ur shared reso urce in the ge t Co nt e nt s() metho d o f So up, the metho d sho uld be synchronized so it wo n't be initiated while it is being accessed by ano ther metho d. If we access and co py o ur metho d at the very mo ment that it is changing, it co uld cause the Co ncurre nt Mo dif icat io nExce pt io n. This is an example o f a race co nditio n. And even if we synchro nized the metho d, we wo uld still have pro blems. Since mo st co llectio ns in Java (fo r example, arrays and ArrayList s) are sent by reference, even tho ugh we use a metho d to get the co ntents and put them into ano ther variable, they still po int to the same place in memo ry. So when we get the co ntents back to the Applet and then print them o ut, we co ntinue to access the shared reso urce in a po tentially dangero us way. This is a pro blem with ArrayList s. In o rder to address these issues, we'll co py the buffer and then pass it back.

Fixing a Race Condition Let's make a few changes to make things a little cleaner. We'll co ntinue wo rking with the classes we created in java4_Lesso n9 . In the java4_Lesso n9 /src/pro dco ns fo lder, edit the Co nsum e r class as sho wn in blue : CODE TO TYPE: Co nsumer package prodcons; class Consumer extends Thread { private Soup soup; private MyTableSetting bowlView; public Consumer(MyTableSetting bowl, Soup s) { bowlView = bowl; soup = s; } public void run() { String c; for (int i = 0; i < 10; i++) { // stop thread when we know there are no m ore coming c = soup.eat(); System.out.println("Ate a letter: " + c); bowlView.recentlyAte(c); // tell what alphabet character to put in the spoon bowlView.repaint(); try { sleep((int)(Math.random() * 3000)); nger or sometimes we never see them! } catch (InterruptedException e) { } } } }

// have consumer sleep a little lo

Save it. A thread sto ps when its run() metho d has finished. So in o ur example, after 10 alphabet letters have been pro duced and then co nsumed, their respective threads will sto p and as such, they are co nsidered "dead." In java4_Lesso n9 /src/pro dco ns, edit the So up class as sho wn in blue :

CODE TO EDIT: So up package prodcons; import java.util.*; class Soup { private ArrayList buffer = new ArrayList();; private int capacity = 6; public synchronized String eat() { while(buffer.isEmpty()){ try { wait(); } catch (InterruptedException e) {} } String toReturn = buffer.get((int)(Math.random() * buffer.size())); buffer.remove(toReturn); buffer.trimToSize(); notifyAll(); return(toReturn); } public synchronized void add(String c) { while (buffer.size() == capacity) { try { wait(); } catch (InterruptedException e) {} } buffer.add(c); notifyAll(); } public synchronized Object [] getContents(){ ficationException. Object [] temp = buffer.toArray(); see this toArray() method return (temp); ot change when getting and/or displaying it } }

// see ArrayList about ConcurrentModi // check out the API for ArrayList to // Make a clean copy so contents do n

Save it. In java4_Lesso n9 /src/pro dco ns, edit the MyT able Se t t ing class as sho wn in blue and re d:

CODE TO EDIT: MyTableSetting package prodcons; import java.applet.Applet; //import java.util.*; import java.awt.*;

// don't need anymore because we have array copy

public class MyTableSetting extends Applet { Soup s; Producer p1; // we need as Instance Variables so we can access outsi de of the init() Consumer c1; int bowlLength = 150; int bowlWidth = 220; int bowlX = 60; int bowlY = 10; String justAte; public void init(){ setSize(400,200); s = new Soup(); p1 = new Producer(this, s); here again or it is only local System.out.println("Producer is in state " + p1.getState()); of the thread at this point c1 = new Consumer(this, s); p1.start(); c1.start(); System.out.println("Consumer is in state " + c1.getState()); e of the thread at this point }

// don't declare // show the state

// show the stat

public void paint(Graphics g){ int x; int y; g.setColor(Color.yellow); g.fillOval(bowlX,bowlY, bowlWidth, bowlLength); g.setColor(Color.cyan); g.fillOval(10,25, 40, 55); g.fillOval(25,80, 8, 75); g.setColor(Color.black); g.drawOval(10,25, 40, 55); g.drawOval(25,80, 8, 75); g.drawOval(bowlX,bowlY, bowlWidth, bowlLength); Font standardFont = getFont();

// tell what just ate in

spoon Font bigFont = new Font("Helvetica", Font.BOLD, 20); g.setFont(bigFont); if (justAte != null) { g.drawString(justAte, 25, 55); justAte = null; } else { g.setFont(standardFont); g.drawString("waiting", 13, 55); g.setFont(bigFont); } Object [] contents = s.getContents(); // bring back a fresh array of Object for(Object each : contents){ // no longer tied in memory to buffer in Soup x = bowlX + bowlWidth / 4 + (int)(Math.random() * (bowlWidth / 2)); y = bowlY + bowlLength / 4 + (int)(Math.random() * (bowlLength / 2)); g.drawString((String)each, x, y); // show the al phabet piece being eaten }

System.out.println("Producer is in state " + p1.getState()); Producer (remember that we put it to sleep) System.out.println("Consumer is in state " + c1.getState()); if(c1.getState()==Thread.State.TIMED_WAITING){ o enumerated types for Thread States checkState(); aint() in so see TERMINATED } }

// show state of // note access t // get last rep

public void recentlyAte(String morsel){ justAte = morsel; } public void checkState(){ try{Thread.sleep(1000); } catch(InterruptedException e) { } t has a Thread. This command puts this (Applet's) Thread to sleep repaint(); } }

// Even the Apple

Save and run it. Lo o k o ver the o utput in bo th the Co nso le and the Applet's bo wl and fo llo w the pro gressio n o f the states. Co mment o ut the print lns and run it again witho ut tho se distractio ns. Yo u'll see that so me states o f the T hre ad are accessible thro ugh their inner enumerated class T hre ad.St at e .

Another Race Condition The Apple t itself is the main thread and can pro duce race co nditio ns. When yo u ran yo ur co de, the co de so metimes indicated that the first alphabet piece was eaten before the piece was even added to the So up:

This sho uldn't happen tho ugh, because o ur pro gram wo uld thro w a NullPo int e rExce pt io n if it had tried to eat so mething fro m the buffer that wasn't there yet. So the letter must have been in the buffer in o rder to then have been taken o ut. Hmm. Maybe the Syst e m .o ut .print lns o f MyT able Se t t ing were running first and gave us a race co nditio n. The main thread (the applicatio n o r the applet) always takes prio rity. Fo rtunately, the class T hre ad co ntains metho ds to handle such situatio ns, including jo in(), wait (), sle e p(), ge t Prio rit y(), and se t Prio rit y(), to name just a few.

Additional Resources There's lo ts o f material available to help yo u to wo rk thro ugh co ncurrency and threads. Here's are just a few o f them: Oracle's tuto rial lesso n o n Threads and Co ncurrency Chapter 17: Threads and Lo cks (in Java Language Specification) Of co urse, the API at java.lang.Thread Bo o ks specifically o n threads, such as this o ne published by O'Reilly Media: Understanding and Mastering Co ncurrent Pro gramming: Java Threads, by Sco tt Oaks and Henry Wo ng.

Using T hreads in a Game No w let's have so me fun. We'll put threads and co ncurrency to gether into a game that tests yo ur typing skills. A gro up

o f letters will be presented. The o bject o f the game is to type them fast eno ugh to keep a virtual bo mb fro m explo ding. Yo u may have five bo mbs (attempts to type) visible at any given time, but in this versio n, after three bo mbs explo de, the game is o ver. A running example is pro vided here fo r yo u. Play aro und with it befo re we write the co de, then as yo u write the co de, the classes and their metho ds will make sense.

Creating the T yping Game Preview: T he Classes Our game pro gram will include the fo llo wing classes: Bo m b: extends T hre ad. Pro duce r: extends T hre ad and pro duces the letters fo r players to type. Co nsum e r (no t a T hre ad since in this versio n we have o nly o ne typist o n a single keybo ard): co nsumes the letters presented. Wo rld, which is the mo nito r. T ype o rDie : extends Apple t and pro vides the user interface.

T he Bomb Each bo mb has a life o f its o wn and is its o wn thread. Each bo mb is asso ciated with the set o f letters o r word presented underneath it and each has its o wn fuse (the red line under the wo rd) that displays the amo unt o f time remaining.

As yo u type the co de, co mments in the co de pro vide info rmatio n abo ut the reaso n fo r each metho d. Also , the fo rmal parameter specifies (char c) fo r metho ds that have a char passed--this do es no t mean the value is c. It just means that c is the variable name fo r the character passed. Our java4_Lesso n10 pro ject co mes with an images fo lder fo r this example. Click here, and then o pen it in the

Package Explo rer. It sho uld lo o k like this:

In java4_Lesso n10 , create a new Bo m b class as sho wn:

Type Bo m b as sho wn in blue :

CODE TO TYPE: Bo mb package bomb; import java.awt.*; import java.applet.Applet; public class Bomb extends Thread { String word; int x, y, ticker; int width = 62; int height = 65; Applet apl; boolean being_disarmed = false; boolean disarmed = false; boolean exploded = false; int amount_disarmed = 0; Image bomb; public Bomb(String word, int x, int y, Applet apl){ super(word); this.word = word; this.x = x; this.y = y; this.ticker = word.length()*6; // time to type is relative to length of word this.apl = apl; bomb = apl.getImage(this.apl.getDocumentBase(), "../images/bomb.png"); } public void run(){ while (ticker > 0) hile loop to show kaboom!!!!! { try { sleep(600); } catch (InterruptedException e) {} ticker--; if (disarmed) ed here { break; hile - this bomb is done } //System.out.println(word+":"+ticker); apl.repaint(); } exploded = true; method of this class } hics passed from Applet public int getX(){ return x; omponent of the bomb's location is returned } public int getY(){ return y; ponent of the bomb's location is returned } public int getWidth(){ return width; bomb is returned }

// have sleep in w

//check if disarm // jump out of w

// KABOOM!!! in draw // will draw in Grap

// The horizontal c

// the vertical com

// The width of the

public int getHeight(){ return height; e bomb is returned }

// The height of th

public void draw(Graphics g){ // The bomb will be drawn to the Graphics object passed in if (!exploded) { g.drawImage(bomb, x, y, Color.WHITE, apl); // not exploded so show bomb g.setFont(new Font("Monospaced", Font.PLAIN, 12)); g.setColor(Color.RED); g.drawChars(word.toCharArray(), 0, amount_disarmed, x, y+60); // le tters user typed turn red g.setColor(Color.BLACK); if (amount_disarmed != word.length()) // letters not ty ped stay black { g.drawChars(word.toCharArray(), amount_disarmed, word.length()-a mount_disarmed, x+(amount_disarmed*7), y+60); } if (being_disarmed) { //System.out.println(word+" is being_disarmed"); // commented o ut System.outs that helped debug code g.setColor(Color.BLUE); // word being "worked on" is circled in blue g.drawRoundRect(x-2, y+49, word.length()*9, 14, 10, 10); } // draw fuse g.setColor(Color.RED); double bar = (double)ticker/(word.length()*5); g.fillRect(x, y+64, (int)(word.length()*7*bar), 5); // red bar unde rneath shows progress } else { g.setColor(Color.RED); // else - bomb explodes g.setFont(new Font("Courier Bold", Font.PLAIN, 12)); g.drawString("KABOOM!!!", x, y+30); } } public boolean startsWith(char c) { t word start with the value typed (passed in here) if (word.charAt(0) == c) return true; return false; } public boolean exploded(){ return exploded; loded variable will be returned }

// does the curren

// The Bomb's exp

public boolean hasPoint(int x, int y){ // If the Bomb occ upies the location passed in, a boolean will be returned true if ( (this.x 0 && fNameField.getDocument().getLength() > 0 && areaCodeField.getDocument().getLength() == 3 && prefixField.getDocument().getLength() == 3 && suffixField.getDocument().getLength() == 4) { addButton.setEnabled(true); if(dEvent.getDocument() == suffixField.getDocument()) { addButton.requestFocus(); getRootPane().setDefaultButton(addButton); } } } public void removeUpdate(DocumentEvent dEvent) { // If last name or first name don't have data or phone number // is not complete, disable the Add button. if(lNameField.getDocument().getLength() == 0 || fNameField.getDocument().getLength() == 0 || areaCodeField.getDocument().getLength() < 3 || prefixField.getDocument().getLength() < 3 || suffixField.getDocument().getLength() < 4 ) addButton.setEnabled(false); } /** Empty implementation. Method necessary for implementation of DocumentListen er */ public void changedUpdate(DocumentEvent dEvent) {} } // End InputListener inner class } // End AddListingDialog class

Save it. We sho uld be all set no w! Run it fro m SimplePho neBo o k. In the Pho ne field, type 314 825 witho ut mo ving the mo use o r using the Tab key--see ho w the "fo cus" mo ves to the o ther Pho ne field area? That's really co nvenient fo r the user.

No w, click Ge t . The entry that has tho se first 6 digits in the pho ne number is retrieved.

Clear the Pho ne number fields, and in the First name field, type Edgar and press Ent e r.

So there ARE names in o ur table o ther than Jo hn! No w let's add so me entries o f o ur o wn. Click the AddButto n (+). Type a name in the Last Nam e field. The Add butto n is no t an o ptio n--can yo u see where this was set in yo ur co de?

Add the remaining info rmatio n into the Add Listing Dialo g Bo x. No te that when all o f the fields have values, Add is enabled. Click Add no w.

Note

One SQL statement and/o r query do es no t implicitly invo ke o thers. It is up to the pro grammer to tell the applicatio n what to do .

To see yo ur new entry, query fo r it, using the appro priate text field(s). Play with the GUI. Yo u can cut and paste fro m o ne text field to ano ther and there's co o l ways to o f tabs, etc. And, with each capability yo u no tice, lo o k at the co de and see ho w it was do ne--o r, if it was inherited, ho w someone (Java? Swing?) did all the wo rk fo r yo u!

Additional Resources We've created a go o d applicatio n and learned so me o f the basics o f JDBC. But there's still a lo t mo re o ut there. Here are a few mo re useful reso urces: Oracle's JDBC page has links to : We've illustrated many facets o f JDBC, but lo ts o f additio nal techniques are available. Amo ng o ther tasks, yo u may want to : Oracle's JDBC Techno lo gy Guide links to Getting Started with the JDBC API. JDBC Intro ductio n. Three nice tuto rials: Basic Tuto rial: includes JDBC Basics. Advanced Tuto rial: sho ws deleting and adding ro ws and much mo re. Ro wSet Tuto rial: useful fo r enterprise applicatio ns. But wait--there's mo re: Online Tuto rial, fro m the Java Develo per Co nnectio n. Ano ther Online Tuto rial—this o ne illustrates co nnecting to a database o n a Windo ws machine using the JDBC/ODBC bridge. And o f co urse, there are bo o ks: Database Pro gramming with JDBC & Java The next lesso n has additio nal reso urce links fo r SQL and do cumentatio n capabilities, because we want you to write great Java co de!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

Documentation: Javadoc, Doc Comments, and Annotation Documenting Your Work After all o f yo ur hard pro grammming wo rk, yo ur lo ng ho urs o f planning and to il, mo st peo ple will generally see just the applicatio n o r running applet. Ho wever, when so meo ne co nsiders purchasing yo ur appplicatio ns, o r o ffering yo u a jo b writing co de, they'll pro bably want yo u to show your work. In these cases especially, yo u want yo ur co de and do cumentatio n to be clean and readable. Pro grammers estimate that abo ut 70 % o f all pro gramming effo rt go es to ward maintenance. Given this percentage, alo ng with the likeliho o d that so meday your co de wil be maintained by o thers, yo u want to be sure to do cument yo ur co de adequately. Fo rtunately, Java makes it easy.

Javadoc and API Pages Oracle pro vides go o d-lo o king API pages fo r Java. We can create similar pages fo r o ur o wn co de. Java pro vides a to o l called Javadoc, which allo ws us to create API pages that have the pro fessio nal and clean lo o k. But befo re we create tho se pages, let's add do cumentatio n to the co de that we created in earlier lesso ns. The edits we make first will no t affect the running o f o ur applicatio n because they'll co nsist o f co mments, no t new co de. The co mments will co ntain the fo rmat and do cumentatio n tags that will enable us to make beautiful--and accessible-do cumentatio n.

Note

So me o f o ur co de already co ntains Javado c co mments we added while creating it.

DatabaseManager In the java4_Lesso n13 pro ject, greenDB package, edit Dat abase Manage r as sho wn in blue :

CODE TO EDIT: DatabaseManager package greenDB; import java.sql.*; public class DatabaseManager { /** A connection to a database */ private Connection conn; /** An executable SQL statement */ private Statement stmt; /** The result of an executed SQL statement */ private ResultSet rset; /* DatabaseManager Constructor */ /** * This constructor connects to the MySQL database at jdbc:mysql://sql.usera ctive.com:3306. * It creates instances of Statement and ResultSet that will be used by the other methods * in the class. It also checks to see if the Listings table already exists. If it does, it * simply returns to the caller. Otherwise, it instantiates the method to cr eate a table * called Listings, and then populates the table. * * PRE: MySQL server is available and account for user has been established . * The MySQL driver is installed on the client workstation and its loc ation * has been defined in CLASSPATH (or for Eclipse, in its Referenced Li braries). * Username and password are not null. * POST: A connection is made and the Listings table is either created and i nitialized on * jdbc:mysql://sql.useractive.com:3306, or the already existing Listi ngs table is * identified. * */ public DatabaseManager (String username, String password ) { // the constru ctor for the database manager // Connect to database and execute the SQL commands for creating and ini tializing the Listings table. try { Class.forName ("com.mysql.jdbc.Driver"); // Load the MySQL JDBC dri ver } catch (ClassNotFoundException e) { System.out.println("Failed to load JDBC/ODBC driver."); e.printStackTrace(); return; } try {

// Connect to the database-// give the whole URL as a parameter rather than using a va

riable conn = DriverManager.getConnection ("jdbc:mysql://sql.useractive.com :3306/" + username, username, password); stmt = conn.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE, Resu ltSet.CONCUR_UPDATABLE); // Create a Statement DatabaseMetaData aboutDB = conn.getMetaData (); // Execute the crea tion and initialization of table query String [] tableType = {"TABLE"};

ResultSet rs = aboutDB.getTables(null, null, "Listings",

tableType)

; if (!inspectForTable (rs, "Listings")) { f the table is already there

// First find out i // there is no tabl

e, make it from the initialization listing String [] SQL = initListingsTable(); thod is below for (int i=0; i < SQL.length; i++) { stmt.execute(SQL[i]); } } }catch (SQLException e) { e.printStackTrace(); } }

// code for this me

/* initListingsTable */ /** * Creates the Listings table and initializes it with some records. This met hod connects * to the MySQL database at jdbc:mysql://sql.useractive.com:3306. It then cr eates a table * called Listings and initializes the table with some arbitrary records. * * PRE: True * POST: SQL String is created for the initial population of a table named L istings. * */ private String [] initListingsTable() { // Executable SQL commands for creating Listings table and inserting ini tial names and phone numbers. String[] SQL = { "create table Listings ("+ "LAST_NAME varchar (16)," + "FIRST_NAME varchar (16)," + "AREA_CODE varchar(3)," + "PREFIX varchar(3)," + "SUFFIX varchar(4))", "insert into Listings values ('ANDERSON', 'JOHN', '314', '825', '1695')", "insert into Listings values ('CABLES', 'WALLY', '212', '434', '9685')", "insert into Listings values ('FRY', 'EDGAR', '415', '542', '5885')", "insert into Listings values ('MARTIN', 'EDGAR', '665', '662', '9001')", "insert into Listings values ('TUCKER', 'JOHN', '707', '696', '8541')", }; return SQL; } /* inspectForTable */ /** * Determines if a table exists in the db. * * PRE: Connection to database has been established. rs is not null. * POST: Table has not been changed, but its presence is verified (or not). * * @param rs ResultSet from DatabaseMetaData query about existing Tables * @param tableName String identifying the table in question */ private boolean inspectForTable (ResultSet rs, String tableName) throws SQL Exception { // exception will be caught when method is used int i;

ResultSetMetaData rsmd = rs.getMetaData (); // Get the ResultSetMetaData. This will be used for the col umn headings int numCols = rsmd.getColumnCount (); // Get the number of columns in the result set boolean more = rs.next (); while (more) { // Get each row, fetching until end of the result set for (i=1; i 0 && fNameField.getDocument().getLength() > 0 && areaCodeField.getDocument().getLength() == 3 && prefixField.getDocument().getLength() == 3 && suffixField.getDocument().getLength() == 4) { addButton.setEnabled(true); if(dEvent.getDocument() == suffixField.getDocument()) { addButton.requestFocus(); getRootPane().setDefaultButton(addButton); } } } /* removeUpdate */ /** * This method is called when data is removed from the text field, either by backspacing or highlighting and * deleting. This method will track the number of characters removed from th e field. If any of the fields * last name, first name, area code, prefix, and extension contain less than the required number of characters * the "Add" button of the Add Listing Dialog box is disabled. * * PRE: * POST: Add Listing Dialog Box "Add" button is disabled if any of the field s contain less than the required * number of characters. * * @param dEvent the document event */ public void removeUpdate(DocumentEvent dEvent) { // If last name or first name don't have data or phone number // is not complete, disable the Add button. if(lNameField.getDocument().getLength() == 0 || fNameField.getDocument().getLength() == 0 || areaCodeField.getDocument().getLength() < 3 || prefixField.getDocument().getLength() < 3 || suffixField.getDocument().getLength() < 4 ) addButton.setEnabled(false); } /** Not implemented */ public void changedUpdate(DocumentEvent dEvent) {} }// End InputListener inner class }// End AddListingDialog class

Save it.

Note

Ano ther Inner class--remember to lo o k fo r this o ne o n the Javado c page to o .

PhoneDocumentListener In java4_Lesso n13, edit Pho ne Do cum e nt List e ne r as sho wn in blue belo w:

CODE TO EDIT: Pho neDo cumentListener package greenDB; import javax.swing.JTextField; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; /* class PhoneDocumentListener */ /** * A listener that is applied to any of the telephone number fields (area code, prefix, * and extension). The purpose of this listener is to prevent more than the expe cted number * of characters from being entered in the telephone number fields. That is, the area code and * prefix fields might only be allowed to contain 3 characters each while the ex tension field * might only be allowed to contain four characters. The PhoneDocumentListener c lass accomplishes * this by accepting a 'numbers allowed' parameter during construction. Every ti me a character is * entered in the phone number field to which this listener applies, the insertU pdate method is * called. The insertUpdate method checks the number of characters in the field and if the number * is equal to 'numbers allowed', focus is transferred to the next component. * * @author David Green * @version 1.0 */ class PhoneDocumentListener implements DocumentListener { /** The phone number text field to which this listener applies */ private JTextField txtField; /** The number of characters that will cause focus to be transferred */ private int numsAllowed; /* PhoneDocumentListener */ /** * The PhoneDocumentListener constructor. * @param tf The phone number text field to which this listener applies. * @param numsAllowed The number of characters that can be entered in this f ield. */ public PhoneDocumentListener(JTextField tf, int numsAllowed) { txtField = tf; this.numsAllowed = numsAllowed; } /* insertUpdate */ /** * Called when a character is typed in the field to which this listener is a pplied. * The field is examined for number of characters and if the number is equal to the * numbers allowed, as specified during construction, focus is transferred t o the next * component. * * PRE: * POST: Focus is transferred if field length equals numsAllowed; else nothi ng happens. * * @param dEvent An event generated as a result of a character being entered in the * telephone number field to which this listener is applied. */

public void insertUpdate(DocumentEvent dEvent) { if(dEvent.getDocument().getLength() == numsAllowed) txtField.transferFocus(); } /** Empty implementation. Method necessary for implementation of DocumentLis tener */ public void removeUpdate(DocumentEvent dEvent) {} /** Empty implementation. Method necessary for implementation of DocumentLis tener */ public void changedUpdate(DocumentEvent dEvent) {} }// End PhoneDocumentListener class

Save it.

PhoneFocusListener In java4_Lesso n13, edit Pho ne Fo cusList e ne r as sho wn in blue belo w:

CODE TO EDIT: Pho neFo cusListener package greenDB; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import javax.swing.JTextField; /* class PhoneFocusListener */ /** * A listener that will delete any data currently in the telephone number fields (area code, prefix, * and extension) whenever focus is detected for those fields. This listener is used in conjunction with * the PhoneDocumentListener to prevent more than the expected number of charact ers from being entered * in the telephone number fields. That is, the area code and prefix fields will only be allowed to * contain three characters each while the extension field will only be allowed to contain four characters. * * @author David Green * @version 1.0 */ class PhoneFocusListener implements FocusListener { /* focusGained */ /** * Called when the field to which this listener applies gains focus. This me thod will delete * any data currently contained in the field. * * PRE: True. * POST: Any data in the telephone number field to which this listener appli es is deleted. * * @param fEvent An event generated as a result of focus being gained on thi s telephone number field. */ public void focusGained(FocusEvent fEvent) { JTextField tf = (JTextField)fEvent.getSource(); tf.setText(""); } /** Not implemented */ public void focusLost(FocusEvent fEvent){} }// End PhoneFocusListener class

Save it.

T he Package Documentation All classes sho uld be fine and erro r-free. Remember that the class to start it all was Sim ple Pho ne Bo o k. Select Sim ple Pho ne Bo o k and run it to verify that all is well. Everything sho uld wo rk the same as befo re; all that we changed was do cumentatio n. Select the java4 _Le sso n13 Pro ject so that it is highlighted. In the to p Eclipse Menu Bar, select Pro je ct | Ge ne rat e J avado c.... In the Ge ne rat e J avado c windo w that o pens, click the Co nf igure ... butto n to get to the directo ry C:\java\bin. Select javado c.e xe to get the javado c executable co de. Keep the default o f Privat e as it is. This will let us see all members (private, package, pro tected, and public). Keep the Destinatio n as it is so the do cumentatio n will be lo cated with the Pro ject. Click Finish. It's the same pro cess we perfo rmed earlier--we are simply no w making the API html pages fo r all o f the classes. This

time we want to see a listing with links to all o f the classes, so o pen do c | inde x.ht m l.

On your generated do cs index page, click o n the links as yo u wo uld any html page to see yo ur class do cumentatio n. In fact, lo o k at these pages until yo u see all that is o ffered, and ho w it all relates to the co mments we wro te. Fo r example, check o ut Pho ne Bo o kFram e .ht m l and AddList ingDialo g.ht m l and lo o k fo r a Ne st e d Class Sum m ary to see their inner classes. Go o d do cumentatio n makes yo ur wo rk mo re refined and finished.

Additional Resources We go ne o ver many JDBC elements, but lo ts o f additio nal techniques are available. Fo r example, yo u might want to : Co nnect to a database in Windo ws and use the JDBC/ODBC bridge. Allo w yo ur co de to co nnect to multiple databases, and use the java.sql.Drive rManage r class to assist in finding the co rrect driver fo r the current database Allo w multiple co nnectio ns to the database by using a javax.sql.Po o le dCo nne ct io n when handling multiple threads o f co nnectivity. Here are additio nal links to assist yo u with yo ur database applicatio ns: SQL Interactive Online SQL Training A SQL Tuto rial W3Scho o ls' SQL Tuto rial Or just go to Go o gle and do a search o n SQL t ut o rial. Our last search go t 149 ,0 0 0 hits--there are LOTS o f tuto rials o n SQL o ut there! And, o f co urse, fo r tho se who like so mething in their hands, there are lo t s o f bo o ks o n SQL! J avado c Oracle's page o n Javado c Techno lo gy, with the Javado c To o l Reference Page Oracle Develo per Netwo rk Javado c to o l page Oracle Develo per Netwo rk Ho w to Write Do c Co mments fo r the Javado c To o l page, with a list o f tags Anno t at io ns Anno tatio ns lo o k similar to Javado c tags (use o f @) but they are actually a fo rm o f metadata that can be added to Java so urce co de. Anno tatio ns co mplement Javado c tags. In general, if the markup is intended to affect o r pro duce do cumentatio n, it sho uld pro bably be a Javado c tag; o therwise, it sho uld be an anno tatio n. The use o f the "@" symbo l in bo th Javado c co mments and in anno tatio ns is no t co incidental-—they are related co nceptually. But no te that the Javado c deprecated tag starts with a lo wercase d and the anno tatio n starts with an uppercase D. We will see them mo re when we lo o k at enterprise applicatio ns. Fo r no w, here are a few reso urces fo r yo u:

Oracle's Anno tatio n Tuto rial Anno tatio ns (fro m The Java Programming Language (fro m Enhancements in JDK 5) If yo u are re ally into it, lo o k up the Anno tatio n Pro cessing To o l.

What's Next? In the next co urse we will co ntinue o ur wo rk with applicatio ns, but delve into distributed co mputing as well. Everything yo u learned in this co urse--GUIs, Exceptio ns, Threads, Database Co nnectivity, Do cumentatio n--yo u'll use again. Yo u're cultivating so me great skills!

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.