0% found this document useful (0 votes)
106 views284 pages

Java Programming 4 Java Application Building v1 PDF

Uploaded by

Victor Gutierrez
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
106 views284 pages

Java Programming 4 Java Application Building v1 PDF

Uploaded by

Victor Gutierrez
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 284

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) + " " + sales[i]);
sum = sum + sales[i];
}
System.out.println("\nTotal sales: " + sum);
}

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.

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,
Note 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.

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
Note 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 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;

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 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;

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 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);
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 java.awt.*;
import java.awt.Dimension;
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 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 (<br>) 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 =
"<html>Sales Figures<br>__________________________<br>";
for (int x = 0; x < sales.length; x++)
{
txtOutput += "Sales Person " + (x + 1) + ": $" + sales[x] + "<br>";
}

txtOutput += "<br>The lowest sales belongs to sales person " +


(app.getMin() + 1) + " with $" + sales[app.getMin()] + "<br>";
txtOutput += "The highest sales belongs to sales person " +
(app.getMax() + 1) + " with $" + sales[app.getMax()] + "<br>";
txtOutput += "<br>The total sales were: $ " +
app.getTotalSales() + "<br>";
txtOutput += "The average sales was: $ " + df1.format(app.getAverage()) +
"<br><br>";
txtOutput += createSalesBarInfo();
txtOutput += "</html>";

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]+ "<br>" ;
}
else if (performance[x] ==0)
{
salesBarOutput += "Sales person " + (x + 1) +
" exactly reached the sales goal with sales of "+ sales[x]+ "<br>" ;
}
}
if (overSalesBar ==1)
salesBarOutput += "Only " + overSalesBar +
" sales person sold more than the sales goal of " + app.getBar() + "<br
><br>";
else
salesBarOutput += overSalesBar +
" sales people sold more than the sales goal of " + app.getBar() + "<br
><br>";
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 Run it. No w
give it these values:

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


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()); // so don't hav
e to be sure they hit enter
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 .

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()); // so don't have to be
sure they hit enter
app.setSalesBar(goal);
}
}

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(); // a panel to set up how


many salespeople
add("North", specifyNumber);
pack(); // put it all together
setVisible(true); // make it show up
}

private class InitPanel extends JPanel {


public InitPanel() {
peopleLabel = new JLabel("Enter the number of sales people");
add(peopleLabel);
peopleField = new JTextField(5);
add(peopleField);
jbNumPeople = new JButton("Submit");
add(jbNumPeople);
jbNumPeople.addActionListener(new NumSalesPeopleListener());
}
}

private class NumSalesPeopleListener implements ActionListener {


public void actionPerformed(ActionEvent event) {
if (inputPanel != null)
{
remove(inputPanel);
app = new SalesApp();
}
numPeople = Integer.parseInt(peopleField.getText());
inputPanel = new InputPanel(app, numPeople, 2);
add("Center", inputPanel);
SalesUserInterface.this.validate();
}
}
}

Save it and Run Main.java. Enter the values, but do 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.
This is a difficult erro r to find, so make sure yo u never give a co nstructo r declaratio n a return
Note 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] = "John";
salesPeople[1] = "Paul";
salesPeople[2] = "George";
salesPeople[3] = "Ringo";

for (int i=0; i <= salesPeople.length; i++)


System.out.println("Element at index " + i + " : " + salesPeople[i])
;
System.out.println("Size of the salesPeople array is " + salesPeople.len
gth);
}
}
Save and run it.

The attribute le ngt h o f an array is equal to the number o f elements in the array, but the indices start at 0, so
the last index is le ngt h - 1. If we try to lo o p to the value o f sale s[le ngt h], we will get a
java.lang.ArrayInde xOut Of Bo undsExce pt io n.

Array Out of Bounds Example 2


Lo o k at Main.java in o ur java4_Lesso n1 pro ject under the sale s1 package:

OBSERVE: Main

package sales1;

public class Main {

public static void main(String[] args){


if (args.length > 0)
{
int argIn = Integer.parseInt(args[0]); // user inputs 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]); // if no args[0],
will throw ArrayIndexOutOfBoundsException
SalesReport mySalesInfo = new SalesReport(argIn);
mySalesInfo.testMe();
}
catch (ArrayIndexOutOfBoundsException exception)
{
SalesReport mySalesInfo = new SalesReport();
mySalesInfo.testMe();
}
}
}

Save and run it.

Because try/catch clauses are mo re labo r intensive (fo r Java and fo r yo u) than co nditio nal
T ip 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"); // create a Reader for a f
ile--we will define this file next
BufferedReader in = new BufferedReader(myFile); // wrap the FileReader in
a class that lets us manipulate it
aLine = in.readLine(); // read in a line of the f
ile
System.out.println(aLine); // 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.

An exceptio n handler is co nsidered appro priate if the type o f the exceptio n o bject thro wn matches the
Note 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"); // create a Rea
der for a file - we will define this file next
System.out.println("I did get here");
in = new BufferedReader(myFile); // wrap the Fil
eReader 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!");
}
try
{
aLine = in.readLine(); // read a line
of the 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();
}
}

That didn't help much:


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 java.io.FileReader; // could also import java.io.*; to get all of


these
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileFetcher {


String aLine="";
FileReader myFile; // declare FileReader and BufferedReader as instance
variables
BufferedReader in;

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 java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;
import 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 java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;
import 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 java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;
import 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 java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;
import 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.


Starting T hre ads is different fro m starting o ther classes and metho ds. T hre ads must have a run()
Note 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; // Define our simple threads.
They will pause for a short time
// and then print out their nam
es
class TestThread extends Thread {
private String whoAmI;
private int delay;

public TestThread(String s, int d) { // Our constructor to receive the name


(whoAmI) and time to sleep (delay)
whoAmI = s;
delay = d;
}
// 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"); // create a B
utton
add(runUs); // add it to
the Applet
runUs.addActionListener(this); // add a List
ener to the Button

t1 = new TestThread("Thread1",(int)(Math.random()*1000)); // instantiat


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(); // clicking t
he Button will allow us to start the threads
t2.start();
t3.start();
}
}

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.

Multiple threads running at the same time and sharing reso urces have the po tential to cause pro blems.
Note 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() { // stop Applet


appletThread = null; // stop Thread by destroying--put in Dead
State
}
}
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)); // sleep for a while so it
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:
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 consumer is given
the GUI that will show what is happening
soup = s; // the consumer is given
the soup--the monitor
}

public void run() {


String c;
for (int i = 0; i < 10; i++) { // stop thread when know the
re are no more coming; here we know there will only be 10
c = soup.eat(); // eat it from the soup
System.out.println("Ate a letter: " + c); // show what happened in
Console
bowlView.repaint(); // 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 <String> buffer = new ArrayList<String>(); // buffer hold
s what is in the soup
private int capacity = 6; //
limit to 6 alphabet pieces

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 <String> getContents(){ // we wa


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
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; // we will show the soup
bowl with the soup's alphabet pieces
int bowlLength = 150; // bowl's dimensions as
variables in case we want to change it
int bowlWidth = 220;
int bowlX = 60;
int bowlY = 10;

public void init(){


setSize(400,200); // make the applet siz
e big enough for our soup bowl
s = new Soup(); // instantiate the Sou
p
Producer p1 = new Producer(this, s); // declare and instant
iate one producer thread - state of NEW
Consumer c1 = new Consumer(this, s); // declare and instant
iate one consumer thread - state of NEW

p1.start(); // start the producer


thread
c1.start(); // start the consumer
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 <String> 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)); // have consumer sleep a little lo
nger or sometimes we never see them!
} catch (InterruptedException e) { }
}
}
}

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 <String> buffer = new ArrayList<String>();;
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(){ // see ArrayList about ConcurrentModi


ficationException.
Object [] temp = buffer.toArray(); // check out the API for ArrayList to
see this toArray() method
return (temp); // Make a clean copy so contents do n
ot change when getting and/or displaying it
}
}

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.*; // don't need anymore because we have array copy
import java.awt.*;

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); // don't declare
here again or it is only local
System.out.println("Producer is in state " + p1.getState()); // show the state
of the thread at this point
c1 = new Consumer(this, s);

p1.start();
c1.start();
System.out.println("Consumer is in state " + c1.getState()); // show the stat
e of the thread at this point
}

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()); // show state of
Producer (remember that we put it to sleep)
System.out.println("Consumer is in state " + c1.getState());
if(c1.getState()==Thread.State.TIMED_WAITING){ // note access t
o enumerated types for Thread States
checkState(); // get last rep
aint() in so see TERMINATED
}
}

public void recentlyAte(String morsel){


justAte = morsel;
}

public void checkState(){


try{Thread.sleep(1000);
} catch(InterruptedException e) { } // Even the Apple
t has a Thread. This command puts this (Applet's) Thread to sleep
repaint();
}
}

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) // have sleep in w
hile loop to show kaboom!!!!!
{
try {
sleep(600);
}
catch (InterruptedException e) {}
ticker--;
if (disarmed) //check if disarm
ed here
{
break; // jump out of w
hile - this bomb is done
}
//System.out.println(word+":"+ticker);
apl.repaint();
}
exploded = true; // KABOOM!!! in draw
method of this class -
} // will draw in Grap
hics passed from Applet

public int getX(){


return x; // The horizontal c
omponent of the bomb's location is returned
}

public int getY(){


return y; // the vertical com
ponent of the bomb's location is returned
}

public int getWidth(){


return width; // The width of the
bomb is returned
}
public int getHeight(){
return height; // The height of th
e bomb is returned
}

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) { // does the curren


t word start with the value typed (passed in here)
if (word.charAt(0) == c)
return true;
return false;
}

public boolean exploded(){


return exploded; // The Bomb's exp
loded variable will be returned
}

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 <= x && x <= (this.x + this.width)) && (this.y <= y && y <=
(this.y + this.height)) )
return true;
else
return false;
}

public void setdisarming(){


being_disarmed = true; // The bomb will
be set to being disarmed
}

public void setarming(){


being_disarmed = false; // The bomb will
be set to not being disarmed
}

public boolean attemptDisarm(char c){


assert amount_disarmed < word.length(); // assert - another
debugging tool
if (word.charAt(amount_disarmed) == c) // If the Bomb ha
s been totally disarmed i.e. the char passed in was the last char needed to diff
use the bomb,
{ // then true is r
eturned, otherwise false
//System.out.println(c+" is a hit on "+word);
amount_disarmed++;
//check if bomb is
totally disarmed
if (amount_disarmed == word.length())
{
//System.out.println(word+" is defused");
disarmed = true;
return true;
}
return false;
}
//System.out.println(c+" is a miss on "+word);
return false;
}
}

Save it.

No w o ur co de co ntains the asse rt statement. The asse rt statement was added to Java versio n 1.4 as a
debugging to o l. We'll address debugging practices in depth later, but fo r no w, check o ut Oracle's
do cumentatio n pages abo ut Pro gramming with Assertio ns.

Producing Words
The Pro duce r in o ur Bo mb game pro gram is also a T hre ad that pro duces the words to type.

In the java4_Lesso n10 pro ject, create a new Pro duce r class as sho wn:
No w type Pro duce r as sho wn in blue :
CODE TO TYPE: Pro ducer
package bomb;

import java.applet.Applet;

public class Producer extends Thread {


private World myWorld;
private String bank = "qwertyuiopasdfghjklzxcvbnm"; // alphabet characters
from standard keyboard
private Applet apl;
private int bombRate = 2000; // rate can be fast or
slow

public Producer(World myWorld, Applet apl) {


this.myWorld = myWorld;
this.apl = apl;
}

public void toggleBombRate(){ // user can change spee


d with GUI button
if (bombRate == 2000)
bombRate = 4000;
else
bombRate = 2000;
}

public void run() {


String str;
while (true)
{
int length = (int)Math.ceil(Math.random() * 6 ); // rand
om length
char []str_arry = new char[length];
for (int i = 0; i < length; i++)
{
str_arry[i] = bank.charAt((int)(Math.random() * bank.length() ))
; // random placement in string
}
str = new String(str_arry);
//str = bank[((int)(Math.random() * 10))];
int x = ((int)(Math.random() * 500)); // rand
om location
int y = ((int)(Math.random() * 335));
Bomb b = new Bomb(str, x, y, apl);
while (myWorld.overlaps(b) )
{
b = new Bomb(str, b.getX()+10, y, apl); // pre
vent bomb overlaps
}
myWorld.add(b);
System.out.println("Added bomb " + str + " to the world at "+b.getX(
)+", "+b.getY() );
apl.repaint();
try {
sleep(bombRate); // put
up new words at speed of rate
}
catch (InterruptedException e) { }
}
}
}

Save it.

Consuming Keys
In o ur example, users are the co nsumers. As users type, they co nsume the wo rds that have been placed in
the Wo rld. To allo w o ur users to do this, we include a Ke yList e ne r class. But since we wo n't need all o f the
Ke yList e ne r metho ds, we'll also use a Ke yAdapt e r.

Lo o k at the Adapter Class java.awt .e ve nt .Adapt e r and the interface that it implements
java.awt .e ve nt .Ke yList e ne r to find o ut which metho ds are available.

In the java4_Lesso n10 pro ject, create a new Co nsum e r class as sho wn:

Type Co nsum e r as sho wn in blue :


CODE TO TYPE: Co nsumer
package bomb;

import java.applet.Applet;
import java.awt.event.*;

public class Consumer extends KeyAdapter {


private World myWorld;
private Applet apl;

public Consumer(World myWorld, Applet apl) {


this.myWorld = myWorld;
this.apl = apl;
apl.addKeyListener(this);
}

public void keyTyped(KeyEvent e) {


myWorld.type(e.getKeyChar() );
apl.repaint();
}
}

Save it.

Monitoring Words
The mo nito r in this example watches o ver all o f the wo rds presented, as well as the user's typing speed and
accuracy. Each new word has a bo mb asso ciated with it. This mo nito r co nsiders the co nsumptio n o f the
wo rds in two ways:

1. Only five words are presented at o ne time.


2. If a wo rd is no t typed fast eno ugh, its bo mb explo des. If three bo mbs explo de, the mo nito r sto ps
paying attentio n to the user.

In the java4_Lesso n10 pro ject, create a new Wo rld class as sho wn:
Type Wo rld as sho wn in blue :
CODE TO TYPE: Wo rld

package bomb;

import java.awt.*;

public class World {


private final int MAX_BOMBS = 5;
private Bomb bombs[] = new Bomb[MAX_BOMBS]; // shared resource - 5 bombs sh
own at a time
private int typeNext = -1;
private int addNext = 0;
private int num_bombs = 0;
private boolean isFull = false;
private boolean isEmpty = true;
private boolean gameOver = false;

public synchronized void type(char c) {


while (isEmpty == true) // if no words/bombs in buffer
, wait for some
{
try {
wait();
} catch (InterruptedException e) {}
}
// check if three bombs have a
lready blown up
int num_exploded = 0;
for(int i = 0; i < MAX_BOMBS; i++) // check bombs in current value o
f shared resource
{
if (bombs[i] != null && bombs[i].exploded)
num_exploded++;
}
if (num_exploded >= 3)
{
gameOver = true;
return;
}
// check if entered
char matches a bomb and if its fully disarmed
if (typeNext < 0 || (bombs[typeNext].exploded) ) // no current diffusin
g bomb or current bomb blew up
{
for(int i = 0;i<MAX_BOMBS; i++)
{
if (bombs[i] != null && !bombs[i].exploded && bombs[i].startsWit
h(c) )
{
typeNext = i;
bombs[typeNext].setdisarming();
break;
}
}
}
if (typeNext > -1 && !bombs[typeNext].exploded && bombs[typeNext].attemp
tDisarm(c) )
{
bombs[typeNext] = null;
num_bombs--;
typeNext = -1;
if (num_bombs == 0 )
{
isEmpty = true;
}
isFull=false;
notifyAll();
}
}

public synchronized boolean overlaps(Bomb b){ // cannot put bombs on top of


each other
for (int i = 0; i<MAX_BOMBS; i++)
{
if (bombs[i] != null && (bombs[i].hasPoint(b.getX(), b.getY()) ||
bombs[i].hasPoint(b.getX()+b.getWidth(), b.getY()) ||
bombs[i].hasPoint(b.getX(), b.getY()+b.getHeight()) ||
bombs[i].hasPoint(b.getX()+b.getWidth(), b.getY()+b.getHeight()) ) )
{
return true;
}
}
return false;
}

public synchronized void clearBombs(){ // clear bombs to reset


for (int i = 0; i<MAX_BOMBS; i++)
{
bombs[i] = null;
}
typeNext = -1;
addNext = 0;
num_bombs = 0;
isFull = false;
isEmpty = true;
gameOver = false;
notifyAll();
}

public synchronized void add(Bomb b) { // add a bomb


while (isFull == true) // cannot add if full
{
try {
wait();
} catch (InterruptedException e) {}
}
//check for empty bomb spot
int i;
for (i = 0; i<MAX_BOMBS; i++)
{
if (bombs[i] == null) // find empty space in array
{
bombs[i] = b;
break; // once find space, get out of for l
oop
}
}
assert bombs[i] == b;
num_bombs++;
bombs[i].start(); // light the fuse baby!

if (num_bombs == MAX_BOMBS)
{
isFull = true;
}
isEmpty = false;
notifyAll();
}

public void draw(Graphics g){ // draw all of the bombs--called fro


m Applet
for (int i=0; i<MAX_BOMBS; i++)
{
if (bombs[i] != null)
bombs[i].draw(g);
}
if (gameOver)
{
g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, 23));
g.drawString("GAME OVER", 10, 390);
}
}
}

Save it.

T he User Interface
The graphical user interface fo r o ur example is relatively straightfo rward:

Do uble-buffering, specifically a Do uble-Buffer Applet, was used here to create the GUI.

In the java4_Lesso n10 pro ject, create a new T ype OrDie class as sho wn:
Type T ype o rDie as sho wn in blue :
CODE TO TYPE: TypeOrDie
package bomb;

import java.awt.event.*;
import java.awt.*;
import java.applet.Applet;

public class TypeOrDie extends Applet implements ActionListener {


World myWorld;
Button start_btn;
Button slow_fast_btn;
boolean started = false;
Producer p1;
Consumer c1;

public void init(){


setSize(560,400);

start_btn = new Button("Start"); // add the buttons and listeners


slow_fast_btn = new Button("Slower");
start_btn.addActionListener(this);
slow_fast_btn.addActionListener(this);
this.add(start_btn);
this.add(slow_fast_btn);
myWorld = new World(); // instantiate everyone
p1 = new Producer(myWorld, this);
c1 = new Consumer(myWorld, this);
}

public void paint(Graphics g) {


Dimension dim = getSize(); // set up double buffer
Image offscreen = createImage(dim.width, dim.height);
Graphics bufferGraphics = offscreen.getGraphics();
bufferGraphics.clearRect(0,0, dim.width, dim.height);

myWorld.draw(bufferGraphics);

g.drawImage(offscreen, 0, 0, this);
}

public void update (Graphics g){


paint(g);
}

public void actionPerformed(ActionEvent e){


if (e.getActionCommand() == "Slower")
{
p1.toggleBombRate();
slow_fast_btn.setLabel("Faster");
}
else if(e.getActionCommand() == "Faster")
{
p1.toggleBombRate();
slow_fast_btn.setLabel("Slower");
}
else
{
myWorld.clearBombs();
if (!started) // start the word creation
{
p1.start();
started = true;
start_btn.setLabel("Again");
}
}
this.requestFocus();
}
}

No w yo u see this in the Package Explo rer:

Save and run it. Alternate between running the co de and reading what each o bject do es so yo u understand
the reaso ns behind the implementatio n.

We Love T hreads
Threads are a challenge, but with practice they'll serve yo u well. Check o ut these implementatio ns o f threads:

Real Games
Yo u'll find lo ts o f real games po wered by Java here.

Blackjack

Blackjack is o ne o f the mo st po pular casino games in the wo rld. Given o ur kno wledge o f threads, and the
card capabilities we acquired in the last co urse, we co uld create o ur o wn blackjack game that allo ws multiple
players.

No w suppo se yo u have two o r three players spread acro ss the internet and they all cho o se to be Hit at the
same time. Yo u could have a race co nditio n, and yo u could co rrupt the game by giving all o f the players the
same card! Having the game set up so that multiple players can each play o n their o wn separate thread will
allo w yo ur game to go o ff witho ut a hitch. Successful java pro gramming--it's all abo ut the threads.

Yo u're do ing great so far, keep it up!

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.
Databases: Connectivity through Java

T he JDBC API
The next few lesso ns fo cus o n co nnecting Java to databases to access info rmatio n.

What is JDBC?
JDBC is a Java API asso ciated with accessing tabular data; that is, data that yo u'd generally want to reco rd in
a table. A table is a single sto re o f related info rmatio n; a database can co nsist o f o ne o r mo re tables o f
info rmatio n that are related in so me way. JDBC is used to pro cess SQL statements and enable yo ur
pro grams to access relatio nal databases. Like o ther Java APIs, the JDBC API co nsists o f classes and
interfaces written in the Java pro gramming language. Their main purpo se is to pro vide a standard API fo r
database develo pers and make it po ssible to write database applicatio ns using a pure Java API, which in turn
ensures that yo ur co de will be po rtable.

Go to the java.sql package to see so me o f the available classes and interfaces. Skim thro ugh the
Package java.sql Descriptio n and its histo ry. Go to the javax.sql package to see so me o f the extended
server-side functio nality that its classes and interfaces pro vide. This co urse fo cuses o n the java.sql package.
(The server-side info rmatio n in javax.sql applies in the J2EE co urse in this series.)

What Does JDBC Help Us Do?


JDBC helps us to write platfo rm-independent Java pro grams. These pro grams can be used to co nnect to a
wide range o f SQL databases and manipulate the data witho ut mo difying and/o r reco mpiling the Java
pro grams, even when mo ving fro m platfo rm to platfo rm o r fro m DBMS to DBMS. In sho rt, JDBC makes it
po ssible to :

1. Establish a co nnectio n with a database.


2. Send SQL statements.
3. Pro cess the results.

Altho ugh JDBC pro vides many classes and interfaces fo r use with databases, tho se three are the mo st
co mmo nly used. Here we've set up a table o f the co mmo n JDBC tasks and their co rrespo nding classes o r
interfaces:

T ask Mo st Use d Class o r Int e rf ace


establish a co nnectio n with a database java.sql.Co nne ct io n
send SQL statements java.sql.St at e m e nt
pro cess the results java.sql.Re sult Se t

We'll illustrate each o f these tasks in an example.

Connecting to the Database


We can't do any wo rk with the database until we co nnect to it.

Access to a Database
SQL can co mmunicate with mo st database systems witho ut changing the SQL co mmands. The MySQL
database server is likely the mo st po pular o pen so urce database so ftware amo ng pro grammers and is
available fo r use o n a wide variety o f platfo rms. We'll use the MySQL database fo r o ur examples.

In this co urse, yo u have been granted access to the MySQL database o n the O'Reilly Scho o l o f Techno lo gy
server. Yo u can access that database using the same username and passwo rd that yo u use to lo g o nto yo ur
co urses.
OST uses a master passwo rd (and Sandbo x lo gin) fo r all lo gin instances. In so me cases, the
passwo rd fo r MySQL can beco me o ut o f sync and cause this o r a similar erro r:

ERROR 10 45 (28 0 0 0 ): Access denied fo r user '<Sandbo x Lo gin>'@'co ld1.useractive.co m'


(using passwo rd: YES)

Note This erro r will also appear if the lo gin o r passwo rd is inco rrect. Ho wever, fo r OST, it is mo st
likely a “passwo rd o ut-o f-sync” issue.

To fix this, update the passwo rd fo r the OST Sandbo x fro m the My Acco unt sectio n o f the
Student Start Page (students.o reillyscho o l.co m/student/). This will reset the passwo rd fo r all
lo gin instances, including MySQL. Yo u can use yo ur current passwo rd witho ut changing it.
Remember that the Sandbo x lo gin and passwo rd are case sensitive.

Even tho ugh SQL is standardized, each database vendo r has a different interface, as well as different
extensio ns o f SQL. Our no tes and examples here will be all-purpo se so yo u can use them o n this, as well as
o ther databases.

T he Driver
Pro grammers use many different databases. Each database needs a driver to co nnect it to the Java pro gram.

There are fo ur types o f JDBC drivers. We will use a Type 4 driver, because it will co mmunicate directly with o ur
data so urce. Let's go ahead and start the Pro ject and get the driver. Create a new dat abase Drive r pro ject fo r
this lesso n.

The driver is actually a co llectio n o f Java classes that assist co nnectivity that are packaged into
Note o ne .jar (Java ARchive) file, similar to a .zip o r .tar file, used to ho ld multiple files. The Java VM
can get into .jar files fo r classes and files it needs if the .jar is in the CLASSPATH.

Put the driver into the CLASSPATH fo r yo ur pro ject. To do this, do wnlo ad the appro priate driver (we've already
do ne this fo r yo u), then right-click the dat abase Drive r pro ject, cho o se Build Pat h | Add Ext e rnal
Archive s, bro wse to the C:\jdbc fo lder and select the m ysql-co nne ct o r-java-5 .1.5 -bin.jar file:

Yo u might find a newer versio n o f the JDBC driver in this fo lder. The newer versio ns sho uld be
Note backward co mpatible with the versio ns referenced in this lesso n.
Then yo u'll see this file:

It pro vides Java with a lo catio n fo r sto ring classes that it needs fo r this applicatio n. Yo u will need to access
this path fo r each pro ject that uses the database, so remember this pro cedure.

It appears that Eclipse places these "external archives" into a fo lder called Re f e re nce d Librarie s, but
because this is the Referenced Library, Eclipse actually just po ints to the lo catio n o f the real file. A
CLASSPATH is a PATH to the CLASSES that Java requires fo r an applicatio n.

Verifying the Connection


No w let's experiment with co nnectivity. Create a new java4 _Le sso n11 pro ject. If yo u're given the o ptio n to
Ope n Asso ciat e d Pe rspe ct ive , click No . Create a Dat abase Manage r class in the pro ject as sho wn:
Type Dat abase Manage r as sho wn in blue :
CODE TO TYPE: DatabaseManager
package db;

import java.sql.*;

public class DatabaseManager {


private Connection connection; /
/ The database connection object.
private Statement statement; /
/ the database statement object, used to execute SQL commands.

public DatabaseManager (String username, String password ) { /


/ the constructor for the database manager.
String url = "jdbc:mysql://sql.useractive.com:3306/" + username; /
/ our database--username is your O'Reilly login username and is passed in.
try {
Class.forName ("com.mysql.jdbc.Driver"); /
/ get the driver for this database.
System.out.println("Driver is set; ready to go!");
}
catch (Exception e) {
System.out.println("Failed to load JDBC/ODBC driver.");
return; /
/ cannot even find the driver--return to caller since cannot do anything.
}

try {
// Establish the database connection, create a statement for execution of SQL
commands.
connection = DriverManager.getConnection (url, username, password );
// username and password are passed into this Constructor.
statement = connection.createStatement();
// statement used to do things in the database (e.g., create the PhoneBook tab
le).
}
catch (SQLException exception ) {
System.out.println ("\n*** SQLException caught ***\n");
while (exception != null)
{
// grab the exception caught to tell us the problem.
System.out.println ("SQLState: " + exception.getSQLState() );
System.out.println ("Message: " + exception.getMessage() );
System.out.println ("Error code: " + exception.getErrorCode() );
exception = exception.getNextException ();
System.out.println ( "" );
}
}
catch (java.lang.Exception exception) {
// perhaps there is an exception that was not SQL related.
exception.printStackTrace();
// shows a trace of the exception error--like we see in the console.
}
}
}

Save it.

Let's go o ver this particular line o f co de first: St ring url = " jdbc:m ysql://sql.use ract ive .co m :330 6 /" +
use rnam e ; pro vides JDBC a way to identify a database. Its structure is such that the appro priate driver
reco gnizes and establishes a co nnectio n with it. The driver writer determines the JDBC URL that identifies
their particular driver. Yo u need no t wo rry abo ut ho w to fo rm a JDBC URL; just use the URL supplied with the
drivers. Fo r instance, if yo ur username is blob, then yo ur url to access the MySQL server at the O'Reilly
Scho o l o f Techno lo gy is jdbc:m ysql://sql.use ract ive .co m :330 6 /blo b.

(The rest o f the co de will be explained in the next few sectio ns o f the lesso n.)
T he Factory Design Pattern
There are certain design patterns in Java that are used frequently. By beco ming familiar with design patterns,
pro grammers can avo id co mmo n pitfalls that may arise when using particular designs. We have already seen two
such frequently used patterns: Model/View/Controller (MVC) and Producer/Consumer (in threads). The co de in o ur
example abo ve inco rpo rates ano ther co mmo n design pattern called the Factory Design Pattern.

The facto ry metho d within the facto ry pattern produces an o bject. Facto ry metho ds are st at ic metho ds that return an
instance o f the interface o r class, usually thro ugh the use o f a Co nstructo r and the ne w co mmand. Facto ry metho ds
enable pro grams to pro duce o bjects witho ut specifying the precise class that will access the o bject because they are
o ften instantiated as an interface rather than a class.

Facto ry metho ds:

unlike co nstructo rs, may have meaningful names, which can clarify co de.
do no t need to create a new o bject o n each invo catio n--o bjects can be cached and reused, if necessary.
can return a subtype o f their return type. That is, they can return an o bject who se implementatio n class is
unkno wn to the caller. This is a very valuable and widely used feature in many framewo rks that use
interfaces as the return type o f static facto ry metho ds.

Co mmo n names fo r facto ry metho ds include ge t Inst ance () and value Of (), tho ugh using these names is o ptio nal--
cho o se whatever makes sense fo r yo ur particular usage.

In o ur example, when co nnecting to the database, we do n't use ne w, but o btain o bjects using the statements
Class.f o rNam e (" co m .m ysql.jdbc.Drive r" ), co nne ct io n = Drive rManage r.ge t Co nne ct io n(url, use rnam e ,
passwo rd), and st at e m e nt = co nne ct io n.cre at e St at e m e nt ().

Go to the java.lang.Class class and lo o k at the st at ic metho d f o rNam e (St ring classNam e ). This metho d
auto matically creates an instance o f a driver and registers it with the DriverManager, so yo u do n't have to create an
instance o f the class.

Go to the java.sql.Drive rManage r class and lo o k at the st at ic metho d ge t Co nne ct io n(St ring url, St ring
use r, St ring passwo rd). The metho d returns an inst ance o f the int e rf ace Co nne ct io n--because it is an interface,
it do es no t have a Co nstructo r and canno t be created with the ne w co mmand. By returning an inst ance o f the
interface, the metho ds are implemented and yo u can use them.

In the java.sql.Drive rManage r class, the Class.f o rNam e () is no lo nger needed. Later we'll co mment it o ut and run
the co de to see if it wo rks as specified.

Go to the java.sql.Co nne ct io n interface and then to the cre at e St at e m e nt () metho d. It returns an instance o f
the int e rf ace St at e m e nt . Again, because St at e m e nt is an interface, it do es no t have a Co nstructo r and canno t be
created with the ne w co mmand.

Finally, when a metho d is st at ic, it can be called fro m the class. Many o f the metho ds we'll use fo r databases and
netwo rking will be st at ic and many o f the classes will use the facto ry metho d pattern.

No w, let's get back to o ur database implementatio n.

T esting Our Connection


It's wise to check the Dat abase Manage r co de o ne step at a time, so let's make a class to instantiate it and check o ur
co nnectio n.

T he Main
In the java4_Lesso n11 pro ject, create a new Pho ne Bo o k class as sho wn:
Type Pho ne Bo o k as sho wn in blue :

CODE TO TYPE: Pho neBo o k


package db;

public class PhoneBook {

public static void main(String[] args) { // args[0] must be the username an


d args[1] must be the password to connect to the mysql database

DatabaseManager databaseManager = new DatabaseManager( args[0], args[1]


); // Create the database manager.
}
}

Save it. We have two ways to run it.

1: Providing Parameters In Code


The first way is to put yo ur username and passwo rd right into yo ur co de. This is usually a bad practice,
especially fo r a shared applicatio n, because whenever so meo ne else wants to use the co de, they need to
edit and reco mpile. Still, fo r the sake o f testing, so metimes it's co nvenient.
Edit Pho ne Bo o k as sho wn in blue belo w to reflect yo ur username and passwo rd:

CODE TO EDIT: Pho neBo o k


package db;

public class PhoneBook {

public static void main ( String[] args ) {


// suppose your username is Iam and password is soCool
DatabaseManager databaseManager = new DatabaseManager( "Iam", "soCool" )
;
}
}

No te that "Iam" and "so Co o l" are not a valid username and passwo rd; replace them with yo ur o wn.

Save and run it. Yo u'll see this in the co nso le:

We'd better change the co de back to the way it was befo re:

CODE TO EDIT: Pho neBo o k


package db;

public class PhoneBook {

public static void main ( String[] args ) { // args[0] must be the usernam
e and args[1] must be the password to connect to the mysql database
DatabaseManager databaseManager = new DatabaseManager( args[0], args[1]
); // Create the database manager.
}
}

Save it. We'll try to run it ano ther way:

2: Giving Parameters in Eclipse


To run this pro gram fro m the command line (o utside o f Eclipse), yo u wo uld enter java Pho ne Bo o k
yo urUse rNam e yo urPasswo rd (with yo ur real username and passwo rd, o f co urse). We use the Eclipse
GUI to do this, and Eclipse enters o ur co mmands fo r us.

Right -click in the edito r windo w fo r Pho neBo o k.java. Cho o se Run As | Run Co nf igurat io ns...:
In the Run windo w that o pens, cho o se the Argum e nt s tab. Pro vide yo ur username and passwo rd in the
Pro gram argum e nt s: bo x. Again, use the username and passwo rd yo u use to access the co urse. Click
Run:
Yo u still see this:

This pro blem is all to o co mmo n. The Co nso le says "Faile d t o lo ad J DBC/ODBC drive r." That text is fro m
the Dat abase Manage r class, in the cat ch blo ck aro und line 16 .

OBSERVE: DatabaseManager Co nstructo r


public DatabaseManager (String username, String password ) { // the
constructor for the database manager
String url = "jdbc:mysql://sql.useractive.com:3306/" + username; // where
username is your O'Reilly login username
try {
Class.forName ("com.mysql.jdbc.Driver");
}
catch (Exception e) {
System.out.println("Failed to load JDBC/ODBC driver.");
return;
}
}

We tho ught that Class.f o rNam e (" co m .m ysql.jdbc.Drive r" ) wo uld go and get the driver, but apparently it
didn't find that driver, because it threw an Exce pt io n that was caught by o ur co de.

We put the driver's .jar into the dat abase Drive r Pro ject, and we put these Java classes into the
java4 _Le sso n11 Pro ject. No w we need to get the driver into the Pro ject we're wo rking o n:

Right-click o n the java4_Lesso n11 Pro ject, then cho o se Build Pat h | Add Ext e rnal Archive s, which o pens
the file bro wser fo r yo u to get the driver. In the file dialo g, start to type the path C:\jdbc\m ysql-co nne ct o r-
java-5 .1.5 -bin.jar. The auto -co mplete sho uld allo w yo u to press Tab to fill in the file name m ysql-
co nne ct o r-java-5 .1.5 -bin.jar.

No w yo ur java4_Lesso n11 sho uld lo o k like this:

Run Pho neBo o k again, giving it the arguments as sho wn abo ve (Eclipse may remember them fo r us).

It's a go o d thing we included that Syst e m .o ut .print ln. If o ur pro gram runs the way we want it to , " Drive r is
se t ; re ady t o go !" will appear in the Co nso le.

Sending SQL Statements


No w that we kno w we have a co nnectio n, we can play with the database. We'll po pulate o ur database with so me
co mmo n SQL statements, specifically, metho ds that do the fo llo wing:

Create a table in the database named Pho neBo o k: st at e m e nt .e xe cut e (" cre at e t able
Pho ne Bo o k(Nam e varchar (32), Pho ne Num be r varchar (18) )" );
Add names and pho ne numbers to the PhoneBook: st at e m e nt .e xe cut e (" inse rt int o Pho ne Bo o k
value s ('" + nam e + " ', '" + pho ne Num be r + " ');" );

Mo st o f the metho d calls are to the instance o f the interface St at e m e nt named st at e m e nt . Even tho ugh o nly a few
metho d calls are to the database, co nstructing them still requires a lot o f co de. Mo st o f that co de is co ntained within
t ry/cat ch blo cks and Syst e m .o ut .print lns and pro vides info rmatio n abo ut exceptio ns.

Go to the interface java.sql.St at e m e nt . Scro ll to the Me t ho d De t ail sectio n (o r click here). Scro ll do wn thro ugh
the metho ds; every o ne o f them thro ws an SQLExce pt io n.

When writing co de with many metho ds that thro w exceptio ns, lo calize yo ur t ry/cat ch blo cks. That is, place
T ip each statement (o r small gro uping) into its o wn t ry clause so yo u can identify the metho d that threw the
exceptio n.

In o ur example, each metho d we define in Dat abase Manage r co ntains relatively few metho d calls. And each metho d
has its o wn try/catch clause fo r the set o f metho d calls within the metho d blo ck. There's no o ther way to do it.

Here's so me additio nal info rmatio n abo ut exceptio ns.

Edit Dat abase Manage r as sho wn in blue :


CODE TO EDIT: DatabaseManager
package db;

import java.sql.*;

public class DatabaseManager {

private Connection connection; // The database connection object.


private Statement statement; // the database statement object, used to execute S
QL commands.

public DatabaseManager (String username, String password ) { // the constr


uctor for the database manager
String url = "jdbc:mysql://sql.useractive.com:3306/" + username; // where user
name is your O'Reilly login username
try {
Class.forName ("com.mysql.jdbc.Driver");
}
catch (Exception e) {
System.out.println("Failed to load JDBC/ODBC driver.");
return;
}

try { //
Establish the database connection, create a statement for execution of SQL commands.
connection = DriverManager.getConnection (url, username, password ); //
username and password are passed into this Constructor
statement = connection.createStatement();
statement.execute("create table PhoneBook (Name varchar (32), PhoneNumber v
archar (18));"); // create a table in the database

}
catch (SQLException exception ) {
System.out.println ("\n*** SQLException caught ***\n");
while (exception != null)
{ /
/ tell us the problem
System.out.println ("SQLState: " + exception.getSQLState() );
System.out.println ("Message: " + exception.getMessage() );
System.out.println ("Error code: " + exception.getErrorCode() );
exception = exception.getNextException ();

System.out.println ( "" );
}
}
catch ( java.lang.Exception exception ) {
exception.printStackTrace();
}
}

public void addEntry (String name, String phoneNumber ){ // a


dds an entry to the Phone Book
try
{
statement.execute ( "insert into PhoneBook values ('" + name + "', '" + pho
neNumber + "');" );
}
catch ( SQLException exception )
{
System.out.println ("\n*** SQLException caught ***\n");

while ( exception != null)


{
System.out.println ("SQLState: " + exception.getSQLState() );
System.out.println ("Message: " + exception.getMessage() );
System.out.println ("Error code: " + exception.getErrorCode() );
exception = exception.getNextException ();
System.out.println ( "" );
}
}
catch(java.lang.Exception exception )
{
exception.printStackTrace();
}
}
}

Save it.

User Access and Input


Next, we need to allo w a user to give co mmands. Granted, we do n't have many co mmands at o ur dispo sal at
this time, but we have to start so mewhere, right?

In the java4_Lesso n11 pro ject, create a Use rInt e rf ace class as sho wn:

Type Use rInt e rf ace as sho wn in blue :


CODE TO TYPE: UserInterface

package db;

import java.sql.*;
import java.util.*;

public class UserInterface {

private DatabaseManager database; // th


e reference to the DatabaseManager object,
// han
dles all requests to access the database

public UserInterface(DatabaseManager theDatabaseManager) {


database = theDatabaseManager;
}

public void start() {


Scanner in = new Scanner (System.in);
while (true) { // Continue until the u
ser quits
System.out.println ("Click in the Console,"
+ "\n then enter a command:"
+ "\n A (then Enter) to Add a phone book entry, \n"
+ "Click red square to quit (terminate) for now.");

String command = in.nextLine();

if ( command.charAt(0) == 'A' )
{
System.out.println ("Enter name: ");
String name = in.nextLine();
System.out.println ("Enter phone number: ");
String phoneNumber = in.nextLine();
database.addEntry (name, phoneNumber); // Add this entry to the
database.
}
}
}
}

Save it.

The st art () metho d o f the class has a lo o p that starts with while (t rue ). This allo ws the applicatio n to stay
o pen fo r co ntinuo us input until the user finishes. After each user input, the lo o p perfo rms the specified actio n
and then returns to pro mpt again.

Edit the Pho ne Bo o k class to instantiate and start this interface as sho wn in blue :
CODE TO EDIT: Pho neBo o k
package db;

public class PhoneBook {

public static void main ( String[] args ) {


// args[0] must be the username and

// args[1] must be the password to connect to the mysql database


DatabaseManager databaseManager = new DatabaseManager(args[0], args[1] )
; // Create the database manager.

UserInterface userInterface = new UserInterface(databaseManager );


// Create access for user input.
userInterface.start();
}
}

Save and run it. Make sure to click in the Co nso le so yo ur input go es there.

Except fo r the red line po inting to the Terminate butto n, yo u'll see this (the gre e n text is o ur sample input):

Closing Our Connections


Never expect the user to use Ct rl+C o r T e rm inat e to end a pro gram; that's po o r design. The user may no t
kno w abo ut these to o ls and mo re impo rtantly, terminating an applicatio n this way might unexpectedly leave
the database witho ut a "clean-up" and info rmatio n co uld be lo st.

Run Pho ne Bo o k again to see ano ther reaso n that an applicatio n must always clo se its o pen co nnectio ns
and pro cesses:
The first statement that we execute after getting o ur co nnectio n is:

st at e m e nt .e xe cut e (" cre at e t able Pho ne Bo o k (Nam e varchar (32), Pho ne Num be r varchar (18)
);" );

Specifically, we execute a statement to create a table named Pho ne Bo o k in the database. So , when we run it
the seco nd time, after terminating witho ut pro per pro cedure, we are trying to create a table that is already
there. We have two ways aro und this pro blem: either avo id re-creating the table every time we access the
database or remo ve the table when finished with the demo nstratio n.

In the pro ject fo r this lesso n, yo u'll need to fix the pro blem so that we can sto p o ur pro gram with so me dignity.

Additional Resources
Here are so me additio nal items yo u can read to learn abo ut Java and database co nnectivity.

Trail: JDBC(TM) Database Access


JavaWo rld article abo ut drivers.
Gamelan's Using JDBC with MySQL, Getting Started

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.
Databases and Java: Processing Information

Getting Results
In this lesso n, we'll add metho ds to o ur co de that will allo w us to :

Pro cess the Re sult Se t we get fro m the JDBC.


Write a ge t Ent rie s() metho d to retrieve names and pho ne numbers and display the entries fro m the
Pho ne Bo o k table.
Write an inspe ct T able s() metho d to determine whether a table already exists befo re creating it,
and a clo se () metho d to drop (remo ve) the table and clo se the database co nnectio n.
Create a better lo gin using a Dialo gBo x.

Databases and SQL


In the previo us lesso n, we co nnected to the database, created a table, and put info rmatio n into it. If we use
JDBC to populate the table o r put the info rmatio n into the database, we use an instance o f the St at e m e nt
interface to execute SQL statements like these:

CREAT E T ABLE (makes who le tables)


INSERT (adds a ro w)
DELET E (remo ves a ro w)
UPDAT E (changes an existing value in a co lumn o r co lumns)

We used an instance st at e m e nt o f St at e m e nt to execute a CREAT E and INSERT . This co urse uses the
technique that uses JDBC, but info rmatio n can be inserted into a table in any o f these ways:

1. Using the database o r IDE graphical interface.


2. Using ANT (and .xml files).
3. Using JDBC SQL written into an applicatio n.

Altho ugh we've o nly created o ne table in o ur example, we can add and manipulate any number o f tables
using the same techniques. Fo r instance, we can J OIN related data fro m multiple tables. Ho wever, in this
lesso n, we'll co ntinue to use o ur simple example and start the pro cedure fo r retrieving info rmatio n.

The JDBC returns results fro m a St at e m e nt 's query in a Re sult Se t o bject, so we need an instance o f the
Re sult Se t interface to ho ld o ur results. The Re sult Se t interface pro vides metho ds fo r retrieving and
manipulating the results o f queries; particularly, it pro vides getter metho ds (such as ge t Bo o le an() and
ge t Lo ng()) fo r retrieving co lumn values fro m the current ro w.

Fo r specifics and examples, see the Oracle tuto rial Relatio nal Database Overview.

ResultSet
A Re sult Se t is the table o f results fro m yo ur query. This table can have o ne o r mo re ro ws. Yo u need to
manipulate these results to get the info rmatio n yo u want fro m the table. Altho ugh we usually lo o k at a table as
a two -dimensio nal array, the JDBC pro vides the Re sult Se t to manipulate this array to extract whatever
info rmatio n yo u need. We'll go o ver so me examples in this lesso n and the next, but the Oracle tuto rial is
always a go o d so urce fo r mo re info rmatio n: Retrieving Values fro m Result Sets—and o f co urse, the API.
Go to the java.sql.Re sult Se t interface and read the intro ductio n.

Getting Information About Information


It's to ugh to anticipate the best ways to manipulate data when we're no t even sure which data is present. We kno w ho w
to make f o r lo o ps to go thro ugh two -dimensio nal arrays, but in the database table, we do n't kno w ho w many ro ws
exist! We can't write co de that go es to so me arrays.le ngt h because we do n't even kno w that we have an array--we
o nly have info rmatio n returned fro m a table. We need to kno w ho w many ro ws are in that info rmatio n. In o ther wo rds,
we need info rmatio n abo ut the info rmatio n we are getting! The JDBC helps us by pro viding MetaData classes.

Metadata
metaknowledge is kno wledge abo ut kno wledge.
metalanguage is a language abo ut languages.
metatheory is the theo ry abo ut theo ries.
metadata is data abo ut data.

Go to the java.sql package. Scro ll thro ugh the Int e rf ace Sum m ary. We see so me interesting names:

DatabaseMetaData
ParameterMetaData
ResultSetMetaData

These interfaces can be instantiated by instances o f the o bjects that help identify the data. Let's take a lo o k at
a metho d that illustrates this idea.

In the java4_Lesso n11 Pro ject, edit Dat abase Manage r as sho wn in blue :
CODE TO EDIT: DatabaseManager
package db;

import java.sql.*;

public class DatabaseManager {

private Connection connection; // The


database connection object.
private Statement statement; // the
database statement object, used to execute SQL commands.

public DatabaseManager (String username, String password ) { // the


constructor for the database manager
String url = "jdbc:mysql://sql.useractive.com:3306/" + username;
try {
Class.forName ("com.mysql.jdbc.Driver");
}
catch (Exception e) {
System.out.println("Failed to load JDBC/ODBC driver.");
return;
}
try { // Est
ablish the database connection, create a statement for execution of SQL commands
.
connection = DriverManager.getConnection (url, username, password );
// username and password are passed into this Constructor
// Get the DatabaseMetaData object and display
// some information about the connection

DatabaseMetaData aboutDB = connection.getMetaData();

System.out.println("\nConnected to " + aboutDB.getURL());


System.out.println("Driver " + aboutDB.getDriverName());
System.out.println("Version " + aboutDB.getDriverVersion());

statement = connection.createStatement();
statement.execute ("create table PhoneBook (Name varchar (32), Phone
Number varchar (18) );"); // create a table in the database

}
catch (SQLException exception ) {
System.out.println ("\n*** SQLException caught ***\n");
while (exception != null)
{
// tell us the problem
System.out.println ("SQLState: " + exception.getSQLState() )
;
System.out.println ("Message: " + exception.getMessage() )
;
System.out.println ("Error code: " + exception.getErrorCode() )
;
exception = exception.getNextException ();

System.out.println ( "" );
}
}
catch ( java.lang.Exception exception ) {
exception.printStackTrace();
}
}

public void addEntry (String name, String phoneNumber ){


// adds an entry to the Phone Book
try
{
statement.execute ( "insert into PhoneBook values ('" + name + "', '"
+ phoneNumber + "');" );
}
catch ( SQLException exception )
{
System.out.println ("\n*** SQLException caught ***\n");

while ( exception != null)


{
System.out.println ("SQLState: " + exception.getSQLState() );
System.out.println ("Message: " + exception.getMessage() );
System.out.println ("Error code: " + exception.getErrorCode() );

exception = exception.getNextException ();


System.out.println ( "" );
}
}
catch(java.lang.Exception exception )
{
exception.printStackTrace();
}
}
}

Save and run it (fro m Pho ne Bo o k). Yo u'll see so mething like this:

Even tho ugh yo u may exit the applicatio n by clicking the red T e rm inat e square, yo u sho uld also either
"Remo ve Launch" (single X) o r "Remo ve All Terminated Launches" by clicking the do uble X:

In databases, when a query returns a Re sult Se t , it is returning a table o f data. Re sult Se t s are used to
retrieve data so that it can be manipulated. Suppo se yo u want to lo o p thro ugh the data to find so mething, but
yo u do n't kno w ho w many ro ws o r co lumns o f data exist, so yo u do n't kno w ho w many times to lo o p. This
wo uld be a perfect time to use Re sult Se t Me t aDat a.

Creating a T able
If o ne do esn't exist already, we'll create a new Pho ne Bo o k table. By do ing this, we wo n't need to dro p the
database table; we can keep the info rmatio n we have just submitted in it fo r the next time we access it. We'll
use bo th the Re sult Se t and the Re sult Se t Me t aDat a interfaces.

In the java4_Lesso n11 Pro ject, edit Dat abase Manage r as sho wn in blue belo w:
CODE TO EDIT: DatabaseManager
package db;

import java.sql.*;

public class DatabaseManager {

private Connection connection; // The database connection object.


private Statement statement; // the database statement object, used to ex
ecute SQL commands.

public DatabaseManager (String username, String password ) { // the


constructor for the database manager
String url = "jdbc:mysql://sql.useractive.com:3306/" + username;
try {
Class.forName ("com.mysql.jdbc.Driver");
}
catch (Exception e) {
System.out.println("Failed to load JDBC/ODBC driver.");
return;
}

try {
// Establish the database connection, create a statement for execution of SQL
commands.
connection = DriverManager.getConnection (url, username, password );
// username and password are passed into this Constructor

statement = connection.createStatement();
DatabaseMetaData aboutDB = connection.getMetaData ();
// do more useful things with the meta class
String [] tableType = {"TABLE"};
ResultSet rs = aboutDB.getTables(null, null, "PhoneBook", tableType
); // for more info about this method, see the getTables method in DatabaseMeta
Data in the API

if (!inspectForTable(rs))
// use this method (written below) to see if we already have the table PhoneB
ook
statement.execute ("create table PhoneBook (Name varchar (32), Phon
eNumber varchar (18) );"); // if we do NOT already have one, we want to do this

rs.close();
// in this example, the ResultSet is local, so close it here
}
catch (SQLException exception ) {
System.out.println ("\n*** SQLException caught ***\n");
while (exception != null)
{
// tell us the problem
System.out.println ("SQLState: " + exception.getSQLState() )
;
System.out.println ("Message: " + exception.getMessage() )
;
System.out.println ("Error code: " + exception.getErrorCode() )
;
exception = exception.getNextException ();

System.out.println ( "" );
}
}
catch ( java.lang.Exception exception ) {
exception.printStackTrace();
}
}

public void addEntry (String name, String phoneNumber ){


// adds an entry to the Phone Book
try
{
statement.execute ( "insert into PhoneBook values ('" + name + "', '
" + phoneNumber + "');" );
}
catch ( SQLException exception )
{
System.out.println ("\n*** SQLException caught ***\n");

while ( exception != null)


{
System.out.println ("SQLState: " + exception.getSQLState() )
;
System.out.println ("Message: " + exception.getMessage() )
;
System.out.println ("Error code: " + exception.getErrorCode() )
;

exception = exception.getNextException ();


System.out.println ( "" );
}
}
catch(java.lang.Exception exception )
{
exception.printStackTrace();
}
}

private static boolean inspectForTable (ResultSet rs) throws SQLException {


// will be caught when used
int i;
ResultSetMetaData rsmd = rs.getMetaData ();
// Get the ResultSetMetaData. This will be used for information about the col
umns.
int numCols = rsmd.getColumnCount ();
// Get the number of columns in the result set

for (i=1; i<=numCols; i++) { /


/ Display column headings
if (i > 1) System.out.print(", ");
// just to show what is there for our curiosity
System.out.print(rsmd.getColumnLabel(i));
}
System.out.println("");

boolean more = rs.next ();


while (more) { // Display data, fetching until end o
f the result set
// Loop through each row, getting the
column data and displaying
for (i=1; i<=numCols; i++)
{
System.out.print(rs.getString(i)+"\n");
if (rsmd.getColumnLabel(i) == "TABLE_NAME")
if (rs.getString(i).equals("PhoneBook"))
{
System.out.println("Found one that equals " + rs.getStri
ng(i)); // is PhoneBook there already or not?
return true;
// it is, tell the method that inquired
}
}
System.out.println("");
more = rs.next ();
// Fetch the next result set row
}
return false;
// went though all of the rows and it was not there
}
}

Save and run it (fro m Pho ne Bo o k). Terminate it and run it again. The SQL Exceptio n alerting yo u to an
existing table sho uld no lo nger appear. We've added a few extra print lns in o ur co de so we can o bserve
what's been returned. We can remo ve them later.

Closing Properly
In the o bjective fo r the last lesso n, yo u added a metho d to clo se (). We'll include it here to o , but we'll also give the user
the o ptio n to either keep the info rmatio n o r dro p the table.

Edit Dat abase Manage r as sho wn in blue :


CODE TO EDIT: DatabaseManager
package db;

import java.sql.*;

public class DatabaseManager {

private Connection connection; // The databa


se connection object.
private Statement statement; // the databa
se statement object, used to execute SQL commands.

public DatabaseManager (String username, String password ) { // the constr


uctor for the database manager
String url = "jdbc:mysql://sql.useractive.com:3306/" + username;
try {
Class.forName ("com.mysql.jdbc.Driver");
}
catch (Exception e) {
System.out.println("Failed to load JDBC/ODBC driver.");
return;
}

try { // Establish
the database connection, create a statement for execution of SQL commands.
connection = DriverManager.getConnection (url, username, password ); // us
ername and password are passed into this Constructor
statement = connection.createStatement();

DatabaseMetaData aboutDB = connection.getMetaData ();


// do more useful things with the meta class
String [] tableType = {"TABLE"};
ResultSet rs = aboutDB.getTables(null, null, "PhoneBook", tableType); //
for more info about this method, see the getTables method in DatabaseMetaData in the AP
I

if (!inspectForTable (rs)) // use this m


ethod to see if we already have the table PhoneBook
statement.execute ("create table PhoneBook (Name varchar (32), PhoneNumber
varchar (18) );"); // if we do NOT already have one, we want to do this
rs.close(); //
in this example, the ResultSet is local - so close it here

}
catch (SQLException exception ) {
System.out.println ("\n*** SQLException caught ***\n");
while (exception != null)
{ /
/ tell us the problem
System.out.println ("SQLState: " + exception.getSQLState() );
System.out.println ("Message: " + exception.getMessage() );
System.out.println ("Error code: " + exception.getErrorCode() );
exception = exception.getNextException ();
System.out.println ( "" );
}
}
catch ( java.lang.Exception exception ) {
exception.printStackTrace();
}
}

public void addEntry (String name, String phoneNumber ){ // a


dds an entry to the Phone Book
try
{
statement.execute ( "insert into PhoneBook values ('" + name + "', '" + pho
neNumber + "');" );
}
catch ( SQLException exception )
{
System.out.println ("\n*** SQLException caught ***\n");
while ( exception != null)
{
System.out.println ("SQLState: " + exception.getSQLState() );
System.out.println ("Message: " + exception.getMessage() );
System.out.println ("Error code: " + exception.getErrorCode() );
exception = exception.getNextException ();
System.out.println ( "" );
}
}
catch(java.lang.Exception exception )
{
exception.printStackTrace();
}
}

private static boolean inspectForTable (ResultSet rs) throws SQLException { // wi


ll be caught when used
int i;
ResultSetMetaData rsmd = rs.getMetaData (); // Ge
t the ResultSetMetaData. This will be used for information about the columns.
int numCols = rsmd.getColumnCount (); // Ge
t the number of columns in the result set
for (i=1; i<=numCols; i++) { // Displ
ay column headings
if (i > 1) System.out.print(", "); // ju
st to show what is there for our curiosity
System.out.print(rsmd.getColumnLabel(i));
}
System.out.println("");

boolean more = rs.next ();


while (more) { // D
isplay data, fetching until end of the result set
// Loop through each row, getting the column data and displaying
for (i=1; i<=numCols; i++)
{
System.out.print(rs.getString(i)+"\n");
if (rsmd.getColumnLabel(i) == "TABLE_NAME")
if (rs.getString(i).equals("PhoneBook"))
{
System.out.println("Found one that equals " + rs.getString(i));
// is PhoneBook there already or not?
return true;
// it is, tell the method that inquired
}
}
System.out.println("");
more = rs.next (); /
/ Fetch the next result set row
}
return false;
// went though all of the rows and it was not there
}

public void close(boolean remove) {


// drops the table and properly closes the database
try
{
if (remove)
{
statement.execute("drop table PhoneBook;");
}
statement.close();
connection.close();
}
catch (SQLException exception)
{
System.out.println("\n*** SQLException caught ***\n");
while (exception != null)
{
System.out.println("SQLState: " + exception.getSQLState());
System.out.println("Message: " + exception.getMessage());
System.out.println("ErrorCode: " + exception.getErrorCode());
exception = exception.getNextException ();
System.out.println("");
}
}
catch(java.lang.Exception exception )
{
exception.printStackTrace();
}
}
}

Save it.

No w we need to acco mmo date the o ptio n in the user interface. In the java4_Lesso n11 pro ject, edit Use rInt e rf ace as
sho wn in blue :
CODE TO EDIT: UserInterface

package db;

import java.sql.*;
import java.util.*;

public class UserInterface {

private DatabaseManager database; // the reference to the DatabaseManager object,


// handles all requests to access the database

public UserInterface(DatabaseManager theDatabaseManager) {


database = theDatabaseManager;
}

public void start() {


Scanner in = new Scanner (System.in);
while (true) { // Continue until the user enters a
quit command
System.out.println ("Click in the Console,"
+ "\n then Enter a command: (choose)"
+ "\n A (then Enter) to Add a phone book entry,"
+ "\n K (then Enter) to Exit and Keep the entries,"
+ "\n or Q (then Enter) to Quit and Remove the entries: " );

String command = in.nextLine();

if (command.charAt(0) == 'A')
{
System.out.println ("Enter name: ");
String name = in.nextLine();
System.out.println ("Enter phone number: ");
String phoneNumber = in.nextLine();
database.addEntry (name, phoneNumber); // Add this entry to the databa
se.
}
else if (command.charAt(0) == 'K')
{
System.out.println("Bye");
database.close(false); // The user entered the quit command, but d
oes not want to delete info.
return;
}
else if (command.charAt(0) != 'Q')
{
System.out.println ("Invalid command. Please enter either A, K, or Q.")
;
}
else // com
mand is Q
{
System.out.println("Bye");
database.close(true); // Th
e user entered the quit command, so shutdown the database and return.
return;
}
}
}
}

Save and run it (fro m Pho ne Bo o k). Exit with K to Keep the table. Run it again.

Seeing T able Contents


Is there even anything in there? Let's find o ut.

Edit Dat abase Manage r as sho wn in blue :


CODE TO EDIT: DatabaseManager
package db;

import java.sql.*;

public class DatabaseManager {

private Connection connection; // The databa


se connection object.
private Statement statement; // the databa
se statement object, used to execute SQL commands.
private ResultSet resultSet; // results fr
om a database query

public DatabaseManager (String username, String password ) { // the constr


uctor for the database manager
String url = "jdbc:mysql://sql.useractive.com:3306/" + username;
try {
Class.forName ("com.mysql.jdbc.Driver");
}
catch (Exception e) {
System.out.println("Failed to load JDBC/ODBC driver.");
return;
}

try { //
Establish the database connection, create a statement for execution of SQL commands.
connection = DriverManager.getConnection (url, username, password ); //
username and password are passed into this Constructor
statement = connection.createStatement();

DatabaseMetaData aboutDB = connection.getMetaData ();


// do
more useful things with the meta class
String [] tableType = {"TABLE"};
ResultSet rs = aboutDB.getTables(null, null, "PhoneBook", tableType); //
check out the getTables method in DatabaseMetaData to see more about this method

if (!inspectForTable (rs)) //
use this method to see if we already have the table PhoneBook
statement.execute ("create table PhoneBook (Name varchar (32), PhoneNum
ber varchar (18) );"); // if we do NOT already have one, we want to do this
rs.close(); //
in this example, the ResultSet is local - so close it here

}
catch (SQLException exception ) {
System.out.println ("\n*** SQLException caught ***\n");
while (exception != null)
{ /
/ tell us the problem
System.out.println ("SQLState: " + exception.getSQLState() );
System.out.println ("Message: " + exception.getMessage() );
System.out.println ("Error code: " + exception.getErrorCode() );
exception = exception.getNextException ();

System.out.println ( "" );
}
}
catch ( java.lang.Exception exception ) {
exception.printStackTrace();
}
}

public void addEntry (String name, String phoneNumber ){ // a


dds an entry to the Phone Book
try
{
statement.execute ( "insert into PhoneBook values ('" + name + "', '" + pho
neNumber + "');" );
}
catch ( SQLException exception )
{
System.out.println ("\n*** SQLException caught ***\n");

while ( exception != null)


{
System.out.println ("SQLState: " + exception.getSQLState() );
System.out.println ("Message: " + exception.getMessage() );
System.out.println ("Error code: " + exception.getErrorCode() );

exception = exception.getNextException ();


System.out.println ( "" );
}
}
catch(java.lang.Exception exception )
{
exception.printStackTrace();
}
}

private static boolean inspectForTable (ResultSet rs) throws SQLException { // wi


ll be caught when used
int i;
ResultSetMetaData rsmd = rs.getMetaData (); // G
et the ResultSetMetaData. This will be used for information about the columns.
int numCols = rsmd.getColumnCount (); // G
et the number of columns in the result set

// for (i=1; i<=numCols; i++) { /


/ Display column headings
//if (i > 1) System.out.print(", "); /
/ just to show what is there for our curiosity
// System.out.print(rsmd.getColumnLabel(i));
//}
//System.out.println("");

boolean more = rs.next ();


while (more) { // D
isplay data, fetching until end of the result set
// Loop through each row, getting the column data and displaying
for (i=1; i<=numCols; i++)
{
//System.out.print(rs.getString(i)+"\n");
if (rsmd.getColumnLabel(i) == "TABLE_NAME")
if (rs.getString(i).equals("PhoneBook"))
{
System.out.println("Found one that equals " + rs.getString(i));
// is PhoneBook there already or not?
return true;
// it is, tell the method that inquired
}
}
System.out.println("");
more = rs.next (); /
/ Fetch the next result set row
}
return false; /
/ went though all of the rows and it was not there
}

public void getEntries(){ // returns


a ResultSet containing all entries in the phone book
try
{
resultSet = statement.executeQuery("SELECT * FROM PhoneBook"); // call the
query and get a ResultSet

ResultSetMetaData metaData = resultSet.getMetaData(); // Get the


ResultSetMetaData.
int numCols = metaData.getColumnCount(); // Get the
number of columns in the result set
int i;
System.out.println("");
for (i=1; i <= numCols; i++)
{
if (i > 1) System.out.print("\t\t\t\t");
System.out.print ( metaData.getColumnLabel(i) );
}
System.out.println("");
System.out.println("");

boolean more = resultSet.next(); // Display data,


fetching until end of the result set
while (more) // Loop through
each column, getting the column data and displaying
{
for (i = 1; i <= numCols; i++)
{
System.out.print (resultSet.getString(i) + "\t\t\t\t" );
}
System.out.println("");
more = resultSet.next(); // Go to the n
ext result set row
}
resultSet.close();
System.out.println("");

}
catch (SQLException exception)
{
System.out.println ("\n*** SQLException caught ***\n");

while ( exception != null)


{
System.out.println("SQLState: " + exception.getSQLState());
System.out.println("Message: " + exception.getMessage());
System.out.println("Error code: " + exception.getErrorCode());

exception = exception.getNextException();
System.out.println("");
}
}
catch (java.lang.Exception exception )
{
exception.printStackTrace();
}
}

public void close(boolean remove){


// drops the table and properly closes the database
try
{
if (remove)
statement.execute("drop table PhoneBook;");
statement.close();
connection.close();
}
catch (SQLException exception)
{
System.out.println("\n*** SQLException caught ***\n");

while (exception != null)


{
System.out.println("SQLState: " + exception.getSQLState());
System.out.println("Message: " + exception.getMessage());
System.out.println("Error code: " + exception.getErrorCode());

exception = exception.getNextException();
System.out.println("");
}
}
catch(java.lang.Exception exception)
{
exception.printStackTrace();
}
}
}

Save it.

We have lo ts o f new additio ns to the DatabaseManager; let's give o ur users mo re capabilities to keep up with tho se
additio ns.

Edit Use rInt e rf ace as sho wn in blue :


CODE TO EDIT: UserInterface

package db;

import java.util.*;

public class UserInterface {

private DatabaseManager database; // the reference to the


DatabaseManager object,
// handles all requests
to access the database
public UserInterface(DatabaseManager theDatabaseManager) {
database = theDatabaseManager;
}

public void start() {


Scanner in = new Scanner (System.in);
while (true) { // Continue until the user
enters a quit command
System.out.println ("Click in the Console,"
+ "\n then Enter a command: (choose)"
+ "\n A (then Enter) to Add a phone book entry, "
+ "\n D (then Enter) to Display all phone book entries,"
+ "\n K (then Enter) to exit and Keep the entries,"
+ "\n or Q (then Enter) to Quit and remove the entries: " );

String command = in.nextLine();

if ( command.charAt(0) == 'A' )
{
System.out.println ("Enter name: ");
String name = in.nextLine();
System.out.println ("Enter phone number: ");
String phoneNumber = in.nextLine();
database.addEntry (name, phoneNumber); // Add this entry to the databa
se.
}
else if (command.charAt(0) == 'D')
{
database.getEntries(); // Query the database for the resultSet
}
else if (command.charAt(0) == 'K' )
{
System.out.println("Bye");
database.close(false); // The user entered the quit comma
nd, but does not want to delete info.
return;
}
else if ( command.charAt(0) != 'Q' )
{
System.out.println ("Invalid command, please enter either A, D, K, or Q
.");
}
else // command is Q
{
System.out.println("Bye");
database.close(true); // The user entered the quit comma
nd, so shutdown the database and return.
return;
}
}
}
}
Note Did yo u no tice the mo dularity here? After adding each metho d, we made a mino r edit to the user interface.

Save and run it (fro m Pho ne Bo o k.java). Use the D o ptio n to see all the entries that may be there already. Type Q
to Quit--this calls the clo se () metho d o f Dat abase Manage r, which dro ps the table and clo ses all o pen co nnectio ns
to the database.

Run it again. Since it was clo sed pro perly, there sho uldn't be any erro rs. Use the D o ptio n to see that all o f the entries
were remo ved. Add a few new entries and try the D co mmand again. Exit with the K o ptio n. In fact, try bo th the Q and K
o ptio ns until yo u feel co nfident that they behave as expected.

SQL Commands
Lo o k o ver the co de in this Observe bo x:

OBSERVE: DatabaseManager Stripped

package db;

import java.sql.*;

public class DatabaseManager {

private Connection connection;


private Statement statement;
private ResultSet resultSet;

public DatabaseManager (String username, String password ) {


// connect
String url = "jdbc:mysql://sql.useractive.com:3306/" + username;
Class.forName ("com.mysql.jdbc.Driver");

connection = DriverManager.getConnection (url, username, password );


statement = connection.createStatement();
statement.execute ("create table PhoneBook (Name varchar (32), PhoneNumber varc
har (18) );");
}

public void addEntry (String name, String phoneNumber ){


// add entries
statement.execute ( "insert into PhoneBook values ('" + name + "', '" + phoneNu
mber + "');" );
}

public ResultSet getEntries(){


// retrieve
return statement.executeQuery ( "SELECT * FROM PhoneBook");
}

public void close(){


// close
statement.execute ("drop table PhoneBook;");
statement.close();
connection.close();
}
}

We've remo ved all o f the t ry/cat ch clauses and Syst e m .o ut .print ln statements to sho w what's left o f the
Dat abase Manage r (o ther than the Meta questio ns). Yo u might be tempted to try to pro gram database applicatio ns
witho ut all o f tho se try/catch clauses, but that wo uld make yo ur applicatio n unstable and, fo r all intents and purpo ses,
unusable. In fact, Java wo n't even let yo u do it. SQLExce pt io ns are no t Runt im e Exce pt io ns and therefo re must be
handled.

We used a wild card (*) in the SELECT query to retrieve all o f the entries fro m o ur data. Explo re mo re o ptio ns using
SELECT with these reso urces:
Oracle's JDBC Intro ductio n.
Mo re fro m Oracle o n the Statement class metho ds.
The SQL SELECT link fro m Key Data.
One o f many bo o ks available o n SQL.

Logging In
So no w we have a running applicatio n, but what user wants to use the co mmand line to enter co mmands? We'll begin
to fix this pro blem by creating a to o l to lo g in. In the next lesso n, we'll reto o l the who le applicatio n to take advantage o f
the info rmatio n we no w kno w abo ut Swing.

In the java4_Lesso n11 pro ject, create a Passwo rdDialo g class:

Type Passwo rdDialo g as sho wn in blue :


CODE TO TYPE: Passwo rdDialo g
package db;

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

class PasswordDialog extends JDialog implements ActionListener {


private JTextField user;
private JPasswordField password;
private String username, passwd;
private static String [] info;
private static boolean set = false;

public PasswordDialog(final JFrame owner) {


// set the dialog title and size
super(owner, "Login", true);
setSize(280, 150);
user = new JTextField(10);
user.addActionListener(this);
password = new JPasswordField(10);
password.addActionListener(this);
// Create the center panel which contains the fields for entering information
JPanel center = new JPanel();
center.setLayout(new GridLayout(3, 2)); // 3 rows leaves a nice space betwe
en
center.add(new JLabel(" Enter UserName:"));
center.add(user);
center.add(new JLabel(" Enter Password:"));
center.add(password);
// Create the south panel which contains the buttons
JPanel south = new JPanel();
JButton submitButton = new JButton("Submit");
submitButton.setActionCommand("SUBMIT");
submitButton.addActionListener(this);

JButton helpButton = new JButton("Help");


south.add(submitButton);
south.add(helpButton);
// Add listeners to the buttons
helpButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent aEvent) { // The user has asked fo
r help
JOptionPane.showMessageDialog(owner,
"Your username and password are the same as those\n" +
"you use to access your O'Reilly School of Technology courses.\n");
}
});
// Add the panels to the dialog window
Container contentPane = getContentPane();
contentPane.add(center, BorderLayout.CENTER);
contentPane.add(south, BorderLayout.SOUTH);
}

public void actionPerformed(ActionEvent e) {


String cmd = e.getActionCommand();
if ("SUBMIT".equals(cmd))
{ // Process the inputs.
username = user.getText();
char[] input = password.getPassword();
passwd = new String(input);
// to verify it is working, print the name and password--remove this line l
ater!
System.out.println("User is " + username + ", password is " + passwd);
info = new String[2];
info[0] = username;
info[1] = passwd;
set = true; // now can send info back
dispose();
}
}

public static void main(String [] args) { // create the frame first and then give
it that frame as owner
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final PasswordDialog addPassword = new PasswordDialog(frame);
addPassword.setVisible(true);
System.exit(0); // terminates
}
}

Save and run it. That's much mo re efficient than go ing into Eclipse frames to set variables. Of co urse, no rmally we
wo uldn't just run a Lo gin Dialo g and sto p there, so the m ain() metho d might seem a little weird as it is.

Let's add a metho d to access the Passwo rdDialo g fro m o ther classes, and change it so it do esn't print the username
and passwo rd.

This example do es no t address security issues--it simply passes the info rmatio n as elements in an
Note array.

Edit Passwo rdDialo g as sho wn in blue :


CODE TO EDIT: Passwo rdDialo g

package db;

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

class PasswordDialog extends JDialog implements ActionListener{


private JTextField user;
private JPasswordField password;
private String username, passwd;
private static String [] info;
private static boolean set = false;

public PasswordDialog(final JFrame owner) {


// set the dialog title and size
super(owner, "Login", true);
setSize(280, 150);
user = new JTextField(10);
user.addActionListener(this);
password = new JPasswordField(10);
password.addActionListener(this);
// Create the center panel which contains the fields for entering information
JPanel center = new JPanel();
center.setLayout(new GridLayout(3, 2)); // 3 rows leaves a nice space betw
een
center.add(new JLabel(" Enter UserName:"));
center.add(user);
center.add(new JLabel(" Enter Password:"));
center.add(password);
// Create the south panel which contains the buttons
JPanel south = new JPanel();
JButton submitButton = new JButton("Submit");
submitButton.setActionCommand("SUBMIT");
submitButton.addActionListener(this);

JButton helpButton = new JButton("Help");


south.add(submitButton);
south.add(helpButton);
// Add listeners to the buttons
helpButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent aEvent) { // The user has asked f
or help.
JOptionPane.showMessageDialog(owner,
"Your username and password are the same as those\n" +
"you use to access your O'Reilly School of Technology courses.\n");
}
});
// Add the panels to the dialog window
Container contentPane = getContentPane();
contentPane.add(center, BorderLayout.CENTER);
contentPane.add(south, BorderLayout.SOUTH);
}

public void actionPerformed(ActionEvent e) {


String cmd = e.getActionCommand();
if ("SUBMIT".equals(cmd))
{ // Process the inputs.
username = user.getText();
char[] input = password.getPassword();
passwd = new String(input);
// to verify it is working, uncomment this line
//System.out.println("User is " + username + " password is " + passwd);
info = new String[2];
info[0] =username;
info[1] = passwd;
set = true; // now can send info back
dispose();
}
}

public static void main(String [] args){ // create the frame first and then give i
t that frame as owner
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final PasswordDialog addPassword = new PasswordDialog(frame);
addPassword.setVisible(true);
}

public static String [] login(Object sender) { // ob


ject who requested login is the sender;
JFrame frame = new JFrame(); // at
tempt is to make as reusable as possible
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final PasswordDialog addPassword = new PasswordDialog(frame);
addPassword.setVisible(true);
while (!set) // w
ait until user has put information in before returning values
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {};
return info;
}
}

Save it. No w, let's link it to o ur database. Edit Pho ne Bo o k as sho wn in blue belo w:

CODE TO EDIT: Pho neBo o k

package db;

public class PhoneBook {

public PhoneBook(){
String [] info = PasswordDialog.login(this); // st
atic login so can call from class
DatabaseManager databaseManager = new DatabaseManager(info[0], info[1]); // Cr
eate the database manager and pass login info.
UserInterface userInterface = new UserInterface(databaseManager ); // Cr
eate access for user input
userInterface.start();
}

public static void main ( String[] args ) {


// instantiate to start
// args[1] must be the password to connect to the mysql databas
e
PhoneBook myApp = new PhoneBook();
}
}

Save and run it.

Fo r o ther examples o f impro ved lo gin dialo gs, see Swing Co mpo nents and Oracle's Java Lo o k and Feel Design
Guidelines bo o k (bo th are co pyright-pro tected so we can't pro vide the co de fo r them here).

Yo u're do ing great! We've unearthed a lo t o f valuable stuff so far!


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.
Database Application With GUI

Refining the Application


Our last example demo nstrated the general co ncepts used to implement JDBC, and so me o f the fundamental
elements yo u enco unter when writing database applicatio ns:

Cre at ing a dat abase : create the database o utside o f Java, with to o ls supplied by the database vendo r, o r
with SQL statements fed to the database fro m a Java pro gram.
Co nne ct ing t o a dat a so urce : use a bridge to co nnect to the data so urce. (yo u can learn ho w to co nnect
to databases o n Windo ws machines using the JDBC/ODBC Bridge.)
Inse rt ing inf o rm at io n int o a dat abase : either enter data o utside o f Java, using database-specific to o ls,
o r with SQL statements sent by a Java pro gram.
Se le ct ive ly re t rie ving inf o rm at io n: use SQL co mmands fro m Java to get results and then use Java to
display o r manipulate that data.

In this lesso n, we'll enable the pho nebo o k applicatio n to :

1. create the database table using SQL that was written into the applicatio n initially to po pulate a table.
2. pro vide the user with a graphical user interface that:
has a graphical display.
allo ws users to search the table fo r desired entries.
allo ws users to edit the database table (add and delete).

Improving the Appearance


Graphical user interfaces make an applicatio n mo re visually appealing, but they co me at a pro gramming co st. Yo u
must pro gram every aspect yo u want to allo w the user to experience. But even tho ugh yo u'll write lo ts o f co de, the end
result may no t lo o k particularly impressive to the untrained eye. Such is the thankless life o f the Java pro grammer.
Yo ur reward will be fo und in the satisfactio n o f kno wing yo u've written amazing, clean co de.

Copying an Existing Class


Create a new java4 _Le sso n13 pro ject. If yo u're given the o ptio n to Ope n Asso ciat e d Pe rspe ct ive , click
No . Right-click yo ur new pro ject and select Ne w | Package . Fo r the Name, enter gre e nDB (this co de was
pro vided co urtesy o f David Green).

Co py the Passwo rdDialo g class fro m the previo us Pro ject (java4_Lesso n11) and paste it into the
java4_Lesso n13 pro ject, greenDB package.

Open the new co py o f Passwo rdDialo g in the edito r to verify that it co ntains package gre e nDB.

Save and run it. We aren't running it fro m ano ther applicatio n, we're just checking the Lo gin dialo g itself.

Creating New Classes


The first new class fo r this applicatio n will co nnect it to the database. Altho ugh the JDBC pro vides mo re
advanced features, yo u'll no tice that the basic elements used when wo rking with database are co nsistent.
Mo st JDBC co de that "talks" to a database lo o ks similar.

As yo u type this class, yo u may no tice so me mino r changes. Fo r example, so me variable and metho d
names have been changed.

The inspe ct Fo rT able () metho d has been made mo re general; it passes the name o f the table yo u are
lo o king fo r rather than having it hard-co ded. This is always a better cho ice fo r reusable co de.

In the java4_Lesso n13 pro ject, create a Dat abase Manage r class as sho wn. (Because it is in a different
package, it can have the same name as the class used in previo us lesso ns):
Type Dat abase Manage r as sho wn in blue :
CODE TO TYPE: DatabaseManager
package greenDB;

import java.sql.*;

public class DatabaseManager {


private Connection conn;
private Statement stmt;
private ResultSet rset;

public DatabaseManager (String username, String password) { // the construc


tor 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 variable
conn = DriverManager.getConnection("jdbc:mysql://sql.useractive.com:
3306/" + username, username, password);
stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, Resul
tSet.CONCUR_UPDATABLE); // Create a Statement
// Execute the creation and initialization of table query
DatabaseMetaData aboutDB = conn.getMetaData();
String [] tableType = {"TABLE"};
ResultSet rs = aboutDB.getTables(null, null, "Listings", tableType)
;
if (!inspectForTable (rs, "Listings")) { // Find out if the tabl
e is already there
// there is no table--make it from the initialization listing
String [] SQL = initListingsTable(); // code for this method
is below
for (int i=0; i < SQL.length; i++)
{
stmt.execute(SQL[i]);
}
}
}catch (SQLException e) {
e.printStackTrace();
}
}

private String [] initListingsTable() {


// Executable SQL commands for creating Listings table
// inserting initial 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', '16
95')",
"insert into Listings values ('CABLES', 'WALLY', '212', '434', '96
85')",
"insert into Listings values ('FRY', 'EDGAR', '415', '542', '58
85')",
"insert into Listings values ('MARTIN', 'EDGAR', '665', '662', '90
01')",
"insert into Listings values ('TUCKER', 'JOHN', '707', '696', '85
41')",
};
return SQL;
}

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 ResultSetMetaDat
a to use for the column headings
int numCols = rsmd.getColumnCount (); // Get the number of column
s in the result set

boolean more = rs.next ();


while (more) { // Get each row, fetching u
ntil end of the result set
for (i=1; i<=numCols; i++) {
if (rsmd.getColumnLabel(i) == "TABLE_NAME") // Loop through ea
ch row, getting the column data looking for Tables
if (rs.getString(i).equals(tableName)) // If the column i
s the TABLE_NAME, is it the one we are looking for?
{
System.out.println("Found one that equals " + rs.getStri
ng(i));
return true;
}
}
System.out.println("");
more = rs.next (); // Fetch the next result set row
}
return false;
}

public void doGetQuery(String query) { // rather than the "getEntries" of t


he previous example
try {
rset = stmt.executeQuery(query);
} catch (SQLException e) {
e.printStackTrace();
}
}

public void doInsertQuery(String query) { // rather than the hard-coded "a


ddEntry" of the previous example
try {
stmt.executeUpdate(query);
} catch (SQLException e) {
e.printStackTrace();
}
}

public ResultSet getResultSet() { // a new method that will let the GUI get
the resultSet to manipulate it
return rset;
}

public void close(boolean remove){ // closes all open connections

try {
if (remove)
stmt.execute ("drop table Listings;");

stmt.close();
conn.close();
}
catch ( SQLException e ) {
System.out.println ("\n*** SQLException caught ***\n");
e.printStackTrace();
}
}
}

Save it (there's no thing to run yet).

We need a class to instantiate and start this applicatio n. We'll create the necessary class, but it wo n't be ready
fo r co nsumptio n until we make the GUI. Please be patient--we need all the ingredients befo re we can co o k!

In the java4_Lesso n13 pro ject, create Sim ple Pho ne Bo o k as sho wn:

Type Sim ple Pho ne Bo o k as sho wn in blue :


CODE TO TYPE: SimplePho neBo o k
package greenDB;

import javax.swing.JFrame;

public class SimplePhoneBook {


public static void main(String args[]) { // Instantiate the phone book fra
me window and display it.
PhoneBookFrame pbFrame = new PhoneBookFrame();
pbFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pbFrame.setVisible(true);
}
} // End SimplePhoneBook class

Even tho ugh there are erro rs, save it. The erro rs are there because we haven't defined the GUI and its
co mpo nents yet.

Creating the View


No w we need to create the J Fram e fo r o ur applicatio n.

Click here to see what the Pho neBo o kFrame will lo o k like, so yo u can co mpare the co de and the GUI as yo u write the
co de.

In the java4_Lesso n13 pro ject, create a Pho ne Bo o kFram e class as sho wn:
Type Pho ne Bo o kFram e as sho wn in blue :
CODE TO TYPE: Pho neBo o kFrame
package greenDB;

import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import javax.swing.*;

class PhoneBookFrame extends JFrame {


/** The initial user interface width, in pixels */
private static final int WIDTH = 577;
/** The initial user interface height, in pixels */
private static final int HEIGHT = 466;
/** Provides methods for displaying a SQL result set in a JTable */
// Commented out for now so the program can run without it.
// private ListingsTableModel tblModel;
/** Used to display the SQL result set in a cell format */
private JTable table;
/** A scrollable view for the SQL result set */
private JScrollPane scrollPane;
/** A text field for entering the phone listing's last name */
private JTextField lNameField = new JTextField(10);
/** A text field for entering the phone listing's first name */
private JTextField fNameField = new JTextField(10);
/** A text field for entering the phone listing's area code. The value in parenthes
es
is the number of columns (NOT necessarily characters) to allow for the field. */
private JTextField areaCodeField = new JTextField(2);
/** A text field for entering the phone listing's prefix */
private JTextField prefixField = new JTextField(2);
/** A text field for entering the phone listing's extension */
private JTextField suffixField = new JTextField(3);
/** Database Operations */
private DatabaseManager myDB;

public PhoneBookFrame() {
String [] info = PasswordDialog.login(this); // static login so can call from
class
// create and initialize the listings table
myDB = new DatabaseManager(info[0], info[1]);
// should have access so make GUI
JButton getButton = new JButton("Get"); // get the listing
JButton add = new JButton("+"); // add a listing
JButton rem = new JButton("-"); // remove a listing
JLabel space = new JLabel(" ");
// set the window size and title
setTitle("Simple Phone Book");
setSize(WIDTH, HEIGHT);
// if user presses Enter, get button pressed
getRootPane().setDefaultButton(getButton);
// create the panel for looking up listing
JPanel south = new JPanel();
south.setLayout(new FlowLayout(FlowLayout.LEFT));

south.add(new JLabel("Last:"));
south.add(lNameField);
south.add(new JLabel(" First:"));
south.add(fNameField);
south.add(new JLabel(" Phone: ("));
south.add(areaCodeField);
south.add(new JLabel(") "));
south.add(prefixField);
south.add(new JLabel("-"));
south.add(suffixField);
south.add(new JLabel(" "));
south.add(getButton);
// create the panel for adding and deleting listings
JPanel east = new JPanel();
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
east.setLayout(gb);
add.setFont(new Font("SansSerif", Font.BOLD, 12));
rem.setFont(new Font("SansSerif", Font.BOLD, 12));

gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gb.setConstraints(add, gbc);
gb.setConstraints(space, gbc);
gb.setConstraints(rem, gbc);
east.setLayout(gb);
east.add(add);
east.add(space);
east.add(rem);

// add the panels


Container contentPane = getContentPane();
contentPane.add(south, BorderLayout.SOUTH);
contentPane.add(east, BorderLayout.EAST);
// Add listeners
// When the application closes, drop the Listings table and close the connectio
n to MySQL
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent wEvent) {
myDB.close(false); // We will want to save our additions to the PhoneBook
, so don't drop table
}
});

// when the UI first displays, do an empty lookup so the center panel doesn't l
ook funny
getButton.doClick();
lNameField.requestFocus(); // set focus to last name field (most common lookup
)
}

public DatabaseManager getDBManager() {


return myDB;
}
} // End PhoneBookFrame class

Save and run it (fro m Sim ple Pho ne Bo o k).

Who o ps! We haven't added the Driver to this package yet--do it no w:

Terminate the current running pro cess fro m the co nso le. Right-click the java4 _Le sso n13 Pro ject and select Build
Pat h | Add Ext e rnal Archive s to o pen the file bro wser so yo u can get the driver. cho o se Build Pat h | Add
Ext e rnal Archive s, which o pens the file bro wser fo r yo u to get the driver. Again, in the file dialo g, start to type the path
C:\jdbc\m ysql-co nne ct o r-java-5 .1.5 -bin.jar. The auto -co mplete feature sho uld allo w yo u to press Tab to fill in the
file name m ysql-co nne ct o r-java-5 .1.5 -bin.jar. Then, click Ope n.

Run the Sim ple Pho ne Bo o k class.

We've made a nice little user interface here, but it may no t seem that impressive yet because we have no t added
listeners. So far, the o nly co mpo nent o n the Pho ne Bo o kFram e that listens at all is the Windo w. At least we can clo se
it.

Clo se the Windo w by clicking the X in the upper right co rner o f the applicatio n windo w.

Creating Controllers for the View


We have quite a few co mpo nents here. Let's arrange them so we can see the table List ing we created thro ugh the
Dat abase Manage r.

Edit Pho ne Bo o kFram e as sho wn in blue :


CODE TO EDIT: Pho neBo o kFrame
package greenDB;

import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import javax.swing.*;

class PhoneBookFrame extends JFrame {


/** The initial user interface width, in pixels */
private static final int WIDTH = 577;
/** The initial user interface height, in pixels */
private static final int HEIGHT = 466;
/** Provides methods for displaying a SQL result set in a JTable */
// Commented out for now so the program can run without it.
// private ListingsTableModel tblModel;
/** Used to display the SQL result set in a cell format */
private JTable table;
/** A scrollable view for the SQL result set */
private JScrollPane scrollPane;
/** A text field for entering the phone listing's last name */
private JTextField lNameField = new JTextField(10);
/** A text field for entering the phone listing's first name */
private JTextField fNameField = new JTextField(10);
/** A text field for entering the phone listing's area code */
private JTextField areaCodeField = new JTextField(2);
/** A text field for entering the phone listing's prefix */
private JTextField prefixField = new JTextField(2);
/** A text field for entering the phone listing's extension */
private JTextField suffixField = new JTextField(3);
/** Database Operations */
private DatabaseManager myDB;

public PhoneBookFrame() {
String [] info = PasswordDialog.login(this); // static login so can call from
class
// create and initialize the listings table
myDB = new DatabaseManager(info[0], info[1]);
// Should have access so make GUI
JButton getButton = new JButton("Get"); // get the listing
JButton add = new JButton("+"); // add a listing
JButton rem = new JButton("-"); // remove a listing
JLabel space = new JLabel(" ");
// set the window size and title
setTitle("Simple Phone Book");
setSize(WIDTH, HEIGHT);
// if user presses enter, get button pressed
getRootPane().setDefaultButton(getButton);
// create the panel for looking up listing
JPanel south = new JPanel();
south.setLayout(new FlowLayout(FlowLayout.LEFT));

south.add(new JLabel("Last:"));
south.add(lNameField);
south.add(new JLabel(" First:"));
south.add(fNameField);
south.add(new JLabel(" Phone: ("));
south.add(areaCodeField);
south.add(new JLabel(") "));
south.add(prefixField);
south.add(new JLabel("-"));
south.add(suffixField);
south.add(new JLabel(" "));
south.add(getButton);

// create the panel for adding and deleting listings


JPanel east = new JPanel();
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
east.setLayout(gb);
add.setFont(new Font("SansSerif", Font.BOLD, 12));
rem.setFont(new Font("SansSerif", Font.BOLD, 12));

gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gb.setConstraints(add, gbc);
gb.setConstraints(space, gbc);
gb.setConstraints(rem, gbc);
east.setLayout(gb);
east.add(add);
east.add(space);
east.add(rem);

// add the panels


Container contentPane = getContentPane();
contentPane.add(south, BorderLayout.SOUTH);
contentPane.add(east, BorderLayout.EAST);

// Add listeners

// When the application closes, drop the Listings table and close the connectio
n to MySQL
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent wEvent) {
myDB.close(false);
}
});

getButton.addActionListener(new GetListener()); // Add the listener for the ge


tButton (GetListener inner class defined below)
// when the UI first displays, do an empty lookup so the center panel doesn't l
ook funny
getButton.doClick();
lNameField.requestFocus(); // set focus to last name field (most common look
up)
}

public DatabaseManager getDBManager(){


return myDB;
}

/* inner class GetListener */


class GetListener implements ActionListener { // Gets the entries from the text fi
elds

public void actionPerformed(ActionEvent aEvent) {


// Get whatever the user entered, trim any white space and change to upper
case
String last = lNameField.getText().trim().toUpperCase();
String first = fNameField.getText().trim().toUpperCase();
String ac = areaCodeField.getText().trim().toUpperCase();
String pre = prefixField.getText().trim().toUpperCase();
String sfx = suffixField.getText().trim().toUpperCase();

// Replace any single quote chars w/ space char or SQL will think the ' is
the end of the string
last = last.replace('\'', ' ');
first = first.replace('\'', ' ');
ac = ac.replace('\'', ' ');
pre = pre.replace('\'', ' ');
sfx = sfx.replace('\'', ' ');
// Get rid of the last result displayed if there is one
if(scrollPane != null)
getContentPane().remove(scrollPane);
// Only execute the query if one or more fields have data, else just displa
y an empty table
if(last.length() > 0 ||
first.length() > 0 ||
ac.length() > 0 ||
pre.length() > 0 ||
sfx.length() > 0) {
// build the query and execute it. Provide the results to the table mod
el
myDB.doGetQuery(buildQuery(last, first, ac, pre, sfx));
ResultSet rset = myDB.getResultSet();
tblModel = new ListingsTableModel(rset);
table = new JTable(tblModel);
} else {
table = new JTable();
}
// Allows the user to only delete one record at a time
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Add the table with the results to the contentPane and display it.
scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane, BorderLayout.CENTER);
pack();
doLayout();
}

public String buildQuery(String last, String first, String ac, String pre, Stri
ng sfx) {
String whereClause = " where";
// Build the where clause
if(last.length() > 0)
whereClause += (" LAST_NAME = '" + last + "'");

if(first.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" FIRST_NAME = '" + first + "'");
}

if(ac.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" AREA_CODE = '" + ac + "'");
}

if(pre.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" PREFIX = '" + pre + "'");
}

if(sfx.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" SUFFIX = '" + sfx + "'");
}

return "select LAST_NAME, FIRST_NAME, AREA_CODE, PREFIX, SUFFIX from Listin


gs" + whereClause;
}
} // End GetListener inner class
}

We've go t a few erro rs, all referring to a List ingT able sMo de l. We need to define this List ingT able sMo de l so that
o ur GUI can display o ur entries in a table fo rmat.

In the java4_Lesso n13 pro ject, create a List ingsT able Mo de l class as sho wn:
Type List ingsT able Mo de l as sho wn in blue :
CODE TO TYPE: ListingsTableMo del

package greenDB;

import java.sql.ResultSet;
import java.sql.SQLException;
import javax.swing.table.AbstractTableModel;

class ListingsTableModel extends AbstractTableModel {


/** The result set from the Listings table to be displayed */
private ResultSet rs;

public ListingsTableModel(ResultSet rs) {


this.rs = rs;
}

public int getRowCount() {


try {
rs.last();
return rs.getRow();
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}

public int getColumnCount() {


return 3;
}

public String getColumnName(int column) {


try {
String colName = rs.getMetaData().getColumnName(column + 1);
// Return column names that look better than the database column names.
// Since getColumnCount always returns 3, we only look for first 3 columns
in
// the result set.
if(colName.equals("LAST_NAME"))
return "Last Name";
else if(colName.equals("FIRST_NAME"))
return "First Name";
else if(colName.equals("AREA_CODE"))
return "Phone Number";
else return colName; // Should never get here.

} catch (SQLException e) {
e.printStackTrace();
return "";
}
}

public Object getValueAt(int row, int column) {


try {
rs.absolute(row + 1);
// for the 3rd column in the results, combine all of the phone number field
s for output
if(column == 2)
return "(" + rs.getObject(column + 1) + ") " + rs.getObject(column + 2)
+ "-" + rs.getObject(column + 3);
else
return rs.getObject(column + 1);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
} // End ListingsTableModel class
Go to java.sql.Re sult Se t and read o ver the metho ds invo ked in this class, and all o f the ge t x() metho ds therein:

last ()
ge t Ro w()
ge t Me t aDat a()
abso lut e (int )
ge t Obje ct (int )

Save ListingsTableMo del.

Go back to the Pho ne Bo o kFram e class and its Instance Variables and unco mment the co de that declares the
ListingsTableMo del, as sho wn in blue :
CODE TO EDIT: Pho neBo o kFrame

package greenDB;

import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import javax.swing.*;

public class PhoneBookFrame extends JFrame {


/** The initial user interface width, in pixels */
private static final int WIDTH = 577;
/** The initial user interface height, in pixels */
private static final int HEIGHT = 466;
/** Provides methods for displaying a SQL result set in a JTable */
private ListingsTableModel tblModel;
/** Used to display the SQL result set in a cell format */
private JTable table;
/** A scrollable view for the SQL result set */
private JScrollPane scrollPane;
/** A text field for entering the phone listing's last name */
private JTextField lNameField = new JTextField(10);
/** A text field for entering the phone listing's first name */
private JTextField fNameField = new JTextField(10);
/** A text field for entering the phone listing's area code */
private JTextField areaCodeField = new JTextField(2);
/** A text field for entering the phone listing's prefix */
private JTextField prefixField = new JTextField(2);
/** A text field for entering the phone listing's extension */
private JTextField suffixField = new JTextField(3);
/** Database Operations */
private DatabaseManager myDB;

public PhoneBookFrame() {
String [] info = PasswordDialog.login(this); // static login so can call from
class
// create and initialize the listings table
myDB = new DatabaseManager(info[0], info[1]);
// should have access so make GUI
JButton getButton = new JButton("Get"); // get the listing
JButton add = new JButton("+"); // add a listing
JButton rem = new JButton("-"); // remove a listing
JLabel space = new JLabel(" ");
// set the window size and title
setTitle("Simple Phone Book");
setSize(WIDTH, HEIGHT);
// if user presses Enter, get button pressed
getRootPane().setDefaultButton(getButton);
// create the panel for looking up listing
JPanel south = new JPanel();
south.setLayout(new FlowLayout(FlowLayout.LEFT));

south.add(new JLabel("Last:"));
south.add(lNameField);
south.add(new JLabel(" First:"));
south.add(fNameField);
south.add(new JLabel(" Phone: ("));
south.add(areaCodeField);
south.add(new JLabel(") "));
south.add(prefixField);
south.add(new JLabel("-"));
south.add(suffixField);
south.add(new JLabel(" "));
south.add(getButton);
// create the panel for adding and deleting listings
JPanel east = new JPanel();
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
east.setLayout(gb);
add.setFont(new Font("SansSerif", Font.BOLD, 12));
rem.setFont(new Font("SansSerif", Font.BOLD, 12));

gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gb.setConstraints(add, gbc);
gb.setConstraints(space, gbc);
gb.setConstraints(rem, gbc);
east.setLayout(gb);
east.add(add);
east.add(space);
east.add(rem);

// add the panels


Container contentPane = getContentPane();
contentPane.add(south, BorderLayout.SOUTH);
contentPane.add(east, BorderLayout.EAST);
// Add listeners
// When the application closes, drop the Listings table and close the connectio
n to MySQL
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent wEvent) {
myDB.close(false); // We will want to save our additions to the Ph
oneBook, so don't drop table
}
});

getButton.addActionListener(new GetListener()); // Add the listener for the ge


tButton (GetListener inner class defined below)

// when the UI first displays, do an empty lookup so the center panel doesn't l
ook funny
getButton.doClick();
lNameField.requestFocus(); // set focus to
last name field (most common lookup)
}

public DatabaseManager getDBManager(){


return myDB;
}

/* inner class GetListener */


class GetListener implements ActionListener { // Gets the entries from the text fi
elds

public void actionPerformed(ActionEvent aEvent) {


// Get whatever the user entered, trim any white space and change to upper
case
String last = lNameField.getText().trim().toUpperCase();
String first = fNameField.getText().trim().toUpperCase();
String ac = areaCodeField.getText().trim().toUpperCase();
String pre = prefixField.getText().trim().toUpperCase();
String sfx = suffixField.getText().trim().toUpperCase();

// Replace any single quote chars w/ space char or SQL will think the ' is
the end of the string
last = last.replace('\'', ' ');
first = first.replace('\'', ' ');
ac = ac.replace('\'', ' ');
pre = pre.replace('\'', ' ');
sfx = sfx.replace('\'', ' ');
// Get rid of the last result displayed if there is one
if(scrollPane != null)
getContentPane().remove(scrollPane);
// Only execute the query if one or more fields have data, else just displa
y an empty table
if(last.length() > 0 ||
first.length() > 0 ||
ac.length() > 0 ||
pre.length() > 0 ||
sfx.length() > 0) {
// build the query and execute it. Provide the results to the table mod
el
myDB.doGetQuery(buildQuery(last, first, ac, pre ,sfx));
ResultSet rset = myDB.getResultSet();
tblModel = new ListingsTableModel(rset);
table = new JTable(tblModel);
} else {
table = new JTable();
}
// Allows the user to only delete one record at a time
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Add the table with the results to the contentPane and display it.
scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane, BorderLayout.CENTER);
pack();
doLayout();
}

public String buildQuery(String last, String first, String ac, String pre, Stri
ng sfx) {
String whereClause = " where";
// Build the where clause
if(last.length() > 0)
whereClause += (" LAST_NAME = '" + last + "'");

if(first.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" FIRST_NAME = '" + first + "'");
}

if(ac.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" AREA_CODE = '" + ac + "'");
}

if(pre.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" PREFIX = '" + pre + "'");
}

if(sfx.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" SUFFIX = '" + sfx + "'");
}

return "select LAST_NAME, FIRST_NAME, AREA_CODE, PREFIX, SUFFIX from Listin


gs" + whereClause;
}
} // End GetListener inner class
}

Save Pho ne Bo o kFram e .

Once List ingsT able Mo de l is saved and its variable declared (unco mmented) in Pho ne Bo o kFram e , yo ur
pro grams sho uld be free o f erro rs, at least fo r the mo ment.

Run it (fro m Sim ple Pho ne Bo o k). Type J o hn in the First Name text field.
Click Ge t to display o ur database table entries that have a first name o f J o hn:
While yo u're there, no tice the area co des in the pho ne numbers. The o ther text fields are listening as well. Keep J o hn
in the First Name text field, and type 7 0 7 in the Pho ne ( ) area co de field. Press Ent e r to display o ur database table
entries that have a first name o f J o hn and a pho ne area co de o f 7 0 7 :

Isn't that co o l? We're o n a ro ll! No w let's inco rpo rate the o ther listeners.

In the java4_Lesso n13 pro ject, add a Pho ne Do cum e nt List e ne r class as sho wn:
Type Pho ne Do cum e nt List e ne r as sho wn in blue :
CODE TO TYPE: Pho neDo cumentListener

package greenDB;

import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

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;

public PhoneDocumentListener(JTextField tf, int numsAllowed) {


txtField = tf;
this.numsAllowed = numsAllowed;
}

public void insertUpdate(DocumentEvent dEvent) {


if(dEvent.getDocument().getLength() == numsAllowed)
txtField.transferFocus();
}

/** Empty implementation. Method necessary for implementation of DocumentListener *


/
public void removeUpdate(DocumentEvent dEvent) {}
/** Empty implementation. Method necessary for implementation of DocumentListener *
/
public void changedUpdate(DocumentEvent dEvent) {}
} // End PhoneDocumentListener class

Save it.

In the java4_Lesso n13 pro ject, create Pho ne Fo cusList e ne r as sho wn:
Type Pho ne Fo cusList e ne r as sho wn in blue :

CODE TO TYPE: Pho neFo cusListener


package greenDB;

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JTextField;

class PhoneFocusListener implements FocusListener {

/** an event generated as a result of focus being gained on this telephone number f
ield. */
public void focusGained(FocusEvent fEvent) {
JTextField tf = (JTextField)fEvent.getSource();
tf.setText("");
}

/** Not implemented */


public void focusLost(FocusEvent fEvent){}

} // End PhoneFocusListener class


Save it.

We need to add these listeners to o ur Pho ne Bo o kFram e . While we're there, we'll also add the listeners fo r the add
(+) and remo ve (-) butto ns to the JFrame.

Edit Pho ne Bo o kFram e as sho wn in blue :


CODE TO EDIT: Pho neBo o kFrame

package greenDB;

import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import javax.swing.*;

class PhoneBookFrame extends JFrame {


/** The initial user interface width, in pixels */
private static final int WIDTH = 577;
/** The initial user interface height, in pixels */
private static final int HEIGHT = 466;
/** Provides methods for displaying a SQL result set in a JTable */
private ListingsTableModel tblModel;
/** Used to display the SQL result set in a cell format */
private JTable table;
/** A scrollable view for the SQL result set */
private JScrollPane scrollPane;
/** A text field for entering the phone listing's last name */
private JTextField lNameField = new JTextField(10);
/** A text field for entering the phone listing's first name */
private JTextField fNameField = new JTextField(10);
/** A text field for entering the phone listing's area code */
private JTextField areaCodeField = new JTextField(2);
/** A text field for entering the phone listing's prefix */
private JTextField prefixField = new JTextField(2);
/** A text field for entering the phone listing's extension */
private JTextField suffixField = new JTextField(3);
/** Database Operations */
private DatabaseManager myDB;

public PhoneBookFrame() {
String [] info = PasswordDialog.login(this); // static login so can call from
class
// create and initialize the listings table
myDB = new DatabaseManager(info[0], info[1]);
// Should have access so make GUI

JButton getButton = new JButton("Get"); // get the listing


JButton add = new JButton("+"); // add a listing
JButton rem = new JButton("-"); // remove a listing
JLabel space = new JLabel(" ");
// set the window size and title
setTitle("Simple Phone Book");
setSize(WIDTH, HEIGHT);
// if user presses Enter, get button pressed
getRootPane().setDefaultButton(getButton);
// create the panel for looking up listing
JPanel south = new JPanel();
south.setLayout(new FlowLayout(FlowLayout.LEFT));

south.add(new JLabel("Last:"));
south.add(lNameField);
south.add(new JLabel(" First:"));
south.add(fNameField);
south.add(new JLabel(" Phone: ("));
south.add(areaCodeField);
south.add(new JLabel(") "));
south.add(prefixField);
south.add(new JLabel("-"));
south.add(suffixField);
south.add(new JLabel(" "));
south.add(getButton);

// create the panel for adding and deleting listings


JPanel east = new JPanel();
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
east.setLayout(gb);
add.setFont(new Font("SansSerif", Font.BOLD, 12));
rem.setFont(new Font("SansSerif", Font.BOLD, 12));

gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gb.setConstraints(add, gbc);
gb.setConstraints(space, gbc);
gb.setConstraints(rem, gbc);
east.setLayout(gb);
east.add(add);
east.add(space);
east.add(rem);

// add the panels


Container contentPane = getContentPane();
contentPane.add(south, BorderLayout.SOUTH);
contentPane.add(east, BorderLayout.EAST);

// Add listeners
// When the application closes, drop the Listings table and close the connectio
n to MySQL
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent wEvent) {
myDB.close(false);
}
});

areaCodeField.addFocusListener(new PhoneFocusListener());
areaCodeField.getDocument().addDocumentListener(new PhoneDocumentListener(areaC
odeField, 3));

prefixField.addFocusListener(new PhoneFocusListener());
prefixField.getDocument().addDocumentListener(new PhoneDocumentListener(prefixF
ield, 3));

suffixField.addFocusListener(new PhoneFocusListener());
suffixField.getDocument().addDocumentListener(new PhoneDocumentListener(suffixF
ield, 4));

add.addActionListener(new AddListingListener(this)); // add (+) listener--defi


ne in own class

// remove (-) listener--delete the highlighted listing from the result set and
database
rem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent aEvent) {
try {
int selected = table.getSelectedRow();
ResultSet rset = myDB.getResultSet();
if(selected != -1 && selected < tblModel.getRowCount()) {
rset.absolute(table.getSelectedRow() + 1);
rset.deleteRow();
table.repaint();
table.clearSelection();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
});

getButton.addActionListener(new GetListener()); // Add the listener for the ge


tButton (GetListener inner class defined below)
// when the ui first displays do an empty lookup so the center panel doesn't lo
ok funny
getButton.doClick();
lNameField.requestFocus(); // set focus to last name field (most common look
up)
}

public DatabaseManager getDBManager(){


return myDB;
}
/* inner class GetListener */
class GetListener implements ActionListener { // Gets the entries from the text fi
elds

public void actionPerformed(ActionEvent aEvent) {


// Get whatever the user entered, trim any white space and change to upper
case
String last = lNameField.getText().trim().toUpperCase();
String first = fNameField.getText().trim().toUpperCase();
String ac = areaCodeField.getText().trim().toUpperCase();
String pre = prefixField.getText().trim().toUpperCase();
String sfx = suffixField.getText().trim().toUpperCase();

// Replace any single quote chars w/ space char or SQL will think the ' is
the end of the string
last = last.replace('\'', ' ');
first = first.replace('\'', ' ');
ac = ac.replace('\'', ' ');
pre = pre.replace('\'', ' ');
sfx = sfx.replace('\'', ' ');
// Get rid of the last result displayed if there is one
if(scrollPane != null)
getContentPane().remove(scrollPane);
// Only execute the query if one or more fields have data, else just displa
y an empty table
if(last.length() > 0 ||
first.length() > 0 ||
ac.length() > 0 ||
pre.length() > 0 ||
sfx.length() > 0) {
// build the query and execute it. Provide the results to the table mod
el
myDB.doGetQuery(buildQuery(last, first, ac, pre ,sfx));
ResultSet rset = myDB.getResultSet();
tblModel = new ListingsTableModel(rset);
table = new JTable(tblModel);
} else {
table = new JTable();
}
// Allows the user to only delete one record at a time
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Add the table with the results to the contentPane and display it.
scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane, BorderLayout.CENTER);
pack();
doLayout();
}

public String buildQuery(String last, String first, String ac, String pre, Stri
ng sfx) {
String whereClause = " where";
// Build the where clause
if(last.length() > 0)
whereClause += (" LAST_NAME = '" + last + "'");

if(first.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" FIRST_NAME = '" + first + "'");
}

if(ac.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" AREA_CODE = '" + ac + "'");
}

if(pre.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" PREFIX = '" + pre + "'");
}

if(sfx.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" SUFFIX = '" + sfx + "'");
}

return "select LAST_NAME, FIRST_NAME, AREA_CODE, PREFIX, SUFFIX from Listin


gs" + whereClause;
}
} // End GetListener inner class
}

There's o nly o ne erro r; we might have expected it--we didn't define the AddList ingList e ne r.

Save it. No w, we'll allo w the user to add entries.

In the java4_Lesso n13 pro ject, add the AddList ingList e ne r class as sho wn:
Type AddList ingList e ne r as sho wn in blue :

CODE TO TYPE: AddListingListener


package greenDB;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

class AddListingListener implements ActionListener {


/** The SimplePhoneBook application frame */
PhoneBookFrame pbf;

public AddListingListener(PhoneBookFrame pbFrame) {


pbf = pbFrame;
}

public void actionPerformed(ActionEvent aEvent) {


AddListingDialog addDialog = new AddListingDialog(pbf);
addDialog.setVisible(true);
}
} // End AddListingListener class
Save it. Can yo u tell what o ur next class will be?

In the java4_Lesso n13 pro ject, add an AddList ingDialo g class as sho wn:

Type AddList ingDialo g as sho wn in blue belo w:


CODE TO TYPE: AddListingDialo g
package greenDB;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

class AddListingDialog extends JDialog {


/** A text field for entering the new phone listing's last name */
private JTextField lNameField = new JTextField(16);
/** A text field for entering the new phone listing's first name */
private JTextField fNameField = new JTextField(16);
/** A text field for entering the new phone listing's area code */
private JTextField areaCodeField = new JTextField(2);
/** A text field for entering the new phone listing's prefix */
private JTextField prefixField = new JTextField(2);
/** A text field for entering the new phone listing's extension */
private JTextField suffixField = new JTextField(3);
/** A button which, when clicked, will add the new listing to the Listings table */
private JButton addButton;

public AddListingDialog(final JFrame owner) {


// set the dialog title and size
super(owner, "Add Listing", true);
setSize(280, 150);

// Create the center panel which contains the fields for entering the new listi
ng
JPanel center = new JPanel();
center.setLayout(new GridLayout(3, 2));
center.add(new JLabel(" Last Name:"));
center.add(lNameField);
center.add(new JLabel(" First Name:"));
center.add(fNameField);

// Here we create a panel for the phone number fields and add it to the center
panel.
JPanel pnPanel = new JPanel();
pnPanel.add(new JLabel("("));
pnPanel.add(areaCodeField);
pnPanel.add(new JLabel(") "));
pnPanel.add(prefixField);
pnPanel.add(new JLabel("-"));
pnPanel.add(suffixField);
center.add(new JLabel(" Phone Number:"));
center.add(pnPanel);

// Create the south panel, which contains the buttons


JPanel south = new JPanel();
addButton = new JButton("Add");
JButton cancelButton = new JButton("Cancel");
addButton.setEnabled(false);
south.add(addButton);
south.add(cancelButton);

// Add listeners to the fields and buttons


lNameField.getDocument().addDocumentListener(new InputListener());
fNameField.getDocument().addDocumentListener(new InputListener());
areaCodeField.getDocument().addDocumentListener(new InputListener());
prefixField.getDocument().addDocumentListener(new InputListener());
suffixField.getDocument().addDocumentListener(new InputListener());

areaCodeField.getDocument().addDocumentListener(new PhoneDocumentListener(areaC
odeField, 3));
prefixField.getDocument().addDocumentListener(new PhoneDocumentListener(prefixF
ield, 3));
suffixField.getDocument().addDocumentListener(new PhoneDocumentListener(suffixF
ield, 4));

areaCodeField.addFocusListener(new PhoneFocusListener());
prefixField.addFocusListener(new PhoneFocusListener());
suffixField.addFocusListener(new PhoneFocusListener());

// listeners to close the window


addButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent aEvent) {
// ((PhoneBookFrame)owner).doInsertQuery(buildQuery());
DatabaseManager ownersDB = ((PhoneBookFrame)owner).getDBManager();
ownersDB.doInsertQuery(buildQuery());
dispose();
}
});

cancelButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent aEvent) {
dispose();
}
});

// Add the panels to the dialog window


Container contentPane = getContentPane();
contentPane.add(center, BorderLayout.CENTER);
contentPane.add(south, BorderLayout.SOUTH);
}

public String buildQuery() {


// Get the data entered by the user, trim the white space and change to upper c
ase
String query = "";
String last = lNameField.getText().trim().toUpperCase();
String first = fNameField.getText().trim().toUpperCase();
String ac = areaCodeField.getText().trim().toUpperCase();
String pre = prefixField.getText().trim().toUpperCase();
String sfx = suffixField.getText().trim().toUpperCase();

// Replace any single quote chars with a space char so the string will not get
truncated by SQL
last = last.replace('\'', ' ');
first = first.replace('\'', ' ');
ac = ac.replace('\'', ' ');
pre = pre.replace('\'', ' ');
sfx = sfx.replace('\'', ' ');

// build and return the insert statement


return new String("insert into Listings values ('" + last + "', '" +
first + "', '" +
ac + "', '" +
pre + "', '" +
sfx + "')");
}

/* inner class InputListener */


class InputListener implements DocumentListener {
public void insertUpdate(DocumentEvent dEvent) {
// If first name and last name have data and phone number is complete
// enable the add button, give it focus and make it clickable if
// user presses Enter.
if(lNameField.getDocument().getLength() > 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.
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
Note 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>
* 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.
* </pre>
*/
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")) { // First find out i
f the table is already there
// there is no tabl
e, make it from the initialization listing
String [] SQL = initListingsTable(); // code for this me
thod is below
for (int i=0; i < SQL.length; i++)
{
stmt.execute(SQL[i]);
}
}
}catch (SQLException e) {
e.printStackTrace();
}
}

/* 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>
* PRE: True
* POST: SQL String is created for the initial population of a table named L
istings.
* </pre>
*/
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>
* PRE: Connection to database has been established. rs is not null.
* POST: Table has not been changed, but its presence is verified (or not).
* </pre>
* @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<=numCols; i++) {
if (rsmd.getColumnLabel(i) == "TABLE_NAME")
// Loop through each row, getting the column data looking
for Tables
if (rs.getString(i).equals(tableName))
// If the column is the TABLE_NAME, is the the one we are
looking for?
{
System.out.println("Found one that equals " + rs.getStri
ng(i));
return true;
}
}
System.out.println("");
more = rs.next ();
// Fetch the next result set row
}
return false;
}

/* doGetQuery */
/**
* Executes the select query specified.
* <pre>
* PRE: Connection to database has been established. Query is assigned and
is a simple
* select statement against the Listings table.
* POST: The query is executed.
* </pre>
* @param query a simple select query against the Listings table
*/
public void doGetQuery(String query) {
// rather than the "getEntries" of the previous example
try {
rset = stmt.executeQuery(query);
} catch (SQLException e) {
e.printStackTrace();
}
}

/* doInsertQuery */
/**
* Executes an insert statement, specified by query.
* <pre>
* PRE: Connection to database has been established. Query is assigned and
is a simple
* insert statement into the Listings table.
* POST: The query is executed.
* </pre>
* @param query a simple insert query into the Listings table
*/
public void doInsertQuery(String query) { // rather than the hard-coded "a
ddEntry" of the previous example
try {
stmt.executeUpdate(query);
} catch (SQLException e) {
e.printStackTrace();
}
}
/* getResultSet */
/**
* Returns the current value of the ResultSet instance
* <pre>
* PRE: True
* POST: ResultSet instance value is returned, its value remains the same as
upon entry.
* </pre>
*/
public ResultSet getResultSet() { // a new method that will let the GUI get
the resultSet to manipulate it
return rset;
}

/* close */
/**
* Closes opened Statements and the Connection.
* <pre>
* PRE: Connection to database has been established. Statement has been cre
ated. Listings is a table in the db
* POST: If remove is true, table Listings is dropped, otherwise it is prese
rved. Open Connection and Statement are closed
* </pre>
* @param remove boolean to specify if the table Listings should be dropped
or not.
*/
public void close(boolean remove){
// closes all open connections

try {
if (remove)
stmt.execute ("drop table Listings;");

stmt.close();
conn.close();
}
catch ( SQLException e ) {
System.out.println ("\n*** SQLException caught ***\n");
e.printStackTrace();
}
}
}

Save it.

Creating Javadocs
In the to p Eclipse Menu Bar, select Pro je ct | Ge ne rat e J avado c...:
In the Generate Javado c windo w, use the Co nf igure ... butto n to get to the C:\jdk\bin\ directo ry. Cho o se
javado c.e xe to retrieve the javado c executable co de. The default sho uld remain Privat e ; this allo ws us to
view all members (private, package, pro tected and public). Keep the Destinatio n as it is, so that the resulting
Javado c will be lo cated with the Pro ject. Click Finish:

In the Update Javado c Lo catio n dialo g bo x that appears, yo u're given the o ptio n to cho o se Javado c lo catio n
as the destinatio n fo lder. Click Ye s:

The co nso le displays the actio ns taking place and then displays a new do c fo lder in the java4_Lesso n13
Pro ject:
Open the do c fo lder and its gre e nDB subfo lder, then do uble-click o n Dat abase Manage r.ht m l. Scro ll
thro ugh this API page o f the applicatio n and o bserve the way the co mments were printed. There is mo re
do cumentatio n within the Co nst ruct o r Sum m ary and Me t ho d Sum m ary, because we used mo re
co mments in tho se areas than in o thers:

When the Javado c e xe cut able runs, it lo o ks fo r the special co mments that begin with /** and end with */.
When they're written fo r class members (instance and class metho ds and variables), these co mments sho uld
be entered just before the field's definitio n o r declaratio n in yo ur co de.

Documenting and T agging the Application


Java pro vides so me tags by default, but yo u can create yo ur o wn as well. In this sectio n, we go o ver each class with
additio nal co mments. The co de is the same; the o nly differences are the added co mments.

Note "@" is used to deno te tags that will be created within the API pages.

So me metho ds include the wo rds PRE and POST . These stand fo r preco nditio ns and po stco nditio ns. Such
specificatio ns fo r metho ds are useful fo r the design, implementatio n, and use o f co de:

A PREco nditio n is an assertio n that must be true in o rder fo r the o peratio n to run pro perly. A preco nditio ns o f true (o r
empty) means that there are no stipulatio ns o n running the particular metho d.

The implementer assumes it to be true and co des acco rdingly.


The user must be sure in his co de that the PREco nditio n is met befo re invo king the metho d.

A POST co nditio n is an assertio n that prevails upo n co mpletio n o f the o peratio n. Po stco nditio ns describe the results
o f the actio ns perfo rmed by the o peratio n. They must be accurately and precisely stated.

The implementer must make the POST co nditio n is true and co des acco rdingly.
The user assumes it will be exactly and co mpletely true after the co de runs.

We will edit and save each class in o ur database applicatio n, and then run the Javado c executable o n the entire
package when the classes all have Javado c co mments.

SimplePhoneBook
In java4_Lesso n13, edit Sim ple Pho ne Bo o k as sho wn in blue belo w:

CODE TO EDIT: SimplePho neBo o k


package greenDB;

import javax.swing.JFrame;

/* class SimplePhoneBook */
/**
* This is the entry point of the SimplePhoneBook application. SimplePhoneBook i
s a Java application
* that uses JDBC to store, retrieve, add, and delete phone number listings in a
MySQL
* database. The SimplePhoneBook class instantiates the application window frame
and displays it
* on screen.
*
* @author David Green
* @version 1.0
*/
public class SimplePhoneBook {
/* main */
/**
* The entry point for the SimplePhoneBook application. The main method inst
antiates the
* application's frame window and displays it.
* <pre>
* PRE:
* POST: SimplePhoneBook started.
* </pre>
* @param args not used.
*/
public static void main(String args[]) {
// Instantiate the phone book frame window and display it.

PhoneBookFrame pbFrame = new PhoneBookFrame();


pbFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pbFrame.setVisible(true);
}
}// End SimplePhoneBook class

Save it.

PhoneBookFrame
In java4_Lesso n13, edit Pho ne Bo o kFram e as sho wn in blue belo w:
CODE TO EDIT: Pho neBo o kFrame
package greenDB;

import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import javax.swing.*;

//* class PhoneBookFrame */


/*
* This class represents the SimplePhoneBook user interface. PhoneBookFrame incl
udes the application
* window as well as the components for retrieving, adding, displaying, and dele
ting phone number listings
* for the user.
*
* @author David Green
* @version 1.0
*/
class PhoneBookFrame extends JFrame {
/** The initial user interface width, in pixels */
private static final int WIDTH = 577;
/** The initial user interface height, in pixels */
private static final int HEIGHT = 466;
/** Provides methods for displaying a SQL result set in a JTable */
private ListingsTableModel tblModel;
/** Used to display the SQL result set in a cell format */
private JTable table;
/** A scrollable view for the SQL result set */
private JScrollPane scrollPane;
/** A text field for entering the phone listing's last name */
private JTextField lNameField = new JTextField(10);
/** A text field for entering the phone listing's first name */
private JTextField fNameField = new JTextField(10);
/** A text field for entering the phone listing's area code */
private JTextField areaCodeField = new JTextField(2);
/** A text field for entering the phone listing's prefix */
private JTextField prefixField = new JTextField(2);
/** A text field for entering the phone listing's extension */
private JTextField suffixField = new JTextField(3);
/** Database Operations */
private DatabaseManager myDB;

/* PhoneBookFrame */
/**
* The PhoneBookFrame constructor.
*/
public PhoneBookFrame() {
String [] info = PasswordDialog.login(this); // static login so can cal
l from class
// create and initialize the listings table
myDB = new DatabaseManager(info[0], info[1]);
// Should have access so make GUI

JButton getButton = new JButton("Get"); // get the listing


JButton add = new JButton("+"); // add a listing
JButton rem = new JButton("-"); // remove a listing
JLabel space = new JLabel(" ");

// set the window size and title


setTitle("Simple Phone Book");
setSize(WIDTH, HEIGHT);

// if user presses Enter, get button pressed


getRootPane().setDefaultButton(getButton);

// create the panel for looking up listing


JPanel south = new JPanel();
south.setLayout(new FlowLayout(FlowLayout.LEFT));

south.add(new JLabel("Last:"));
south.add(lNameField);
south.add(new JLabel(" First:"));
south.add(fNameField);
south.add(new JLabel(" Phone: ("));
south.add(areaCodeField);
south.add(new JLabel(") "));
south.add(prefixField);
south.add(new JLabel("-"));
south.add(suffixField);
south.add(new JLabel(" "));
south.add(getButton);

// create the panel for adding and deleting listings


JPanel east = new JPanel();
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
east.setLayout(gb);
add.setFont(new Font("SansSerif", Font.BOLD, 12));
rem.setFont(new Font("SansSerif", Font.BOLD, 12));

gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gb.setConstraints(add, gbc);
gb.setConstraints(space, gbc);
gb.setConstraints(rem, gbc);
east.setLayout(gb);
east.add(add);
east.add(space);
east.add(rem);

// add the panels


Container contentPane = getContentPane();
contentPane.add(south, BorderLayout.SOUTH);
contentPane.add(east, BorderLayout.EAST);

// Add listeners
getButton.addActionListener(new GetListener());

areaCodeField.addFocusListener(new PhoneFocusListener());
areaCodeField.getDocument().addDocumentListener(new PhoneDocumentListene
r(areaCodeField, 3));

prefixField.addFocusListener(new PhoneFocusListener());
prefixField.getDocument().addDocumentListener(new PhoneDocumentListener(
prefixField, 3));

suffixField.addFocusListener(new PhoneFocusListener());
suffixField.getDocument().addDocumentListener(new PhoneDocumentListener(
suffixField, 4));

add.addActionListener(new AddListingListener(this));

// delete the highlighted listing from the result set and database
rem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent aEvent) {
try {
int selected = table.getSelectedRow();
ResultSet rset = myDB.getResultSet();
if(selected != -1 && selected < tblModel.getRowCount())
{
rset.absolute(table.getSelectedRow() + 1);
rset.deleteRow();
table.repaint();
table.clearSelection();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
});

// When the application closes, drop the Listings table and close the co
nnection to MySQL
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent wEvent) {
myDB.close(false);
}
});

// when the ui first displays do an empty lookup so the center panel doe
sn't look funny
getButton.doClick();
lNameField.requestFocus(); // set focus to last name field (most comm
on lookup)
}

public DatabaseManager getDBManager(){


return myDB;
}

/* inner class GetListener */


/**
* An inner class for handling the event when the user clicks the "Get" butt
on.
*
* @author David Green
* @version 1.0
*/
class GetListener implements ActionListener {

/* actionPerformed */
/**
* This method builds a select Query and executes it against the Listings ta
ble to retrieve
* records. This method creates a select string based on what the user has e
ntered in the
* fields for Last Name, First Name, Area Code, Prefix, and Extension. The u
ser may look up a
* record in the Listings table based on any combination of data entered in
the text fields.
* The actionPerformed method builds the query string based on the user's in
put, executes the
* query, and displays it in a scrollable cell format. All data entered in t
he text fields
* is converted to upper case and any single quote character is replaced wit
h a space
* character before the query is executed.
* <pre>
* PRE: A connection to the database has been established. All text fields
can be empty.
* POST: A select string is created based on what was entered, the query is
executed and the
* results are displayed.
* </pre>
* @param aEvent an event generated as a result of the "Get" button being cl
icked
*/
public void actionPerformed(ActionEvent aEvent) {
// Get whatever the user entered, trim any white space and change to
upper case
String last = lNameField.getText().trim().toUpperCase();
String first = fNameField.getText().trim().toUpperCase();
String ac = areaCodeField.getText().trim().toUpperCase();
String pre = prefixField.getText().trim().toUpperCase();
String sfx = suffixField.getText().trim().toUpperCase();

// Replace any single quote chars w/ space char or SQL will think th
e ' is the end of the string
last = last.replace('\'', ' ');
first = first.replace('\'', ' ');
ac = ac.replace('\'', ' ');
pre = pre.replace('\'', ' ');
sfx = sfx.replace('\'', ' ');

// Get rid of the last result displayed if there is one


if(scrollPane != null)
getContentPane().remove(scrollPane);

// Only execute the query if one or more fields have data, else just
display an empty table
if(last.length() > 0 ||
first.length() > 0 ||
ac.length() > 0 ||
pre.length() > 0 ||
sfx.length() > 0) {

// build the query and execute it. Provide the results to the ta
ble model
myDB.doGetQuery(buildQuery(last, first, ac, pre ,sfx));
ResultSet rset = myDB.getResultSet();
tblModel = new ListingsTableModel(rset);
table = new JTable(tblModel);

} else {
table = new JTable();
}

// Allows the user to only delete on record at a time


table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

// Add the table with the results to the contentPane and display it.
scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane, BorderLayout.CENTER);
pack();
doLayout();
}

/* buildQuery */
/**
* This method builds a simple select statement for retrieving records from
the Listings table.
* The select statement is returned as a string. The select statement includ
es the last, first, ac,
* pre, and sfx parameters as the search strings in the where clause of the
select statement.
* If more than one parameter has data, buildQuery will combine them with an
"AND" in the where
* clause.
* <pre>
* PRE: One or more parameters has length > 0.
* POST: A SQL select statement is returned that selects records from the Li
stings table.
* </pre>
* @param last create a SQL query that searches Listings where LAST_NAME =
last.
* @param first create a SQL query that searches Listings where FIRST_NAME =
first.
* @param ac create a SQL query that searches Listings where AREA_CODE =
ac.
* @param pre create a SQL query that searches Listings where PREFIX = pre
.
* @param sfx create a SQL query that searches Listings where SUFFIX = sfx
.
* @return a SQL select statement that selects records from the Listings tab
le
*/
public String buildQuery(String last, String first, String ac, String pr
e, String sfx) {
String whereClause = " where";
// Build the where clause
if(last.length() > 0)
whereClause += (" LAST_NAME = '" + last + "'");

if(first.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" FIRST_NAME = '" + first + "'");
}

if(ac.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" AREA_CODE = '" + ac + "'");
}

if(pre.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" PREFIX = '" + pre + "'");
}

if(sfx.length() > 0) {
if(whereClause.length() > 6)
whereClause += " AND";
whereClause += (" SUFFIX = '" + sfx + "'");
}

return "select LAST_NAME, FIRST_NAME, AREA_CODE, PREFIX, SUFFIX from


Listings" + whereClause;
}
}// End GetListener inner class
}// End PhoneBookFrame class

Save it.

Note We have an Inner class defined in this class. Remember to check it o ut in the Javado c page!

ListingsT ableModel
In java4_Lesso n13, edit List ingsT able Mo de l as sho wn in blue :
CODE TO EDIT: ListingsTableMo del
package greenDB;

import java.sql.ResultSet;
import java.sql.SQLException;

import javax.swing.table.AbstractTableModel;

/* class ListingsTableModel */
/**
* This class provides methods for displaying the results returned from the List
ings
* table. The methods are used by a JTable object so the results may displayed i
n a cell format.
*
* @author David Green
* @version 1.0
*/
public class ListingsTableModel extends AbstractTableModel {
/** The result set from the Listings table to be displayed */
private ResultSet rs;

/* ListingsTableModel */
/**
* The ListingsTableModel constructor.
* @param rs the result set from the Listings table to be displayed.
*/
public ListingsTableModel(ResultSet rs) {
this.rs = rs;
}

/* getRowCount */
/**
* Returns the number of rows in the result set.
* <pre>
* PRE: True
* POST: The number of rows in the result set is returned.
* </pre>
* @return the number of rows in the result set.
*/
public int getRowCount() {
try {
rs.last();
return rs.getRow();
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}

/* getColumnCount */
/**
* Returns the number of columns to be displayed for the result set. Note th
at
* this does not return the number of columns IN the result set. The three p
hone
* number fields (area code, prefix, and extension) are combined together to
form
* a single column for output. This method always returns 3 for Last Name, F
irst
* Name, and Phone Number.
* <pre>
* PRE: True
* POST: The number 3 is returned.
* </pre>
* @return the number 3, for the three output columns Last Name, First Name,
and Phone Number.
*/
public int getColumnCount() {
return 3;
}

/* getColumnName */
/**
* Returns the name of the column specified by the index.
* <pre>
* PRE: Column is assigned and 0 >= column <= 2.
* POST: A column name is returned.
* </pre>
* @param column the index of the column name to be returned.
* @return the column name specified.
*/
public String getColumnName(int column) {
try {
String colName = rs.getMetaData().getColumnName(column + 1);
// Return column names that look better than the database column nam
es.
// Since getColumnCount always returns 3 we only look for first 3 co
lumns in
// the result set.
if(colName.equals("LAST_NAME"))
return "Last Name";
else if(colName.equals("FIRST_NAME"))
return "First Name";
else if(colName.equals("AREA_CODE"))
return "Phone Number";
else return colName; // Should never get here.

} catch (SQLException e) {
e.printStackTrace();
return "";
}
}

/* getValueAt */
/**
* Returns the value in the result set at the location specified by row and
column. If column
* is equal to 2 (the AREA_CODE), combine the AREA_CODE, PREFIX, and SUFFIX
for that row and
* return the combined string.
* <pre>
* PRE: row and column are assigned and 0 >= column <= 2 and row is within
range.
* POST: The value in the result set at row and column is returned, or the c
ombined
* phone number is returned if column = 2.
* </pre>
* @param row the row of the result set whose value is to be returned.
* @param column the column of the result set whose value is to be returned.
* @return the value in the result set at row and column is returned, or the
combined
* phone number is returned if column = 2.
*/
public Object getValueAt(int row, int column) {
try {
rs.absolute(row + 1);
// for the 3rd column in the results, combine all of the phone numbe
r fields for output
if(column == 2)
return "(" + rs.getObject(column + 1) + ") " + rs.getObject(colu
mn + 2) + "-" + rs.getObject(column + 3);
else
return rs.getObject(column + 1);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}

}// End ListingsTableModel class

Save it.

phew! This is a lo t o f wo rk. It's wo rth do ing tho ugh to make sure that everyo ne who enco unters o ur co de
understands it, appreciates its elegance and efficiency, and can use and mo dify it as needed. That's the
reaso n we do cument. OK, no w get back to wo rk!

AddListingListener
In java4_Lesso n13, edit AddList ingList e ne r as sho wn in blue :

CODE TO EDIT: AddListingListener


package greenDB;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/* class AddListingListener */
/**
* A listener for when the add button is clicked. The add button looks like a pl
us ("+") sign. The
* AddListingListener creates and displays an AddListingDialog box when actionPe
rformed is called.
*
* @author David Green
* @version 1.0
*/
class AddListingListener implements ActionListener {
/** The SimplePhoneBook application frame */
PhoneBookFrame pbf;

/* AddListingListener */
/**
* The AddListingListener constructor.
* @param pbFrame the SimplePhoneBook application frame object.
*/
public AddListingListener(PhoneBookFrame pbFrame) {
pbf = pbFrame;
}

/* actionPerformed */
/**
* Instantiates and displays the Add Listings Dialog Box. This method is
* called when the "+" button is clicked.
* <pre>
* PRE:
* POST: The Add Listings Dialog Box is displayed on screen.
* </pre>
* @param aEvent an event generated as a result of the "+" button being clic
ked.
*/
public void actionPerformed(ActionEvent aEvent) {
AddListingDialog addDialog = new AddListingDialog(pbf);
addDialog.setVisible(true);
}
}// End AddListingListener class

Save it.
AddListingDialog
In java4_Lesso n13, edit AddList ingDialo g as sho wn in blue belo w:
CODE TO EDIT: AddListingDialo g

package greenDB;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/* class AddListingDialog */
/**
* A dialog box for adding a new listing to the Listings table. The AddListingDi
alog has text
* fields for gathering the new listing's last name, first name, and phone numbe
r. All of the text
* fields are assigned an InputListener, which is responsible for enabling the "
Add" button once
* all fields contain data. This dialog box is displayed when the user clicks th
e "+" button on
* the application frame window.
*
* @author David Green
* @version 1.0
*/
public class AddListingDialog extends JDialog {
/** A text field for entering the new phone listing's last name */
private JTextField lNameField = new JTextField(16);
/** A text field for entering the new phone listing's first name */
private JTextField fNameField = new JTextField(16);
/** A text field for entering the new phone listing's area code */
private JTextField areaCodeField = new JTextField(2);
/** A text field for entering the new phone listing's prefix */
private JTextField prefixField = new JTextField(2);
/** A text field for entering the new phone listing's extension */
private JTextField suffixField = new JTextField(3);
/** A button which, when clicked, will add the new listing to the Listings t
able */
private JButton addButton;

/* AddListingDialog */
/**
* The AddListingDialog constructor. Creates a dialog box for adding a new li
sting to the
* Listings table.
* @param owner the Frame from which the dialog is displayed.
*/
public AddListingDialog(final JFrame owner) {
// set the dialog title and size
super(owner, "Add Listing", true);
setSize(280, 150);

// Create the center panel which contains the fields for entering the ne
w listing
JPanel center = new JPanel();
center.setLayout(new GridLayout(3, 2));
center.add(new JLabel(" Last Name:"));
center.add(lNameField);
center.add(new JLabel(" First Name:"));
center.add(fNameField);

// Here we create a panel for the phone number fields and add it to the
center panel.
JPanel pnPanel = new JPanel();
pnPanel.add(new JLabel("("));
pnPanel.add(areaCodeField);
pnPanel.add(new JLabel(") "));
pnPanel.add(prefixField);
pnPanel.add(new JLabel("-"));
pnPanel.add(suffixField);
center.add(new JLabel(" Phone Number:"));
center.add(pnPanel);

// Create the south panel which contains the buttons


JPanel south = new JPanel();
addButton = new JButton("Add");
JButton cancelButton = new JButton("Cancel");
addButton.setEnabled(false);
south.add(addButton);
south.add(cancelButton);

// Add listeners to the fields and buttons


lNameField.getDocument().addDocumentListener(new InputListener());
fNameField.getDocument().addDocumentListener(new InputListener());
areaCodeField.getDocument().addDocumentListener(new InputListener());
prefixField.getDocument().addDocumentListener(new InputListener());
suffixField.getDocument().addDocumentListener(new InputListener());

areaCodeField.getDocument().addDocumentListener(new PhoneDocumentListene
r(areaCodeField, 3));
prefixField.getDocument().addDocumentListener(new PhoneDocumentListener(
prefixField, 3));
suffixField.getDocument().addDocumentListener(new PhoneDocumentListener(
suffixField, 4));

areaCodeField.addFocusListener(new PhoneFocusListener());
prefixField.addFocusListener(new PhoneFocusListener());
suffixField.addFocusListener(new PhoneFocusListener());

// listeners to close the window


addButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent aEvent) {
// ((PhoneBookFrame)owner).doInsertQuery(buildQuery());
DatabaseManager ownersDB = ((PhoneBookFrame)owner).getDBMana
ger();
ownersDB.doInsertQuery(buildQuery());
dispose();
}
});

cancelButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent aEvent) {
dispose();
}
});

// Add the panels to the dialog window


Container contentPane = getContentPane();
contentPane.add(center, BorderLayout.CENTER);
contentPane.add(south, BorderLayout.SOUTH);
}

/* buildQuery */
/**
* This method builds an insert statement for inserting a new record into th
e Listings table.
* The insert statement is returned as a string. The insert statement will i
nclude the last name,
* first name, area code, prefix, and extension that the user entered in the
add listing dialog
* box.
* <pre>
* PRE: All of the fields in the Add Listing Dialog box contain data.
* POST: A SQL insert statement is returned that inserts a new listing into
the Listings table.
* </pre>
* @return a SQL insert statement that will insert a new listing in the List
ings table.
*/
public String buildQuery() {
// Get the data entered by the user, trim the white space and change to
upper case
String query = "";
String last = lNameField.getText().trim().toUpperCase();
String first = fNameField.getText().trim().toUpperCase();
String ac = areaCodeField.getText().trim().toUpperCase();
String pre = prefixField.getText().trim().toUpperCase();
String sfx = suffixField.getText().trim().toUpperCase();

// Replace any single quote chars with a space char so the string will n
ot get truncated by SQL
last = last.replace('\'', ' ');
first = first.replace('\'', ' ');
ac = ac.replace('\'', ' ');
pre = pre.replace('\'', ' ');
sfx = sfx.replace('\'', ' ');

// build and return the insert statement


return new String("insert into Listings values ('" + last + "', '" +
first + "', '" +
ac + "', '" +
pre + "', '" +
sfx + "')");
}

/* inner class InputListener */


/**
* This inner class is a Listener for the text fields of the Add Listing Dia
log Box.
* The listener keeps track of whether all fields (last name, first name, ar
ea code,
* prefix, and extension) have data entered in them. If all fields contain d
ata, the
* "Add" button of the Add Listing Dialog box is enabled for the user. If an
y one of
* the fields is empty or if the phone number fields contain fewer character
s than
* required, the "Add" button is unavailable.
*
* @author David Green
* @version 1.0
*/
class InputListener implements DocumentListener {

/* insertUpdate */
/**
* This method is called when data is put in the text field, either by typin
g or by a paste operation.
* This method tracks the number of characters entered in the field. If all
fields, last name,
* first name, area code, prefix, and extension have data and the phone numb
er fields contain the correct
* number of characters (that is, 3 characters for area code and prefix and
4 characters for extension),
* then the Add Listing Dialog box "Add" button is enabled.
* <pre>
* PRE:
* POST: Add Listing Dialog Box "Add" button is enabled if all fields have t
he required number of characters
* entered.
* </pre>
* @param dEvent the document event
*/

public void insertUpdate(DocumentEvent dEvent) {


// If first name, last name have data and phone number is complete
// enable the add button, give it focus and make it clickable if
// user hits <enter>.
if(lNameField.getDocument().getLength() > 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>
* PRE:
* POST: Add Listing Dialog Box "Add" button is disabled if any of the field
s contain less than the required
* number of characters.
* </pre>
* @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>
* PRE:
* POST: Focus is transferred if field length equals numsAllowed; else nothi
ng happens.
* </pre>
* @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>
* PRE: True.
* POST: Any data in the telephone number field to which this listener appli
es is deleted.
* </pre>
* @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.

You might also like