All

Published on June 2016 | Categories: Documents | Downloads: 61 | Comments: 0 | Views: 853
of x
Download PDF   Embed   Report

Comments

Content






COMP1405/1005
Introduction to Object-Oriented Programming


Course Notes


Notes maintained by Mark Lanthier (2009 version)

COMP1005/1405 - Introduction to Object-Oriented Programming Fall 2009

2

Table of Contents
1 Programming Basics …………………………………………………………….................
1.0 Programming-Related Courses In Computer Science ……………..……….............
1.1 Understanding Programming ………………………………………………….............
1.2 The J AVA Programming Language …………………………………...…………....…
1.3 Writing Your First J AVA Program …………………………………………………......
1.4 Displaying Information ………………………………………………….......................
1.5 Getting User Input …………………………………………………..............................
1.6 Types of Data: Primitives vs. Objects …………...………………..............................
1.7 Making Your Own Objects …………...………………...............................................
4
5
6
10
12
16
21
25
30
2 Variables and Objects …….......……………………………………………………….…….
2.1 Variables ……………………...…………………….…………………………………….
2.2 Instance Variables / Object Attributes ………………………………………………....
2.3 Initializing Our Objects By Using Constructors …………………….……………..….
2.4 Shared Data: Static/Class Variables ….……………………………………………….
34
35
42
57
66
3 Decision Making …………………..……………………………………………….…………
3.1 Using the IF Statement ………………………...………………………………..………
3.2 The Switch Statement ……..…………………………………………………………….
3.3 A Decision Making Example Program …………………………………………………
73
74
84
87
4 Defining Your Own Functions / Methods …………………………………..…………….
4.1 What Are Methods ? .……………………………………………………………………
4.2 Defining Methods – Functions .………………………………………………………...
4.3 Defining Methods – Procedures ……..………………………………………………...
4.4 Null Pointer Exceptions ..………………………………………………………………..
4.5 Static / Class Methods ………………..…...…………………………………………….
93
94
95
104
109
114
5 Calculations, Formatting and Conversions …..………………………………………….
5.1 Calculations and Formulas ……………………………………………………………..
5.2 Formatting Your Output …………………………………………………………………
5.3 Type Conversion ………………………………………………………………………...
119
120
126
131
6 Loops and ArrayLists ……………………………….……………………………………….
6.1 Repeating Code Using For and While Loops …..…………………………………….
6.2 Collecting Objects Together Using ArrayLists …...…………………………………...
6.3 Team/League Example ………………………………………………………………….
6.4 Car/Autoshow Example ………………………………………………………………....
136
137
142
145
160
7 Organizing Classes To Use Inheritance …...…………………………..…………………
7.1 Class Hierarchy ..…………………………………………………………………………
7.2 Inheriting Attributes ……………………………………………………………………...
7.3 Inheriting Behavior .……………………………………………………………………...
7.4 University Database Example ………………………………………………………….
176
177
183
189
199
COMP1005/1405 - Introduction to Object-Oriented Programming Fall 2009

3
7.5 Abstract Classes & Methods ……………………………………………………………
7.6 Defining Interfaces ………….……………………………………………………………
206
214
8 Exception Handling ………………………..…………………………………………………
8.1 Simple Debugging ……………………………………………………………………….
8.2 Exceptions ………………………………………………………………………………..
8.3 Examples of Handling Exceptions …..…………………………………………………
8.4 Creating and Throwing Your Own Exceptions ..……………………………………...
219
220
222
232
239
9 Proper Coding Style ..………………………………………………………………………..
9.1 Protecting and Simplifying an Object ………………………………………………….
9.2 Simplifying Constructors and Eliminating “This” ….…………………………………..
9.3 Type-Casting, Polymorphism and Double-Dispatching ………………….…………..
9.4 Proper Testing ………………………………….………………………………………..
248
249
260
264
278
10 Code Efficiency ……………………………………………………………………………...
10.1 Exiting Loops Efficiently …………………...…………………………………………..
10.2 Proper Use of Booleans ……………………………………………………………….
10.3 Enumerated Types ……………………………………………………………………..
10.4 Equality Vs. Identity …………………….………………………………………………
10.5 Arrays ……………………………………………………………………………………
282
283
286
289
292
303
11 Saving and Loading Information ………………………………………………………...
11.1 Introduction to Files and Streams ….…………………………….…………………..
11.2 Reading and Writing Binary Data …………………………………………………….
11.3 Reading and Writing Text Data ……………………...……………..…………………
11.4 Reading and Writing Whole Objects ..…………………..……………………………
11.5 Saving and Loading the Autoshow ....…………………..……………………………
11.6 The File Class ………………………………………………………………......………
318
319
321
327
330
334
342
12 Some Useful Tools …………….……………………………………………………………
12.1 The String Class ………………………………………………………………………..
12.2 The StringBuilder & Character Classes ………….…………………………………..
12.3 The Date and Calendar Classes ……………………………………………………...
12.4 Iterators ………………………………………………...………………………………..
346
347
353
357
362
13 Other Collections ……………………………………………………………………………
13.1 Collection Organization ………………………………………………………………..
13.2 Example: Bracket Matching …………………………………………………………...
13.3 Example: Maze Search ……………………………………...………………………...
13.4 Sorting Objects …………………………………………………………………………
13.5 Removing Duplicates …..………………………………………………………………
368
369
373
378
384
391


Chapter 1
Programming Basics


What is in This Chapter ?
This first chapter explains very briefly the difference between Traditional vs. Object-Oriented
programming and how the JAVA programming language basically works. It then discusses
some of the basic concepts behind Object-Oriented programming and how to write a simple
"Hello World" program in J AVA. Also, since all programs should have some kind of input and
output, we look here at how to write programs that display information and get information
from the user. Lastly, since J AVA is NOT purely Object-Oriented (i.e., it contains basic data
types called primitives which form the basis for all data stored in objects), we will also look at
these primitives as basic building blocks in J AVA and how they are used to form Objects.




COMP1005/1405 – Programming Basics Fall 2009

- 5 -

1.0 Programming-Related Courses In Computer Science

This is your first programming course here in the School of Computer Science at Carleton.
You have some more core programming courses coming up after this one. Here is a break
down of how this course fits in with your first 2 years of programming courses:


1
st
Year You are
Here.
COMP 1405/1005

(Java)



COMP 1406/1006
(Java)


COMP 1402/1002
(C)




2
nd
Year

COMP 2003

(TASM)


COMP 2404/2004
(C++)



COMP 2405/2005

(Perl, XML, HTML, JavaScript)



Of course, there are other computer science courses as well. These are just the core courses
that nearly everyone is required to take. After this course is over, you should understand how
to write computer programs. You will also understand what it means to do object-oriented
programming. In the winter term, you will take COMP1406/1006 which is like a continuation
of this course. Together, these two courses give you a solid programming background in
J AVA and you will be able to learn other computer languages easily afterwards … since they
all have common features. If you want to do well in this course, attend all lectures and
tutorials and do your assignments.

COMP1005/1405 – Programming Basics Fall 2009

- 6 -
1.1 Understanding Programming

What are Computers Used For ?
• Communications: Internet, e-mail, cell phones
• Word Processing: typing/printing documents
• Business Applications: accounting, spreadsheets
• Engineering Applications: scientific analysis, simulations
• Database Management: police records, stock market
• Entertainment: games, multimedia applications
• Manufacturing: CAD/CAM, robotics, assembly
• ... many more ...
Who is Involved With Computers ?
• System/Hardware Designers =people that design computers and related products.
• Manufacturers =people that actually build and assemble computers.
• Software Designers =people that design applications to be used with the computers.
• Programmers =people that write computer programs to achieve working applications,
games and other software packages.
• End User =people that buy and use the software when it is done.
We are going to play the role of the Programmer in this course. In a way, we will also be
playing the role of the End User when we test our programs. Testing is an important part of
programming to ensure that the user is happy with software that is not full of bugs (i.e.,
problems)!
What is a program ?
A program is traditionally known as:
a sequence of instructions that can be
executed by a computer to solve some
problem.
In this course, we will learn to write our own
programs to solve some very simple problems.
Writing programs is often called "writing code".
So the code is actually the program itself. We
also use the term "source code" to represent the
program logic.



COMP1005/1405 – Programming Basics Fall 2009

- 7 -
How do we write good programs ?
There are some important standards which you should always adhere to if you want to write
"good" programs:
• C orrectness - Make sure that your program does what it is supposed to do.
• R obustness - Make sure the program does not crash. Testing helps prevent this.
• I nterface Usability - Make sure that the interface is easy to use and intuitive.
• S implicity - Keep the code as simple as possible, while still meeting requirements.
• P resentation and Documentation - Make sure that software is thoroughly
documented for maintenance purposes. (Don't forget about the TA's that need to mark
your assignments)
• E fficiency - Make sure that the software runs fast and does not use up too much
computer memory (i.e., that it is time and/or space efficient).

There are other issues that may need to be considered when writing programs:

• Portability – The ability to run your program on different kinds of machines (e.g.,
Windows PC, Mac, Sun Workstation, etc…)
• Security – The need to make sure that information entered by the user is not visible to
anyone who wants it (e.g., VISA or bank account info, passwords, personal data, etc…)

hat is a programming language ? W
amming language To write a program, we need to use what is called a progr which is:
ed to
as
structural programming languages (e.g.,
programming languages have become more
g languages such as functional programming
2

• an artificial language designed to automate the task of organizing and manipulating
information, and to express algorithms (i.e., problem solutions) precisely.

programming language “boils down to” a set of words, rules and tools that are us A
explain (or define) your program. There are many different programming languages just
there are many different "spoken" languages.
Traditional programming languages were known as
C, Fortran, Pascal, Cobol, Basic).
Since the late 80's however, object-oriented
popular (e.g., J AVA, C++, C#, Smalltalk)
There are also other types of programmin
languages and logic programming languages. According to WikiPedia, as of 2008 the 1
most actively used programming languages are (in alphabetical order): C, C++, C#, Java,
JavaScript, Perl, PHP, Python, Ruby, Shell, SQL and VisualBasic.
COMP1005/1405 – Programming Basics Fall 2009

- 8 -
What is Procedural programming ?
Procedural Programming involves writing code line-by-line in the order that
the code must be executed (i.e., run or evaluated) to solve some problem.
That is, the order of the set of steps is determined beforehand in a way that
solves the problem logically. All programming languages involve writing
"some" code in a procedural manner. This is because, to solve most
problems, there is usually a set of steps that must be followed in order. In
the real world, we also follow steps to accomplish a task. For example, to
withdraw some money from a bank machine we usually follow these steps:
1. insert your bank card
2. enter your PIN #
3. select "withdraw"
4. enter amount to withdraw … press "ok"
5. take your money and your card
Usually, the problem that is trying to be solved by the computer is broken down into smaller
sub-problems and these are solved in turn. For such simple structured programs, they often
follow these steps:
1. Get input data from the user (e.g., name, account#, deposit amount, etc.)
2. Perform some calculations, make some decisions, store/change some data (e.g., adjust
account balance)
3. Display some data output on the screen (e.g., tell user what happened)
What is Object-Oriented programming ?
Object-Oriented Programming (a.k.a., OOP) is similar to that of procedural
programming in that it involves executing a set of instructions in some specified
order. However, it differs from procedural programming in the way that your
code is organized. Programming using object-oriented style, involves organizing
your code in "chunks" that logically correspond to real-world objects. For
example, you may group all of your code related to a person into one file (called
a class) while code related to a car or a bank account would be grouped
together in separate files (i.e., classes).

When doing OOP, the programmer (i.e., you) spends much time defining (i.e., writing code
for) various objects by specifying small/simple behaviors that the object will need to respond to
(e.g., deposit, withdraw, compute interest, get age, save data etc...) Hence, OOP is all about
knowing:

• which objects to use,
• the kind of information we need to know about the objects,
• how objects behave, and
• how to use them with each other
COMP1005/1405 – Programming Basics Fall 2009

- 9 -
There is nothing magical about OOP. Programmers have been coding for years in traditional
top/down structured programming languages. So what is so great about OO-Programming ?
Well, OOP uses 3 main powerful concepts:

Inheritance
• promotes code sharing and re-usability
• intuitive hierarchical code organization
Encapsulation
• provides notion of security for objects
• reduces maintenance headaches
• more robust code
Polymorphism
• simplifies code understanding
• standardizes method naming

We will discuss these concepts later in the course once we are familiar with the J AVA
language.
Through these powerful concepts, object-oriented code is typically:
• easier to understand (relates to real world objects)
• better organized and hence easier to work with
• simpler and smaller in size
• more modular (made up of plug-n’-play re-usable pieces)
• better quality
This leads to:
• high productivity and a shorter delivery cycle
• less manpower required
• reduced costs for maintenance
• more reliable and robust software
• pluggable systems (updated UI’s, less legacy code)




COMP1005/1405 – Programming Basics Fall 2009

- 10 -
1.2 The JAVA Programming Language
J AVA is a very popular object-oriented programming language from SUN Microsystems. It has
become a basis for new technologies such as: Enterprise J ava Beans (EJ B’s), Servlets and
J ava Server Pages (J SPs) , etc. In addition, many packages have been added which extend
the language to provide special features:
• J ava Media Framework (for video streaming, webcams, MP3 files, etc)
• J ava 3D (for 3D graphics)
• J 2ME (for wireless communications such as cell phones, PDAs)

J AVA is continually changing/growing. Each new release fixes bugs and adds features. New
technologies are continually being incorporated into J AVA. Many new packages are available.
J ust take a look at the www.java.sun.com website for the latest updates.

There are many reasons to use J AVA:
• architecture independence
o ideal for internet applications
o code written once, runs anywhere
o reduces cost $$$
• distributed and multi-threaded
o useful for internet applications
o programs can communicate over network (e.g., web)
o uses RMI (Remote Method Invocation) API
• dynamic
o code loaded only when needed
• memory managed
o automatic memory allocation / de-allocation
o garbage collector releases memory for unused objects
o simpler code & less debugging
• robust
o strongly typed
o automatic bounds checking
o no “pointers” (you will understand this in COMP1402/1002)

COMP1005/1405 – Programming Basics Fall 2009

- 11 -
The J AVA programming language itself (i.e., the SDK that you
download from SUN) actually consists of many program pieces
(or object class definitions) which are organized in groups called
packages (i.e., similar to the concept of libraries in other
languages) which we can use in our own programs.
When programming in J AVA, you will usually use:
• classes from the J AVA class libraries (used as tools)
• classes that you will create yourself
• classes that other people make available to you
Using the J AVA class libraries whenever possible is a good idea since:
• the classes are carefully written and are efficient.
• it would be silly to write code that is already available to you.
We can actually create our own packages as well, but this will not be discussed in this course.

How do you get started in JAVA?
We will be using the latest version of J AVA (see course outline) from Sun Microsystems which
you can download from the SUN website if you are working at home.

When you download and install the latest JAVA SDK (i.e., J AVA Software Development Kit),
you will not see any particular application that you can run which will bring up a window that
you can start to make programs in. That is because the SUN guys, only supply the J AVA
SDK which is simply the compiler and virtual machine. J AVA programs are just text files, they
can be written in any type of text editor. Using a most rudimentary approach, you can actually
open up windows NotePad and write your program ... then compile it using the windows
Command Prompt window. This can be tedious and annoying since J AVA programs usually
require you to write and compile multiple files.

A better approach is to use an additional piece of application software called an Integrated
Development Environment (IDE). Such applications allow you to:
• write your code with colored/formatted text
• compile and run your code
• browse java documentation
• create user interfaces visually
• and use other java technologies (e.g. J ava Beans, EJ B's, Servlet programming etc...)
We will be using the JCreatorLE IDE within this course (from www.jcreator.com). It is a
powerful, yet simple “Windows-based” IDE that fits all of our needs. As you become a more
skilled programmer, you may wish to switch over to the Eclipse IDE (from www.eclipse.org)
which has additional features... but is overly complex for beginning students. If you are using a
Mac computer, jGRASP (from www.jGRASP.org) is a nice simple IDE as well.
COMP1005/1405 – Programming Basics Fall 2009

- 12 -
1.3 Writing Your First JAVA Program
The process of writing and using a J AVA program is as follows:
1. Writing: define your classes by writing what is called .java files (a.k.a. source code).
2. Compiling: send these .java files to the J AVA compiler, which will produce .class files
3. Running: send one of these .class files to the J AVA interpreter to run your program.


The java compiler:
• prepares your program for running
• produces a .class file containing byte-codes (which is a program that is ready to run).

If there were errors during compiling (i.e., called "compile-time" errors), you must then fix
these problems in your program and then try compiling it again.

The java interpreter (a.k.a. Java Virtual Machine (JVM)):
• is required to run any J AVA program
• reads in .class files (containing byte codes) and translates them into a language that
the computer can understand, possibly storing data values as the program executes.
J ust before running a program, J AVA uses a class loader to put the byte codes in the
computer's memory for all the classes that will be used by the program. If the program
produces errors when run (i.e., called "run-time" errors), then you must make changes to the
program and re-compile again.

As mentioned, the J AVA language consists of various class libraries that you can make use of.
All of J AVA’s classes are arranged in packages. There are MANY standard packages in
J AVA, each with many classes.

COMP1005/1405 – Programming Basics Fall 2009

- 13 -
Here are just some of the standard packages that you will likely use in this course:

java.lang
Basic classes and interfaces required by many J AVA programs. It is
automatically imported into all programs.
java.util
Utility classes and interfaces such as date/time manipulations, random numbers,
string manipulation, collections ...
java.io
Classes that enable programs to input and output data.
java.text
Classes and interfaces for manipulating numbers, dates, characters and strings.
Provides internationalization capabilities as well.
When you want to make use of some of these classes, you will use the import keyword to tell
J AVA that you want to use a class:
import <packageName>. *;

Basically, the import statement is used to tell the compiler which package (i.e., directory) the
class files are sitting in. You can always replace the * by a class name (where the class name
is in the package) but then readers of your code will not be clear on which classes you are
using. Keep in mind though that the import statement does not load any classes, it merely
instructs the compiler where to find them when you run your code.

Our First Program
The first step in using any new programming language is to understand how to write/compile
and run a simple program. By convention, the most common program to begin with is always
the "hello world" program which when run ... should output the words "Hello World" to the
computer screen. We will describe how to do this now.
All of your programs will consist of one or more files called classes. That is, each time you
want to make a program, you need to define a class.
Here is the program that we will write:

class Hel l oWor l dPr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( " Hel l o Wor l d" ) ;
}
}


Here are a few points of interest in regards to ALL of the programs that you will write in this
course:
COMP1005/1405 – Programming Basics Fall 2009

- 14 -
• The program must be saved in a file with the same name as the class name (spelled the
same exactly with upper/lower case letters and with a .java file extension).
• The first line beings with word class and then is followed by the name of the program
(which must match the file name, except not including the .java extension).
• The entire class is defined within the first opening brace { at the end of the first line and
the last closing brace } on the last line.
• The 2nd line (i.e., public static void main(String args[]) {) defines the starting place
for your program and will ALWAYS look exactly as shown.
• The 2nd last line will be a closing brace }.
So … ignoring the necessary "template" lines, the actual program consists of only one line:
System.out.println(" Hello World" ); which actually prints out the characters Hello World to
the screen. The text between the double quotes is called a String. In fact, we could replace
the Hello World text with any characters that we want displayed. Notice also that there is a
semicolon character (;) at the end of the line. All J AVA statements (i.e., lines of code) will end
with a ; character.

So to summarize, EVERY java program that you will write will have the following basic format:

class {
public static void mai n( St r i ng ar gs[ ] ) {
;
;
;
}
}


J ust remember that YOU get to pick the program name (e.g., MyProgram) which should
ALWAYS start with a capital letter. Also, your code MUST be stored in a file with the same
name (e.g., MyProgram.java). Then, you can add as many lines of code as you would like ins
between the inner { } braces. You should ALWAYS line up ALL of your brackets using the
Tab key on the keyboard.
Where do we write this code ? In the J Creator IDE. J Creator has a main window area into
which we write the code and another area at the bottom where we view the results:
COMP1005/1405 – Programming Basics Fall 2009

- 15 -








(2) Click here
to com




(3) Click here
to run
pile
(1) Write your
program here


(4) Output is displayed here







Supplemental Information (Capturing Screen Output)
If by default, your J Creator window opens up a black Command Prompt window when
compiling or running, you will need to configure J Creator to allow the compiled results and
the program output to appear at the bottom of your J Creator window. Do the following
steps (you only need to do this once):
1. Click on the Configure menu on the menu bar.
2. Select Options... and a dialog box will appear.
3. Select JDK Tools from the left list (2/3 of the way down the list).
4. Make sure that the Select Tool Type: is set to Compiler in the drop down list.
5. Select <Default> in the list below (even though it appears as grayed out).
6. Press the Edit... button.
7. Click "on" the Capture output checkbox (i.e., a check mark will appear).
8. Press OK.
9. Now choose from the Select Tool Type: drop down list the option of Run Application.
10. Select <Default> in the list below (even though it appears as grayed out).
11. Press the Edit... button.
12. Click "on" the Capture output checkbox (i.e., a check mark will appear).
13. Press OK.
14. Press OK again to close the dialog box.

COMP1005/1405 – Programming Basics Fall 2009

- 16 -
1.4 Displaying Information

We have seen in our first program how to use the System.out.println() statement to output a
simple line of text characters to the screen. However, this J AVA statement can do much
more. For example, it can also output results of computations. In this section we will look at a
few more examples of what can be displayed on the screen.

Here is another program which represents a calculator that can find the average of three
numbers (e.g., 34, 89 and 17) and display the answer on the console window:

class Cal cul at or Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
/ / Thi s code comput es a si mpl e cal cul at i on
Syst em. out . pr i nt ( " The aver age of 34, 89 and 17 i s " ) ;
Syst em. out . pr i nt l n( ( 34 + 89 + 17) / 3. 0) ;
}
}

There are some points of interest regarding the code:
• In addition to displaying text characters, the System.out.println can display the
"results" of mathematical computations.

• The code in green (i.e., following the // characters) is called a comment. J AVA
ignores this when compiling. You can place comments anywhere in your code to
provide an explanation of what your code is doing. This helps later on when you
look at your code at a future date ... because we all tend to forget what we did in the
past.
o Generally, you should use // when you have a single line to comment (i.e.,
everything after the // characters on that line is ignored).
o If you want to have a multiple line comment, you can alternatively begin the
comment with /* characters and end it with */ characters. For example:
/ * Thi s i s a mul t i pl e l i ne comment
because i t appear s on mor e
t han one l i ne i n t he pr ogr am. */
Of course, you can still use the // characters instead 3 times if you want:
/ / Thi s i s a mul t i pl e l i ne comment
/ / because i t appear s on mor e
/ / t han one l i ne i n t he pr ogr am.

COMP1005/1405 – Programming Basics Fall 2009

- 17 -
• The first line uses print while the second line uses println. When using just print,
the next text to be printed will be immediately to the right of this text. When using
println, a line feed and carriage return is printed, which means that the text to follow
will appear at the beginning of the next line of the console (i.e., output window).

Here is the output when the CalculatorProgram is run:

The aver age of 34, 89 and 17 i s 46. 666666666666664


Of course, this program always computes the same average using the same 3 numbers, but in
the section we will look at how to get different numbers from the user each time we run the
code. Notice as well that the calculations are not perfectly accurate … they are off a little after
15 decimal places.
Can you tell how the output of the following piece of code will be formatted ?

Syst em. out . pr i nt ( " My name i s " ) ;
Syst em. out . pr i nt l n( " Mar k. " ) ;
Syst em. out . pr i nt l n( " These st r i ngs " + " ar e" + " j oi ned. " ) ;
Syst em. out . pr i nt ( " Number s can be appended . . . see: " + 54. 342) ;
Syst em. out . pr i nt l n( " and even char act er s: " + 'A' + 'B' + 'C') ;
Syst em. out . pr i nt l n( ) ;
Syst em. out . pr i nt l n( " The l i ne above was l ef t bl ank. " ) ;
Syst em. out . pr i nt l n( " Now l eave 4 bl ank l i nes at t he end. \ n\ n\ n\ n" ) ;
Syst em. out . pr i nt l n( " Count t he bl anks above. " ) ;


Here is the output:


My name i s Mar k.
These st r i ngs ar e j oi ned.
Number s can be appended . . . see: 54. 342 and even char act er s: ABC

The l i ne above was l ef t bl ank.
Now l eave 4 bl ank l i nes at t he end.




Count t he bl anks above.


Notice that we can use the + to join:
• join two strings before display them.
• join numbers or characters (defined between single quotes ' ') to the end of a String.
COMP1005/1405 – Programming Basics Fall 2009

- 18 -

isplaying results in a window
Also notice that:
• when the brackets () are left empty on a println(), then a blank line is printed.
• we can use some \n characters at the end of the string to leave a blank lines.
D
the bottom part of the J Creator window
he simplest way to do this is to use one of the standard dialog boxes in J AVA. We can use
can bring up a little
The output of our programs above was displayed in
(called the console). However, we can also have our program results appear in a nice little
window that pops up on the screen. The code behind making a window can be a little
confusing at this point in the course, so we will not make our own. Instead, J AVA has some
pre-defined windows called JOptionPanes which will allow us to display some simple test
results.

T
something called a JOptionPane which is in the javax.swing package.
Consider for example, our CalculatorProgram that we wrote earlier. We
window with our answer in it as follows:


he window would come up, wait for us to press the OK button and then close. Here is the
g. J Opt i onPane;
T
code that does this:
import j avax. swi n

class Wi ndowCal cul at or Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
J Opt i onPane. showMessageDialog( null,
" The aver age of 34, 89 and 17 i s " + ( 34+89+17) / 3. 0) ;
}
}


a B
"command" is a pre-defined J AVA function that brings up the window with the text that we
choose (just replace the … characters with your text). JOptionPane is actually the J AVA
class that does this for us. Remember that since we are using a pre-defined J AVA class, w
need to tell the J AVA compiler where to find it. That is why we write the following statement a
the top of our program:

import javax.swing.J O
sically, it is a one-line-program again. The JOptionPane.showMessageDialog(null, … );
e
t
ptionPane;
COMP1005/1405 – Programming Basics Fall 2009

- 19 -
The JO fficially known as a method) called
howMessageDialog which contains code for displaying the window. This method requires
eters is
e 1st parameter is null. We will discuss this later.
2. The 2nd parameter is the information that you want to display in the middle of the
arious things in the window.
t to have
multiple lines of text, you can do this by appending a \n character between the lines that you

ptionPane class has a function (o
s
you to supply 2 pieces of information called parameters. Each of these param
separated by a comma character and is shown on a separate line in the code to make things
clearer:
1. Th
window. It can be any code, but is often a String.
Experiment with the example above by trying to display v
Normally, JOptionPanes are meant to have a single line of text. However, if you wan
want. This will tell J AVA to go to the next line before continuing. For example, if we add
some \n characters to our String as shown below, notice what the window will look like:

J Opt i onPane. ( , showMessageDialog null
" The aver age of \n34, 89 and 17\ni s\n\n" + ( 34 + 89 + 17) / 3. 0) ;


Here is the window that is produced:

Of course, the appearance is not as pleasant because everything is aligned to the left. As is,
there is no way to change this alignment. Nevertheless, it is sometimes nice to be able to
ate your own
windows and arrange everything as you want.
display text in our own windows as opposed to simply in the console window.
In the follow-up course to this one (COMP1406/1006), you will learn how to cre



COMP1005/1405 – Programming Basics Fall 2009

- 20 -
Supplemental Information (Other MessageDialogs)
There are variations for the showMessageDialog method that allow 2 more parameters so
that you can change the title on the window as well as add a picture. The format is:

J Opt i onPane. showMessageDi al og( null,
" The aver age of 34, 89 and 17 i s " + ( 34 + 89 + 17) / 3. 0,
" Answer " ,
J Opt i onPane. PLAI N_MESSAGE) ;

The 3rd parameter here is the title for the window, in this case "Answer". The 4th parameter
is the type of window to open. Here we say JOptionPane.PLAIN_MESSAGE, but there are
other options which will bring up the window but will also display a little picture (called an Icon)
that allows you to distinguish between different kinds of messages. Below is a table of the
other options and their icons. In fact, you can look at the J AVA documentation and see that
you can also use your own pictures/icons.

JOptionPane.WARNING_MESSAGE
Use this when you want to warn the user
about something in the program.

JOptionPane.ERROR_MESSAGE
Use this when you want to tell the user that
an error has occurred in the program.

JOptionPane.PLAIN_MESSAGE
Use this when you do not want a picture.

JOptionPane.INFORMATION_MESSAGE
Use this when you want to tell the user
something in the program.



COMP1005/1405 – Programming Basics Fall 2009

- 21 -

1.5 Getting User Input

In addition to outputting information to the console window, J AVA has the capability to get
input from the user. Unfortunately, things are a little "messier/uglier" when getting input. For
many years, previous versions of J AVA did not have any nice clean way to read data from the
keyboard. Since version 1.5 however, a new class was created which allows simplified input
from the keyboard. The class is called Scanner and it is available in the java.util package.
So, to get input from the user, we need to create a new Scanner object for the System
console. We will talk much more at a later time about creating new objects, but for now, here
is the line of code that gets a line of text from the user:
new Scanner ( Syst em. i n) . next Li ne( ) ;
This line of code will wait for the user (i.e., you) to enter some text characters using the
keyboard. It actually waits until you press the Enter key. Then, it returns to you the
characters that you typed (not including the Enter key). You can then do something with the
characters, such as print them out. Here is a simple program that asks the user for his name
and then says hello to him/her:
import j ava. ut i l . Scanner ;

class Gr eet i ngPr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( " What i s your name ?" ) ;
Syst em. out . pr i nt l n( " Hel l o, " + new Scanner ( Syst em. i n) . next Li ne( ) ) ;
}
}


Notice the output from this program if the letters Mark are entered by the user (Note that the
blue text (i.e., 2nd line) was entered by the user and was not printed out by the program):


What i s your name ?
Mar k
Hel l o, Mar k


As you can see, the Scanner portion of the code gets the input from the user and then
combines the entered characters by preceding it with the " Hello, " string before printing to the
console on the second line.
Interestingly, we can also read in integers from the keyboard as well by using:
new Scanner ( Syst em. i n) . nextInt();
COMP1005/1405 – Programming Basics Fall 2009

- 22 -
For example, consider this modified calculator program that finds the average of three
numbers entered by the user:
import j ava. ut i l . Scanner ;

class Bet t er Cal cul at or Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( " Ent er t hr ee number s: " ) ;
Syst em. out . pr i nt l n( " The aver age of t hese number s i s " +
( new Scanner ( Syst em. i n) . next I nt ( ) +
new Scanner ( Syst em. i n) . next I nt ( ) +
new Scanner ( Syst em. i n) . next I nt ( ) ) / 3. 0) ;
}
}


Here is the output when the BetterCalculatorProgram is run with the numbers 34, 89 and 17
entered:


Ent er t hr ee number s:
34
89
17
The aver age of t hese number s i s 46. 666666666666664


Supplemental Information (Bug)
In some versions of J Creator, there was a bug when getting keyboard input from the
output window. The above code for example, when using the "Capture output"
feature in J Creator (under the RunApplication tool option) will give the value of
the first number entered (which is 34 here). If this happens to you, you can
avoid this problem by disabling (i.e., uncheck) the "Capture output" feature when
running programs that require keyboard input.

Of course, we can enter any numbers. Here is the output from entering 10, 20 and 50:

Ent er t hr ee number s:
10
20
50
The aver age of t hese number s i s 26. 666666666666668


As we will see in the next section, we do not need to make 3 Scanner objects, we can actually
use the same Scanner each time. There is much more we can learn about the Scanner
class. It allows for quite a bit of flexibility in reading input.
COMP1005/1405 – Programming Basics Fall 2009

- 23 -
For example, if we enter 10, 20 and then some junk characters ... an error will occur as follows:

Ent er t hr ee number s:
10
20
j unk
Except i on i n t hr ead " mai n" j ava. ut i l . I nput Mi smat chExcept i on
at j ava. ut i l . Scanner . t hr owFor ( Scanner . j ava: 819)
at j ava. ut i l . Scanner . next ( Scanner . j ava: 1431)
at j ava. ut i l . Scanner . next I nt ( Scanner . j ava: 2040)
at j ava. ut i l . Scanner . next I nt ( Scanner . j ava: 2000)
at Bet t er Cal cul at or Pr ogr am. mai n( Bet t er Cal cul at or Pr ogr am. j ava: 6)


Woops! That's not very nice. This is J AVA's way of telling us that something bad just
happened. It is called an Exception. We will discuss more about this later. For now, assume
that valid integers are entered.
Getting input from a window
Getting input directly from the keyboard into a console is an obsolete task these days. Most
software has some kind of user interface that allows the user to enter text using dialog boxes,
text fields, sliders, list boxes etc... In J AVA, we can use also JOptionPane to get input from
the user. If we would like to ask the user for a simple piece of text, such as his/her name, we
can use the following line: J Opt i onPane. showI nput Di al og( " Pl ease i nput your name" ) ;
This code will bring up the following window which will let us enter the name:

So, the pre-defined JOptionPane class has a showInputDialog(…) method that brings up a
window and waits for us to enter data. The one parameter to that method allows us to give
instructions to the user in the form of a sentence. Consider now changing our
GreetingProgram to use this new window instead:
import j avax. swi ng. J Opt i onPane;

class ogr am{ Wi ndowGr eet i ngPr
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( " Hel l o, " +
J Opt i onPane. showInputDialog( " What i s your name ?" ) ) ;
}
}
COMP1005/1405 – Programming Basics Fall 2009

- 24 -
Notice that the output from this program in the System.out console only displays the result
now:


Hel l o, Mar k


We can actually combine this with the showMessageDialog method as follows:
import j avax. swi ng. J Opt i onPane;

class Wi ndowGr eet i ngPr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
J Opt i onPane. showMessageDialog( null, " Hel l o, " +
J Opt i onPane. showInputDialog( " What i s your name ?" ) ) ;
}
}


Then we end up with windows for input and for output … with nothing printed to the console:



There are other types of input windows. For example, we can bring up a window that asks the
user a YES/NO question as follows:

J Opt i onPane. showConfirmDialog( null, " Ar e you sur e you want t o qui t ?" ) ;

This method will bring up the following window:

We will see later that we can find out which of the buttons the user pressed (i.e., Yes, No or
Cancel) and then respond accordingly in our program. We will not discuss any other types of
input windows at this point because they require you to understand more about J AVA.

So, you should now have a solid understanding of how to display information as well as get
information from the user. We will now continue to get a better understanding of the J AVA
programming language.

COMP1005/1405 – Programming Basics Fall 2009

- 25 -
1.6 Types of Data: Primitives vs. Objects

Up until this point, we have done some simple programs that perform some simple
calculations. In general, a typical computer is only able to compute one piece of information
at a time. However, for big problems, there may be a lot of computations, number crunching,
data organization, etc… So, in order to write some more meaningful programs, we need to
understand how to store information.
For example, recall our example for averaging 3 numbers entered by the user. What if we
then wanted to find the maximum, the minimum and perhaps the sales tax on the total ? We
would need to store these numbers somewhere so that we can do multiple computations on
the same numbers without having to re-enter them again.
All information in the computer is actually stored in the electronics as voltages … high and low
voltages that can be thought of as billions of 1’s and 0’s that have some kind of meaning to
them. That is, all user information (whether it is a name, phone number, picture, email,
database, game, etc..) is stored as 1’s and 0’s. As humans, we have a hard time working at
such a low level. We do better working with things like numbers, characters and real-world
objects. Whenever we want to store any data whatsoever, we will always need to specify the
type of that data.
So, when programming, we will define all of our data as these higher level things which we call
data types. All data in J AVA is stored as one of the following two data types:

1. Primitive: represents a single piece of information
such as a number or character.

2. Object: represents multiple pieces of information
that are grouped together in a way similar to placing an
elastic around a bunch of primitives to hold them together.
So put simply, objects are bundles of data. As you will see
later, an object can actually be made up of other objects as
well.

In J AVA there are exactly 8 primitive types. Each differs with respects to:

• the kind of information being stored
• the amount of space they take up in the computer's memory.

You should use the data type that suits your needs but does not waste memory by using more
space than is necessary.



COMP1005/1405 – Programming Basics Fall 2009

- 26 -
If you want to store a simple integer number (i.e., no decimal part), J AVA offers you 4
possibilities:

Type Space Used (bytes) Stores Integer Number in Range
byte 1 -128 to 127
short 2 -32,768 to 32,767
int 4 -2,147,483,648 to 2,147,483,647
long 8 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

If you want to store a real number (i.e., with a decimal part), J AVA offers you 2 possibilities:

Type Space Used (bytes) Stores Real Number in Range
float 4 a real number over 10
38

double 8 a real number over 10
308

If you want to store a single character you have one choice:
Type Space Used (bytes) Stores Single Character
char 2 Represented by single quotes (e.g., 'a' 'B' 'c' '$' '>')
Finally, if you want to store a single true/false value, you also have one choice:
Type Space Used (bytes) Stores Boolean Value
boolean 1 a value of either true or false
So those are the 8 primitive data types that J AVA offers. You may have noticed that Strings
are not listed there. That is because Strings are actually objects which are made up of
multiple char primitives. That is, Strings are nothing more than a group of characters.
Notice in our code so far we have used numbers directly (e.g., 34, 89, 34.52) as well as
characters (e.g., 'a', 'M', ‘\n’). These are called literal values (a.k.a. literals or constants)
because the value is literally what you see when you read it in the program.

In J AVA, when you use a literal real number constant such as 34.685, J AVA assumes that you
want this number to be used as a double. If you just want it to be a float (which has half the
precision), then you must supply an f character after the number as follows: 34.685f …
otherwise … J AVA may give you a compile error. Similarly, long numbers must have an L
after them to distinguish them from ints.

There are some special pre-defined characters that have special meanings. They are actually
specified as 2 characters ... the backslash being the first. Here is a list of just a few of them:


COMP1005/1405 – Programming Basics Fall 2009

- 27 -
• '\n' (newline)
• '\b' (backspace)
• '\'' (single quote)
• '\t' (tab)
• '\\' (backslash)
• '\" ' (double quote)
It is likely that sooner or later you will need to use these special characters in a String.
Here is a simple test program that prints out some primitive values:

class Pr i mi t i veTest Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( 239) ; / / an i nt eger
Syst em. out . pr i nt l n( 823100267876L) ; / / a l ong
Syst em. out . pr i nt l n( 3. 141592653589793) ; / / a doubl e
Syst em. out . pr i nt l n( 3. 141592653589793f) ; / / a f l oat

Syst em. out . pr i nt l n( ' A' ) ; / / a l et t er char act er
Syst em. out . pr i nt l n( ' 5' ) ; / / a di gi t char act er
Syst em. out . pr i nt l n( ' ' ) ; / / t he space char act er
Syst em. out . pr i nt l n( ' \ " ' ) ; / / t he doubl e- quot e char act er
Syst em. out . pr i nt l n( ' \ n' ) ; / / t he bl ank- l i ne char act er

Syst em. out . pr i nt l n( true) ; / / t he bool ean val ue t r ue
Syst em. out . pr i nt l n( false) ; / / t he bool ean val ue f al se
}
}


Here is the resulting output:


239
823100267876
3. 141592653589793
3. 1415927
A
5

"


t r ue
f al se


Do you remember doing new Scanner(System.in).nextLine() when getting user input ?
Well, in place of nextLine(), we could have used any one of the following to specify the kind of
primitive data value that we would like to get from the user:

COMP1005/1405 – Programming Basics Fall 2009

- 28 -
next I nt ( ) , next Shor t ( ) , next Long( ) , next Byt e( ) , next Fl oat ( ) ,
next Doubl e( ) , next Bool ean( ) , next ( )

Notice that there is no nextChar() method available. The next() method actually returns a
String of characters, just like nextLine(). If you wanted to read a single character from the
keyboard (but don't forget that we still need to also press the Enter key), you could use the
following: next().charAt(0). We will look more into this later when we discuss String
functions.
Now that we have taken a look at the most primitive forms of data, lets see how we can use
objects in our programs.

Objects
We have already seen how to make Strings of characters and how to display them to the
screen. Strings are objects. So we have already begun using objects in our programs.
What other kinds of objects are there ? Well, J AVA has a lot of pre-defined objects.

class Obj ect Test Pr ogr am {
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( new j ava. l ang. Obj ect ( ) ) ; / / gener al obj ect
Syst em. out . pr i nt l n( new j ava. l ang. St r i ng( ) ) ; / / bl ank st r i ng
Syst em. out . pr i nt l n( new j ava. ut i l . Dat e( ) ) ; / / dat e obj ect
Syst em. out . pr i nt l n( new j ava. awt . Poi nt ( 50, 75) ) ; / / poi nt obj ect
Syst em. out . pr i nt l n( new j ava. awt . Rect angl e( 5, 10, 20, 30) ) ; / / r ect angl e
}
}


You may notice that for each object, we use the word new. This is required because objects
have to be constructed (i.e., built or created) before we can use them. In this case, we are
just creating the objects and then displaying them. Notice that the Point and Rectangle
objects required additional information (called parameters) to define them when they were
created. That is, it does not make sense to define a point without an x and y values. And a
rectangle cannot be created unless we know the top left corner (e.g., x=5,y=10) and the width
and height (e.g., Width=20, Height=30).

Notice how these objects are displayed in the output:


j ava. l ang. Obj ect @3e25a5

Mon Apr 06 11: 40: 51 EDT 2009
j ava. awt . Poi nt [ x=50, y=75]
j ava. awt . Rect angl e[ x=5, y=10, wi dt h=20, hei ght =30]


Each object displays itself differently. Notice that the Date object that was created actually
COMP1005/1405 – Programming Basics Fall 2009

- 29 -
corresponds to today’s date. Also, notice that the String object was actually an empty string
(i.e., no characters were displayed).
Please as well notice that after the word new, all of the objects have either java.lang.,
java.util. or java.awt. specified. That is because all of J AVA’s objects are logically organized
into subfolders (called packages). As it turns out, Object and String objects are in the
default folder called java.lang, Date objects are in the java.util folder and Point & Rectangle
objects are in the java.awt folder. If we do not specify where to find the objects, J AVA can
become confused when compiling our code and will generate compile errors.
Having to specify the folders each time we use an object is a little cumbersome and tedious.
We can actually simplify the code a little by using the import statement at the top of our
program. The import statement allows us to specify where to find each of the objects that we
are planning to use. In that way, the code becomes simpler. Notice the difference:
import j ava. l ang. Obj ect ;
import j ava. l ang. St r i ng;
import j ava. ut i l . Dat e;
import j ava. awt . Poi nt ;
import j ava. awt . Rect angl e;

class Obj ect Test Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( new Obj ect ( ) ) ; / / gener al obj ect
Syst em. out . pr i nt l n( new St r i ng( ) ) ; / / bl ank st r i ng
Syst em. out . pr i nt l n( new Dat e( ) ) ; / / dat e obj ect
Syst em. out . pr i nt l n( new Poi nt ( 50, 75) ) ; / / poi nt obj ect
Syst em. out . pr i nt l n( new Rect angl e( 5, 10, 20, 30) ) ; / / r ect angl e obj ect
}
}


Notice now that the code is less cluttered. In fact, all classes in the java.lang package are
automatically imported so we do not need the first two import statements. Also, when we
have multiple classes being imported from the same package (e.g., Point, Rectangle), we can
use a single import statement with the * wildcard character to tell J AVA to import any classes
from that package. (Note that the import statement does not load any classes, it just instructs
the compiler where to find them). So here is the simplest form of the code:
import j ava. ut i l . *;
import j ava. awt . *;

class Obj ect Test Pr ogr am3 {
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( new Obj ect ( ) ) ; / / gener al obj ect
Syst em. out . pr i nt l n( new St r i ng( ) ) ; / / bl ank st r i ng
Syst em. out . pr i nt l n( new Dat e( ) ) ; / / dat e obj ect
Syst em. out . pr i nt l n( new Poi nt ( 50, 75) ) ; / / poi nt obj ect
Syst em. out . pr i nt l n( new Rect angl e( 5, 10, 20, 30) ) ; / / r ect angl e obj ect
}
}
COMP1005/1405 – Programming Basics Fall 2009

- 30 -
1.7 Making Your Own Objects

Now that we have looked at how to use some of J AVA’s pre-defined objects … lets see how
you can make your own. Lets try to make a Car object and a Person object as follows:


class MyObj ect Test Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( new Car ( ) ) ; / / car obj ect
Syst em. out . pr i nt l n( new Per son( ) ) ; / / per son obj ect
}
}


If you were to compile the above code in J Creator, it would give you the following errors:


cannot f i nd symbol cl ass Car
cannot f i nd symbol cl ass Per son


J AVA cannot find any libraries that define the Car and Person objects. That is because the
J AVA language does not have Car or Person classes. If we want to use such objects, we
must define them on our own.
How do we define our an object of our own ?
• Each object that we define must be defined as its own separate class
• Each of these classes must be saved as their own .java file

For example, we would define a Car object by making a Car.java file that defines the class as
follows:

class Car {

}


That’s it! We just create this file in J Creator as a new java file, then save it and compile it.
You need to make sure that the file name is exactly the same (i.e., upper/lowercase letters) as
the name of the class … but with a .java file extension.

This file represents a class definition of the Car object (i.e., it specifies (or defines) exactly
what a Car object actually is). If we want to define a Person object, we can follow the same
process by making a Person.java file and writing/saving/compiling the following code:



COMP1005/1405 – Programming Basics Fall 2009

- 31 -

class Per son {

}


After doing this, our MyObjectTestProgram should then compile ok.


class MyObj ect Test Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( new Car ( ) ) ;
Syst em. out . pr i nt l n( new Per son( ) ) ;
}
}


Notice now the output from the program:


Car @19821f
Per son@42e816


0000000000
0000000001
0000000002


9999999997
9999999998
9999999999
1GB RAM
reference
reference
(memory)
a Car
object
a
Person
object
This is what objects look like by default. They show the
name of the class, then an @ symbol, and finally a
strange combination of numbers and letters.

This number/letter combination represents the location
(or address) of the object in the computer’s memory.
We call this the reference, because this memory
address “refers to” the object. The actual value of the
address is unimportant to us, however, it is important for
you to understand that each time we make an object, it
“uses up” a portion of the computer’s memory.

Later we will see how to change the appearance of our
objects so that they show more meaningful information
when displayed.

We can actually make a bunch of new Car and Person objects. Here is an example:







COMP1005/1405 – Programming Basics Fall 2009

- 32 -

class MyObj ect Test Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( new Car ( ) ) ; / / car obj ect
Syst em. out . pr i nt l n( new Car ( ) ) ; / / a di f f er ent car obj ect
Syst em. out . pr i nt l n( new Per son( ) ) ; / / a per son obj ect
Syst em. out . pr i nt l n( new Per son( ) ) ; / / anot her per son obj ect
Syst em. out . pr i nt l n( new Per son( ) ) ; / / yet anot her per son obj ect
}
}


Notice in the output that each Car and Person has its own unique location in the computer’s
memory (shown by the uniqueness of the numbers/letters after the @):


Car @19821f
Car @addbf 1
Per son@9304b1
Per son@190d11
Per son@a90653


When we save and compile the file Car.java and Person.java, we are actually defining two
new types of objects. We are really defining new classes (or categories) of objects that
J AVA now understands, although no objects are actually created in memory.

Keep in mind that the Car, Person and MyObjectTestProgram classes are each defined in
their own files and should be keep in the same folder on your hard drive.


class Car {

}


class Person {

}


class MyObjectTestProgram {
public static void mai n( St r i ng ar gs[ ] )
{
...
}
}

Also, remember that you cannot run the Car or Person class, because they are not programs
… they are just object definitions. You can only run classes that have the public static void
main(…) code in them.

When we say new Car() … then we are actually now making a new object of that class type.
This is called making an instance of the Car class. Hence, every time we say new Car() we
are getting back an instance of the Car class. So an instance is an object.


COMP1005/1405 – Programming Basics Fall 2009

- 33 -
You may want to think of the Car class as a factory that makes Car objects (i.e., makes
instances of the Car class). In general, every time we say new, J AVA goes to the factory for
that class and makes an instance of that class for us and then gives it back to us. So… the
class is the “factory”, the instance is the particular “object” that we can start using now in our
programs.

The Car Class new Car() Instance of type Car






The Person Class new Person() Instance of type Person





The House Class new House() Instance of type House





So … at this point you need to understand that all data (i.e., information) in the program is
represented as either one of the 8 primitives or as an object.
You must also understand that objects are just “chunks of data” of a certain type, category or
class. You can use objects in your program just as easily as using simple numbers. In the
next section we will find out how to start using the data (or information) in more meaningful
ways.


Chapter 2
Variables and Objects


What is in This Chapter ?
In this chapter we will consider the notion of variables as a means of storing information.
Variables are fundamental building blocks of object-oriented programming since in order to
fully understand object-oriented programming, you must know where your data is at all times
as well as how to get it and how to change it. We will look at examples of how to use
variables to store primitive information as well as object information. You will learn how to
define your own objects that contain their own variables called instance variables. We will
then look at how to create constructors that allow us to provide initial values to all of our
object’s instance variables. Finally, we will examine shared data in the form of class/static
variables.


COMP1005/1405 – Variables and Objects Fall 2009

- 35 -
2.1 Variables

Consider a piece of code in which we are given some numbers and we would like to compute
some calculations with those numbers such as adding, multiplying and averaging. We could
write the following code:


Syst em. out . pr i nt l n( " Gi ven number s 34, 89 and 17: " ) ;
Syst em. out . pr i nt l n( " The sumi s " + ( 34 + 89 + 17) ) ;
Syst em. out . pr i nt l n( " The pr oduct i s " + ( 34 * 89 * 17) ) ;
Syst em. out . pr i nt l n( " The aver age i s " + ( ( 34 + 89 + 17) / 3) ) ;


The code is straight forward. J AVA will perform the calculation and then print the results. If
we wanted to change one of the numbers (e.g., change 34 to 15) then we would have to make
this change in 4 places:


Syst em. out . pr i nt l n( " Gi ven number s 15, 89 and 17: " ) ;
Syst em. out . pr i nt l n( " The sumi s " + ( 15 + 89 + 17) ) ;
Syst em. out . pr i nt l n( " The pr oduct i s " + ( 15 * 89 * 17) ) ;
Syst em. out . pr i nt l n( " The aver age i s " + ( ( 15 + 89 + 17) / 3) ) ;


This is not a desirable situation. In this particular code it is a simple change, however in larger
programs, this may require you to go through hundreds of lines of code to make the simple
change.

Another undesirable characteristic of the program is that the sum (15 +89 +17) is computed
twice:


Syst em. out . pr i nt l n( " Gi ven number s 15, 89 and 17: " ) ;
Syst em. out . pr i nt l n( " The sumi s " + (15 + 89 + 17)) ;
Syst em. out . pr i nt l n( " The pr oduct i s " + ( 15 * 89 * 17) ) ;
Syst em. out . pr i nt l n( " The aver age i s " + ( (15 + 89 + 17)/ 3) ) ;


Again, this is not so serious in a small program, but in a larger program we would not want to
duplicate portions of our code in many places because:

• it wastes space (i.e., computer memory)
• it requires more programming time to write the same code over and over again
• if a change needs to be made, we must remember every place that the duplicated code
appears and then go make the change multiple times.

In order to avoid these two problems, we can use the notion of a variable.
COMP1005/1405 – Variables and Objects Fall 2009

- 36 -
Simply put, a variable is a placeholder for a piece of information. That is,
a variable is used to “hold on to” or remember a data value for later use.

In real life, we often “jot down” something on a piece of
paper to remember it later (e.g., a phone number, an
address, a price etc…). That way, whenever we want to
recall the value that we wrote down, we just get that
piece of paper and read the value that we wrote there.

So when programming, we use a variable to store
information for later use (i.e., for reference later).

But what if you have written down a few phone numbers
? How do you tell which one is which ?

We need something that will let us distinguish between
the different phone numbers. It should be something
unique … shouldn’t it ?

In real life, we would probably also write the name of
each person along with the phone number. As long as
the names are unique, we will know exactly which
phone number belongs to each person.

When programming, variables also have a name. This
allows us to refer to the variable’s value at any time in
our program.

Thus, when we use the variable name in our program,
we are actually referring to the value stored in the
variable with that name.

We know that a phone number is a sequence of digit characters with a ‘-‘ character. We can
thus represent a phone number using a string as follows: “ 555-2678” . In this case, we
would say that phone numbers can be stored in variables that are of type String. Hence,
String would be the data type for our individual phone numbers.

So, in J AVA, to make a variable that holds (i.e., stores, keeps, remembers, etc.) a phone
number, we need to specify both the type and name (must be unique) of the variable. Here is
how we would do this for our 5 phone numbers above:

St r i ng mar y;
St r i ng bet t y;
St r i ng j en;
St r i ng bob;
St r i ng j i m;

Notice that we specify the type on the left and the name on the right (separated by one or
more spaces or tab characters) and then conclude with a ‘;’ character. It should be noted
COMP1005/1405 – Variables and Objects Fall 2009

- 37 -
however, that the variable definitions above do not specify the value for the variables, it just
“reserves space” for the variable (i.e., it just makes the “sheet of paper” with the label on it, but
does not yet have a value).

Make sure to pick meaningful variable names that are not too long !! The name must be
unique and it is case-sensitive (i.e., Hello and hello would not be considered the same).

Variable names may contain only letters, digits and the ‘_’ character (i.e., no spaces in the
name). As standard convention, multiple word names should have every word capitalized
(except the first). Here are some good examples of variable names:

• count • poundsPerSquareInch
• average • aString
• insuranceRate • latestAccountNumber
• timeOfDay • weightInKilograms

Here are some more examples of how we can
declare some additional variables for various kinds of
objects:

Car myCar ;
Dat e t oday;
Per son mar k;
St or e aSt or e;
BankAccount account ;

Note that we can also declare variables whose type is
one of the 8 data types. For example:

int days;
float age;
double wei ght ;
char sex;
boolean hungr y;

Now that we know how to declare (i.e., define) a variable, we need to know how to use them
in our programs. We use the term assign to represent the idea of “giving a value” to a
variable. In J AVA, the assignment operator is the = sign. So, we use =to put a value into a
variable. Here are a few example of how we can do this with some of the variables that we
declared earlier:

days = 15; mar y = " 555- 2678" ;
age = 21. 3f ; bob = " 782- 3244" ;
myCar = new Car ( ) ; wei ght = 165. 23;
mar k = new Per son( ) ; sex = ' M' ;
hungr y = true;

COMP1005/1405 – Variables and Objects Fall 2009

- 38 -
Something VERY important to remember when learning to program is that the value of the
variable must be the same type of object (or primitive) as the variable’s type that was
specified when you declared it earlier. So for example, in the following table, make sure that
you understand why the examples on the left are wrong, while the right examples are correct:

St r i ng mar y;
mar y = 23;

St r i ng mar y;
mar y = " 555- 2678" ;

Car myCar ;
myCar = " Por sche" ;

Car myCar ;
myCar = new Car ( ) ;

int days;
days = 10. 2789;

int days;
days = 10;

boolean hungr y;
hungr y = ' y' ;

boolean hungr y;
hungr y = false;

char sex;
sex = " F" ;

char sex;
sex = 'F';


To help cut down the number of lines of code in our program, J AVA actually allows us to both
declare and assign a value to our variables all on one line. So, from our earlier examples, we
can do the following:

St r i ng mar y = " 555- 2678" ;
St r i ng bob = " 782- 3244" ;
Car myCar = new Car ( ) ;
Per son mar k = new Per son( ) ;
int days = 15;
float age = 21. 3f ;
double wei ght = 165. 23;
char sex = ' M' ;
boolean hungr y = true;

It is interesting that we use the term variable to store our information. The word “variable” is
derived from the word “vary”, which means that the value may change (or vary) as time goes
on. For example, if our friend changes their phone number, we can simply erase the old
number from our piece of paper and write in the new number. Thus, the value of the variable
will have changed.

So, even though a variable may be declared only once in the program, we may assign a
value to it multiple times. Can you determine the output of this piece of code:

int days;

Syst em. out . pr i nt l n( days) ;
days = 43;
Syst em. out . pr i nt l n( days) ;
days = 15;
Syst em. out . pr i nt l n( days) ;

COMP1005/1405 – Variables and Objects Fall 2009

- 39 -
There are some interesting aspects to take note of in this code:
• the code prints out numbers 0, 43 and 15 (notice that days starts off with a value of 0)
• the code prints out the days variable simply by using the variable’s name
• even though the code displays the same days variable 3 times, its value is different
because it is re-assigned a value 2 times during the program.

Variables can be re-assigned a value, but cannot be declared again. Therefore, the
following code will not compile:

int days = 365;
Syst em. out . pr i nt l n( days) ;
int days = 7; / / cannot decl ar e days agai n
Syst em. out . pr i nt l n( days) ;

Here are some more pieces of code. Do you know what the output is ?
int x;
int y;
x = 34;
y = 23;
Syst em. out . pr i nt l n( x + y) ;
Here is a similar example. Notice in J AVA that we are allowed to declare multiple variables of
the same type on the same line, each separated by a ',':
int x, y;
x = 34;
y = x;
Syst em. out . pr i nt l n( x + y) ;
Here is another one:
int x, y, z;

x = 3*2*1;
y = x + x;
z = x;
Syst em. out . pr i nt l n( z) ;
Note that even though we use x a few times, it does not change its value. Here is one that is
a little more interesting:
int t ot al ;
float aver age;

t ot al = 12 + 25 + 36 + 15;
aver age = t ot al / 4;
Syst em. out . pr i nt l n( " The aver age i s " + aver age) ;
Notice that we can append the variable to a String using the '+' before printing it. J AVA simply
takes the value of the variable and appends it to the end of the String.
COMP1005/1405 – Variables and Objects Fall 2009

- 40 -
The result is 22.0, not just 22. That is because average is declared as type float. If we
would have declared it as type int, the answer would have been 22.
Here is an example using char variables. Can you guess the output ?

char a=' M' , b=' A' , c=' R' , d=' K' ;

Syst em. out . pr i nt l n( " My name i s: " + a + b + c + d) ;
Syst em. out . pr i nt l n( " My name backwar ds i s: " + d + c + b + a) ;

b = ' E' ;
c = ' I ' ;

Syst em. out . pr i nt l n( " The myst er y name i s: " + a + c + d + b) ;


Here is the output:


My name i s: MARK
My name backwar ds i s: KRAM
The myst er y name i s: MI KE

Don’t forget … variables are used to hold whole objects and they too are used exactly the
same way as variables that hold primitives:
Dat e t oday = new j ava. ut i l . Dat e( ) ;
Syst em. out . pr i nt l n( " Dat e of Bi r t h = " + t oday) ;
Now let us consider a more complete example that uses the Scanner object to get three
numbers from the user and then computes the sum, product and average. You will notice that
code uses variables named a, b, c and sum which all hold integer values. Notice also how a
single Scanner object is used now since we can store it in a variable:
COMP1005/1405 – Variables and Objects Fall 2009

- 41 -
import j ava. ut i l . Scanner ;

class Super Cal cul at or Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
int a, b, c, sum;
Scanner keyboar d = new Scanner ( Syst em. i n) ;
Syst em. out . pr i nt l n( " Ent er t hr ee number s: " ) ;
a = keyboar d. next I nt ( ) ;
b = keyboar d. next I nt ( ) ;
c = keyboar d. next I nt ( ) ;
sum= a + b + c; / / comput e t he sumand st or e i t
Syst em. out . pr i nt l n( " Gi ven number s " + a + " , " + b +
" and " + c + " : " ) ;
Syst em. out . pr i nt l n( " The sumi s " + sum) ;
Syst em. out . pr i nt l n( " The pr oduct i s " + ( a * b * c) ) ;
Syst em. out . pr i nt l n( " The aver age i s " + ( sum/ 3) ) ;
}
}


Here is the output (making sure to disable the "Capture Output" option in J Creator):
Ent er t hr ee number s:
34
88
16
Gi ven number s 34, 88 and 16:
The sumi s 138
The pr oduct i s 47872
The aver age i s 46


Notice now that by simply changing the first integer to 64, we get different output:
Ent er t hr ee number s:
64
88
16
Gi ven number s 64, 88 and 16:
The sumi s 168
The pr oduct i s 90112
The aver age i s 56


At this point, you should have a good understanding of what a variable is and how to use them.
The only question you may have is “When do I need to use a variable ?”.

COMP1005/1405 – Variables and Objects Fall 2009

- 42 -
In summary, we need to use a variable:
• to store intermediate results in an extensive computation
• whenever we need to use a value more than once in a computation
• to simplify steps in a piece of code.
• to reduce code duplication
Other than that, there is not much more to say about variables. You just need to start getting
practice using them.


2.2 Instance Variables / Object Attributes
We have just discussed the use of simple variables in our programs. Recall that all variables
have a name and a type and that the type must either be one of the 8 primitives or an object
of our choosing.
Remember too that an object is really just a bunch of data grouped together. The
data (i.e., information) itself defines the particular attributes of the object. J ust as
we use variables to “remember” data in our programs, an object must “remember”
its data at all times. These attributes define the state of the object as time goes on.
The state of the object is stored (i.e., remembered) by using variables. Therefore, at the most
basic level, an object is simply made up of a group of variables. Since an object is an
instance of some class, we call these particular kinds of variables instance variables.
Consider a variable that stores a person’s name:

St r i ng name; / / decl ar e a var i abl e t o hol d t he name

name = " Hank Ur chi f " ;
Syst em. out . pr i nt l n( name) ;

The code above displays “ Hank Urchif” to the console window. Very simple. Suppose that
we wanted the option of displaying the name as either “ Hank Urchif” or “ Urchif, Hank” . To
do this, we would need to distinguish between the first and last name by using two variables as
follows:

St r i ng f i r st Name, l ast Name; / / decl ar e 2 var i abl es t o hol d t he name

f i r st Name = " Hank" ;
l ast Name = " Ur chi f " ;
Syst em. out . pr i nt l n( f i r st Name + " " + l ast Name) ;
Syst em. out . pr i nt l n( l ast Name + " , " + f i r st Name) ;

COMP1005/1405 – Variables and Objects Fall 2009

- 43 -
The code above displays both “ Hank Urchif” and “ Urchif, Hank” to the console window.
Here is a corresponding diagram showing that each of the two String variables are pointing to
(i.e., holding on to) their own unique String objects:

String object
String object
“ Urchif”
lastName variable
firstName variable


“ Hank”
What would we do though if we had three person’s names ? Likely, we would do something
like this:

St r i ng f i r st Name1, l ast Name1; / / var i abl es t o hol d t he 1
st
per son’ s name
St r i ng f i r st Name2, l ast Name2; / / var i abl es t o hol d t he 2
nd
per son’ s name
St r i ng f i r st Name3, l ast Name3; / / var i abl es t o hol d t he 3
r d
per son’ s name

f i r st Name1 = " Hank" ;
l ast Name1 = " Ur chi f " ;
f i r st Name2 = " Hol l y" ;
l ast Name2 = " Day" ;
f i r st Name3 = " Bobby" ;
l ast Name3 = " Socks" ;
Syst em. out . pr i nt l n( l ast Name1 + " , " + f i r st Name1) ;
Syst em. out . pr i nt l n( l ast Name2 + " , " + f i r st Name2) ;
Syst em. out . pr i nt l n( l ast Name3 + " , " + f i r st Name3) ;

This code would produce the following output:

Ur chi f , Hank
Day, Hol l y
Socks, Bobby


You can see however, that with just a few more people, the number of variables that you need
can increase quickly. A more convenient way of writing this code would be to group the
firstName and lastName into one object (since they always belong together anyway).

We can group these two variables together by defining a new object (i.e., remember that an
object is just variables with an elastic around them). What would be a good name for the
object ? It makes sense to call it something like Person. Here is how we do this …

COMP1005/1405 – Variables and Objects Fall 2009

- 44 -

class Person {
St r i ng f i r st Name; / / i nst ance var t o hol d a per son’ s f i r st name
St r i ng l ast Name; / / i nst ance var t o hol d a per son’ s l ast name
}

Notice that the Person class defines one single person, not multiple people (that is why we
called the class Person and not Persons). Notice as well that all we did is move the two
variables inside this object. Once we do this and compile our Person class, J AVA now knows
that all Person objects are made up of a first name and a last name. That is, whenever we
make a new Person object, they will each have their own firstName and lastName as part of
them. Recall, that these two variables are now called the instance variables (or attributes)
of the Person class.
Assuming that we have defined, saved and compiled the Person class above, now we can
simplify our program to use the new Person object as shown below. Note that the original
code is included as a comment so that you can compare them side-by-side:

Per son aPerson; / / decl ar e t he var i abl e t hat wi l l hol d t he per son

aPerson = new Per son( ) ;
aPerson. f i r st Name = " Hank" ;
aPerson. l ast Name = " Ur chi f " ;
Syst em. out . pr i nt l n( aPerson. f i r st Name + " " + aPerson. l ast Name) ;
Syst em. out . pr i nt l n( aPerson. l ast Name + " , " + aPerson. f i r st Name) ;

/ * Her e was t he or i gi nal code:
St r i ng f i r st Name, l ast Name;

f i r st Name = " Hank" ;
l ast Name = " Ur chi f " ;
Syst em. out . pr i nt l n( f i r st Name + " " + l ast Name) ;
Syst em. out . pr i nt l n( l ast Name + " , " + f i r st Name) ; */

Notice that we now have one variable (called aPerson) which is of type Person now instead of
type String. That is, the aPerson variable will now hold a whole Person object (which
includes the firstName and lastName within it.
Now although the type of the variable MUST be Person, the actual name of the variable is
unimportant. We could have used any variable name such as p, x, hank, friend, man,
customer, etc…
When we define the variable on the first line, it does not create any Person objects. It simply
declares that we will be using (and storing) a Person object in the program. So, we actually
need to create a new Person object on our own and store it in the variable (see the 2
nd
line).
COMP1005/1405 – Variables and Objects Fall 2009

- 45 -
Here is a diagram showing what has now happened. Notice that the firstName and lastName
variables hold String objects. That is, the names themselves are unique objects (hence drawn
as separate objects here). We therefore draw the arrows to indicate that the firstName and
lastName variables are just pointing to (or referring to) the two String objects. Notice in the
diagram below, that the two String variables are still there, they are just inside the Person
object now:

Variable definitions without
using the Person object.
String object
firstName variable
“ Urchif”
String object
lastName variable
“ Hank”
String object
“ Urchif”
“ Hank”

lastName
firstName
aPerson variable

instance variable
instance variable
Person object
Variable definitions when
using the Person object.
String object
Notice as well in our program that we use the dot . character after the aPerson variable name
in order to get inside of it. After the dot we specify which of the two variables we are trying to
refer to (i.e., either firstName or lastName). Otherwise, the code is the same as before.
That is, we use the instance variables inside the object just as if they were regular ordinary
String variables.
You should realize that all instance variables are allowed to be changed at any time (i.e, they
can vary). For example, assume that Hank watches a “Simpsons” episode on TV and then
decides to change his name to “Max Power”.

Per son aPerson;

/ / Make nk Ha wi t h hi s or i gi nal name
aPerson = new Per son( ) ;
aPerson. f i r st Name = " Hank" ;
aPerson. l ast Name = " Ur chi f " ;

/ / Assume t hat Hank wat ches The Si mpsons and deci des t o make t he change

aPerson. f i r st Name = " Max" ; / / Change t he f i r st name t o " Max"
aPerson. l ast Name = " Power " ; / / Change t he l ast name t o " Power "
Syst em. out . pr i nt l n( aPerson. f i r st Name + " " + aPerson. l ast Name) ;


The output will show the new name “Max Power”. It is important that you do not forget that
instance variables are just regular variables … but they are inside the object now.
COMP1005/1405 – Variables and Objects Fall 2009

- 46 -
Now what about our example that used 3 names ? We can make 3 separate Person objects
as follows:

Per son p1, p2, p3; / / decl ar e t he t hr ee var i abl es

/ / eat Cr e t he 3 peopl e ( i . e. , make 3 i nst ances of t he Per son cl ass)
p1 = new Per son( ) ;
p2 = new Per son( ) ;
p3 = new Per son( ) ;

/ / Set al l of t hei r names ( i . e. , gi ve val ues t o t hei r i nst ance var i abl es)
p1. f i r st Name = " Hank" ;
p1. l ast Name = " Ur chi f " ;
p2. f i r st Name = " Hol l y" ;
p2. l ast Name = " Day" ;
p3. f i r st Name = " Bobby" ;
p3. l ast Name = " Socks" ;

/ / Di spl ay t he names t o conf i r mt hat t he names wer e set cor r ect l y
Syst em. out . pr i nt l n( p1. l ast Name + " , " + p1. f i r st Name) ;
Syst em. out . pr i nt l n( p2. l ast Name + " , " + p2. f i r st Name) ;
Syst em. out . pr i nt l n( p3. l ast Name + " , " + p3. f i r st Name) ;

Notice that the diagram would appear as follows:

String object
String object
“ Socks”

lastName
firstName


instance variable
instance variable
Person object
“ Bobby”
String object
String object
“ Day”

lastName
firstName


p3 variable
instance variable
instance variable
Person object
“ Holly”
String object
String object
p1 variable
“ Urchif”

lastName
firstName


p2 variable
instance variable
instance variable
Person object
“ Hank”
COMP1005/1405 – Variables and Objects Fall 2009

- 47 -
So as you can see, objects are used to hold a group of data together so that your program
code stays more organized and relates better to real world objects.
Suppose that we want to add some more interesting attributes to the Person object to keep
track of a person’s age, gender and whether or not they are retired. We can do this by adding
3 more instance variables as follows:

class Person {
/ / These ar e t he i nst ance var i abl es t hat def i ne a Per son obj ect
St r i ng f i r st Name; / / per son’ s f i r st name
St r i ng l ast Name; / / per son’ s l ast name
int age; / / per son’ s age
char gender ; / / per son’ s gender ( i . e. , ' M' or ' F' )
boolean r et i r ed; / / per son’ s r et i r ement st at us
}

Note that the order of the instance variables does not matter. It is wise though to keep them
all together like this. Here is how we can set them and then print them out to test if it works:
Per son p1, p2; / / decl ar e t he t wo var i abl es

/ / eat Cr e t he f i r st per son and set hi s i nst ance var i abl es
p1 = new Per son( ) ;
p1. f i r st Name = " Hank" ;
p1. l ast Name = " Ur chi f " ;
p1. age = 19;
p1. gender = ' M' ;
p1. r et i r ed = false;

/ / Cr eat e t he second per son and set her i nst ance var i abl es
p2 = new Per son( ) ;
p2. f i r st Name = " Hol l y" ;
p2. l ast Name = " Day" ;
p2. age = 67;
p2. gender = ' F' ;
p2. r et i r ed = true;

/ / Di spl ay t he i nst ance var i abl es f or t hese peopl e
Syst em. out . pr i nt l n( p1. l ast Name + " , " + p1. f i r st Name + " , " +
p1. age + " , " + p1. gender + " , " + p1. r et i r ed) ;
Syst em. out . pr i nt l n( p2. l ast Name + " , " + p2. f i r st Name + " , " +
p2. age + " , " + p2. gender + " , " + p2. r et i r ed) ;

Here is the output that you should expect to see:

Ur chi f , Hank, 19, M, f al se
Day, Hol l y, 67, F, t r ue

COMP1005/1405 – Variables and Objects Fall 2009

- 48 -
Do you now understand why we use the term “instance variable” ? Recall that every time we
use new to get a new object (e.g., new Person()), we get back a new instance of that class.
Thus, the data that defines the object (e.g., first name, last name, age, etc..) will vary from
object to object, that is, from instance to instance.

Here is the diagram for the above example. Notice that the age, gender and retired are
primitive types and so they each store their literal value directly, whereas firstName and
lastName just stored “pointers” to the actual String objects that they store:

String object
String object
“ Day”
true retired
instance variable
'F' gender
instance variable
67 age
instance variable

lastName
firstName


instance variable
instance variable
Person object
“ Holly”
String object
String object
p1 variable
“ Urchif”
false
'M' gender
instance variable
19 age
instance variable
p2 variable
retired
instance variable

lastName
firstName


instance variable
instance variable
Person object
“ Hank”

COMP1005/1405 – Variables and Objects Fall 2009

- 49 -

Supplemental Information (Choosing Instance Variables)
In the above example, we chose firstName, lastName, age, gender and retired as attributes
for our Person object. Why did we choose these ? We chose them as an example of how to
build an object. In reality however, the application that we are trying to develop may require us
to keep information for a Person that is different from the attributes we chose here. For
example, we might want to keep their emailAddress, weight, height, favoriteColor, etc.. The
choice depends on the application and on what we need to store for this object.

For example, consider defining a Car class. We should think of what characteristics we will
need to store for each car (e.g., make, model, color, mileage, etc..):


The choice will depend on the program/application you are making. Consider these possible
applications in which a Car object may be used:
• a program for a car repair shop
• a program for a car dealership
• a program for a car rental agency
• a program for an insurance company
So, now lets examine what kind of attributes (i.e., instance variables) that we would likely need
to define for a Car in each of these individual applications:
• repair shop
make, model, year, engine size, spark plug type, air/oil filter types, air hose
diameter, repair history, owner etc...
• car dealership
model, price, warranty, interior finish (leather/material), color, engine size, fuel
efficiency rating, etc...
• rental agency
sedan or coupe, make, model, license plate, price per hour, mileage, repair
history, etc...
• insurance company
year, make, model, owner, insurance type (fire/theft/collision/liability), color, license
plate, etc...

So you can see that it is not always straight forward to identify the state for an object. You
need to always understand how it fits into the application.

COMP1005/1405 – Variables and Objects Fall 2009

- 50 -
So far, we have looked at storing just String and primitive values in the variables within our
object. We should realize however, that since variables can store any kind of object, then our
instance variables too can store arbitrary objects. That is, the objects that we make may
themselves be made up of other objects.

For example assume that we want to store an address as well for our person. We can define
the following Address object to maintain all the appropriate information for an address:


class Address {
These ar e t he i nst ance var i abl es t hat def i ne an Addr ess obj ect / /
int st r eet Number ; / / addr ess’ s st r eet number
char uni t l et t er ; / / addr ess’ s uni t l et t er ( e. g. , ' A' ) ;
St r i ng st r eet Name; / / addr ess’ s st r eet name
St r i ng ci t y; / / addr ess’ s ci t y
St r i ng pr ovi nce; / / addr ess’ s pr ovi nce
St r i ng post al Code; / / addr ess’ s post al code
}


Assume that this class was compiled and saved to a local directory, we could make one as
follows:

Addr ess addr; / / decl ar e t he var i abl e t hat wi l l hol d t he addr ess

/ / cr eat e an Addr ess obj ect and t hen set i t s val ues
addr = new Addr ess( ) ;
addr. st r eet Number = 1526;
addr. uni t Let t er = ' B' ;
addr. st r eet Name = " Oak St . " ;
addr. ci t y = " Ot t awa" ;
addr. pr ovi nce = " Ont ar i o" ;
addr. post al Code = " K1S 5B6" ;


Notice the diagram that would represent this object and its composite instance variable values:


“ K1S 5B6”

postalCode
province
city
streetName
unitLetter 'B'
addr variable

“ Ontario”
“ Ottawa”
“ Oak St.”
instance variable
instance variable
instance variable
instance variable
instance variable
streetNumber 1526
instance variable
Address object
COMP1005/1405 – Variables and Objects Fall 2009

- 51 -
Now that the Address object has been defined, we can add an instance variable to our
Person class that keeps track of a person’s address by using this new Address object as
follows:

class Person {
/ / These ar e t he i nst ance var i abl es t hat def i ne a Per son obj ect
St r i ng f i r st Name; / / per son’ s f i r st name
St r i ng l ast Name; / / per son’ s l ast name
int age; / / per son’ s age
char gender ; / / per son’ s gender ( i . e. , ' M' or ' F' )
boolean r et i r ed; / / per son’ s r et i r ement st at us
Addr ess addr ess; / / per son’ s addr ess
}

Here is how we can try this out to see if it all works:

Per son p; / / decl ar e a Per son var i abl e

/ / Cr eat e t he per son and set hi s i nst ance var i abl es
p = new Per son( ) ;
p. f i r st Name = " Hank" ;
p. l ast Name = " Ur chi f " ;
p. age = 19;
p. gender = ' M' ;
p. r et i r ed = false;

p. addr ess = new Addr ess( ) ; / / we need t o cr eat e an addr ess obj ect
p. addr ess. st r eet Number = 1526;
p. addr ess. uni t Let t er = ' B' ;
p. addr ess. st r eet Name = " Oak St . " ;
p. addr ess. ci t y = " Ot t awa" ;
p. addr ess. pr ovi nce = " Ont ar i o" ;
p. addr ess. post al Code = " K1S 5B6" ;

/ / Di spl ay some i nst ance var i abl es f r omt he addr ess
Syst em. out . pr i nt l n( p. addr ess. st r eet Number ) ; / / di spl ays 1526
Syst em. out . pr i nt l n( p. addr ess. ci t y) ; / / di spl ays Ot t awa

You will notice that the person’s address variable stores a new Address object (which we had
to create). Then, we simply set the address attributes by using an extra dot . character to get
inside the address object to change its internal values. Here is a diagram of what is
happening …
COMP1005/1405 – Variables and Objects Fall 2009

- 52 -

Lets go one more level of objects. Suppose that we have a BankAccount object that keeps
track of a person’s bank account information. We can define it as follows:

class BankAccount {
/ / These ar e t he i nst ance var i abl es t hat def i ne a BankAccount obj ect
Per son owner ; / / per son who owns t he account
int account Number ; / / t he account number
float bal ance; / / amount of money cur r ent l y i n t he account
}

Notice that the owner is of type Person, which will contain all the owners information (i.e.,
names, address, phone number, age, etc…). We can test it as follows:
BankAccount account; / / decl ar e a BankAccount var i abl e

/ / Cr eat e t he bank account and set i t s i nst ance var i abl es
account = new BankAccount ( ) ;
account. account Number = 178193; / / ar bi t r ar i l y chosen
account. bal ance = 100; / / new account wi t h $100.00

account. owner = new Per son( ) ; / / must cr eat e a new Person obj ect
account. owner . f i r st Name = " Hank" ;
account. owner . l ast Name = " Ur chi f " ;
account. owner . age = 19;
account. owner . gender = ' M' ;
account. owner . r et i r ed = false;
“ Urchif”
“ Hank”

lastName
firstName

p variable

Person object
instance variable
instance variable
age
19
gender 'M'
retired false
instance variable
instance variable
instance variable
“ K1S 5B6”

postalCode
province
city
streetName
unitLetter 'B'
“ Ontario”
“ Ottawa”
“ Oak St.”
instance variable
instance variable
instance variable
instance variable
instance variable
address
instance variable
instance variable
1526 streetNumber
Address object
COMP1005/1405 – Variables and Objects Fall 2009

- 53 -

account. owner . addr ess = new Addr ess( ) ; / / must cr eat e an Address obj ect t oo
account. owner . addr ess. st r eet Number = 1526;
account. owner . addr ess. uni t Let t er = ' B' ;
account. owner . addr ess. st r eet Name = " Oak St . " ;
account. owner . addr ess. ci t y = " Ot t awa" ;
account. owner . addr ess. pr ovi nce = " Ont ar i o" ;
account. owner . addr ess. post al Code = " K1S 5B6" ;

Here is the diagram showing how all of the data is stored. Make sure that it makes sense to
you because a fundamental part of programming in an object-oriented language involves
“understanding where the data is” and “how to get to it”:

“ Urchif”
“ Hank”

lastName
firstName

Person object
instance variable
instance variable
age 19
gender 'M'
retired false
instance variable
instance variable
instance variable
“ K1S 5B6”

postalCode
province
city
streetName
unitLetter 'B'
“ Ontario”
“ Ottawa”
“ Oak St.”
instance variable
instance variable
instance variable
instance variable
instance variable
account variable
address
instance variable
Address object
streetNumber 15
instance variable

owner

accountNumber 178193
balance 100
instance variable
instance variable
instance variable
BankAccount object
Also, in regards to this example, we can actually simplify things a little by using intermediate
variables. Note that the following code does the same thing, but seems a bit simpler:
Notice that the owner is of type Person, which will contain all the owners information (i.e.,
names, address, phone number, age, etc…). We can test it as follows:
COMP1005/1405 – Variables and Objects Fall 2009

- 54 -
BankAccount account; / / decl ar e a BankAccount var i abl e
Per son p; / / t empor ar y var i abl e t hat r ef er s t o t he Per son
Addr ess adr; / / t empor ar y var i abl e t hat r ef er s t o t he Addr ess

/ / Cr eat e t he bank account and set i t s i nst ance var i abl es
account = new BankAccount ( ) ;
account. account Number = 178193;
account. bal ance = 100;

/ / st or e t he new Person obj ect i n a t empor ar y var i abl e so t hat we can r ef er
/ / t o t he per son usi ng j ust p i n t he code bel ow i nst ead of account.owner
p = new Per son( ) ;
account. owner = p;
p. f i r st Name = " Hank" ;
p. l ast Name = " Ur chi f " ;
p. age = 19;
p. gender = ' M' ;
p. r et i r ed = false;

/ / st or e t he new Address obj ect i n a t empor ar y var i abl e so t hat we can r ef er
/ / t o t he addr ess usi ng j ust adr i n t he code bel ow i nst ead of p.address
adr = new Addr ess( ) ;
p. addr ess = adr;
adr. st r eet Number = 1526;
adr. uni t Let t er = ' B' ;
adr. st r eet Name = " Oak St . " ;
adr. ci t y = " Ot t awa" ;
adr. pr ovi nce = " Ont ar i o" ;
adr. post al Code = " K1S 5B6" ;


Variable Bindings
At this point, it would be good to mention something about variable bindings. A variable is
bound to (i.e., attached to) a value when we assign something to it using the = operator.
You need to understand that each time we make a new object, we get back a new instance of
that object which is stored in a separate location in memory.

myCar

yourCar bobsCar



Car myCar , your Car , bobsCar ;

myCar = new Car ( ) ;
your Car = new Car ( ) ;
bobsCar = new Car ( ) ;


So in the above code, all three variables point to different/unique objects. In fact, it is often
the case that one or more variables may point to (or refer to) the same object.

COMP1005/1405 – Variables and Objects Fall 2009

- 55 -
myCar

yourCar

For example two people may share the same car as in this code:

Car myCar, your Car ;

myCar = new Car ( ) ;
your Car = myCar;

In this case, the two variables point to the exact same object.
What would happen if we re-assign a value to an object variable ?
Car myCar;

myCar
myCar

After Before

myCar = new Car ( ) ;
/ / . . .
myCar = new Car ( ) ;

In this case, the previous Car
object that was assigned to the
myCar variable is discarded and
the variable simply points to the
new Car object.

There is one more thing that you should understand with respect to variables that store
objects. When we declare a variable of some object type, but do not assign it a value, the
J AVA compiler will usually complain. For example, consider the following code:
Car myCar;
Syst em. out . pr i nt l n( myCar) ;
This code will generate a compile error saying:
variable myCar might not have been initialized
What does this mean ? Well, when we declare a variable, it simply reserves space, but no
object is actually created. Objects that have not been through the construction process are
considered to be "undefined". The construction process occurs only when we use the new
keyword (e.g., new Car ( ) ).

Sometimes we do not want to give a value right away to our variable. We are allowed to
assign the value of null to any reference variable. The following code will satisfy the compiler:
Car myCar = null;
Syst em. out . pr i nt l n( myCar) ;
The above example does not generate an error, and prints out null to the console. Null
represents an undefined object and you should remember that if you declare a variable of
some type of Object but do not give it a value, then its value will always be null.

COMP1005/1405 – Variables and Objects Fall 2009

- 56 -

Supplemental Information (The Garbage Collector)
Objects that have been created and are no longer being "pointed to" by anyone are garbage
collected. Garbage collection usually does not happen immediately when you are finished
with an object. It may happen at a later convenient time (decided upon by the J ava Virtual
Machine). Note that in the example above, the old car may not be garbage collected
immediately, and so it may actually remain around for a while.

The garbage collector :
• is a low priority process running in the J ava Virtual Machine
• is used to free up memory for unused objects
• is necessary to release resources
• runs automatically, programmer need not do anything
• can be forced to run by using System.gc()



Object Summary
So in summary:
• objects must be defined before we can use them

• objects are each defined as their own class

• objects contain instance variables (or attributes) which are just regular “run-of-the-
mill” variables that happen to store information about a particular object.

• objects may contain other objects

• we access the “ insides” of an object by using the dot . operator followed by the name
of the information that we are trying to get

• we can modify the “ insides” of an object by setting its instance variables to new
values using the = operator.


COMP1005/1405 – Variables and Objects Fall 2009

- 57 -

2.3 Initializing Our Objects By Using Constructors
When testing our newly created object examples in the previous section (i.e., Person, Address,
BankAccount), we explicitly assigned values to each instance variable and then printed them
out to test whether or not it worked. Now, we will see an easier way to put some initial values
into our objects using the notion of a constructor.
A constructor is a special chunk of code that we can write in our object classes that will allow
us to hide the ugliness of setting all of the initial values for our objects each time we use
them. The main advantage of making a constructor is that it will allow us to reduce the
amount of code that we need to write each time we make a new object.
Consider the simple Person object that we defined previously:

class Person {
St r i ng f i r st Name;
St r i ng l ast Name;
}

Recall the code that we wrote to try out our new object:

Per son p1, p2;

/ / eat Cr e t he f i r st per son and set hi s i nst ance var i abl es
p1 = new Per son( ) ;
p1. f i r st Name = " Hank" ;
p1. l ast Name = " Ur chi f " ;

/ / Cr eat e t he second per son and set her i nst ance var i abl es
p2 = new Per son( ) ;
p2. f i r st Name = " Hol l y" ;
p2. l ast Name = " Day" ;

Notice that for each Person, we must write out the names of the instance variables each time
so that we can assign them some initial values. This is tedious and annoying.
Recall that the code new Car() is responsible for making (or constructing) a Car object. The
Car() portion of the code is actually called a constructor. We explained this earlier as if we
were asking the Car factory to create (or construct) a Car object for us. Similarly, new
Person() calls the Person class constructor that creates a new Person object that we can use
in our program.
In real life however, when ordering something from a factory we usually specify some of the
features that we would like our object to have (e.g., the car’s make, model, color, other options,
COMP1005/1405 – Variables and Objects Fall 2009

- 58 -
etc…). In J AVA, we can do the same thing. Whenever we use a constructor in our code, we
can supply some additional information pertaining to some of the features that we would like
our new object to have.
In J AVA, whenever we use round brackets (), we are often allowed to supply some additional
information in between the brackets. This additional information is known as parameters.
So, what we would like to do in our program is something like this:


Per son p1, p2;

p1 = new Per son( " Hank" , " Ur chi f " ) ;
p2 = new Per son( " Hol l y" , " Day" ) ;

This would be “wonderful” because it greatly reduces the amount of code that we would need
to write. However, the code above will not compile because we first need to tell J AVA that we
will be passing all of this new information as parameters to the constructor. That is, we need
to specify exactly what information is being “passed in” (or supplied) as well as what to do with
the information.
To make this work, we therefore need to go back to our Person class and write our own
constructor. Normally we do this just below the list of instance variables. Below is the
constructor that we need for our Person class:

class Per son {
St r i ng f i r st Name;
St r i ng l ast Name;

/ / Thi s i s a const r uct or
Per son( St r i ng s1, St r i ng s2) {
this. f i r st Name = s1;
this. l ast Name = s2;
}
}

Notice that the constructor has the same name as the class itself (i.e., Person). Also notice
that when defining a constructor we DO NOT use the new word anywhere. That is, new is
only used to call (i.e., to use) a constructor.
Notice that some code appears between the round brackets ( ). We will look at this in a
moment. You should also notice that there is an opening brace character { and later …
aligned underneath the constructor name is the closing brace } character. These braces
define what is called the body of the constructor. The body represents all the code that is to
be evaluated by J AVA whenever we call the constructor.
COMP1005/1405 – Variables and Objects Fall 2009

- 59 -
Notice the code inside the body. It is code that assigns values to the instance variables
firstName and lastName. This code looks similar in format to what we were doing in our test
code. Look at it side by side:
TEST CODE:

p1. f i r st Name = " Hank" ;
p1. l ast Name = " Ur chi f " ;
CONSTRUCTOR CODE:

this. f i r st Name = s1;
this. l ast Name = s2;

Let us examine this a bit more carefully. Notice that in both cases, we are setting the values
for the firstName and lastName instance variables. Take note of the object that appears in
front of the dot operator in each case. In the test code, p1 represents the person whose name
that we were trying to set. The word this in the constructor must therefore also represent that
same person, otherwise the code would not work.
In fact, the word this is a special word in J AVA that is used in constructors (and in other places
as we will see later) to represent the object that we are constructing. So, when we say:

p1 = new Per son( " Hank" , " Ur chi f " ) ;

J AVA goes into our constructor to evaluate our code and at that time, the word this will
represent (or refer to) the person p1 that we constructed. Then when we say:

p2 = new Per son( " Hol l y" , " Day" ) ;

J AVA goes into the constructor again, making a new object again and then the word this will
represent (or refer to) the second person p2 that we constructed.

So the word this is just a temporary “nickname” for the particular object that is being created
and used in the constructor.
Notice what else differed from the test case and our constructor. The firstName and
lastName are set to s1 and s2, respectively. That means, s1 must refer to “Hank” and s2
must refer to “Urchif” in order for our code to work. This is indeed what is happening. To
understand, we must look in between the round brackets now.
Notice that the code between the brackets looks like two variable declarations separated by a
comma. These are called the parameters of the constructor. A parameter actually is a
variable … a temporary one … that can be used only within the constructor but not outside of
it.
J ust as the word this referred to the particular person that we were trying to set the names for,
the strings s1 and s2 refer to the particular values that we want the firstName and lastName
instance variables to be set to. Hence, when we say:
p1 = new Per son( " Hank" , " Ur chi f " ) ;
then s1 becomes a “nickname” for the incoming value “Hank” and s2 becomes a “nickname”
for the incoming value “Urchif”.
COMP1005/1405 – Variables and Objects Fall 2009

- 60 -
That is, in our constructor, whenever we refer to s1, we are actually referring to the string
“Hank” that was passed in as a parameter. The actual name of these parameters is
unimportant. We could have chosen any names. For example, we could have written the
constructor in any of the following ways:

Per son( St r i ng a, St r i ng b) {
this. f i r st Name = a;
this. l ast Name = b;
}



Per son( St r i ng name1, St r i ng name2) {
this. f i r st Name = name1;
this. l ast Name = name2;
}



Per son( St r i ng initialFirstName, St r i ng initialLastName) {
this. f i r st Name = initialFirstName;
this. l ast Name = initialLastName;
}

It is important that the type of s1 matches the type of firstName and that the type of s2
matches the type of lastName. That is why we had to specify the type of s1 and s2 to be
String parameters within the round brackets. So the following constructors would NOT be
valid:

Per son( int s1, int s2) { / / WRONG TYPES! ! !
this. f i r st Name = s1;
this. l ast Name = s2;
}



Per son Address s1, Person s2) { / / WRONG TYPES! ! ! (
this. f i r st Name = s1;
this. l ast Name = s2;
}

In general, a constructor should ALWAYS set ALL of the instance variables to some initial
value. For example, consider the Person class being defined as follows …
COMP1005/1405 – Variables and Objects Fall 2009

- 61 -
class Person {
St r i ng f i r st Name;
St r i ng l ast Name;
int age;
char gender ;
boolean r et i r ed;
}
We would thus write a constructor as follows:
Per son( St r i ng fn, St r i ng ln, int a, char g, boolean r) {
this. f i r st Name = fn;
this. l ast Name = ln;
this. age = a;
this. gender = g;
this. r et i r ed = r;
}

And here is how we would call the constructor (as it pertains to an earlier example):


Per son p1, p2;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hol l y" , " Day" , 67, ' F' , true) ;

Of course, we can create constructors for all of our classes. Consider the larger example that
used a Person object, an Address object and a BankAccount object:

BankAccount account;

account = new BankAccount ( ) ;
account. account Number = 178193;
account. bal ance = 100;

account. owner = new Per son( ) ;
account. owner . f i r st Name = " Hank" ;
account. owner . l ast Name = " Ur chi f " ;
account. owner . age = 19;
account. owner . gender = ' M' ;
account. owner . r et i r ed = false;

account. owner . addr ess = new Addr ess( ) ;
account. owner . addr ess. st r eet Number = 1526;
account. owner . addr ess. uni t Let t er = ' B' ;
account. owner . addr ess. st r eet Name = " Oak St . " ;
account. owner . addr ess. ci t y = " Ot t awa" ;
account. owner . addr ess. pr ovi nce = " Ont ar i o" ;
account. owner . addr ess. post al Code = " K1S 5B6" ;

COMP1005/1405 – Variables and Objects Fall 2009

- 62 -
Here is what the simplified code would look like if we created constructors as well for Address
and BankAccount:

BankAccount account;
Per son per;
Addr ess adr;

adr = new Addr ess( 1526, ' B' , " Oak St . " , " Ot t awa" , " Ont ar i o" , " K1S 5B6" ) ;
per = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false, adr) ;
account = new BankAccount ( 178193, 100, per) ;

We could even simplify this into one big line … but this looks a little uglier:

BankAccount account;

account = new BankAccount ( 178193, 100,
new Per son( " Hank" , " Ur chi f " , 19, ' M' , false,
new Addr ess( 1526, ' B' , " Oak St . " ,
" Ot t awa" , " Ont ar i o" ,
" K1S 5B6" ) ) ) ;


Of course, for this to all work, we would need to define the following constructors in the
Person, Address and BankAccount classes, respectively:


Addr ess( int n, char ul, St r i ng sn, St r i ng ci, St r i ng pr, St r i ng pc) {
this. st r eet Number = n;
this. uni t Let t er = ul;
this. st r eet Name = sn;
this. ci t y = ci;
this. pr ovi nce = pr;
this. post al Code = pc;
}



Per son( St r i ng fn, St r i ng ln, int a, char g, boolean r, Addr ess adr) {
this. f i r st Name = fn;
this. l ast Name = ln;
this. age = a;
this. gender = g;
this. r et i r ed = r;
this. addr ess = adr;
}


COMP1005/1405 – Variables and Objects Fall 2009

- 63 -

BankAccount ( int acc, float bal, Per son per) {
this. account Number = acc;
this. bal ance = bal;
this. owner = per;
}


Certainly, you can see that the constructor allows us to greatly simplify our code when we
need to create objects in our program.
Suppose though, that we do not know the initial value to use. That is,
suppose that we want to create an object but are unsure as to what
parameters to use. This would be analogous to the situation in real
life where someone fills out a form but leaves some information blank.
What do we do when the person leaves out information ? We have
two possible choices. Either (1) do not let them leave out any
information, or (2) choose some kind of “default” values for the blank
parts (i.e., make some assumptions by filling in something
appropriate).
At this point in our program, we have chosen to force the user of our objects to supply
parameters for ALL of the instance variables when they use (i.e., call) our constructor. So, we
have taken approach number (1) above. However, in J AVA, we are allowed to create more
than one constructor as long as the constructors each have a unique list of parameter types.
Consider the constructor for the Person class that we just defined:


Per son( St r i ng fn, St r i ng ln, int a, char g, boolean r, Addr ess adr) {
this. f i r st Name = fn;
this. l ast Name = ln;
this. age = a;
this. gender = g;
this. r et i r ed = r;
this. addr ess = adr;
}


What if, for example, we did not know the person’s age, nor their address.


Per son p1, p2;
p1 = new Per son( " Hank" , " Ur chi f " , , ' M' , false, ) ;
p2 = new Per son( " Hol l y" , " Day" , , ' F' , true, ) ;


In this situation, we are not allowed to pass in nothing as our parameters. But what we ARE
allowed to do is to make another constructor that leaves these two parameters out:
COMP1005/1405 – Variables and Objects Fall 2009

- 64 -


Per son( St r i ng fn, St r i ng ln, char g, boolean r) {
this. f i r st Name = fn;
this. l ast Name = ln;
this. gender = g;
this. r et i r ed = r;
this. age = 0; / / No age gi ven, choose def aul t val ue
this. addr ess = null; / / No addr ess gi ven, choose def aul t val ue
}

Notice that there are two less parameters now (i.e., no age and no address). However, you
will notice that we still set the age and address to some default values of our choosing. What
is a good default age and address ? Well, we used 0 and null. The word null is a special
J AVA keyword that represents an undefined object. That is, since we do not have an
Address object to store in the address instance variable, we leave it undefined by setting it to
null. Alternatively, we could have created a “dummy” Address object with some kind of
values that would be recognizable as invalid such as:
this. addr ess = new Addr ess( - 1, ' ?' , " Unknown" , " Unknown" , " Unknown" , " Unknown" ) ;
It is entirely up to you to decide what the default values should be. Make sure not to pick
something that may be mistaken for valid data. For example, some bad default values for
firstName and lastName would be “J ohn” and “Doe” because there may indeed be a real
person called “J ohn Doe”.
Here is one more constructor that takes no parameters. It has a special name and is known
as the zero-parameter constructor, the zero-argument constructor or the default
constructor. This time there are no parameters at all, so we need to pick default values for
all the instance variables:


Per son( ) {
this. f i r st Name = "UNKNOWN";
this. l ast Name = "UNKNOWN";
this. gender = '?';
this. r et i r ed = false;
this. age = 0;
this. addr ess = null;
}

Remember, that we can make many constructors. We just write them all one after each other
in our class definition and the user can decide which one to use at any time. Here is our
resulting Person class definition showing the three constructors that we just defined …
COMP1005/1405 – Variables and Objects Fall 2009

- 65 -

class Person {
St r i ng f i r st Name;
St r i ng l ast Name;
int age;
char gender ;
boolean r et i r ed;
Addr ess addr ess;

/ / Thi s i s t he zer o- par amet er const r uct or
Per son( ) {
this. f i r st Name = " UNKNOWN" ;
this. l ast Name = " UNKNOWN" ;
this. gender = ' ?' ;
this. r et i r ed = false;
this. age = 0;
this. addr ess = null;
}

/ / Thi s i s a 4- par amet er const r uct or
Per son( St r i ng f n, St r i ng l n, char g, boolean r ) {
this. f i r st Name = f n;
this. l ast Name = l n;
this. gender = g;
this. r et i r ed = r ;
this. age = 0;
this. addr ess = null;
}

/ / Thi s i s a 6- par amet er const r uct or
Per son( St r i ng f n, St r i ng l n, int a, char g, boolean r , Addr ess adr ) {
this. f i r st Name = f n;
this. l ast Name = l n;
this. age = a;
this. gender = g;
this. r et i r ed = r ;
this. addr ess = adr ;
}
}

At any time we can use any of these constructors:

Per son p1, p2, p3,

p1 = new Per son( ) ;
p2 = new Per son( " Hol l y" , " Day" , ' F' , true) ;
p3 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false,
new Addr ess( 1526, ' B' , " Oak St . " , " Ot t awa" ,
" Ont ar i o" , " K1S 5B6" ) ) ;


COMP1005/1405 – Variables and Objects Fall 2009

- 66 -
Note that it is always a good idea to ensure that you have a zero-parameter constructor. As it
turns out, if you do not write any constructors, J AVA provides a zero-parameter constructor for
free. That is, we can always say new Car(), new Person(), new Address(), new
BankAccount() without even writing those constructors. However, once you write a
constructor that has parameters, the free zero-parameter constructor is no longer available.
That is, for example, if you write constructors in your Person class that all take one or more
parameters, then you will no longer be able to use new Person(). J AVA will generate an
error saying:
cannot f i nd symbol const r uct or Per son( )

In general, you should always make your own zero-parameter constructor along with any
others that you might like to use because others who use your class may expect there to be a
zero-parameter constructor available.


2.4 Shared Data: Static/Class Variables

Recall that an instance variable stores an attribute of a particular object. You should now
know that an object can have many attributes, and thus many instance variables. The values
of the instance variables will vary from object to object. For example, all Car objects my have
a color attribute, but the color of different cars will likely be different:



In some situations, however, there may be an attribute for an object in which the value does
not differ between objects of that class. That is, each object that we make of that type would
have the same value for that particular attribute. For example, all Car objects may have 4
wheels. We could define an instance variable for that attribute (e.g., numWheels) and simply
set all the values to 4 in the constructor as follows …
color
“ Green
color
“ Red”
color
myCar

yourCar

bobsCar
“ Blue”
COMP1005/1405 – Variables and Objects Fall 2009

- 67 -

class Car {
St r i ng col or ;
int numWheel s;

Car ( ) {
this. col or = " " ;
this. numWheel s = 4;
}
}

myCar yourCar bobsCar

4

numWheels
color
“ Green
4

numWheels
color
“ Red”

4

numWheels
color
“ Blue”

Thus, if we were to access this numWheels variable for any of our cars, we would get the
value 4:


Car myCar , your Car , bobsCar ;

myCar = new Car ( ) ;
your Car = new Car ( ) ;
bobsCar = new Car ( ) ;

Syst em. out . pr i nt l n( myCar . numWheel s) ; / / di spl ays 4
Syst em. out . pr i nt l n( your Car . numWheel s) ; / / di spl ays 4
Syst em. out . pr i nt l n( bobsCar . numWheel s) ; / / di spl ays 4


This strategy works fine and correctly stores the proper number of wheels for each Car that we
make. However, think about the duplication involved.

Every Car object that we create will store the number 4 inside of it. This takes up
space in the computer’s memory. It is wasteful to have the same value stored over and over
again when we know already that the value is the same for all cars.



COMP1005/1405 – Variables and Objects Fall 2009

- 68 -
the Car class








For situations like this … in which all instances of a class (i.e., all objects created of one type)
will share the same attribute value, you should create what is called a class variable (also
known as static variable). Class variables are "a little like" instance variables in that you can
access them as part of your object. However, they are actually stored in one location in
memory and all objects share that location.
In this example, we could create a class variable called NUM_WHEELS to store the value 4.
(Although it is not necessary, class variables names are often chosen as uppercase characters
with an underscore character _ separating the words). To create a class variable, we write it
at the top of our class definition (usually before the instance variables) and put the word static
in front of it as follows:

class Car {
static int NUM_WHEELS = 4; / / a cl ass var i abl e ( st at i c)
St r i ng col or ; / / an i nst ance var i abl e ( not st at i c)

Car ( ) {
this. col or = " " ;
}
}


Normally, for static variables, we supply an initial value for it when we create it. In this case,
we assign it the value of 4 as needed. Here is a diagram showing how the storage has now
changed …

color
numWheels
4

color
numWheels
4

color
numWheels
4
Duplicated!
(wasted space)

numWheels
4
color

numWheels
4
color

numWheels
4
color
COMP1005/1405 – Variables and Objects Fall 2009

- 69 -


NUM_WHEELS
4
the Car class


Better Now!
(no wasted space)
class variable


color
color

color
color
color


color


Notice that the number 4 is now stored in only one location … it is not duplicated every time
that a Car object is created.
We can also perhaps imagine creating classes to store other kinds of vehicles in which we
declare a similar NUM_WHEELS variable as follows:

class Mot or cycl e {
static int NUM_WHEELS = 2;
/ / . . .
}



class Uni cycl e {
static int NUM_WHEELS = 1;
/ / . . .
}



class Boat {
static int NUM_WHEELS = 0;
/ / . . .
}


The NUM_WHEELS variable is a variable just like any other. We can access it at any time
and change its value. However, the way in which we access the value. You can access
static/class variables anywhere in your program by preceding them by the class name that
they are defined in, followed by the dot . operator. The following code would produce the
values 4, 2, 1, 0 and 6 …

COMP1005/1405 – Variables and Objects Fall 2009

- 70 -

Syst em. out . pr i nt l n( " A car has " + Car . NUM_WHEELS + " wheel s" ) ;
Syst em. out . pr i nt l n( " A mot or cycl e has " + Mot or cycl e. NUM_WHEELS + " wheel s" ) ;
Syst em. out . pr i nt l n( " A uni cycl e has " + Uni cycl e. NUM_WHEELS + " wheel s" ) ;
Syst em. out . pr i nt l n( " A boat has " + Boat . NUM_WHEELS + " wheel s" ) ;

Car . NUM_WHEELS = 6;
Syst em. out . pr i nt l n( " A car now has " + Car . NUM_WHEELS + " wheel s" ) ;


With regards to the NUM_WHEELS static/class variable that we defined above, it is likely that
we would never change the value from 4 to 6 in a real system. Likely, the NUM_WHEELS
variable should remain constant. In this case, we use the term static constant (or class
constant) instead of static variable … since its value will never vary but instead remain the
same over time. In J AVA, whenever we want to prevent a value from being changed (i.e., to
make it a constant), we use the final keyword when we declare the variable as follows:


class Car {
static final int NUM_WHEELS = 4; / / a st at i c( cl ass) const ant

. . .
}


After we do this, we are no longer allowed to change the value of this variable in our program:


Car . NUM_WHEELS = 6;


Static/class variables are sometimes used to store a commonly accessed value that is shared
between many objects, such as a global counter. For example, consider a BankAccount
object, where each account is assigned a unique accountNumber. When creating a new
BankAccount, it is unlikely in real life that we would be able to specify the accountNumber.
Usually, these are assigned automatically to the customer. What accountNumber should a
new BankAccount receive? Its up to us to decide (In real life however, the Bank who is
hiring you to write their program would specify their account numbering strategy).
Let us assume that the first created account is assigned the account number 100001, the
second gets 100002, the third 100003 and so on. In this scenario, we can simply keep a
counter that starts at 100001 and increases each time a new account is created.
To do this, we can create a class variable in the BankAccount class to represent this
counter. We can call it LAST_ACCOUNT_NUMBER which will store the account number that
was last given out. We can give this variable an initial value of 100000 as follows …

COMP1005/1405 – Variables and Objects Fall 2009

- 71 -

class BankAccount {
static int LAST_ACCOUNT_NUMBER = 100000;

int account Number ; / / i nst ance var i abl e
String owner ; / / i nst ance var i abl e
float bal ance; / / i nst ance var i abl e

/ / . . .
}


Then, when a new BankAccount is created, we can give it an
accountNumber which is one more than the
LAST_ACCOUNT_NUMBER and then increment this counter to get
it ready for the next time. This counter of ours will work exactly like
one of those ticket dispensers when you wait in line at a store.

This can be done by adjusting all of the BankAccount constructors
so that they do not allow the user to "specify" the
accountNumber. But rather set it to the next available number and
then increment the counter. Here is the code that we would need to
write:


class t { BankAccoun
static int LAST_ACCOUNT_NUMBER = 100000;

int account Number ;
St r i ng owner ;
float bal ance;

/ / Thi s i s t he 0- par amet er const r uct or
BankAccount ( ) {
this. owner = " " ;
LAST_ACCOUNT_NUMBER = LAST_ACCOUNT_NUMBER + 1;
this. account Number = LAST_ACCOUNT_NUMBER;
this. bal ance = 0;
}

/ / Thi s i s a 2- par amet er const r uct or
BankAccount ( St r i ng n, float b) {
this. owner = n;
LAST_ACCOUNT_NUMBER = LAST_ACCOUNT_NUMBER + 1;
this. account Number = LAST_ACCOUNT_NUMBER;
this. bal ance = b;
}

/ / . . .
}

COMP1005/1405 – Variables and Objects Fall 2009

- 72 -
Here is some testing code:

class BankAccount Test Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
BankAccount b, m, j ;

b = new BankAccount ( " Bob" , 250. 00f ) ;
m= new BankAccount ( " Mar y" , 6387. 27f ) ;
j = new BankAccount ( " J ay" , 915. 45f ) ;

Syst em. out . pr i nt l n( b. account Number ) ;
Syst em. out . pr i nt l n( m. account Number ) ;
Syst em. out . pr i nt l n( j . account Number ) ;
}
}


The account numbers printed will be 100001, 100002 and 100003.




Chapter 3
Decision Making


What is in This Chapter ?
In this chapter we discuss how to write code that makes decisions using the if statement and
the switch statement.







COMP1005/1405 – Decision Making Fall 2009

- 74 -

3.1 Using the IF Statement

It is often necessary to be able to make a decision in a program and to act accordingly. For
example, assume that we would like to examine a student's final mark in this course and print
out an appropriate message. For example:

If the student’s grade is less than 50 then print out "Oh well, there's always next term”,
otherwise print out "Congratulations!".

You will notice that this sentence is logically made up of 3 parts to it: (1) the part that checks
whether or not the student passed (2) the part that specifies what to do if they do not pass, and
(3) the part that specifies what to do if the student does pass.

We can use the if statement in J AVA to make a similar decision like this:


if ( gr ade < 50)
Syst em. out . pr i nt l n( " Oh wel l , t her e' s al ways next t er m. " ) ;
else
Syst em. out . pr i nt l n( " Congr at ul at i ons! " ) ;


Depending on the value of the grade variable, the code will print out only one of the two
statements, but not both. Do you know why the following code works the same way ?:


if ( gr ade >= 50)
Syst em. out . pr i nt l n( " Congr at ul at i ons! " ) ;
else
Syst em. out . pr i nt l n( " Oh wel l , t her e' s al ways next t er m. " ) ;


In some cases, when making a decision, we do not need to “do something” in both cases. For
example, what if we just wanted to print “Congratulations!” for students who passed, and do
nothing in the case that they failed ? In this case, we could leave off the else part:


if ( gr ade >= 50)
Syst em. out . pr i nt l n( " Congr at ul at i ons! " ) ;


In the examples above, we had one line of code to evaluate for each branch of the if condition.
That is, we only had one statement to print each time. In general, when using an if statement,
you are allowed to have multiple lines of J AVA code evaluated for each case. When you have
more than one line, you need to insert some braces { } after the if and else as follows …

COMP1005/1405 – Decision Making Fall 2009

- 75 -

if ( gr ade >= 50) {
Syst em. out . pr i nt ( " Congr at ul at i ons! " ) ;
Syst em. out . pr i nt ( gr ade) ;
Syst em. out . pr i nt l n( " i s a passi ng gr ade. " ) ;
}
else {
Syst em. out . pr i nt ( gr ade) ;
Syst em. out . pr i nt l n( " i s qui t e l ow. Oh wel l , t her e' s al ways next t er m. " ) ;
}


It is often a good idea to use the braces anyway, even if you have only one line of code
because it may prevent you from making some mistakes. For example, the following code is
not the same as above:


if ( gr ade >= 50)
Syst em. out . pr i nt ( " Congr at ul at i ons! " ) ;
Syst em. out . pr i nt ( gr ade) ;
Syst em. out . pr i nt l n( " i s a passi ng gr ade. " ) ;
else
Syst em. out . pr i nt ( gr ade) ;
Syst em. out . pr i nt l n( " i s qui t e l ow. Oh wel l , t her e' s al ways next t er m. " ) ;


The code above will not compile. Since the brackets are missing, J AVA interprets the code as
if there is only one line in the if body as follows:


if ( gr ade >= 50)
Syst em. out . pr i nt ( " Congr at ul at i ons! " ) ;
Syst em. out . pr i nt ( gr ade) ;
Syst em. out . pr i nt l n( " i s a passi ng gr ade. " ) ;
else
Syst em. out . pr i nt ( gr ade) ;
Syst em. out . pr i nt l n( " i s qui t e l ow. Oh wel l , t her e' s al ways next t er m. " ) ;


It then sees the else as being out of place … and will give a compile error saying:
‘else’ without ‘if’. An even worse scenario is when J AVA does not notice the error at all.
Consider the following:


if ( gr ade >= 50)
Syst em. out . pr i nt ( " Congr at ul at i ons! " ) ;
Syst em. out . pr i nt ( gr ade) ;
Syst em. out . pr i nt l n( " i s a passi ng gr ade. " ) ;
Syst em. out . pr i nt l n( " Al l Done. " ) ;


COMP1005/1405 – Decision Making Fall 2009

- 76 -
In the above code, a grade of 75 will output the following:


Congr at ul at i ons! 75 i s a passi ng gr ade.
Al l Done.


… and a grade of 25 will output this:


25 i s a passi ng gr ade.
Al l Done.


Clearly this is wrong. Also, be careful not to place a semi-colon ; after the if statement
brackets:


if ( gr ade >= 50) ;
Syst em. out . pr i nt l n( " Congr at s! " + gr ade + " i s a passi ng gr ade. " ) ;


In the above code, a grade of 25 will output the following:


Congr at s! 25 i s a passi ng gr ade.


Why ? Because the semi-colon ; at the end of the first line tells J AVA that there is no body for
the if statement. Thus, the System.out.println(…) line is outside the if statement altogether
and is therefore always evaluated.

Notice that we used < and >= in our examples above. These are called logical operators
because they take two values, compare them, and then determine a logical boolean result of
true or false. They are often used to compare numbers. Here is the list of logical operators
that we can use:
• < less than
• <= less than or equal to
• == equal to
• != not equal to
• >= greater than or equal to
• > greater than
When writing an IF statement, you must make sure that whatever is placed between the round
brackets ( ) is a J AVA expression that results in a boolean (i.e., the answer is either true or
false).
COMP1005/1405 – Decision Making Fall 2009

- 77 -
It is necessary to examine the == operator for a moment. When dealing with primitive types,
this operator basically tests whether or not the two primitives have the same value. So, x == 5
will return true if x is equal to 5 (i.e., x has the value of 5).
However, when dealing with objects, the == operator does not check to see if the objects are
equal, instead it checks to see if the objects are identical. Two objects are only considered
to be identical if they are the exact same object in the computer’s memory.
Recall this code:

Car myCar , your Car , bobsCar ;

myCar = new Car ( ) ;
your Car = new Car ( ) ;
bobsCar = new Car ( ) ;

In this case, each Car is its own unique object in memory. Hence the following code would
always produce false:

if ( myCar == your Car )
Syst em. out . pr i nt l n( true) ;
else
Syst em. out . pr i nt l n( false) ;

Consider, however, the following code:

Car myCar , your Car ;

myCar = new Car ( ) ;
your Car = ar ; myC
if ( myCar == your Car )
Syst em. out . pr i nt l n( true) ;
else
Syst em. out . pr i nt l n( false) ;

Now the code would always produce true because the object stored in the myCar variable is
the exact same object stored in the yourCar variable.
If we really wanted to check if two objects were equal (i.e., they have the same values inside
them), then we must use a special function called equals(). The equals() function is used as
follows …
COMP1005/1405 – Decision Making Fall 2009

- 78 -

Car myCar , your Car ;

myCar = new Car ( ) ;
your Car = new Car ( ) ;
if ( myCar .equals(your Car ))
Syst em. out . pr i nt l n( true) ;
else
Syst em. out . pr i nt l n( false) ;

In this case, J AVA will check to determine whether or not the two Car objects have the same
internal attributes (e.g., make, model, color … whatever we defined as attributes).
Hence, the rule of thumb is that whenever you want to know if two primitives are equal, you
use the == operator and whenever you want to know if two objects are equal, you use the
equals() function.
But, there is a word of caution. Most of the existing pre-defined objects in J AVA
(e.g., String, Date, etc..) have an appropriate equals() function that properly checks
equality. However, when you create your own objects (e.g., Person, Car,
BankAccount), the equals() function will be default still only check whether the
objects are identical. Hence, you would need to write your own equals() method to make
sure that it properly checks for equality. We will discuss this in a later chapter.
Consider now an example in which we take the number grade of the student (i.e., from 0% to
100%) and output a letter grade (from F to A+). How would we do this ? Well we would need
to understand which letter grade corresponds to which number grades.
A =80% - 100% D =50% - 59%
B =70% - 79% F =0% - 49%
C =60% - 69%

So, given a grade, how can we output the appropriate letter ? We could use the if statement:

if ( gr ade >= 80)
Syst em. out . pr i nt l n( " A" ) ;
else {
if ( gr ade >=70)
Syst em. out . pr i nt l n( " B" ) ;
else {
if ( gr ade >= 60)
Syst em. out . pr i nt l n( " C" ) ;
{ else
if ( gr ade >= 50)
Syst em. out . pr i nt l n( " D" ) ;
else
Syst em. out . pr i nt l n( " F" ) ;
}
}
}
COMP1005/1405 – Decision Making Fall 2009

- 79 -
You may notice now that we have an if statement inside of an else statement’s body. This is
known as nested if statements. Notice how the code is indented carefully so that when
reading the code we can see what is inside each if/else statement’s body.
In fact, an if/else statement is actually considered one J AVA statement. So, we do not need
the braces here. In fact, we can even place the succeeding if statements up on the same line
as the else statements and align everything up on the left. The following is how we usually
write such code:

if ( gr ade >= 80)
Syst em. out . pr i nt l n( " A" ) ;
else if ( gr ade >=70)
Syst em. out . pr i nt l n( " B" ) ;
else if ( gr ade >= 60)
Syst em. out . pr i nt l n( " C" ) ;
else if ( gr ade >= 50)
Syst em. out . pr i nt l n( " D" ) ;
else
Syst em. out . pr i nt l n( " F" ) ;


Consider another example in which we are given an integer representing a month and we
would like to store (in a variable called days) the number of days in that month (we will
assume that it is not a leap year). Here is the table of information that we need to know:

Month Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Days 31 28 31 30 31 30 31 31 30 31 30 31

Here is how we can do this with if statements:


int mont h, days;

mont h = . . . / / assume t hat we got t hi s f r omt he user

if ( mont h == 1)
days = 31;
else if ( mont h == 2)
days = 28;
else if ( mont h == 3)
days = 31;
else if ( mont h == 4)
days = 30;
... etc ...


However, you can see that the if statement will be 24 lines long! Since there are only 3
values for the months (i.e., 31, 30 and 28), there should be a way to arrange it in a format like
this …

COMP1005/1405 – Decision Making Fall 2009

- 80 -
int mont h, days;

mont h = . . . / / assume t hat we got t hi s f r omt he user
if ( . . . ) / / J an, Mar , May, J ul , Aug, Oct , Dec
days = 31;
else if ( . . . ) / / Apr , J un, Sep, Nov
days = 30;
else if ( . . . ) / / Feb onl y
days = 28;


To do this, we need to have some way of asking whether or not the month is J an or Mar or
May or J ul etc… Well, it is a good thing then that J AVA supplies us with what is known as
Boolean operators. Here are three useful boolean operators
• && conditional and
• || conditional or
• ! not (prefix)
The && and || operators are used in between two J AVA expressions that evaluate to
booleans. Below is a table explaining the results of using these two boolean values b1and b2
in various expressions:

b1 b2 if (b1 && b2) if (b1 || b2) if ( !b1) if (b1)
false false false false true false
false true false true true false
true false false true false true
true true true true false true

Notice that the && results in true only when both booleans are true, and false otherwise.
Conversely, the || results in false only when both booleans are false, and true otherwise.
Also note that the ! results in the opposite value of the boolean.

We can actually combine multiple booleans operators together in various ways:

if (bool ean1 && bool ean2 && bool ean3) || ( bool ean1 && !bool ean2)) …

So how do we use this in our month/days example ? Well, each boolean expression can be in
a format something like this: (month == 2). Therefore, our solution may look like this:


if (( mont h == 1) || ( mont h == 3) || ( mont h == 5) || ( mont h == 7) ||
( mont h == 8) || ( mont h == 10) || ( mont h == 12) )
days = 31;
else if (( mont h == 4) || ( mont h == 6) || ( mont h == 9) || ( mont h == 11) )
days = 30;
else if (mont h == 2)
days = 28;

COMP1005/1405 – Decision Making Fall 2009

- 81 -
So we just used a bunch of “or” operators. We can actually simplify the code by noticing
something interesting. Since the first 2 if statements checked 11 of the 12 months, then we
do not need to ask if (month == 2) in the last if statement because it is the only possible
month remaining (assuming that the month was in the valid range of 1 to 12). Also, it does not
matter which order we check the months in. So, the following code will also do the same
thing, but is much shorter:

if (mont h == 2)
days = 28;
else if (( mont h == 4) || ( mont h == 6) || ( mont h == 9) || ( mont h == 11) )
days = 30;
else
days = 31;


As one more example, how would we use an if statement that prints out the message “valid”
when a given number is within a given range (e.g., from 1 to 10) and “invalid” otherwise ?
Here is a common mistake that most student’s make:


int number ;

number = . . . ; / / code l ef t out i nt ent i onal l y

if (1 <= number <= 10)
Syst em. out . pr i nt l n( " Val i d" ) ;
else
Syst em. out . pr i nt l n( " I nval i d" ) ;


J AVA does not allow us to check ranges in this manner. Instead, we have to check the two
sides of the range separately.


int number ;

number = . . . ; / / code l ef t out i nt ent i onal l y

if ( ( number >= 1) && ( number <= 10) )
Syst em. out . pr i nt l n( " Val i d" ) ;
else
Syst em. out . pr i nt l n( " I nval i d" ) ;


Consider this example that defines three Person objects and then tries to determine the oldest
person. The code is not complete … it uses pseudo-code. How would you complete it ? …
COMP1005/1405 – Decision Making Fall 2009

- 82 -

Per son p1, p2, p3;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hol l y" , " Day" , 67, ' F' , true) ;
p3 = new Per son( " Bobby" , " Socks" , 12, ' M' , false) ;

/ / Wr i t e code t o set t he ol dest var i abl e t o be t he ol dest per son.
/ / Remember t hat we can say p.age t o get t he age of per son p
/ /
/ / Per son ol dest ;
/ / IF ( t he 1
st
per son i s ol der t han t he 2
nd
and 3
r d
per son)
/ / t hen t he ol dest shoul d be t he 1
st
per son;
/ / OTHERWISE IF ( t he 2
nd
per son i s ol der t han t he 1
st
and 3
r d
per son)
/ / t hen t he ol dest shoul d be t he 2
nd
per son;
/ / OTHERWISE
/ / t he ol dest shoul d be t he 3
r d
per son;



Here is one solution. Do you see how it follows from the pseudo code ?


Per son p1, p2, p3;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hol l y" , " Day" , 67, ' F' , true) ;
p3 = new Per son( " Bobby" , " Socks" , 12, ' M' , false) ;

/ / Wr i t e code t o set t he ol dest var i abl e t o be t he ol dest per son.
Per son ol dest ;

if ( ( p1. age > p2. age) && ( p1. age > p3. age) )
ol dest = p1;
else if ( ( p2. age > p1. age) && ( p2. age > p3. age) )
ol dest = p2;
else
ol dest = p3;


Now what code would you write if you wanted print out a message saying “All retired” if all
three people are retired. If none are retired then print out “None retired”. Here is the pseudo
code:
IF (the 1
st
, 2
nd
and 3
rd
person are all retired) then print “All Retired”;
IF (the 1
st
, 2
nd
and 3
rd
person are all not retired) then print “None Retired”;

Here is the solution in actual J AVA code …
COMP1005/1405 – Decision Making Fall 2009

- 83 -

if ( p1. r et i r ed && p2. r et i r ed && p3. r et i r ed)
Syst em. out . pr i nt l n( " Al l Ret i r ed" ) ;
if ( ! p1. r et i r ed && ! p2. r et i r ed && ! p3. r et i r ed)
Syst em. out . pr i nt l n( " None Ret i r ed" ) ;


Lastly, assume that we wanted to have a special “Grandma/Granddaughter Night” at the
theatre and wanted to give a discount of 50% to women that are retired or to girls who are 12
and under. The discount should otherwise be 0%. How would we use if statements to
examine a Person p and appropriately set the discount variable ?


Per son p = new Per son( ) ;

. . . / / some code omi t t ed t o set t he name, age, gender , et c. . .

/ / Wr i t e code t o comput e t he appr opr i at e di scount
int di scount ;

IF ( t he per son i s f emal e and i f t hey ar e under 13 or r et i r ed)
t hen t he di scount i s 50%
OTHERWISE
t he di scount i s 0%


Here is the solution in actual J AVA code:


Per son p = new Per son( ) ;

. . . / / some code omi t t ed t o set t he name, age, gender , et c. . .

/ / Wr i t e code t o comput e t he appr opr i at e di scount
int di scount = 0;

if ( ( p. gender == ' F' ) && ( p. age < 13 | | p. r et i r ed) )
di scount = 50;


As you can see, working with the IF statements in J AVA is quite easy as long as you know
what objects you have and what information is inside of them.


COMP1005/1405 – Decision Making Fall 2009

- 84 -

Supplemental Information: The Selection Operator

In addition to the if statement, J AVA allows a Selection Operator to be used. Here is the
general format:

<booleanCondition> ? <valueIfTrue> : <valueIfFalse>

The result of the expression is one of the two values supplied, depending on whether the
condition is true or false. For example,

St r i ng r esul t ;

r esul t = gr ade > 50 ? " pass" : " f ai l " ;


does the same thing as:

St r i ng r esul t ;

if ( gr ade > 50)
r esul t = " pass" ;
else
r esul t = " f ai l " ;


So this saves the amount of code to write, but it does sacrifice a little bit of readability of the
code.




3.2 The Switch Statement

In addition to the if statement, there is another construct in J AVA called the switch statement
which is beneficial for simplifying code that contains nested if statements.

Consider a simplified letter grade that is given for a course project (i.e., A, B, C, D, F). (i.e., for
the purpose of brevity, we will assume that there is no A+, A-, B+, B- ... grades). Sometimes
when a student receives a letter grade he/she would like to know what percentage range
corresponds to that letter. For example, a B corresponds to a grade between 70% and 79%.

Consider code that uses if statements to compute the proper range as follows …

COMP1005/1405 – Decision Making Fall 2009

- 85 -

char aLet t er ;

aLet t er = . . . ; / / t he code f or obt ai ni ng t he gr ade has been omi t t ed

if ( aLet t er == ' A' )
Syst em. out . pr i nt l n( " 80%- 100%" ) ;
else if ( aLet t er == ' B' )
Syst em. out . pr i nt l n( " 70%- 79%" ) ;
else if ( aLet t er == ' C' )
Syst em. out . pr i nt l n( " 60%- 69%" ) ;
else if ( aLet t er == ' D' )
Syst em. out . pr i nt l n( " 50%- 59%" ) ;
else if ( aLet t er == ' F' )
Syst em. out . pr i nt l n( " 0%- 49%" ) ;
else
Syst em. out . pr i nt l n( " not def i ned" ) ;


Looking at the code, we can see that there are 5 if statements and it looks very
messy. If we later decide to handle the A+, A-, B+, etc.. cases, then the code
will look much longer and cluttered. There is a better way to write this code.
We can use a switch statement. The switch statement is typically used in
situations where we have a sequence of nested if statements in which only one
of the if statements is to be executed.

The switch statement has the following format in J AVA:

switch ( aPrimitiveExpression) {
case val1:
/ *one
break;
or mor e l i nes of J AVA code*/;
case val2:
/ *one or mor e l i nes of J AVA code*/;
break;
. . .
case valN:
/ *one
break;
default:
or mor e l i nes of J AVA code*/;
/ *one or mor e l i nes of J AVA code*/;
break;
}

In the above code, aPrimitiveExpression is either a primitive variable (e.g., a variable of type
int, char, float, etc…) or any J AVA code that results in a primitive value. The values of val1,
val2, …, valN must all be primitive constant values of the same type as
aPrimitiveExpression.

COMP1005/1405 – Decision Making Fall 2009

- 86 -
The switch statement works as follows:

1. It evaluates aPrimitiveExpression to obtain a value (the expression MUST result in a
primitive data type, it cannot be an object)

2. It then checks the values val1, val2, …, valN in order from top to bottom until a value is
found equal to the value of aPrimitiveExpression. If none match, then the default
case is executed.

3. It then evaluates the statements corresponding to the case whose value matched.

4. If there is a break at the end of the lines of J AVA code for that case, then the switch
statement quits. Otherwise it continues to evaluate all the successive case statements
that follow ... until a break is found or until no more cases remain.
Here is how can we make use of the switch statement for solving the grade range problem ?
char aLet t er ;

aLet t er = . . . ; / / t he code f or obt ai ni ng t he gr ade has been omi t t ed

switch( aLet t er ) {
case ' A' : Syst em. out . pr i nt l n( " 80%- 100%" ) ; break;
case ' B' : Syst em. out . pr i nt l n( " 70%- 79%" ) ; break;
case ' C' : Syst em. out . pr i nt l n( " 60%- 69%" ) ; break;
case ' D' : Syst em. out . pr i nt l n( " 50%- 59%" ) ; break;
case ' F' : Syst em. out . pr i nt l n( " 0%- 49%" ) ; break;
default: Syst em. out . pr i nt l n( " not def i ned" ) ;
}


We can clearly see that the code is simpler to read. However, this is not the only advantage of
a switch statement. Consider our previous example in which we were given an integer
representing a month and we would like to know the number of days in that month:


if ( mont h == 2)
days = 28;
else if ( ( mont h == 4) | | ( mont h == 6) | | ( mont h == 9) | | ( mont h == 11) )
days = 30;
else
days = 31;


Here is how we can use a switch statement …

COMP1005/1405 – Decision Making Fall 2009

- 87 -

switch( mont h) {
case 2: days = 28; break;
case 4:
case 6:
case 9:
case 11: days = 30; break;
default: days = 31;
}


Note that when the month is 4, 6, 9, or 11, then the days=30 is evaluated. The code is not
necessarily much shorter, but it is simpler to read. This is the main advantage of a switch
statement.
One thing that needs mentioning is that the value of the cases must be primitive literals.
That is, they cannot be expressions, ranges, nor Strings. Nor can we make use of the logical
operators such as and'ing and or'ing.
So these two examples will not work:

switch ( age) {
case 1- 12: pr i ce = 5. 00; break; / / Won’ t compi l e
case 13- 17: pr i ce = 8. 00; break; / / Won’ t compi l e
case 18- 54: pr i ce = 10. 00; break; / / Won’ t compi l e
default: pr i ce = 6. 00;
}


switch ( name) {
case " Mar k" : bonus = 3; break; / / Won’ t compi l e
case " Bet t y" : bonus = 2; break; / / Won’ t compi l e
case " J ane" : bonus = 1; break; / / Won’ t compi l e
default: bonus = 0;
}




3.3 A Decision Making Example Program

Consider writing a program that will be placed at a kiosk in front of a bank to allow customers
to determine whether or not they qualify for the bank’s new “Entrepreneur Startup Loan”.
Assume that this kind of loan is only given out to someone who is currently employed and who
is a recent University graduate, or someone who is employed, over 30 and has at least 10
years of full-time work experience.

COMP1005/1405 – Decision Making Fall 2009

- 88 -
The program should display information to the screen as well as ask the user various
questions … and then determine if the person qualifies. Using the Scanner object to get user
input, and various variables to store the input, lets try to write such a program.

Here is a start to the program that displays some opening instructions:

import j ava. ut i l . Scanner ;

class LoanQual i f i cat i onPr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
/ / Get a Scanner obj ect f or user i nput
Scanner keyboar d = new Scanner ( Syst em. i n) ;

/ / Di spl ay some openi ng i nst r uct i ons
Syst em. out . pr i nt l n( " Bank of J ava" ) ;
Syst em. out . pr i nt l n( " ============" ) ;
Syst em. out . pr i nt l n( " Fol l ow t he i nst r uct i ons bel ow t o " +
" det er mi ne whet her or not you qual i f y " +
" f or a Ent r epr eneur St ar t up Loan. . . \ n" ) ;
}
}

Now we need to start asking the user some questions. We can first ask whether or not he/she
is employed. Likely we will as for a character such as ‘y’, ‘Y’, ‘n’ or ‘N’. We can then check
the input and store the employment status as a boolean. Here is the new code to add:

char char I nput ;
boolean empl oyed;

/ / Det er mi ne whet her or not t he user i s empl oyed
Syst em. out . pr i nt ( " Ar e you cur r ent l y empl oyed ( Y/ N) ? " ) ;
char I nput = keyboar d. next Li ne( ) . char At ( 0) ;
if ( ( char I nput == ' y' ) | | ( char I nput == ' Y' ) )
empl oyed = true;
else
empl oyed = false;


We can similarly ask whether or not they have a recent University degree:

boolean hasDegr ee;

/ / Det er mi ne i f t he user has a r ecent Uni ver si t y degr ee
Syst em. out . pr i nt ( " Di d you gr aduat e wi t h a Uni ver si t y degr ee " +
" i n t he past 6 mont hs ( Y/ N) ? " ) ;
char I nput = keyboar d. next Li ne( ) . char At ( 0) ;
if ( ( char I nput == ' y' ) | | ( char I nput == ' Y' ) )
hasDegr ee = true;
else
hasDegr ee = false;


COMP1005/1405 – Decision Making Fall 2009

- 89 -
In a similar manner, we can ask for the user’s age and the number of years that they have
worked at full time status:

int age, year sWor ked;

/ / Det er mi ne t he user ' s age
Syst em. out . pr i nt ( " How ol d ar e you ? " ) ;
age = keyboar d. next I nt ( ) ;

/ / Det er mi ne t he number of year s wor ked at f ul l t i me st at us
Syst em. out . pr i nt ( " How many year s have you been wor ki ng " +
" at f ul l t i me st at us ? " ) ;
year sWor ked = keyboar d. next I nt ( ) ;
Syst em. out . pr i nt l n( " \ n" ) ;


Finally, we can determine whether or not they qualify:

char char I nput ;
boolean empl oyed, hasDegr ee;
int age, year sWor ked;

. . .

/ / Now det er mi ne whet her or not t he per son qual i f i es f or t he l oan
if ( empl oyed) {
if ( hasDegr ee)
Syst em. out . pr i nt l n( " Congr at ul at i ons! You qual i f y " +
" f or t he ESL l oan. " ) ;
else {
if ( age >= 30) {
if ( year sWor ked >= 10)
Syst em. out . pr i nt l n( " Congr at ul at i ons! You qual i f y " +
" f or t he ESL l oan. " ) ;
else
Syst em. out . pr i nt l n( " Sor r y, you must have wor ked " +
" at l east 10 year s at f ul l " +
" t i me st at us t o qual i f y. " ) ;
}
else
Syst em. out . pr i nt l n( " Sor r y, you must be a r ecent " +
" Uni ver si t y gr aduat e or be at " +
" l east 30 year s of age. " ) ;
}
}
else {
Syst em. out . pr i nt l n( " Sor r y, you must be cur r ent l y " +
" empl oyed t o qual i f y. " ) ;
}


COMP1005/1405 – Decision Making Fall 2009

- 90 -
Here is the code altogether …


import j ava. ut i l . Scanner ;

class LoanQual i f i cat i onPr ogr am {
public static void mai n( St r i ng ar gs[ ] ) {

char char I nput ;
boolean empl oyed, hasDegr ee;
int age, year sWor ked;

/ / Get a Scanner obj ect f or user i nput
Scanner keyboar d = new Scanner ( Syst em. i n) ;

/ / Di spl ay some openni ng i nst r uct i ons
Syst em. out . pr i nt l n( " Bank of J ava" ) ;
Syst em. out . pr i nt l n( " ============" ) ;
Syst em. out . pr i nt l n( " Fol l ow t he i nst r uct i ons bel ow t o " +
" det er mi ne whet her or not you qual i f y " +
" f or a Ent r epr eneur St ar t up Loan. . . \ n" ) ;

/ / Det er mi ne whet her or not t he user i s empl oyed
Syst em. out . pr i nt ( " Ar e you cur r ent l y empl oyed ( Y/ N) ? " ) ;
char I nput = keyboar d. next Li ne( ) . char At ( 0) ;
if ( ( char I nput == ' y' ) | | ( char I nput == ' Y' ) )
empl oyed = true;
else
empl oyed = false;

/ / Det er mi ne i f t he user has a r ecent Uni ver si t y degr ee
Syst em. out . pr i nt ( " Di d you gr aduat e wi t h a Uni ver si t y degr ee " +
" i n t he past 6 mont hs ( Y/ N) ? " ) ;
char I nput = keyboar d. next Li ne( ) . char At ( 0) ;
if ( ( char I nput == ' y' ) | | ( char I nput == ' Y' ) )
hasDegr ee = true;
else
hasDegr ee = false;

/ / Det er mi ne t he user ' s age
Syst em. out . pr i nt ( " How ol d ar e you ? " ) ;
age = keyboar d. next I nt ( ) ;

/ / Det er mi ne t he number of year s wor ked at f ul l t i me st at us
Syst em. out . pr i nt ( " How many year s have you been wor ki ng " +
" at f ul l t i me st at us ? " ) ;
year sWor ked = keyboar d. next I nt ( ) ;
Syst em. out . pr i nt l n( " \ n" ) ;

Now det er mi ne whet her or not t he per son qual i f i es f or t he l oan / /
if ( empl oyed) {
if ( hasDegr ee)
Syst em. out . pr i nt l n( " Congr at ul at i ons! You qual i f y " +
" f or t he ESL l oan. " ) ;
COMP1005/1405 – Decision Making Fall 2009

- 91 -
else {
if ( age >= 30) {
if ( year sWor ked >= 10)
Syst em. out . pr i nt l n( " Congr at ul at i ons! You qual i f y " +
" f or t he ESL l oan. " ) ;
else
Syst em. out . pr i nt l n( " Sor r y, you must have wor ked " +
" at l east 10 year s at f ul l " +
" t i me st at us t o qual i f y. " ) ;
}
else
Syst em. out . pr i nt l n( " Sor r y, you must be a r ecent " +
" Uni ver si t y gr aduat e or be at " +
" l east 30 year s of age. " ) ;
}
}
else
Syst em. out . pr i nt l n( " Sor r y, you must be cur r ent l y " +
" empl oyed t o qual i f y. " ) ;
}
}


Here is the output from three sample runs of the program:


Bank of J ava
============
Fol l ow t he i nst r uct i ons bel ow t o det er mi ne whet her or not you qual i f y f or a
Ent r epr eneur St ar t up Loan. . .

Ar e you cur r ent l y empl oyed ( Y/ N) ? Y
Di d you gr aduat e wi t h a Uni ver si t y degr ee i n t he past 6 mont hs ( Y/ N) ? N
How ol d ar e you ? 28
How many year s have you been wor ki ng at f ul l t i me st at us ? 8


Sor r y, you must be a r ecent Uni ver si t y gr aduat e or be at l east 30 year s of age.



Bank of J ava
============
Fol l ow t he i nst r uct i ons bel ow t o det er mi ne whet her or not you qual i f y f or a
Ent r epr eneur St ar t up Loan. . .

Ar e you cur r ent l y empl oyed ( Y/ N) ? Y
Di d you gr aduat e wi t h a Uni ver si t y degr ee i n t he past 6 mont hs ( Y/ N) ? Y
How ol d ar e you ? 22
How many year s have you been wor ki ng at f ul l t i me st at us ? 1


Congr at ul at i ons! You qual i f y f or t he ESL l oan.


COMP1005/1405 – Decision Making Fall 2009

- 92 -

Bank of J ava
============
Fol l ow t he i nst r uct i ons bel ow t o det er mi ne whet her or not you qual i f y f or a
Ent r epr eneur St ar t up Loan. . .

Ar e you cur r ent l y empl oyed ( Y/ N) ? N
Di d you gr aduat e wi t h a Uni ver si t y degr ee i n t he past 6 mont hs ( Y/ N) ? Y
How ol d ar e you ? 24
How many year s have you been wor ki ng at f ul l t i me st at us ? 3


Sor r y, you must be cur r ent l y empl oyed t o qual i f y.





Chapter 4
Defining Your Own Functions/Methods


What is in This Chapter ?
Object-Oriented Programming (OOP) involves creating objects of our own. In this set of
notes, we will discuss how to write functions and procedures for our objects, which are also
called methods. Methods are the set of instructions that perform some operations with the
data or objects that you need to work with. You will spend 99% of your time writing methods
in J AVA, since all code must belong to some method. Finally, we will discus static methods,
which are more general methods that do not need an object in order to compute something.





COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 94 -

4.1 What are Methods ?

At this point in the course, we have already defined a few of our own objects (i.e., Car,
Person, Address, BankAccount, etc..). Do you remember why we defined them ? We did it
so that we could group information together in a meaningful way. For example, we associated
information such as firstName, lastName, age, gender and retired as all being part of a
Person object. This idea of grouping information/attributes together relates to real-life since
all real life objects (tangible or not) are defined by their attributes. Over time, these attributes
will change. Hence at any time in the object’s existence, the combined attributes represent the
object’s current state at that point in time.
There is also something else that real objects have in addition to their state … they have
behavior. That is, objects in general, can do things and when used, they respond in certain
ways. For example, you can ask a person “What is your name ?”, and they usually respond
by stating the value of their name attribute. Also, a person can be asked to “Stand up!”, and
we usually expect that the person would change their position from a lying/sitting position to a
standing position.
Objects also have the ability to change their own state as well as the state of other objects.
For example, if a Person spends money from their bank account, the account’s balance is
changed afterwards. Also, if a Person gives money to another Person, one person’s money
increases, while the other’s money decreases.
Object-Oriented programming languages such as J AVA, allow us to define both state and
behavior for our objects. In fact, objects will generally have a whole set of pre-defined
functions/behaviors that we can use to modify and manipulate the object in some way. It is
important that you understand that Objects are defined by two things: (1) their state, and (2)
their behaviors:
Object =
STATE
+
BEHAVIOR

Since we have already discussed how to define state (or attributes, instance variables) for our
objects, it is now time to discuss how we can define the object’s behaviors. All object
behaviors are defined individually, one at a time. Each behavior is defined in something
called a method. In English, the word “method” is defined as “a way of doing something”.
So, we will actually be defining “how” the object will behave (i.e., what it needs to do) when it is
asked to exhibit one of its behaviors.

COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 95 -
So, you can think of each object in J AVA as having a list of instance variables as well as a list
of method definitions:
Person object

There are basically two types of methods in J AVA:
• Functions – methods that do something and then return a value
• Procedures – methods that do something but do not return a value.


4.2 Defining Methods - Functions

To help motivate our discussion, recall this code that we used to determine a discount for
“Grandma/Granddaughter Night” at the theatre. Recall that the discount should be 50% for
women that are retired or to girls who are 12 and under. For all other people, the discount
should otherwise be 0%:


Per son p = new Per son( ) ;

. . . / / some code omi t t ed t o set t he name, age, gender , et c. . .

/ / Wr i t e code t o comput e t he appr opr i at e di scount
int di scount = 0;

if ( ( p. gender == ' F' ) && ( p. age < 13 | | p. r et i r ed) )
di scount = 50;

String object
“ Urchif”
methods
public int comput ei st ( ) {
/ / I f you can r ead
/ / t hi s, you have
/ / amazi ng eyesi ght
/ / and t oo much t i me.
if nder == ' F} ( ( p. ge
return 50;
else
return 0;
}
false retired
instance variable
'M' gender
instance variable
19 age
instance variable

firstName
instance variable
lastName
instance variable
“ Hank”
String object
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 96 -
Recall that the code p.age actually goes into the p Person object and retrieves its age
attribute’s value. Similarly, the p.gender code retrieves the gender value from Person p.
We can actually define a function in the Person class that computes the appropriate discount
for the person. Such a function should return an integer answer of either 0 or 50, representing
the percentage of discount that the Person is entitled to have.
To do this, we need to understand first how the function is to be used. In J AVA, all
functions/methods are invoked (i.e., called or used) by using the dot . operator on the object
whose method you want to use. We then need to have a meaningful name for the method.
In our example, perhaps we can call it computeDiscount since it aptly describes what we are
trying to do. Method calls all require round brackets ( ) at the end. So to call the method, we
may write something like this:
p.computeDiscount()
Since this function will return the discount value, we can then make use of the value returned
from this method call and simplify our code as follows:

Per son p = new Per son( ) ;

. . . / / some code omi t t ed t o set t he name, age, gender , et c. . .

/ / Wr i t e code t o comput e t he appr opr i at e di scount
int di scount = p.computeDiscount();


Of course though, the code above does not compile, at least not until we define the method
called computeDiscount in the Person class. So now lets go do that.



class Per son {
/ / These ar e t he i nst ance var i abl es
St r i ng f i r st Name;
St r i ng l ast Name;
int age;
char gender ;
boolean r et i r ed;

/ / These ar e t he const r uct or s
Per son( ) {
. . .
}
Per son( St r i ng f n, St r i ng l n, int a, char g, boolean r ) {
. . .
}
/ / St ar t wr i t i ng your met hods her e
. . .
Put your methods here
}
Recall that the Person class
already has instance variables
defined in it as well as one or
more constructors. To define a
new behavior for the Person, we
simply add a new method …
usually at the bottom of the class
(but still within the final closing
brace } for the class … as
shown here:

So how do we write the method ?
The steps are given on the next
page.
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 97 -
STEP 1 – METHOD SIGNATURE:
The 1
st
step is to write the method’s signature. The method signature is the first line of the
method where you specify its name, parameters and return type. It is called a signature
because it is what makes the method unique with respect to any other methods in this class.
Here is the basic format of our method:

int computeDiscount() {
}

Notice that the first word in the method’s signature is the return type of the method (i.e., the
type of thing (i.e., primitive or object) that will be returned by the method). In our example, we
are trying to compute a discount value which is an integer. Hence, we specify int to be the
type of thing returned. The next part of the signature is the method name. J ust like variable
names, the method name should have no spaces or weird characters and should be lower
case with capitalized words (except the first). The method name should be immediately
followed by opening and closing round brackets ( ). Finally, we use the braces { } to
encapsulate the code that is to be evaluated when the method is called … the code within the
braces is called the method body … which is currently empty at the moment.
STEP 2 – RETURN VALUE:
The 2
nd
step when writing a method is to create a temporary variable of the same type as the
return type and to return it at the end of the method. (This step is not required, but it always
helps you “keep your head straight” when you are learning to write methods. After you
complete this step, your method should compile fine, although it is not complete). Here is how
we can do this for our example:

int ut eDi scount ( ) { comp
int answer = 0;


return answer ;
}


Notice that the variable answer will contain the final answer for our method. We start off by
assuming a value of 0 but will modify this soon. The last line of the method returns the
variable’s value. In J AVA, the return statement is used to stop the method immediately and
return with the specified value.
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 98 -
STEP 3 –METHOD BODY:
The 3
rd
and last step is to write the remaining part of the method body. This is the interesting
part that requires you to think. In our example, we need to write code that will determine the
correct discount. How can we do this ? Well, we actually already did this previously so we
can start with our previous code. Here is how we can do this, although some changes are
needed:

int Di scount ( ) { comput e
int answer = 0;

int di scount = 0;
if ( ( p. gender == ' F' ) && ( p. age < 13 | | p. r et i r ed) )
di scount = 50;

return answer ;
}


In the code above, you will notice some issues. To begin, we can replace the discount
variable with our new answer variable which will contain the proper discount at the end of the
method.


int ut eDi scount ( ) { comp
int answer = 0;

if ( ( p. gender == ' F' ) && ( p. age < 13 | | p. r et i r ed) )
answer = 50;

return answer ;
}


Next, you will notice that the variable p is not defined anywhere in this method. This variable
was the variable defined in our other program and it was used to represent the person for
whom we are to compute the discount. p.gender actually went into Person object p and
retrieved that person’s gender. Similarly p.age and p.retired retrieved those attributes of p.
But we are now writing code that is inside the Person class definition. The code we are
writing must work for all Person objects, not just p. Hence, the p must vary according to the
person that we are trying to compute the discount for.

We need to replace p by the word this, because this is the placeholder (i.e., nickname) of the
object for which we called this computeDiscount method. That is, whenever we are defining
an object’s behavior (i.e., writing a method), the word this represents the object that will be
exhibiting the behavior … the object whose data we need to access.

(For a review of the word this, perhaps go back and read the notes on constructors).

So, here is resulting code …
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 99 -

int comput eDi scount ( ) {
int answer = 0;

if ( ( this. gender == ' F' ) && ( this. age < 13 | | this. r et i r ed) )
answer = 50;

return answer ;
}

In fact, we can simplify this code even further by realizing that the method is simple enough to
not need the answer variable:
int comput eDi scount ( ) {
if ( ( this. gender == ' F' ) && ( this. age < 13 | | this. r et i r ed) )
return 50;
else
return 0;
}

Here is a picture of what is happening inside the object when we test it using the code
p.computeDiscount();

String object
“ Urchif”
“ Hank”
String object

lastName
firstName
age
instance variable
19
gender
'M'
retired
false
instance variable
instance variable
methods
int computeDiscount() {

if ( ( this. gender == ' F' ) && ( this. age < 13 | | this. r et i r ed) )



return 50;
else

return 0;
}

p variable

instance variable
instance variable
Person object
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 100 -
We have successfully define our first method/behavior ! But … practice makes perfect … so
let us do another method. Recall the code we wrote that determined the oldest Person:
Per son p1, p2, p3;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hol l y" , " Day" , 67, ' F' , true) ;
p3 = new Per son( " Bobby" , " Socks" , 12, ' M' , false) ;

/ / Wr i t e code t o set t he ol dest var i abl e t o be t he ol dest per son.
Per son ol dest ;

if ( ( p1. age > p2. age) && ( p1. age > p3. age) )
ol dest = p1;
else if ( ( p2. age > p1. age) && ( p2. age > p3. age) )
ol dest = p2;
else
ol dest = p3;


Lets write a method called isOlderThan() which allows us to determine whether one Person is
older than another. The method should return a boolean (i.e., true or false):

boolean isOlderThan() {
boolean answer = false;
. . .
return answer ;
}


To call this method, we may write something like this: p1.isOlderThan()
But wait! The something seems to be missing. We need to know the other person that we
want to compare p1’s age with. If we look at our original code, we are comparing p1 with p2,
p1 with p3, p2 with p1, and p2 with p3. So we need a way of “telling the method” which
person to compare p1 with. This is additional information that the method requires.
Recall from our discussion on constructors that we can pass-in additional information as
parameters between the round ( ) brackets. So, we can pass in the person to compare with
as follows: p1.isOlderThan(p2)
We then need to go into our method definition and define the incoming parameter by
specifying its type as well as giving it a name:

boolean der Than( Person x) { i sOl
boolean answer = false;
. . .
return answer ;
}

COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 101 -
Now the method has the two Person objects that are needed to make the comparison (i.e.,
this and x). So, how do we write the rest of the code ? We just need to compare this’s age
with x’s age as follows:

boolean i sOl der Than( Per son x) {
boolean answer = false;

if ( this. age > x. age)
answer = true;
else
answer = false;

return answer ;
}


And we are done! Remember though, that this can be simplified:

boolean i sOl der Than( Per son x) {
if ( this. age > x. age)
return true;
else
return false;
}


and even more if we really want to be efficient…

boolean i sOl der Than( Per son x) {
return ( this. age > x. age) ;
}


Here is what the resulting test code looks like when we make use of this method now:

if ( p1.isOlderThan(p2) && p1.isOlderThan(p3))
ol dest = p1;
else if ( p2.isOlderThan(p1) && p2.isOlderThan(p3))
ol dest = p2;
else
ol dest = p3;


COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 102 -
Consider what happens inside p1 as we call p1.isOlderThan(p2):
p1 variable

For an interesting exercise, how would we write another method so that it takes two Person
objects as parameters and checks both of them ? We would use it as follows:

if ( p1.isOlderThan(p2,p3))
ol dest = p1;
else if ( p2.isOlderThan(p1,p3))
ol dest = p2;
else
ol dest = p3;


Here is the new method (notice that it now takes 2 parameters):


boolean i sOl der Than( er son x, Per son y) { P
if ( ( this. age > x. age) && ( this. age > y. age) )
return true;
else
return false;
}

“ Urchif”
“ Hank”
lastName
firstName


instance variable
instance variable
String object
age 19
gender 'M'
retired false
instance variable
instance variable
instance variable
methods
boolean isOlderThan(Person x) {


if ( this. age > x. age)

return true;

else

return false;
}
“ Day”
“ Holl y”

lastName
firstName
p2 variable
Person object
String object
String object
instance variable
instance variable
age
67
gender 'F'
retired true
instance variable
instance variable
instance variable
String object
Person object

COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 103 -
You may have noticed that we wrote two methods with the same name (i.e., isOlderThan).
How can J AVA allow this ? That is, when we call the method, how does J AVA know which
method to use ?
Recall that the first line of the method is the method’s signature. The signature is what
makes it unique. In our first isOlderThan() method, there was one parameter. In the second
isOlderThan() method there are two parameters. That is how J AVA distinguishes between
the two methods. When calling a method, J AVA always looks at the method’s name as well
as its list of parameter types to determine which one to call.
When we write two methods in the same class with the same name, this is called
overloading. Overloading is only allowed if the similar-named methods have a different set
of parameters. Normally, when we write programs we do not think about writing methods with
the same name … we just do it naturally. For example, imagine implementing a variety of
eat() methods for the Person class as follows:
void eat ( Appl e x) { …}
void eat ( Or ange x) { …}
void eat ( Banana x, Banana y) { …}
Notice that all the methods are called eat(), but that there is a variety or parameters, allowing
the person to eat either an Apple, an Orange or two Banana objects. Imagine the code below
somewhere in your program that calls the eat() method, passing in anObject of some type:
Per son p;

p = new Per son( ) ;
p. eat ( z) ;
How does J AVA know which of the 3 eat() methods to call ? Well, J AVA will look at what kind
of object z actually is. If it is an Apple object, then it will call the 1
st
eat() method. If it is an
Orange object, it will call the 2
nd
method. What if z is a Banana ? It will NOT call the 3
rd

method … because the 3
rd
method requires 2 Bananas and we are only passing in one. A call
of p.eat(z, z) would call the 3
rd
method if z was a banana. In all other cases, the J AVA
compiler will give you an error stating:
cannot f i nd symbol met hod eat(...)
where the ... above is a list of the types of parameters that you are trying to use.
J AVA will NOT allow you to have two methods with the same name and parameter types
because it cannot distinguish between the methods when you go to use them. So, the
following code will not compile:

double calculatePayment( BankAccount account ) {. . . }
double calculatePayment( BankAccount x) {. . . }

You will get an error saying:

calculatePayment(BankAccount) i s al r eady def i ned i n Person
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 104 -
Getting back to writing methods, how can we write a method called oldest() that returns the
oldest of the 3 Person objects ? If we had such a method, then we can simplify our earlier
test code to this one simple line:

ol dest = p1.oldest(p2,p3);


Do you notice something different with the type of thing returned from the method ? This
method would now need to return a Person object (i.e., the oldest of the three) instead of a
boolean. Hence, the return type for the method must be different. Here is what we would
start with:


Person oldest(Person x, Person y) {
Per son answer ;

...

return answer ;
}


Then, we simply check all of the ages as before:


Per son ol dest ( Per son x, Per son y) {
if ( ( this. age > x. age) && ( this. age > y. age) )
return this;
else if ( ( x. age > this. age) && ( x. age > y. age) )
return x;
else
return y;
}


You should notice that this code looks VERY much like our original test code. Basically, all
we have done is moved the age-checking code into the method. So, a method is really just a
chunk of code that is defined for an object.



4.3 Defining Methods - Procedures

Now lets write a couple of procedures (i.e., methods that do not return anything). Lets write a
method called retire() that changes a Person object’s retired state from false to true. You
should realize that there is no “answer” for such a method. We simply need to change the
value of the retired variable, but we don’t need to return any specific object or primitive from
the method. In this case, there is no return value. In J AVA, when we do not have a value to
return, we specify the return type for the method to be void as follows:
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 105 -

void retire() {
...
}


So what do we need to do in the method ? We just need to change the retired variable to
true:

void r et i r e( ) {
this. r et i r ed = true;
}


That’s it! Lets try one more. How would we write a method called swapNameWith() that
allows one person to change names with another Person ? Hopefully, you now understand
that the return type should be void, since it performs an operation on the Person but no answer
is required to be returned. Also, you should realize that the 2
nd
person should be passed-in as
a parameter. Here is the structure:


void swapNameWith(Person x) {
. . .
}


How do we swap the names ? Well, the first person’s firstName attribute should become the
second person’s firstName attribute and vice versa. We also need to do this for the
lastName attributes. Here is a first attempt:


void swapNameWi t h( Per son x) {
/ / Swap t he f i r st names
this. f i r st Name = x. f i r st Name;
x. f i r st Name = this. f i r st Name;

/ / Swap t he l ast names
this. l ast Name = x. l ast Name;
x. l ast Name = this. l ast Name;
}


But this code will not work. Why ? Well, the first line of code replaces this’s firstName with
x’s firstName. Hence, we are overwriting (i.e., erasing) this’s firstName value … and the
value will be gone forever. So on the second line of code, when we try to put this’s firstName
into x, we are actually putting in x’s firstName that we stored there from the first line. So both
persons will end up with the same firstName, which was x’s original firstName. The same
problem occurs for the lastName. The following shows what happens with the firstName …
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 106 -

“ Hank”

firstName

this Person object
String object
instance variable
“ Holl y”

firstName

x Person object
String object
instance variable
“ Hank”

firstName
this Person object
String object
instance variable
“ Holl y”

firstName
x Person object
String object
instance variable
… …
… …
before the method is called after this. f i r st Name = x. f i r st Name;

“ Hank”

firstName

this Person object
String object
instance variable
“ Holl y”

firstName

x Person object
String object
instance variable

firstName
this Person object
instance variable
“ Holl y”

firstName
x Person object
String object
instance variable
… …
… …
after x. f i r st Name = this. f i r st Name; after the method completes

To fix this, we need a temporary variable to hold on to this’s firstName/lastName so that we
don’t lose it:

void swapNameWi t h( Per son x) {
String tempName;

t he st names / / Swap f i r
tempName = this. f i r st Name;
this. f i r st Name = x. f i r st Name;
x. f i r st Name = tempName;

/ / Swap t he l ast names
tempName = this. l ast Name;
this. l ast Name = x. l ast Name;
x. l ast Name = tempName;
}


Here is the diagram showing how the swapping takes place …
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

-

“ Hank”

firstName

this Person object
String object
instance variable
“ Holl y”

firstName

x Person object
String object
instance variable


after tempName = this. f i r st Name;
tempName variable
“ Hank”

firstName
this Person object
String object
instance variable
“ Holl y”

firstName
x Person object
String object
instance variable


after this. f i r st Name = x. f i r st Name;
tempName variable
“ Hank”

firstName

this Person object
String object
instance variable
“ Holl y”

firstName

x Person object
String object
instance variable


“ Hank”

firstName
this Person object
String object
instance variable
“ Holl y”

firstName
x Person object
String object
instance variable


after the method completes
tempName variable
after x. f i r st Name = tempName;
You should realize that the tempName variable is indeed temporary. In fact, after the method
completes, the tempName variable no longer exists. Any variables defined within a method
are non-existent after the method completes … hence the term “temporary”.
107 -
At this point lets step back and see what we
have done. We have created 5 interesting
methods (i.e., behaviors) for our Person object
(i.e., computeDiscount(), isOlderThan(),
oldest(), retire() and swapNameWith()).
All of these methods were written one after
another within the class, usually after the
constructors. Here, to the right, is the
structure of the class now as it contains all the
class Per son {
/ / These ar e t he i nst ance var i abl es
St r i ng f i r st Name;
St r i ng l ast Name;
int age;
char gender ;
boolean r et i r ed;

/ / These ar e t he const r uct or s
Per son( ) { . . . }
Per son( St r i ng f n, St r i ng l n, . . . ) { . . . }

/ / These ar e our met hods
int comput eDi scount ( ) { . . . }
boolean i sOl der Than( Per son x) { . . . }
Per son ol dest ( Per son x, Per son y) { . . . }
void r et i r e( ) { . . . }
void swapNameWi t h( Per son x) { . . . }
}
Our methods
methods that we wrote (the method code has
been left blank to save space).




COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 108 -
Now although these methods were defined in the class, they are not used within the class.
We wrote various pieces of test code that call the methods in order to test them. Here is a
more complete test program that tests all of our methods in one shot:

class Ful l Per sonTest Pr ogr am {
public st at i c void mai n( St r i ng ar gs[ ] ) {
Per son p1, p2, p3;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hol l y" , " Day" , 67, ' F' , true) ;
p3 = new Per son( " Bobby" , " Socks" , 12, ' F' , false) ;

Syst em. out . pr i nt l n( " The di scount f or Hank i s " +
p1. computeDiscount( ) ) ;
Syst em. out . pr i nt l n( " The di scount f or Hol l y i s " +
p2. computeDiscount( ) ) ;
Syst em. out . pr i nt l n( " The di scount f or Bobby i s " +
p3. computeDiscount( ) ) ;

Syst em. out . pr i nt l n( " I s Hank ol der t han Hol l y ? . . . " +
p1. isOlderThan( p2) ) ;
Syst em. out . pr i nt l n( " The ol dest per son i s " +
p1. oldest( p2, p3) . f i r st Name) ;

Syst em. out . pr i nt l n( " Hank i s r et i r ed ? . . . " + p1. r et i r ed) ;
p1. retire( ) ;
Syst em. out . pr i nt l n( " Hank i s r et i r ed ? . . . " + p1. r et i r ed) ;
p2. swapNameWith( p3) ;
Syst em. out . pr i nt l n( " Hol l y’ s name i s now: " +
p2. f i r st Name + " " + p2. l ast Name) ;
Syst em. out . pr i nt l n( " Bobby’ s name i s now: " +
p3. f i r st Name + " " + p3. l ast Name) ;
}
}


Here is the output:


The di scount f or Hank i s 0
The di scount f or Hol l y i s 50
The di scount f or Bobby i s 50
I s Hank ol der t han Hol l y ? . . . false
The ol dest per son i s Holly
Hank i s r et i r ed ? . . . false
Hank i s r et i r ed ? . . . true
Hol l y’ s name i s now: Bobby Socks
Bobby’ s name i s now: Holly Day



COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 109 -
4.4 Null Pointer Exceptions

In regards to calling methods, we must make sure that the object whose method we are trying
to call has been through the construction process. For example, consider the following code:

Per son p;

Syst em. out . pr i nt l n( p. comput eDi scount ( ) ) ;


This code will not compile. J AVA will give a compile error for the second line of code saying:

var i abl e p mi ght not have been i ni t i al i zed
J AVA is trying to tell you that you forgot to give a value to the variable p. In this case, we
forgot to create a Person object.
Lets assume then that we created the Person as follows and then tried to get the streetName:

Per son p;

p = new Per son( " Hank" , " Ur chi f " , 19, ' M' , f al se) ;
Syst em. out . pr i nt l n( p. addr ess. st r eet Name) ;


This code will now compile. Assume that the Person class was defined as follows:


class Per son {
St r i ng f i r st Name;
St r i ng l ast Name;
int age;
char gender ;
boolean r et i r ed;
Addr ess addr ess;

Per son( St r i ng f n, St r i ng l n, int a, char g, boolean r ) {
this. f i r st Name = f n;
this. l ast Name = l n;
this. age = a;
this. gender = g;
this. r et i r ed = r ;
}
. . .
}


Here the address attribute stores an Address object which is assumed to have an instance
variable called streetName.
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 110 -
What will happen when we do this:
p. addr ess. st r eet Name
The code will generate a java.lang.NullPointerException. That means, J AVA is telling you
that you are trying to do something with an object that was not yet defined. Whenever you get
this kind of error, look at the line of code on which the error was generated. The error is
always due to something in front of a dot . character being null instead of being an actual
object. In our case, there are two dots in the code on that line. Therefore, either p is null or
p.address is null, that is the only two possibilities. Well, we are sure that we assigned a
value to p on the line above, so then p.address must be null. Indeed that is what is
happened, as you can tell from the constructor.
To fix this, we need to do one of three things:
1. Remove the line that attempts to access the streetName from the address, and access
it late in the program after we are sure there is an address there.
2. Check for a null before we try to print it and then don’t print if it is null … but this may
not be desirable.
3. Think about why the address is null. Perhaps we just forgot to set it to a proper value.
We can make sure that it is not null by giving it a proper value before we attempt to use
it.
NullPointerExceptions are one of the most common errors that you will get when
programming in J AVA. Most of the time, you get the error simply because you forgot to
initialize a variable somewhere (i.e., you forgot to create a new object and store it in the
variable).


4.5 Displaying an Object Using toString()

Do you recall what happens when we display one of our objects to the System console ?

class MyObj ect Test Pr ogr am {
public static void mai n( St r i ng ar gs[ ] ) {
Syst em. out . pr i nt l n( new Car ( ) ) ; / / car obj ect
Syst em. out . pr i nt l n( new Per son( ) ) ; / / per son obj ect
}
}


The result on the screen was as follows:


Car @19821f
Per son@42e816

COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 111 -
Remember that J AVA displays all of the objects that you make in this manner. What J AVA
happens to be doing is converting an object to a String object first and then displaying the
resulting characters to the screen. In fact, every object in J AVA has, by default, a method
called toString() which will convert the object to a String. Hence, the following code will take
a Car and a Person object, convert them to String objects and then display the resulting
String objects:

Car c;
Per son p;
St r i ng s1, s2;

c = new Car ( ) ;
p = new Per son( ) ;
s1 = c. toString(); / / s1 wi l l be " Car @19821f "
s2 = p. toString(); / / s2 wi l l be " Per son@42e816"

Syst em. out . pr i nt l n( " The Car as a St r i ng i s " + s1) ;
Syst em. out . pr i nt l n( " The Per son as a St r i ng i s " + s2) ;


Notice that the output will be:

The Car as a St r i ng i s Car @19821f
The Per son as a St r i ng i s Per son@42e816


The String objects have the exact same characters that are displayed when we just display
the objects directly using System.out.println(). That is because when J AVA attempts to
display anything to the console, it automatically calls the toString() method for the object to
convert it to characters before displaying. So, the following two lines of code do exactly the
same thing:

Syst em. out . pr i nt l n( p) ;
Syst em. out . pr i nt l n( p.toString()) ;


Why do we care ? Well, we can actually replace the default toString() behavior by writing our
own toString() method for all of our own objects that defines exactly how to convert our object
to a String. That is, we can control the way our object “looks” when we print it on the screen.
Suppose that we wanted our Person object to display something like this:
Per son named Hank
You should notice that the first two words of this result are fixed and it is only the last part (i.e.,
the first name of the Person) that varies from person to person. We can make this to be the
standard output format for all Person objects simply by writing the following method in the
Person class:
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 112 -

public St r i ng t oSt r i ng( ) {
return ( " Per son named " + this. f i r st Name) ;
}

Notice that the method has the word public at the front. This means that the method you are
writing is publicly accessible (i.e., it can be used from anywhere in your program). This is
necessary for the toString() method, but we will defer our discussion of public method access
until a later time in the course.
Notice that the method is called toString() with no parameters and that it has a return type of
String. This is important in order for the method to be used properly by J AVA. Even the
spelling and upper/lower case letters must match exactly. Then, you may notice that the
method returns an actual String object that is made up of the letters " Person named " and
then followed by the value of this Person object’s firstName attribute.
What would therefore be the output of the following code:

Per son p1, p2, p3,

p1 = new Per son( ) ; / / assume f i r st name i s set t o " " wi t hi n const r uct or
p2 = new Per son( " Hol l y" , " Day" , ' F' , true) ;
p3 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;

Syst em. out . pr i nt l n( p1) ;
Syst em. out . pr i nt l n( p2) ;
Syst em. out . pr i nt l n( p3) ;


Here is the output … were you correct ?


Per son named
Per son named Hol l y
Per son named Hank


Now what if we wanted the output to be in this format instead:
19 year ol d Per son named Hank Ur chi f
To write an appropriate toString() method, we need to understand what is fixed in this output
and what will vary. The number 19 should vary for each person as well as the first and last
names. Here is how we could write the code (replacing our previous toString() method):

public St r i ng t oSt r i ng( ) {
return ( this. age + " year ol d Per son named " +
this. f i r st Name + " " + this. l ast Name) ;
}
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 113 -
Notice that the basic idea behind creating a toString() method is to simply keep joining
together String pieces to form the resulting String.
Now here is a harder one. Lets see if we can make it into this format:
19 year ol d non- r et i r ed per son named Hank Ur chi f
Here we have the age and names being variable again but now we also have the added
variance of their retirement status and gender. Here is one attempt:

public St r i ng t oSt r i ng( ) {
return ( this. age + " year ol d " + this. r et i r ed + " per son named "
+ this. f i r st Name + " " + this. l ast Name) ;
}


However, this is not quite correct. This would be the format we would end up with:
19 year ol d false per son named Hank Ur chi f
Notice that we cannot simply display the value of the retired attribute but instead need to write
“ retired” or “ non-retired” for the retired status.
To do this then, we will need to use an IF statement. However, in J AVA, we cannot write an IF
statement in the middle of a return statement. So we will need to do this using more than one
line of code. Lets make an answer variable to hold the result and then break down our
method into logical pieces that append to this answer:

public St r i ng t oSt r i ng( ) {
St r i ng answer;

answer = this. age + " year ol d " ;
answer = answer + this. r et i r ed;
answer = answer + " per son named " +
this. f i r st Name + " " + this. l ast Name) ;

return answer;
}


Now we can insert the appropriate if statements as follows:
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 114 -

public St r i ng t oSt r i ng( ) {
St r i ng answer ;

answer = this. age + " year ol d " ;

if ( this. r et i r ed)
answer = answer + " r et i r ed" ;
else
answer = answer + " non- r et i r ed" ;
answer = answer + " per son named " + this. f i r st Name + " " +
this. l ast Name;

return answer ;
}


The result is what we wanted. Note however, that we can simplify this code a little further:

public St r i ng t oSt r i ng( ) {
St r i ng answer = this. age + " year ol d " ;

if ( ! this. r et i r ed)
answer = answer + " non- " ;

return ( answer + " r et i r ed per son named " +
this. f i r st Name + " " + this. l ast Name) ;
}


So, you can see that the toString() method may be more than one line of code but again …
the main idea is to simply keep appending to the String as you go … building it up.



4.6 Static/Class Methods

Recall that we discussed static/class variables in a previous chapter. Class variables were
used as a means of specifying attributes that were shared among all members of the class.
We looked, for example, at how all Car objects shared the same NUM_WHEELS value of 4
and how all BankAccount objects used the same LAST_ACCOUNT_NUMBER counter to
obtain their accountNumber upon creation.
In J AVA, we can also create class methods (also known as static methods). Since methods
are behaviors for the object (i.e., not attributes), the saving of memory space is not an issue.
Therefore, the motivation for using a class/static method is different than that of using a
class/static variable. Since every method must either be an instance method or a class
method, we need to understand the difference.
COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 115 -
Instance methods represent behaviors (functions and procedures) that are to be performed
on the particular object that we called the method for (i.e., the receiver of the
method/message). That is, such methods typically access the inner parts of the receiver
object (i.e., its attributes) and perform some calculation or change the object’s attributes in
some way.
Class methods, on the other hand, are actually used (i.e., called) in a different manner,
without a particular receiver object in mind. Therefore, they do not represent a behavior to be
performed on a particular receiver object. Instead, a class method represents a general
function/procedure that simply happens to be located within a particular class, but does not
necessarily have anything to do with instances of that class.
Recall the computeDiscount() method that we created for the Person class earlier in this
chapter:

int computeDiscount() {
if ( ( this. gender == ' F' ) && ( this. age < 13 | | this. r et i r ed) )
return 50;
else
return 0;
}


This is an instance method in the Person class which computed the discount based on the
internal attributes (i.e., gender, age and retired) of the particular person that received the
method call. So, in the code below, the receiver of the 1
st
call to computeDiscount() is p1
and the receiver of the 2
nd
call to it is p2. It was necessary to specify the appropriate instance
(i.e., Person object) before calling the method in order for the instance method to know which
object to access when computing the discount.

Per son p1, p2;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hol l y" , " Day" , 67, ' F' , true) ;

Syst em. out . pr i nt l n( " Di scount f or Hank i s: " + p1.computeDiscount() ; )
Syst em. out . pr i nt l n( " Di scount f or Hol l y i s: " + p2.computeDiscount()) ;


However, we could have supplied the appropriate Person object as a parameter to the method
as follows:
. . .
Syst em. out . pr i nt l n( " Di scount f or Hank i s: " + computeDiscount(p1)) ;
Syst em. out . pr i nt l n( " Di scount f or Hol l y i s: " + computeDiscount(p2)) ;


To make this work, we would need to re-write the method as follows …

COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 116 -

int comput eDi scount ( Person p) {
if ( ( p. gender == ' F' ) && ( p. age < 13 | | p. r et i r ed) )
return 50;
else
return 0;
}


Notice how the method now accesses the person p that is passed in as the parameter, instead
of the receiver this. If we do this, the code result is now fully-dependent on the attributes of
the incoming parameter p, and hence independent of the receiver altogether. Therefore, it is
no longer acting as an instance method, but is actually a general method that can be written
and used anywhere. This is the essence of a class/static method … the idea that the method
does not necessarily need to be called by using an instance of the class.
Although we can leave the method as it is written, it would be proper coding style to indicate to
everyone that the method no longer accessed the internal attributes of the receiver object. To
do this, we simply add the word static in the method’s signature as follows:

static int comput eDi scount ( Per son p) {
if ( ( p. gender == ' F' ) && ( p. age < 13 | | p. r et i r ed) )
return 50;
else
return 0;
}


J ust as we specify the class name when we access class/static variables (e.g.,
Car.NUM_WHEEELS and BankAccount.LAST_ACCOUNT_NUMBER), we also use the
class name to call class/static methods. So we would change our test code as follows:
. . .
Syst em. out . pr i nt l n( " Di scount f or Hank i s: " + Person. comput eDi scount ( p1) ) ;
Syst em. out . pr i nt l n( " Di scount f or Hol l y i s: " + Person. comput eDi scount ( p2) ) ;


In fact, the computeDiscount() method does not even need to be written within the Person
class. We can, for example, make a completely different class and place such tool-like
methods in there. Here, for example, is a collection of static/class methods defined in a class
called UsefulPeopleTools that perform functions on Person objects that are passed in as
parameters (these methods are similar to the methods that we wrote earlier in this chapter) …

COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 117 -

class Usef ul Peopl eTool s {
static int comput eDi scount ( Per son p) {
if ( ( p. gender == ' F' ) && ( p. age < 13 | | p. r et i r ed) ) return 50;
return 0;
}
static boolean i sOl der Than( Per son x, Per son y) {
return ( x. age > y. age) ;
}
static Per son ol dest ( Per son x, Per son y) {
if ( x. age > y. age) return x;
return y;
}
static void swapNames( Per son x, Per son y) {
St r i ng t empName;

t empName = x. f i r st Name;
x. f i r st Name = y. f i r st Name;
y. f i r st Name = t empName;
t empName = x. l ast Name;
x. l ast Name = y. l ast Name;
y. l ast Name = t empName;
}
}


We can then use these methods by writing test code as follows:

class Usef ul Peopl eTool sTest Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
Per son p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
Per son p2 = new Per son( " Hol l y" , " Day" , 67, ' F' , true) ;

Syst em. out . pr i nt l n( " Di scount f or Hank i s: " +
UsefulPeopleTools.computeDiscount(p1)) ;
Syst em. out . pr i nt l n( " Di scount f or Hol l y i s: " +
UsefulPeopleTools.computeDiscount(p2)) ;
Syst em. out . pr i nt l n( " Hank i s ol der t han Hol l y ?: " +
UsefulPeopleTools.isOlderThan(p1, p2)) ;
Syst em. out . pr i nt l n( " Hol l y i s ol der t han Hank ?: " +
UsefulPeopleTools.isOlderThan(p2, p1)) ;
Syst em. out . pr i nt l n( " The ol dest of Hank and Hol l y i s: " +
UsefulPeopleTools.oldest(p1, p2)) ;

UsefulPeopleTools.swapNames(p1, p2);
Syst em. out . pr i nt l n( " Hank’ s name i s now: " + p1. f i r st Name +
" " + p1. l ast Name) ;
Syst em. out . pr i nt l n( " Hol l y’ s name i s now: " + p2. f i r st Name +
" " + p2. l ast Name) ;
}
}

COMP1005/1405 – Defining Your Own Functions / Methods Fall 2009

- 118 -
Hopefully, you will have noticed that the main difference between an instance method and a
class/static method is simply in the way in which it is called. To repeat … instance methods
are called by supplying a specific instance of the object in front of the method call (i.e., a
variable of the same type as the class in which the method is defined in), while class methods
supply a class name in from of the method call:

/ / cal l i ng an i nst ance met hod. . .
var i abl eOf TypeX.i nst anceMet hodWr i t t enI nCl assX( …) ;

/ / cal l i ng a cl ass met hod. . .
cl assNameY.st at i cMet hodWr i t t enI nCl assY( …) ;

Often, we use class methods to write functions that may have nothing to do with objects at all.
For example, consider methods that convert a temperature value from Centigrade to
Fahrenheit and vice-versa:


static double cent i gr adeToFahr enhei t ( double t emp) {
return t emp * ( 9. 0 / 5. 0) + 32. 0;
}
static double f ahr enhei t ToCent i gr ade( double t emp) {
return 5. 0 * ( t emp - 32. 0) / 9. 0;
}


Where do we write such methods since they only deal with primitives, not objects ? The
answer is … we can write them anywhere. We can place them at the top of the class that we
would like to use them in. Or … if these functions are to be used from multiple classes in our
application, we could make another tool-like class and put them in there:

class Conver si onTool s {
. . .
}

Then we could use it as follows:

double f = Conver si onTool s. cent i gr adeToFahr enhei t ( 18. 762) ;


Chapter 5
Calculations, Formatting and Conversions


What is in This Chapter ?
In this chapter we discuss how to do basic math calculations as well as use some readily
available Math functions in J AVA. We then look at the String.format() function as a means of
formatting the output of our programs. Lastly, we look at some of the ways in which we can
use typecasting to convert between various primitive data types as well as to/from Strings.







COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 120 -

5.1 Calculations and Formulas

Obviously, a computer can compute solutions to mathematical expressions. We can actually
perform simple math expressions such as:
30 +5 * 2 - 18 / 2 – 2

In such a math expression, we need to understand the order that these calculations are done
in. You may recall from high school the BEDMAS memory aid which tells you to perform
Brackets first, then Exponents, then Division & Multiplication, followed by Addition and
Subtraction.

So, for example, in the above J AVA expression, the multiplication * operator has preference
over the addition + operator. In fact, the * and / operators are evaluated first from left to right
and then the + and -. Thus, the step-by-step evaluation of the expression is:

30 +5 * 2 - 18 / 2 - 2
30 +10 - 18 / 2 - 2
30 + 10 - 9 - 2
40 - 9 - 2
31 - 2
29

We can always add round brackets (called parentheses) to the expression to force a different
order of evaluation. Expressions in round brackets are evaluated first (left to right):

(30 + 5) * (2 - (18 / 2 - 2))
35 * (2 - (18 / 2 - 2))
35 * (2 - (9 - 2))
35 * (2 - 7)
35 * -5
-175

In J AVA, it is good to add round brackets around code when it helps the person reading the
program to understand what calculations/operations are done first.

Another operator here that is often useful is the modulus operator which returns the remainder
after dividing by a certain value. In J AVA we use the % sign as the modulus operator:

10 % 2 // results in the remainder after dividing 10 by 2 which is 0
10 % 3 // results in the remainder after dividing 10 by 3 which is 1
10 % 4 // results in the remainder after dividing 10 by 4 which is 2
39 % 20 // results in the remainder after dividing 39 by 20 which is 19

COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 121 -
Note that using a modulus of 2 will allow you to determine if a number is an odd number or an
even number … which may be useful in some applications.

In addition to the standard math operations (i.e., +, -, *, / and %), there are some math
operators that can reduce the amount of code that you need to write. For example, the
following code outputs the values 8 and 9 to the console window:

int n = 8;

Syst em. out . pr i nt l n( n) ;

n = n + 1;

Syst em. out . pr i nt l n( n) ;


There is an increment operator called ++ which is a quick way to add 1 to a variable.
The following code does the same thing:

int n = 8;

Syst em. out . pr i nt l n( n) ;

n++; / / same as n = n + 1

Syst em. out . pr i nt l n( n) ;


In fact, this short form of incrementing a variable by 1 is often used within other J AVA
expressions. For example, the following produces the same result:

int n = 8;

Syst em. out . pr i nt l n( n++) ;
Syst em. out . pr i nt l n( n) ;


Here, the ++ is considered a post-operator in that it increments n after it is used in the
expression (i.e., after it is printed). The ++can also be used as a pre-operator by placing
it in front of the variable so that the value of the variable is incremented before it is used in
the expression. Hence, the following code produces the same result of 8 and 9:

int n = 8;

Syst em. out . pr i nt l n( n) ;
Syst em. out . pr i nt l n( ++n) ;


Similarly, there is a -- post-operator/pre-operator that can be used to decrement a variable.
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 122 -

In addition to these operators, there are some binary assignment operators that perform an
operation on some numbers and also assign the new value to the variable. For example,
consider the following code which outputs 8 and 80 to the console:

int n = 8;

Syst em. out . pr i nt l n( n) ;

n = n * 10;

Syst em. out . pr i nt l n( n) ;


We can replace n =n * 10 with a shorter form which does the same thing as follows:

int n = 8;

Syst em. out . pr i nt l n( n) ;

n *= 10; / / same as n = n * 10

Syst em. out . pr i nt l n( n) ;


This code multiples n by 10 and then puts the result back into n again. There are similar
binary operators for the other standard operations: +=, -=, /= and %=.

If you want to do something beyond these simple operations, you may want to look at the Math
library in J AVA. The following is a list of some of the more useful functions in the Math library
that can be used on numbers:
Trigonometric:
• Math.sin( 0) / / r et ur ns 0. 0 whi ch i s a doubl e
• Math.cos( 0) / / r et ur ns 1. 0
• Math.tan( 0. 5) / / r et ur ns 0. 5463024898437905
• Math.PI / / r et ur ns 3. 141592653589793
(note that Math.PI has no brackets … because it is a fixed constant value, not a function)

Conversion and Rounding:
• Math.round( 6. 6) / / r et ur ns 7
• Math.round( 6. 3) / / r et ur ns 6
• Math.ceil( 9. 2) / / r et ur ns 10
• Math.ceil( - 9. 8) / / r et ur ns - 9
• Math.floor( 9. 2) / / r et ur ns 9
• Math.floor( - 9. 8) / / r et ur ns - 10
• Math.abs( - 7. 8) / / r et ur ns 7. 8
• Math.abs( 7. 8) / / r et ur ns 7. 8
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 123 -
Powers and Exponents:
• Math.sqrt( 144) / / r et ur ns 12. 0
• Math.pow( 5, 2) / / r et ur ns 25. 0
• Math.exp( 2) / / r et ur ns 7. 38905609893065
• Math.log( 7. 38905609893065) / / r et ur ns 2. 0
Comparison:
• Math.max( 560, 289) / / r et ur ns 560
• Math.min( 560, 289) / / r et ur ns 289
Generation of a Random Number:
• Math.random( ) / / r et ur ns a doubl e >=0. 0 and <1. 0
These functions are used just as shown above. Notice that we need to write Math. In front of
all of these functions in order to use them. This is the way we tell J AVA that we want to use
the functions in the Math library. In fact, Math is just one of the many useful pre-defined
classes in J AVA. We do not need to import the Math class because it is in the java.lang
package and is thus automatically imported.
As an example, consider how to write a program that computes the
volume of a ball (e.g., how much space a ball takes up).
How would we write a J AVA code that computes and displays the
volume of such a ball with radius of 25cm ?
We need to understand the operations. We need to do a division,
some multiplications, raise the radius to the power of 3 and we need
to know the value of π (i.e., pi).
Here is the simplest, most straight forward solution:
int r = 25;
Syst em. out . pr i nt l n( ( 4 * Math.PI * Math.pow(r, 3) / 3) ) ;
This would also have worked, but requires the radius r to be duplicated:
Syst em. out . pr i nt l n( ( 4 * Math.PI * ( r*r*r) / 3) ) ;
We could even substitute our own value for π :
Syst em. out . pr i nt l n( ( 4 * 3. 141592653589793 * ( r*r*r) / 3) ) ;
Alternatively, we could have evaluated the 4/3 first:
Syst em. out . pr i nt l n( ( 4/ 3 * Math.PI * Math.pow(r, 3)) ) ;
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 124 -
Or even pre-compute 4 π /3 (which is roughly 4.188790204786) :
Syst em. out . pr i nt l n( ( 4. 188790204786 * Math.pow(r, 3)) ) ;
The point is that there are often many ways to write out an expression. You will find in this
course that there are many solutions to a problem and that everyone in the class will have their
own unique solution to a problem (although much of the code will be similar because we will all
usually follows the same guidelines when writing our programs).
What if the ball’s radius was stored as an instance variable in a Ball class as follows:

class Bal l {
int r adi us;

Bal l ( int r ) {
this. r adi us = r ;
}
}

Then we could make some Ball objects and use their radius in the equations:

Bal l b1, b2, b3;

b1 = new Bal l ( 25) ;
b2 = new Bal l ( 10) ;
b3 = new Bal l ( 46) ;

Syst em. out . pr i nt l n( ( 4/ 3 * Math.PI * Math.pow(b1.radius, 3)) ) ;
Syst em. out . pr i nt l n( ( 4/ 3 * Math.PI * Math.pow(b2.radius, 3)) ) ;
Syst em. out . pr i nt l n( ( 4/ 3 * Math.PI * Math.pow(b3.radius, 3)) ) ;

COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 125 -


Supplemental Information (Mathematical Operators)
J AVA also provides bitwise operators for integers and booleans:
~ bitwise complement (prefix unary operator)
& bitwise and
| bitwise or
^ bitwise exclusive-or
<< shift bits left, filling in with zeros
>> shift bits right, filling in with sign bit
>>> shift bits right, filling in with zeros
To understand how these work, you must understand how the numbers are stored as bits in
the computer. We will not discuss bit manipulation in this course.

Here is a table showing the operators in J AVA (some which we have not yet discussed) and
their precedence (i.e., the order that they get evaluated in). The topmost elements of the
table have higher precedence and are therefore evaluated first (in a left to right fashion). In
the table, <exp> represents any J AVA expression. If however, you are writing code that
depends highly on this table, then it is likely that your code is too complex.

postfix operators [] . () ++ --
prefix operators ++ -- - ~ !
creation/cast new (<typecast>)
multiplication/division/modulus * / %
addition/subtraction + -
shift << >> >>>
comparison < <= > >= instanceof
equality = = ! =
bitwise-and &
bitwise-xor ^
bitwise-or |
logical and &&
logical or ||
conditional <bool_exp>? <true_val>: <false_val>
assignment =
operation assignment += -= *= /= %=
bitwise assignment >>= <<= >>>=
boolean assignment &= ^= |=
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 126 -
5.2 Formatting Your Output

Consider the following program which asks the user for the price of a product, then displays
the cost with taxes included, then asks for the payment amount and finally prints out the
change that would be returned:
import j ava. ut i l . Scanner ;

class ChangeCal cul at or Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
/ / Decl ar e t he var i abl es t hat we wi l l be usi ng
double pr i ce, t ot al , payment , change;

/ / Get t he pr i ce f r omt he user
Syst em. out . pr i nt l n( " Ent er pr oduct pr i ce: " ) ;
pr i ce = new Scanner ( Syst em. i n) . next Fl oat ( ) ;

/ / Comput e and di spl ay t he t ot al wi t h 13%t ax
t ot al = pr i ce * 1. 13;
Syst em. out . pr i nt l n( " Tot al cost : $" + t ot al ) ;

/ / Ask f or t he payment amount
Syst em. out . pr i nt l n( " Ent er payment amount : " ) ;
payment = new Scanner ( Syst em. i n) . next Fl oat ( ) ;

/ / Comput e and di spl ay t he r esul t i ng change
change = payment - t ot al ;
Syst em. out . pr i nt l n( " Change: $" + change) ;
}
}


Here is the output from running this program with a price of 35.99 and payment of 50:


Ent er pr oduct pr i ce:
35. 99
Tot al cost : $40. 66870172505378
Ent er payment amount :
50
Change: $9. 33129827494622

Notice all of the decimal places. This is not pretty. Even worse …if you were to run the
program and enter a price of 8.85 and payment of 10, the output would be as follows:


Ent er pr oduct pr i ce:
8. 85
Tot al cost : $10. 0005003888607
Ent er payment amount :
10
Change: $- 5. 003888607006957E- 4
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 127 -
The E-4 indicates that the decimal place should be moved 4 units to the left…so the resulting
change is actually -$0.0005003888607006957. While the above answers are correct, it would
be nice to display the numbers properly as numbers with 2 decimal places.
J AVA’s String class has a nice function called format() which will allow us to format a String in
almost any way that we want to. Consider (from our code above) replacing the change output
line to:
Syst em. out . pr i nt l n( " Change: $" + St r i ng. format(" %, 1. 2f " , change)) ;
The String.format() always returns a String object with a format that we get to specify. In our
example, this String will represent the formatted change which is then printed out. Notice
that the function allows us to pass-in two parameters (i.e., two pieces of information separated
by a comma , character). Recall that we discussed parameters when we created our own
object constructors.
The first parameter is itself a String object that specifies how we want to format the resulting
String. The second parameter is the value that we want to format (usually a variable name).
Pay careful attention to the brackets. Clearly, change is the variable we want to format.
Notice the format string "%,1.2f". These characters have special meaning to J AVA. The %
character indicates that there will be a parameter after the format String (i.e., the change
variable). The 1.2f indicates to J AVA that we want it to display the change as a floating point
number with at least 1 digit before the decimal and exactly 2 digits after the decimal. The ,
character indicates that we would like it to automatically display commas in the money amount
when necessary (e.g., $1,500,320.28). Apply this formatting to the total amount as well:
import j ava. ut i l . Scanner ;

class ChangeCal cul at or Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
/ / Decl ar e t he var i abl es t hat we wi l l be usi ng
double pr i ce, t ot al , payment , change;

/ / Get t he pr i ce f r omt he user
Syst em. out . pr i nt l n( " Ent er pr oduct pr i ce: " ) ;
pr i ce = new Scanner ( Syst em. i n) . next Fl oat ( ) ;

/ / Comput e and di spl ay t he t ot al wi t h 13%t ax
t ot al = pr i ce * 1. 13;
Syst em. out . pr i nt l n( " Tot al cost : $" + St r i ng. format(" %, 1. 2f " , t ot al )) ;

/ / Ask f or t he payment amount
Syst em. out . pr i nt l n( " Ent er payment amount : " ) ;
payment = new Scanner ( Syst em. i n) . next Fl oat ( ) ;

/ / Comput e and di spl ay t he r esul t i ng change
change = payment - t ot al ;
Syst em. out . pr i nt l n( " Change: $" + St r i ng. format(" %, 1. 2f " , change)) ;
}
}

COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 128 -
Here is the resulting output for both test cases:


Ent er pr oduct pr i ce:
35. 99
Tot al cost : $40. 67
Ent er payment amount :
50
Change: $9. 33



Ent er pr oduct pr i ce:
8. 85
Tot al cost : $10. 00
Ent er payment amount :
10
Change: $- 0. 00


It is a bit weird to see a value of -0.00, but that is a result of the calculation. Can you think of a
way to adjust the change calculation of payment - total so that it eliminates the - sign ? Try it.

The String.format() can also be used to align text as well. For example, suppose that we
wanted our program to display a receipt instead of just the change. How could we display a
receipt in this format:
Pr oduct Pr i ce 35. 99
Tax 4. 68
- - - - - - - - - - - - - - - - - - - - - - - - -
Subt ot al 40. 67
Amount Tender ed 50. 00
=========================
Change Due 9. 33
If you notice, there the largest line of text is the “Amount Tendered” line which requires 15
characters. After that, the remaining spaces and money value take up 10 characters. We
can therefore see that each line of the receipt takes up 25 characters. We can then use the
following format string to print out a line of text:
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %15s%10. 2f " , aSt r i ng, aFl oat ) ) ;
Here, the %15s indicates that we want to display a string which we want to take up exactly 15
characters. The %10.2f then indicates that we want to display a float value with 2 decimal
places that takes up exactly 10 characters in total (including the decimal character). Notice
that we then pass in two parameters: which must be a string and a float value in that order
(these would likely be some variables from our program). We can then adjust our program to
use this new String format as follows …
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 129 -
import j ava. ut i l . Scanner ;

class ChangeCal cul at or Pr ogr am3 {
public static void mai n( St r i ng ar gs[ ] ) {
/ / Decl ar e t he var i abl es t hat we wi l l be usi ng
double pr i ce, t ax, t ot al , payment , change;

/ / Get t he pr i ce f r omt he user
Syst em. out . pr i nt l n( " Ent er pr oduct pr i ce: " ) ;
pr i ce = new Scanner ( Syst em. i n) . next Fl oat ( ) ;

/ / Ask f or t he payment amount
Syst em. out . pr i nt l n( " Ent er payment amount : " ) ;
payment = new Scanner ( Syst em. i n) . next Fl oat ( ) ;

/ / Comput e t he t ot al wi t h 13%t ax as wel l as t he change due
t ax = pr i ce * 0. 13;
t ot al = pr i ce + t ax;
change = payment - t ot al ;

/ / Di spl ay t he whol e r ecei pt
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %15s%10. 2f " , " Pr oduct Pr i ce" , pr i ce) ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %15s%10. 2f " , " Tax" , t ax) ) ;
Syst em. out . pr i nt l n( " - - - - - - - - - - - - - - - - - - - - - - - - - " ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %15s%10. 2f " , " Subt ot al " , t ot al ) ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %15s%10. 2f " , " Amount Tender ed" , payment ) ) ;
Syst em. out . pr i nt l n( " =========================" ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %15s%10. 2f " , " Change Due" , change) ) ;
}
}


The result is the correct formatting that we wanted. Realize though that in the above code, we
could have also left out the formatting for the 15 character strings by manually entering the
necessary spaces:

Syst em. out . pr i nt l n( St r i ng. f or mat ( " Pr oduct Pr i ce%10. 2f " , pr i ce) ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " Tax%10. 2f " , t ax) ) ;
Syst em. out . pr i nt l n( " - - - - - - - - - - - - - - - - - - - - - - - - - " ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " Subt ot al %10. 2f " , t ot al ) ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " Amount Tender ed%10. 2f " , payment ) ) ;
Syst em. out . pr i nt l n( " =========================" ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " Change Due%10. 2f " , change) ) ;

However, the String.format function provides much more flexibility. For example, if we used
%-15S instead of %15s, we would get a left justified result (due to the -) and capitalized letters
(due to the capital S) as follows:
PRODUCT PRI CE 34. 99
TAX 4. 55
- - - - - - - - - - - - - - - - - - - - - - - - -
SUBTOTAL 39. 54
AMOUNT TENDERED 50. 00
=========================
CHANGE DUE 10. 46

COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 130 -
There are many more format options that you can experiment with. J ust make sure that you
supply the required number of parameters. That is, you need as many parameters as you
have % signs in your format string.

For example, the following code will produce a MissingFormatArgumentException since one of the
arguments (i.e., values) is missing (i.e., 4 % signs in the format string, but only 3 supplied
values:

Syst em. out . pr i nt l n( St r i ng. f or mat ( " $%. 2f + $%. 2f + $%. 2f = $%. 2f " , x, y, z) ) ;

Also, you should be careful not to miss-match types, otherwise an error may occur (i.e.,
IllegalFormatConversionException).

Supplemental Information (Other String.format Flags)

There are a few other format types that may be used in the format string:

Type Description of What it Displays Example Output
%d a general integer 4096
%x an integer in lowercase hexadecimal f f
%X an integer in uppercase hexadecimal FF
%o an integer in octal 377
%f a floating point number with a fixed number of spaces 83. 43
%e an exponential floating point number 7. 869877e- 03
%g a general floating point number with a fixed number of significant digits 0. 008
%s a string as given " Hel l o"
%S a string in uppercase " HELLO"
%n a platform-independent line end <CR><LF>
%b a boolean in lowercase t r ue
%B a boolean in uppercase FALSE

There are also various format flags that can be added after the % sign:
Format Flag
Description of What It Does
Example Output
- numbers are to be left justified 2378. 348 f ol l owed by
any necessar y spaces
0 leading zeros should be shown 000244. 87
+ plus sign should be shown if positive number +67. 34
( enclose number in round brackets if negative ( 439. 67)
, show decimal group separators 2, 347, 892. 99

There are many options for specifying various formats including the formatting of Dates and Times, but
they will not be discussed any further here. Please look at the java documentation.
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 131 -
5.3 Type Conversion

When programming, we often find ourselves working with different kinds of data. For
example, even when performing simple calculations, we may end up using a variety of
primitive data types.

float pr i ce;
int payment ;
double t axes, change;

pr i ce = 34. 56f;
t axes = pr i ce * 0. 13;
payment = 50;

change = payment - pr i ce - t axes;

Notice that the above code performs calculations using ints, floats and doubles. When
performing such calculations, J AVA performs some automatic type-conversion. That is, it
converts one type of data into another when performing the calculation.

During computations, J AVA will always produce its calculated result as being the same type as
the more precise data type that was used in the calculation. In the above example, doubles
are more precise than floats and ints. Therefore, when we do price * 0.13, J AVA notices
that this is a calculation using a float (less precise) and a double (more precise). Therefore
the float is converted to a double during the computation and the resulting answer is returned
as a double, and stored in the taxes variable.

Consider the difference in output of the following code:

float pr i ce1 = 34. 56f ;
double pr i ce2 = 34. 56;
Syst em. out . pr i nt l n( pr i ce1 * 0. 13f ) ; / / di spl ays 4. 4928
Syst em. out . pr i nt l n( pr i ce2 * 0. 13f ) ; / / di spl ays 4. 492799835205078

Notice that the same calculation is performed in both cases but that one uses a float price
amount while the other uses a double price amount. The 1
st
calculation uses two floats, and
so the result is a less precise float value. The 2
nd
calculation uses a double, so the entire
calculation is performed using doubles, generating a more precise result.

What if we changed the code to store the results as follows:

float pr i ce1 = 34. 56f ;
double pr i ce2 = 34. 56;
float r esul t ;

r esul t = pr i ce1 * 0. 13f ;
r esul t = pr i ce2 * 0. 13f ; / / gi ves “possi bl e l oss of pr eci si on” er r or

The above code will not compile. J AVA notices that in the last line, it is performing a
calculation that will result in a double. However, result is of type float. Since floats are less
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 132 -
precise that doubles, the J AVA compiler informs us that there would be a loss of precision if
we tried to take the double answer and “squeeze” it into a smaller float variable.

When assigning calculation results to a variable, J AVA always checks to make sure that the
resulting type of the calculation will “fit” into the variable. We CANNOT store a :

• double result in a variable of type float, int, long, byte, short
• float result in a variable of type int, long, byte, short
• long result in a variable of type int, byte, short
• int result in a variable of type byte, short
• short result in a variable of type byte

If we attempt to assign a result into a variable of a less precise type (as above), then we will
ALWAYS get a compiler error stating “possible loss of precision”.

However, we CAN store a:

• double result in a variable of type double
• float result in a variable of type float, double
• long result in a variable of type long, float, double
• int result in a variable of type int, long, float, double
• short result in a variable of type short, int, long, float, double
• byte result in a variable of type byte, short, int, long, float, double

Sometimes, however, we may want to take a more precise calculated value and store it into a
less precise variable, perhaps for later use. For example, we may want to perform a money-
based calculation precisely but then we may only be interested in the whole number portion, or
maybe only 2 decimal places. This example calculates the change owed to a person, extracts
and stores the whole portion (as whole monetary bills to be returned to the customer … ignore
the fact that toonies and loonies are not bills) and the remaining change as a separate value:

float pr i ce, changeDue;
int payment , bi l l sDue;
double t axes, change;

pr i ce = 34. 56f ;
t axes = pr i ce * 0. 13;
payment = 50;

change = payment - pr i ce - t axes;
bi l l sDue = (int) nge; cha
changeDue = (float)change - bi l l sDue;
Syst em. out . pr i nt l n( change) ; / / di spl ays 10. 947198448181151
Syst em. out . pr i nt l n( bi l l sDue) ; / / di spl ays 10
Syst em. out . pr i nt l n( changeDue) ; / / di spl ays 0. 94719887

Notice that we used (int) and (float). These are called explicit type-casts. In English, the
term typecasting means to identify as belonging to a certain type. What we are doing is
telling the J AVA compiler that we would like to “convert” a particular value into the type that we
specified between parentheses ( ).
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 133 -
We can typecast any numeric type (i.e., double, float, int, long, byte, short, char) to any
other numeric type at any time. We just need to remember what happens each time as
follows:

• if x is a double or float then (long)x, (int)x, (short)x, (byte)x and (char)x will discard
the decimal places … it will NOT round off, just truncate.

• if x is a long, int, short, byte or char, then (double)x and (float)x will set the decimal
places to be .0.

• if a more precise value (e.g., long or double) is type-casted to a less precise value
(e.g., int or double) then some data WILL be lost.

Here are some examples in which the conversion results in data loss:

(float)34. 56767867 ==> 34. 56768 // rounded off
(int)2. 4 ==> 2 // decimal places lost
(int)2. 9 ==> 2 // does not round off

Here are some examples in which the conversion results in data loss:

(char)947384 ==> ' ?'
(int)123456789012345678L ==> - 1506741426
Conversions may be intermediate and unintentional:
int sum= 30;
double avg = sum/ 4; // result is 7.0, not 7.5 !!!
Perhaps a more common type of conversion is that of converting numbers to Strings and vice-
versa. In J AVA, there are some pre-defined functions to do this for us.
In order to convert a given String to a particular numeric data type, there are various
class/static functions available:
St r i ng s = . . . ;

Integer.parseInt( s) ; / / r et ur ns int val ue of s
Double.parseDouble( s) / / r et ur ns double val ue of s
Float.parseFloat( s) / / r et ur ns float val ue of s
Here are some examples:
Integer.parseInt( " 7438" ) ; / / r et ur ns int val ue 7438
Double.parseDouble( " 234. 65" ) / / r et ur ns double val ue 234. 65
Float.parseFloat( " 234. 65" ) / / r et ur ns float val ue 234. 65f
We sometimes need to use these functions if we are given a String from the user and would
like to convert the input string to a numeric value for calculation purposes. For example, the
COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 134 -
following code asks the user for his/her age and then uses the input to determine the number
of years until their retirement:


St r i ng i nput ;
Int age;

i nput = J Opt i onPane. showI nput Di al og( " What i s your age ?" ) ;
age = Integer.parseInt( i nput ) ;

J Opt i onPane. showMessageDi al og( null, " You have " + ( 65 - age) +
" year s unt i l r et i r ement " ) ;

Unfortunately, the approach for converting a String into a boolean or char are different.
St r i ng i nput ;
boolean r et i r ed;

i nput = J O nput Di al r et i r ed . . . t r ue or f al se ?" ) ; pt i onPane. showI og( " You ar e
r et i r ed = Boolean.valueOf( i nput ) . booleanValue( ) ;

if ( r et i r ed)
J Opt i onPane. showMessageDi al og( null, " You get a di scount " ) ;
else
J Opt i onPane. showMessageDi al og( null, " You pay f ul l pr i ce" ) ;

Here is an example of getting a single character from the user:
St r i ng i nput ;
char r egi st er ed;

i nput = J Opt i onPane. showI nput Di al og( " Ar e you a r egi st er ed user ?" ) ;
r egi st er ed = i nput . charAt(0); / / get s t he 1
st
char act er

if ( ( r egi st er ed == ' y' ) | | ( r egi st er ed == ' Y' ) )
J Opt i onPane. showMessageDi al og( null, " OK. Si gn i n pl ease" ) ;
else
J Opt i onPane. showMessageDi al og( null, " Sor r y, you must r egi st er f i r st " ) ;

The advantage of the above code is that the user may type in any of the following strings
which will acknowledge that he/she is a registered user: “y”, “Y”, “Yes”, “YES”, “yes”, etc..
Unfortunately, it will allow them to use “Yellow”, “yarn” and “you confuse me” as “yes”
answers too ;).
Finally, we would also like to be able to convert in the other direction. That is, perhaps we
would like to convert a number to a String. This is often necessary in order to place numeric
information into a text field on a window.

COMP1005/1405 – Calculations, Formatting and Conversions Fall 2009

- 135 -
The simplest way to do this is to take the int/float/double/long value and simply add it to an
empty String. J AVA will then convert it:


int age;
St r i ng s;

age = 21;
s = age; / / compi l e er r or : incompatible types
s = " " + age; / / t hi s wi l l wor k


Another option is to use any of the following functions:

Integer.toString( 225) ==> " 225"
Double.toString( 225. 56) ==> " 225. 56"
Float.toString( 225. 56f ) ==> " 225. 56"

Integer.toBinaryString( 225) ==> " 11100001"
Integer.toHexString( 34728) ==> " 87a8"
Integer.toOctalString( 34728) ==> " 103650"

Notice that the last 3 are quite useful because they actually change the appearance of the
integer value within the string according to the desired number system.




Chapter 6
Loops and ArrayLists


What is in This Chapter ?
When programming, it is often necessary to repeat a selected portion of code a specific
number or times, or until some condition occurs. We will look here at the FOR and WHILE
loop constructs that are available in most programming languages. It is also common, when
programming, to gather (or collect) a bunch of data (or objects) together. This is commonly
referred to as a collection. We will look at the simplest way of collecting information together
by using the ArrayList.


COMP1005/1405 – Loops and ArrayLists Fall 2009

- 137 -

6.1 Repeating Code Using For and While Loops

Suppose that you wanted to ask the user for 5 exam marks and then print the average exam
mark. You might write a program that looks like this:


int n1, n2, n3, n4, n5;

Syst em. out . pr i nt ( " Ent er exammar k 1: " ) ;
n1 = new Scanner ( Syst em. i n) . next I nt ( ) ;
Syst em. out . pr i nt ( " Ent er exammar k 2: " ) ;
n2 = new Scanner ( Syst em. i n) . next I nt ( ) ;
Syst em. out . pr i nt ( " Ent er exammar k 3: " ) ;
n3 = new Scanner ( Syst em. i n) . next I nt ( ) ;
Syst em. out . pr i nt ( " Ent er exammar k 4: " ) ;
n4 = new Scanner ( Syst em. i n) . next I nt ( ) ;
Syst em. out . pr i nt ( " Ent er exammar k 5: " ) ;
n5 = new Scanner ( Syst em. i n) . next I nt ( ) ;

Syst em. out . pr i nt l n( " The aver age i s " + ( ( n1+n2+n3+n4+n5) / 5) ) ;


The above code gets all the exam marks first and stores them into variables… afterwards
computing the sum and average. Instead of storing each number separately, we can instead
add each number to an accumulating sum as follows:

int sum;

Syst em. out . pr i nt ( " Ent er exammar k 1: " ) ;
sum= new Scanner ( Syst em. i n) . next I nt ( ) ;
Syst em. out . pr i nt ( " Ent er exammar k 2: " ) ;
sum= sum+ new Scanner ( Syst em. i n) . next I nt ( ) ;
Syst em. out . pr i nt ( " Ent er exammar k 3: " ) ;
sum= sum+ new Scanner ( Syst em. i n) . next I nt ( ) ;
Syst em. out . pr i nt ( " Ent er exammar k 4: " ) ;
sum= sum+ new Scanner ( Syst em. i n) . next I nt ( ) ;
Syst em. out . pr i nt ( " Ent er exammar k 5: " ) ;
sum= sum+ new Scanner ( Syst em. i n) . next I nt ( ) ;

Syst em. out . pr i nt l n( " The aver age i s " + ( sum/ 5) ) ;


While this code may work fine, what would happen if we needed 100 numbers ? Clearly,
some part of the code is repeating over and over again (i.e., adding the next number to the
sum).
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 138 -
What if we could somehow tell J AVA to repeat something over and over again like this:
sum = 0
REPEAT 100 TIMES {
Get the next exam mark and add it to the sum
}

Well, in J AVA, we do have a way of repeating code using something called a FOR loop. Here
is how we could modify our program to get 100 numbers:

int sum= 0;

for ( int count=1; count<=100; count++) {
Syst em. out . pr i nt ( " Ent er exammar k " + count + " : " ) ;
sum= sum+ new Scanner ( Syst em. i n) . next I nt ( ) ;
}

Syst em. out . pr i nt l n( " The aver age i s " + ( sum/ 100) ) ;


Notice that the for loop has brackets ( ) and then is followed by braces { } which contains the
body of the loop (i.e., the code that is to be repeated). Lets take a look at what is inside the ( )
brackets.
Notice that it declares an int variable called count and gives it a value of 1 to begin. This
variable count is called a loop variable and in our example it represents a counter. After
the first semi-colon ; there is a conditional J AVA expression count<=100 … which is called the
loop stopping condition. That is, the loop will keep repeating as long as our counter is less
than or equal to 100. The moment the count reaches 101, the loop stops and our answer is
printed. After one more semi-colon ; there is the loop update expression code count++
which is evaluated each time the loop completes a cycle. In our case, each time through the
loop we just need to increase the counter by 1.
Notice that we can even use the value of the count variable within the loop. In our case we
are printing out the counter in order to indicate which exam mark we are going to enter. In
general, a loop variable can be used any time within the loop but it cannot be used outside the
loop body.
In our example, what if we did not know how many time to repeat (i.e., we don’t know how
many exam marks there will be) ? Well, in that case we can ask the user of the program for
the total number as follows …
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 139 -
import j ava. ut i l . Scanner ;

class Cal cul at eAver agePr ogr am {
public static void mai n( St r i ng ar gs[ ] ) {

int nums, sum;
Syst em. out . pr i nt l n( " How many exammar ks do you want t o aver age ?" ) ;

nums = new Scanner ( Syst em. i n) . next I nt ( ) ;
sum= 0;

/ / Get t he number s one at a t i me, and add t hem
for ( int count =1; count <=nums; count ++) {
Syst em. out . pr i nt ( " Ent er exammar k " + count + " : " ) ;
sum+= new Scanner ( Syst em. i n) . next I nt ( ) ;
}
Syst em. out . pr i nt l n( " The aver age i s " + ( sum/ nums) ) ;
}
}


Notice that the program is now flexible in the number of exam marks that it is able to average.
Here is an example of the output:


How many exammar ks do you want t o aver age ?
5
Ent er exammar k 1: 10
Ent er exammar k 2: 67
Ent er exammar k 3: 43
Ent er exammar k 4: 96
Ent er exammar k 5: 20
The aver age i s 47


Here is another example. Suppose that we wanted to print out the odd numbers from 1 to 100.
How could we do this ? Do you know how to check whether or not a number is odd ?
We can check if the remainder after dividing by two is zero. The modulus operator % gives the
remainder after dividing, so we do n%2 on number n. We just need to put this into a for loop:

for ( int n=1; n<=100; n++) {
if ( ( n%2) > 0)
Syst em. out . pr i nt l n( n) ;
}


Notice that we can use the same counter (but called it n this time). Then in the loop we just
check if the modulus is non-zero and print out the number in that case since it would be odd.

We could eliminate the if statement by simply counting by twos (starting at 1) as follows …
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 140 -

for ( int n=1; n<=100; n=n+2) {
Syst em. out . pr i nt l n( n) ;
}


The above code too will print out only the odd numbers from 1 to 100 since now the counter n
increases by two each time, thereby skipping over all the even numbers. You should realize
that the update expression can be any J AVA code.

Here is another example that prints outall the even numbers backwards from 100 to 1:


for ( int n=100; n>0; n=n- 2) {
Syst em. out . pr i nt l n( n) ;
}


Notice how we started n at a higher value now and that we subtract by two each time (i.e.,
100, 98, 96, etc..). Also, notice that the stopping condition now uses a > instead of <= (i.e., as
long as n is above zero we keep decreasing it by 2).

What would happen if the stopping-expression evaluated to false right away as follows:

for ( int n=1; n>=100; n++) {
Syst em. out . pr i nt l n( n) ;
}


In this case, n starts at 1 and the stopping condition determines that it is not greater than or
equal to 100. Thus, the loop body never gets evaluated. That is, the for loop does nothing
… your program ignores it.
A similar unintentional situation may occur if you accidentally place a semi-colon ; after the
round brackets by mistake:
for ( int n=1; n<=100; n++) ; {
Syst em. out . pr i nt l n( n) ;
}

In this situation, J AVA assumes that the for loop ends at that semi-colon ; and that it has no
body to be evaluated. In this case, the body of the loop is considered to be regular code
outside of the for loop and it is evaluated once. Hence J AVA “sees” the above code as:

for ( int n=1; n<=100; n++) {
}
Syst em. out . pr i nt l n( n) ;

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 141 -
One last point regarding for loops is that you do not need the braces around the loop body if
the loop body contains just one J AVA expression (i.e., just like the if statement):


for ( int n=100; n>0; n=n- 2)
Syst em. out . pr i nt l n( n) ;


In that case though, you should still indent your code so that it is clear what is in the loop.


WHILE LOOPS:

In some situations, we do not know how many times to repeat something. That is, we may
need to repeat some code until a particular condition occurs. For example, we may wish to
enter exam marks and find their average but we may not know how many exams we have.
Instead of forcing the user to count them all ahead of time, we can allow him to enter the
marks one at a time and then enter a “special” value that will indicate the completion of the
entries (e.g., -1).

Whenever we have such a situation, we use the easier while loop which has this format:

while (loop stopping condition) {

}

Here is how it can be used in our exam mark program:

import j ava. ut i l . Scanner ;

class Cal cul at or Aver agePr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
int count , sum, num;

count = 1;
sum= 0;
num= 0;

while ( num>= 0) {
Syst em. out . pr i nt ( " Ent er exammar k " + count +
" ( use - 1 t o qui t ) : " ) ;
num= new Scanner ( Syst em. i n) . next I nt ( ) ;
if ( num>= 0) {
sum+= num;
count ++;
}
}
Syst em. out . pr i nt l n( " The aver age i s " + ( sum/ ( count - 1) ) ) ;
}
}

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 142 -
The stopping condition must evaluate to true or false … and in this case it starts off with true
since num=0. When the user enters -1, the stopping condition will become false and the loop
will end.

J ust as with for loops, you should be careful not to put a semi-colon ; after the round brackets,
otherwise your loop body will not be evaluated. Usually your code will loop forever because
the stopping condition will likely never change to false:


while ( num< 100) ; { / / Thi s code wi l l l oop f or ever
Syst em. out . pr i nt l n( n++) ;
}


As with the if statements and for loops, the braces { } are not necessary when the loop body
contains a single J AVA expression:


while ( num>= 0)
Syst em. out . pr i nt l n( n++) ;


Some students tend to confuse the while loop with if statements and try to replace an if
statement with a while loop. Do you understand the difference in the two pieces of code
below ?

if ( aPer son. age > 18) while ( aPer son. age > 18)
di scount = 0; di scount = 0;

Assume that the person’s age is 20. The leftmost code will set the discount to 0 and move on.
The rightmost code will loop forever, continually setting the discount to 0.


6.2 Collecting Objects Together Using ArrayLists

In real life, objects often appear in groups. For
example, ParkingLots contain multiple Cars, Banks
contain multiple BankAccounts, Stores have multiple
Customers , a Student has multiple Assignment
marks etc..
When programming, we often need to group together
objects in order to treat the group itself as a kind of
“container” object that contains other objects. In
J AVA there are many kinds of container-type objects.
We will look at the most common one called an
ArrayList.
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 143 -
An ArrayList is an object that contains multiple arbitrary objects. To create an ArrayList, we
can simply call the constructor from J AVA’s ArrayList class. Here is an example of creating
an ArrayList and storing it in a variable so that we can use it:
Ar r ayLi st myLi st ;
myLi st = new Ar r ayLi st ( ) ;

This is the general format for an ArrayList that can hold any kinds of objects. However, it is
"highly recommended" that we specify the type of objects that will be stored in the ArrayList.
We do this by specifying the type between < and > characters just before the round brackets (
) as follows:

Ar r ayLi st <Object> myLi st ;
myLi st = new Ar r ayLi st <Object>( ) ;


The above code allows us to store any kind of object in the ArrayList. We then use the
ArrayList’s add() method to add an object to the list as follows:

import j ava. ut i l . Ar r ayLi st ;

class Ar r ayLi st Test Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
Ar r ayLi st <Obj ect > myLi st ;

myLi st = new Ar r ayLi st <Obj ect >( ) ;
myLi st . add( " Hel l o" ) ;
myLi st . add( 25) ;
myLi st . add( new Per son( ) ) ;
myLi st . add( new Tr uck( ) ) ;
Syst em. out . pr i nt l n( myLi st ) ;
}
}


It is as if the ArrayList is a kind of “bag” into which we can place any objects.
The ArrayList keeps the objects altogether so that we can pass the list around
now as a single object … just like we can consider a “bag” to be a single
object, although it may contain many other objects.
Notice in the above code that we are adding a String, and int, a Person
object and a Truck object. Notice as well that at the top of the program we
imported java.util.ArrayList. This is necessary in order for J AVA to know where to find the
ArrayList class and its methods. The output for the program is as follows (assuming that
Person and Truck do not have toString() methods):
[ Hel l o, 25, Per son@addbf 1, Tr uck@42e816]


COMP1005/1405 – Loops and ArrayLists Fall 2009

- 144 -
Did you notice how ArrayLists look when you print them out ? They show all the
elements/items in the list separated by commas , in between square brackets [ ].

Often, the objects in an ArrayList are of the same type, but this need not be the case. If we
know, for example, that all of the objects in the ArrayList will be Strings (e.g., names of
people), then we should declare and create the list as follows:

Ar r ayLi st <String> myLi st ;
myLi st = new Ar r ayLi st <String>( ) ;
. . .


Similarly, if the objects to be stored in the list were of type Person, BankAccount or Car …
then we would specify the type as <Person>, <BankAccount> or <Car>, respectively.

In J AVA, ArrayLists keep their items stored
myList
in the order that we added them and it

an ArrayList

assigns each item its own unique index (i.e.,
a number that is used to identify its position in
the list). The index of the first item
in the list is always 0.
"Hello"
25


The ArrayList class has a method called
0 1 2 3
get() that allows us to extract the item at a
given position in the list. We supply the
desired position as a parameter to the
method. If we want the first item, we use
position 0 by saying get(0) to the ArrayList.
Another useful ArrayList method is the size()
method. Which returns the number of elements currently in the list.

Here is an example that uses these methods:

Ar r ayLi st <Obj ect > myLi st ;

myLi st = new Ar r ayLi st <Obj ect >( ) ;
Syst em. out . pr i nt l n( myLi st . size()) ; / / out put s 0
myLi st . add( " Hel l o" ) ;
myLi st . add( 25) ;
myLi st . add( new Per son( ) ) ;
myLi st . add( new Car ( ) ) ;
Syst em. out . pr i nt l n( myLi st . get(0)) ; / / out put s " Hel l o"
Syst em. out . pr i nt l n( myLi st . get(2)) ; / / out put s Per son@addbf 1
Syst em. out . pr i nt l n( myLi st . get(4)) ; / / an IndexOutOfBoundsException
Syst em. out . pr i nt l n( myLi st . size()) ; / / out put s 4




COMP1005/1405 – Loops and ArrayLists Fall 2009

- 145 -
6.3 Team/League Example

Lets consider a realistic use of the ArrayList object by creating
classes called Team and League in which a League object will
contain a bunch of Team objects. That is, the League object will
have an instance variable of type ArrayList to hold onto the multiple
Team objects within the league.
Consider first the creation of a Team class that will represent a single
team in the league. For each team, we will maintain the team’s
name as well as the number of wins, losses and ties for the games
that they played. Here is the basic class (review the previous
chapters in the notes if any of this is not clear):



class Team {
St r i ng name; / / The name of t he Team
int wi ns; / / The number of games t hat t he Teamwon
int l osses; / / The number of games t hat t he Teaml ost
int t i es; / / The number of games t hat t he Teamt i ed

Team( St r i ng aName) {
this. name = aName;
this. wi ns = 0;
this. l osses = 0;
this. t i es = 0;
}

public St r i ng t oSt r i ng( ) {
return( " The " + this. name + " have " + this. wi ns + " wi ns, " +
this. l osses + " l osses and " + this. t i es + " t i es. " ) ;
}

/ / Ret ur ns t he t ot al number of poi nt s f or t he t eam
int t ot al Poi nt s( ) {
return ( this. wi ns * 2 + this. t i es) ;
}

/ / Ret ur ns t he t ot al number of games pl ayed by t he t eam
int gamesPl ayed( ) {
return ( this. wi ns + this. l osses + this. t i es) ;
}
}


COMP1005/1405 – Loops and ArrayLists Fall 2009

- 146 -
We can test out our Team object with the following test code, just to make sure it works:


class TeamTest Pr ogr am {
public static void mai n( St r i ng ar gs[ ] ) {
Team teamA, teamB;

teamA = new Team( " Ot t awa Senat or s" ) ;
teamB = new Team( " Mont r eal Canadi ans" ) ;

/ / Si mul at e t he pl ayi ng of a game i n whi ch t eamA beat t eamB
Syst em. out . pr i nt l n( teamA. name + " j ust beat " + teamB. name) ;
teamA. wi ns++;
teamB. l osses++;

/ / Si mul at e t he pl ayi ng of anot her game i n whi ch t hey t i ed
Syst em. out . pr i nt l n( teamA. name + " j ust t i ed " + teamB. name) ;
teamA. t i es++;
teamB. t i es++;

/ / Now pr i nt out some st at i st i cs
Syst em. out . pr i nt l n( teamA) ;
Syst em. out . pr i nt l n( teamB) ;
Syst em. out . pr i nt ( " The " + teamA. name + " have " ) ;
Syst em. out . pr i nt ( teamA. t ot al Poi nt s( ) + " poi nt s and pl ayed " ) ;
Syst em. out . pr i nt l n( teamA. gamesPl ayed( ) + " games. " ) ;
Syst em. out . pr i nt ( " The " + teamB. name + " have " ) ;
Syst em. out . pr i nt ( teamB. t ot al Poi nt s( ) + " poi nt s and pl ayed " ) ;
Syst em. out . pr i nt l n( teamB. gamesPl ayed( ) + " games. " ) ;
}
}


name
“Ottawa Senators”
wins 1
losses 0
ties 1
teamA


name
“Montreal Canadians”
teamB
Here is what the Team objects look like after playing the two games:





wins 0
losses 1

ties 1

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 147 -
Here is the output from our little test program:
Ot t awa Senat or s j ust beat Mont r eal Canadi ans
Ot t awa Senat or s j ust t i ed Mont r eal Canadi ans
The Ot t awa Senat or s have 1 wi ns, 0 l osses and 1 t i es.
The Mont r eal Canadi ans have 0 wi ns, 1 l osses and 1 t i es.
The Ot t awa Senat or s have 3 poi nt s and pl ayed 2 games.
The Mont r eal Canadi ans have 1 poi nt s and pl ayed 2 games.


Now let us implement the League class. A league will also have a name as well as an
ArrayList (called teams) of Team objects. Here is the basic class structure (notice the import
statement at the top):
import j ava. ut i l . Ar r ayLi st ;

class League {
St r i ng name;
Ar r ayLi st <Team> t eams;

League( St r i ng n) {
this. name = n;
this. t eams = new Ar r ayLi st <Team>( ) ; / / Doesn’ t make any Team obj ect s
}

/ / Thi s speci f i es t he appear ance of t he League
public St r i ng t oSt r i ng( ) {
return ( " The " + this. name + " l eague" ) ;
}
}

Notice that the ArrayList is created within the constructor and that it is initially empty. That
means, a brand new league has no teams in it. It is important to note also that there are no
Team objects created at this time.

At this point, we have defined two objects: Team and League. One thing that we will need to
do is to be able to add teams to the league. Here is an example of how we can create a
league with three teams in it:

League nhl ;

nhl = new League( " NHL" ) ;
nhl . t eams. add(new Team( " Ot t awa Senat or s" ) );
nhl . t eams. add(new Team( " Mont r eal Canadi ans" ) );
nhl . t eams. add(new Team( " Tor ont o Mapl e Leaf s" ) );


In order to add the team to the league, we simply add it to the league's teams by using the
add() method that is already defined in the ArrayList class. Here is a diagram showing how
the League object stores the 3 Teams …
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 148 -




nhl

teams
name

“NHL”
losses 0
ties 0

name “Montreal Canadians”
wins 0
losses 0
ties 0
0 1 2
nhl.teams ArrayList
0 ties
0 losses
wins 0
“Toronto Maple Leafs”
name

0 wins
“Ottawa Senators” name

Suppose now that we wanted to print out the teams in the league. We will write a method in
the League class called showTeams() to do this. The method will need to go through each
team in the teams ArrayList and display the particular team’s information … perhaps using the
toString() method from the Team class.

Hopefully, you “sense” that printing out all the teams involves repeating some code over and
over again. That is, you should realize that we need a loop of some type. We have already
discussed the for and while loops, but there is a special kind of for loop that is to be used
when traversing through a collection such as an ArrayList. This loop is called the “for-each”
loop, and its structure is a little simpler than the traditional for loop. Here is how we will use it
to write the showTeams() method.


void showTeams( ) {
for ( Teamt : this. t eams)
Syst em. out . pr i nt l n( t ) ; / / or Syst em. out . pr i nt l n( t . t oSt r i ng( ) ) ;
}


Notice that the for-each loop starts with for again, but this time the information within the round
() brackets is different. The format of this information is as follows. First we specify the type
of object that is in the ArrayList … in this case Team. Then we specify a variable name which
will be used to represent the particular team as we loop through them all … in this case we
called it simply t.
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 149 -
Then we use a colon : character followed by the name of the ArrayList that we want to loop
through … in this case this.teams. So, if we were to translate the for-each loop into English, it
would sound something like this: “For each team t in the teams array list do the loop”.

Notice that within the loop, we simply use t as we would use any other variable. In our
example, t is the Team object that we are examining during that round through the loop. So t
points to the 1
st
team in the league when we begin the loop, then it points to the 2
nd
team the
next time through the loop, then the 3rd team etc..

Let us test our method out using the following test program:

class LeagueTest Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
League nhl ;

nhl = new League( " NHL" ) ;

/ / Add a pi l e of t eams t o t he l eague
nhl . t eams. add( new Team( " Ot t awa Senat or s" ) ) ;
nhl . t eams. add( new Team( " Mont r eal Canadi ans" ) ) ;
nhl . t eams. add( new Team( " Tor ont o Mapl e Leaf s" ) ) ;
nhl . t eams. add( new Team( " Vancouver Cannucks" ) ) ;
nhl . t eams. add( new Team( " Edmont on Oi l er s" ) ) ;
nhl . t eams. add( new Team( " Washi ngt on Capi t al s" ) ) ;
nhl . t eams. add( new Team( " New J er sey Devi l s" ) ) ;
nhl . t eams. add( new Team( " Det r oi t Red Wi ngs" ) ) ;

/ / Di spl ay t he t eams
Syst em. out . pr i nt l n( " \ nHer e ar e t he t eams: " ) ;
nhl . showTeams( ) ;
}
}


Here is the output so far:

Her e ar e t he t eams:
The Ot t awa Senat or s have 0 wi ns, 0 l osses and 0 t i es.
The Mont r eal Canadi ans have 0 wi ns, 0 l osses and 0 t i es.
The Tor ont o Mapl e Leaf s have 0 wi ns, 0 l osses and 0 t i es.
The Vancouver Cannucks have 0 wi ns, 0 l osses and 0 t i es.
The Edmont on Oi l er s have 0 wi ns, 0 l osses and 0 t i es.
The Washi ngt on Capi t al s have 0 wi ns, 0 l osses and 0 t i es.
The New J er sey Devi l s have 0 wi ns, 0 l osses and 0 t i es.
The Det r oi t Red Wi ngs have 0 wi ns, 0 l osses and 0 t i es.


Notice that all the teams have no recorded wins, losses or ties. Lets write a method that will
record a win and a loss for two teams that play together, and another method to record a tie
when the two teams play and tie.
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 150 -

void recordWinAndLoss( Teamwi nner , Teaml oser ) {
wi nner . wi ns++;
l oser . l osses++;
}

void recordTie( Teamt eamA, Teamt eamB) {
t eamA. t i es++;
t eamB. t i es++;
}


If we wanted to test these methods now, we could write test code like this:


League nhl ;
Team team1, t eam2, t eam3;

nhl = new League( " NHL" ) ;
nhl . t eams. add( team1 = new Team( " Ot t awa Senat or s" ) ) ;
nhl . t eams. add( t eam2 = new Team( " Mont r eal Canadi ans" ) ) ;
nhl . t eams. add( t eam3 = new Team( " Tor ont o Mapl e Leaf s" ) ) ;

nhl . r ecor dWi nAndLoss( team1, t eam2) ;
nhl . r ecor dTi e( team1, t eam2) ;
nhl . r ecor dWi nAndLoss( t eam3, t eam2) ;
/ / . . . et c . . .


You should now notice something tedious. We would have to make variables for each team if
we want to record wins, losses and ties among them. Why ? Because the recording methods
require Team objects ... the same Team objects that we added to the League ... so we would
have to remember them ... hence requiring us to store them in a variable. Perhaps a better
way to record wins, losses and ties would be to do something like this:

League nhl ;

nhl = new League( " NHL" ) ;
nhl . t eams. add( new Team( " Ot t awa Senat or s" ) ) ;
nhl . t eams. add( new Team( " Mont r eal Canadi ans" ) ) ;
nhl . t eams. add( new Team( " Tor ont o Mapl e Leaf s" ) ) ;

nhl . r ecor dWi nAndLoss( " Ot t awa Senat or s" , " Mont r eal Canadi ans" ) ;
nhl . r ecor dTi e( " Ot t awa Senat or s" , " Mont r eal Canadi ans" ) ;
nhl . r ecor dWi nAndLoss( " Tor ont o Mapl e Leaf s" , " Mont r eal Canadi ans" ) ;

/ / . . . et c . . .


This way, we do not need to create extra variables. However, we would have to make new
recording methods that took Strings (i.e., the Team names) as parameters instead of Team
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 151 -
objects. Here are the methods that we would need to implement (notice the difference in the
parameter types):


void recordWinAndLoss( St r i ng wi nner Name, St r i ng l oser Name) {

}

void recordTie( St r i ng t eamAName, St r i ng t eamBName) {

}


To make this work, however, we still need to get into the appropriate Team objects and update
their wins/losses/ties. Therefore, we will have to take the incoming team names and find the
Team objects that correspond with those names. We would need to do this 4 times: once for
the winnerName, once for the loserName, once for teamAName and once for teamBName.
Rather than repeat the code 4 times, we will make a method to do this particular sub-task of
finding a team with a given name. Here is the method that we will write:


TeamteamWithName( St r i ng nameToLookFor ) {
Team answer ;
...
return answer ;
}


Notice that it will take the team’s name as a parameter and then return a Team object. How
would we complete this method ? We can use the for-each loop to traverse through all the
teams and find the one with that name as follows:


Teamt eamWi t hName( St r i ng nameToLookFor ) {
Team answer = null;
for ( Teamt : this. t eams) {
if ( t . name. equal s( nameToLookFor ) )
answer = t ;
}
return answer ;
}


Notice a few points. First, we set the answer to null. I we do not find a Team with the given
name, the method returns null … which is the only appropriate answer. Next, notice that for
each team t, we compare its name with the incoming string aName and if these two strings are
equal, then we have found the Team object that we want, so we store it in the answer variable
to be returned at the completion of the loop.



COMP1005/1405 – Loops and ArrayLists Fall 2009

- 152 -
This method can be shortened as follows:


Teamt eamWi t hName( St r i ng nameToLookFor ) {
for ( Teamt : this. t eams)
if ( t . name. equal s( nameToLookFor ) )
return t ;
return null;
}


Now that this method has been created, we can use it in our methods for recording wins/losses
and ties as follows:


void recordWinAndLoss( St r i ng wi nner Name, St r i ng l oser Name) {
Team wi nner , l oser ;

wi nner = this. t eamWi t hName( wi nner Name) ;
l oser = this. t eamWi t hName( l oser Name) ;
wi nner . wi ns++;
l oser . l osses++;
}

void recordTie( St r i ng t eamAName, St r i ng t eamBName) {
Team t eamA, t eamB;

t eamA = this. t eamWi t hName( t eamAName) ;
t eamB = this. t eamWi t hName( t eamBName) ;
t eamA. t i es++;
t eamB. t i es++;
}


The methods work as before, but there are potential problems. What if we
cannot find the Team objects with the given names (e.g., someone spelt the
name wrong) ? In this case, perhaps winner, loser, teamA or teamB will be
null and we will get a NullPointerException when we try to access the team’s
attributes. We can check for this with an if statement.


void r ecor dWi nAndLoss( St r i ng wi nner Name, St r i ng l oser Name) {
Team wi nner , l oser ;

wi nner = this. t eamWi t hName( wi nner Name) ;
l oser = this. t eamWi t hName( l oser Name) ;
if ( ( wi nner ! = null) && ( l oser ! = null) ) {
wi nner . wi ns++;
l oser . l osses++;
}
}
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 153 -

void r ecor dTi e( St r i ng t eamAName, St r i ng t eamBName) {
Team t eamA, t eamB;

t eamA = this. t eamWi t hName( t eamAName) ;
t eamB = this. t eamWi t hName( t eamBName) ;
if ( ( t eamA ! = null) && ( t eamB ! = null) ) {
t eamA. t i es++;
t eamB. t i es++;
}
}

Now the games are only recorded when we have successfully identified the two Team objects
that need to be updated as a result of the played game. Interestingly though, the same
problem may occur in our previous recording methods … that is … the Team objects passed in
may be null. Also, in our code, we already have method for recording the wins/losses/ties in
the case where we have the Team objects, so we should call those methods from here. We
can simply call the previous recording methods from these two new ones and move the null-
checking in there instead as follows:

TeamteamWithName( St r i ng nameToLookFor ) {
for ( Teamt : this. t eams)
if ( t . name. equal s( nameToLookFor ) )
return t ;
return null;
}
void recordWinAndLoss( Teamwi nner , Teaml oser ) {
if ( ( wi nner ! = null) && ( l oser ! = null) ) {
wi nner . wi ns++;
l oser . l osses++;
}
}
void recordTie( Teamt eamA, Teamt eamB) {
if ( ( t eamA ! = null) && ( t eamB ! = null) ) {
t eamA. t i es++;
t eamB. t i es++;
}
}
void r ecor dWi nAndLoss( St r i ng wi nner Name, St r i ng l oser Name) {
Team wi nner , l oser ;

wi nner = this. teamWithName( wi nner Name) ;
l oser = this. teamWithName( l oser Name) ;
this. recordWinAndLoss( wi nner , l oser ) ;
}
void r ecor dTi e( St r i ng t eamAName, St r i ng t eamBName) {
Team t eamA, t eamB;

t eamA = this. teamWithName( t eamAName) ;
t eamB = this. teamWithName( t eamBName) ;
this. recordTie( t eamA, t eamB) ;
}
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 154 -
In fact, we can even shorten the last two methods by noticing that the variables are not really
necessary:


void r ecor dWi nAndLoss( St r i ng wi nner Name, St r i ng l oser Name) {
. ( this recordWinAndLoss this. teamWithName( wi nner Name) ,
this. teamWithName( l oser Name) ) ;
}

void r ecor dTi e( St r i ng t eamAName, St r i ng t eamBName) {
this. recordTie( this. teamWithName( t eamAName) ,
this. teamWithName( t eamBName) ) ;
}


Consider a method called totalGamesPlayed() which is supposed to return the total number
of games played in the league. All we need to do is count the number of games played by all
the teams (i.e., we will need some kind of counter) and then divide by 2 (since each game was
played by two teams, hence counted twice). Here is the format:

int totalGamesPlayed( ) {
int t ot al = 0;

return t ot al / 2;
}


We will also need a for-each loop to go through each team:


int t ot al GamesPl ayed( ) {
int t ot al = 0;
for ( Teamt : this. t eams) {

}
return t ot al / 2;
}


Now, if you were to look back at the Team class, you would notice a method in there called
gamesPlayed(). That means, we can ask a team how many games they played by simply
calling that method. We should be able to make use of this value as follows:

int t ot al GamesPl ayed( ) {
int t ot al = 0;
for ( Teamt : this. t eams)
t ot al += t . gamesPl ayed( ) ;

return t ot al / 2;
}
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 155 -
Notice that the method is quite simple, as long as you break it down into simple steps like we
just did. For more practice, let us find the team that is in first place (i.e., the Team object that
has the most points). We can start again as follows:

TeamfirstPlaceTeam( ) {
Team t eamWi t hMost Poi nt s = null;
...
return t eamWi t hMost Poi nt s;
}


Notice that it returns a Team object. Likely, you realize that we also need a for-each loop
since we need to check all of the teams:


Teamf i r st Pl aceTeam( ) {
Team t eamWi t hMost Poi nt s = null;

for ( Teamt : this. t eams) {
. . .
}
return t eamWi t hMost Poi nt s;
}


Again, we can make use of a pre-defined method in the Team class called totalPoints() which
returns the number of points for a particular team:

Teamf i r st Pl aceTeam( ) {
int poi nt s;
Team t eamWi t hMost Poi nt s = null;

for ( Teamt : this. t eams) {
poi nt s = t . t ot al Poi nt s( ) ;
}
return t eamWi t hMost Poi nt s;
}


But now what do we do ? The current code will simply grab each team’s point values one at
a time. We need to somehow compare them. Many students have trouble breaking this
problem down into simple steps. The natural tendency is to say to yourself “I will compare the
1
st
team’s points with the 2
nd
team’s points and see which is greater”. If we do this however,
then what do we do with that answer ? How does the third team come into the picture ?
Hopefully, after some thinking, you would realize that as we traverse through the teams, we
need to keep track of (i.e., remember) the best one so far.
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 156 -
Imagine for example, searching through a basket of apples to find the best one.
Would you not grab an apple and hold it in your hand and then look through the
other apples and compare them with the one you are holding in your hand ?
If you found a better one, you would simply trade the one currently in your hand
with the new better one. By the time you reach the end of the basket, you are
holding the best apple.

Well we are going to do the same thing. The teamWithMostPoints variable
will be like our good apple that we are holding. Whenever we find a team that is better
(i.e., more points) than this one, then that one becomes the teamWithMostPoints. Here is
the code:


Teamf i r st Pl aceTeam( ) {
Team teamWithMostPoints = null;

for ( Teamt : this. t eams) {
if ( t . t ot al Poi nt s( ) > teamWithMostPoints. t ot al Poi nt s( ) )
teamWithMostPoints = t ;
}
return teamWithMostPoints;
}


Does it make sense ? There is one small issue though. J ust like we need to begin our apple
checking by picking up a first apple, we also need to pick a team (any Team object) to be the
“best” one before we start the search. Currently the teamWithMostPoints starts off at null so
we need to set this to a valid Team so start off. We can perhaps take the first Team in the
teams ArrayList:


Teamf i r st Pl aceTeam( ) {
Team t eamWi t hMost Poi nt s = this. t eams. get(0);

for ( Teamt : this. t eams) {
if ( t . t ot al Poi nt s( ) > t eamWi t hMost Poi nt s. t ot al Poi nt s( ) )
t eamWi t hMost Poi nt s = t ;
}
return t eamWi t hMost Poi nt s;
}


We are not done yet! It is possible, in a weird scenario, that there are no teams in the league!
In this case teams.get(0) will return null and we will get a NullPointerException again when
we go to ask for the totalPoints(). So, we would need to add a special case to return null if
the teams list is empty. Here is the new code …

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 157 -

Teamf i r st Pl aceTeam( ) {
Team t eamWi t hMost Poi nt s;

if ( this. t eams. si ze( ) == 0)
return null;

t eamWi t hMost Poi nt s = this. t eams. get(0);
for ( Teamt : this. t eams) {
if ( t . t ot al Poi nt s( ) > t eamWi t hMost Poi nt s. t ot al Poi nt s( ) )
t eamWi t hMost Poi nt s = t ;
}
return t eamWi t hMost Poi nt s;
}


What would we change in the above code if we wanted to write a method called
lastPlaceTeam() that returned the team with the least number of points ? Try to do it.
For the purpose of a summary, here is the entire League class as we have defined it:
import j ava. ut i l . Ar r ayLi st ;

class League {
St r i ng name;
Ar r ayLi st <Team> t eams;

League( St r i ng n) {
this. name = n;
this. t eams = new Ar r ayLi st <Team>( ) ; / / Doesn’ t make any Teamobj ect s
}

/ / Thi s speci f i es t he appear ance of t he League
public St r i ng t oSt r i ng( ) { return ( " The " + this. name + " l eague" ) ; }

/ / Di spl ay al l t he t eams t o t he consol e
void showTeams( ) {
for ( Teamt : this. t eams)
Syst em. out . pr i nt l n( t ) ; / / or Syst em. out . pr i nt l n( t . t oSt r i ng( ) ) ;
}

/ / Fi nd and r et ur n t he Teamobj ect t hat has t he gi ven name, nul l i f not f ound
Teamt eamWi t hName( St r i ng nameToLookFor ) {
for ( Teamt : this. t eams)
if ( t . name. equal s( nameToLookFor ) )
return t ;
return null;
}

/ / Recor d a wi n f or t eamwi nner and l oss f or t eaml oser
void r ecor dWi nAndLoss( Teamwi nner , Teaml oser ) {
if ( ( wi nner ! = null) && ( l oser ! = null) ) {
wi nner . wi ns++;
l oser . l osses++;
}
}

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 158 -
/ / Recor d a t i e f or each of TeamA and TeamB
void r ecor dTi e( Teamt eamA, Teamt eamB) {
if ( ( t eamA ! = null) && ( t eamB ! = null) ) {
t eamA. t i es++;
t eamB. t i es++;
}
}

/ / Recor d a wi n f or t he t eamwi t h name wi nner Name
/ / and a l oss f or t he t eamwi t h name l oser Name
void r ecor dWi nAndLoss( St r i ng wi nner Name, St r i ng l oser Name) {
this. r ecor dWi nAndLoss( this. t eamWi t hName( wi nner Name) ,
this. t eamWi t hName( l oser Name) ) ;
}

/ / Recor d a t i e f or t he t wo t eams wi t h t he gi ven names
void r ecor dTi e( St r i ng t eamAName, St r i ng t eamBName) {
this. r ecor dTi e( this. t eamWi t hName( t eamAName) ,
this. t eamWi t hName( t eamBName) ) ;
}

/ / Ret ur n t he t ot al number of games pl ayed i n t he l eague
int t ot al GamesPl ayed( ) {
int t ot al = 0;
for ( Teamt : this. t eams)
t ot al += t . gamesPl ayed( ) ;

return t ot al / 2;
}

/ / Ret ur n t he t eamt hat has t he most poi nt s
Teamf i r st Pl aceTeam( ) {
Team t eamWi t hMost Poi nt s;

if ( this. t eams. si ze( ) == 0)
return null;

t eamWi t hMost Poi nt s = this. t eams. get ( 0) ;
for ( Teamt : this. t eams) {
if ( t . t ot al Poi nt s( ) > t eamWi t hMost Poi nt s. t ot al Poi nt s( ) )
t eamWi t hMost Poi nt s = t ;
}
return t eamWi t hMost Poi nt s;
}

/ / Ret ur n t he t eamt hat has t he l east poi nt s
Teaml ast Pl aceTeam( ) {
Team t eamWi t hLeast Poi nt s;

if ( this si ze( ) == 0) . t eams.
return null;

t eamWi t hLeast Poi nt s = this. t eams. get ( 0) ;
for ( Teamt : this. t eams) {
if ( t . t ot al Poi nt s( ) < t eamWi t hLeast Poi nt s. t ot al Poi nt s( ) )
t eamWi t hLeast Poi nt s = t ;
}
return t eamWi t hLeast Poi nt s;
}
}
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 159 -
Here is a program that can be used to test our methods:

class LeagueTest Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
League nhl = new League( " NHL" ) ;

/ / Add a pi l e of t eams t o t he l eague
nhl . t eams. add( new Team( " Ot t awa Senat or s" ) ) ;
nhl . t eams. add( new Team( " Mont r eal Canadi ans" ) ) ;
nhl . t eams. add( new Team( " Tor ont o Mapl e Leaf s" ) ) ;
nhl . t eams. add( new Team( " Vancouver Cannucks" ) ) ;
nhl . t eams. add( new Team( " Edmont on Oi l er s" ) ) ;
nhl . t eams. add( new Team( " Washi ngt on Capi t al s" ) ) ;
nhl . t eams. add( new Team( " New J er sey Devi l s" ) ) ;
nhl . t eams. add( new Team( " Det r oi t Red Wi ngs" ) ) ;

/ / Now we wi l l r ecor d some games
nhl . r ecor dWi nAndLoss( " Ot t awa Senat or s" , " New J er sey Devi l s" ) ;
nhl . r ecor dWi nAndLoss( " Edmont on Oi l er s" , " Mont r eal Canadi ans" ) ;
nhl . r ecor dTi e( " Ot t awa Senat or s" , " Det r oi t Red Wi ngs" ) ;
nhl . r ecor dWi nAndLoss( " Mont r eal Canadi ans" , " Washi ngt on Capi t al s" ) ;
nhl . r ecor dWi nAndLoss( " Ot t awa Senat or s" , " Edmont on Oi l er s" ) ;
nhl . r ecor dTi e( " Washi ngt on Capi t al s" , " Edmont on Oi l er s" ) ;
nhl . r ecor dTi e( " Det r oi t Red Wi ngs" , " New J er sey Devi l s" ) ;
nhl . r ecor dWi nAndLoss( " Vancouver Cannucks" , " Tor ont o Mapl e Leaf s" ) ;
nhl . r ecor dWi nAndLoss( " Tor ont o Mapl e Leaf s" , " Edmont on Oi l er s" ) ;
nhl . r ecor dWi nAndLoss( " New J er sey Devi l s" , " Det r oi t Red Wi ngs" ) ;

/ / Thi s one wi l l not wor k
nhl . r ecor dWi nAndLoss( " Mar k' s Team" , " Det r oi t Red Wi ngs" ) ;

/ / Now di spl ay t he t eams agai n
Syst em. out . pr i nt l n( " \ nHer e ar e t he t eams af t er r ecor di ng t he " +
" wi ns, l osses and t i es: \ n" ) ;
nhl . showTeams( ) ;

/ / Her e ar e some st at i st i cs
Syst em. out . pr i nt l n( " \ nThe t ot al number of games pl ayed i s " +
nhl . t ot al GamesPl ayed( ) ) ;
Syst em. out . pr i nt l n( " The f i r st pl ace t eami s " +
nhl . f i r st Pl aceTeam( ) ) ;
Syst em. out . pr i nt l n( " The l ast pl ace t eami s " +
nhl . l ast Pl aceTeam( ) ) ;
}
}


Here would be the output (make sure that it makes sense to you) …

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 160 -

Her e ar e t he t eams af t er r ecor di ng t he wi ns, l osses and t i es:

The Ot t awa Senat or s have 2 wi ns, 0 l osses and 1 t i es.
The Mont r eal Canadi ans have 1 wi ns, 1 l osses and 0 t i es.
The Tor ont o Mapl e Leaf s have 1 wi ns, 1 l osses and 0 t i es.
The Vancouver Cannucks have 1 wi ns, 0 l osses and 0 t i es.
The Edmont on Oi l er s have 1 wi ns, 2 l osses and 1 t i es.
The Washi ngt on Capi t al s have 0 wi ns, 1 l osses and 1 t i es.
The New J er sey Devi l s have 1 wi ns, 1 l osses and 1 t i es.
The Det r oi t Red Wi ngs have 0 wi ns, 1 l osses and 2 t i es.

The t ot al number of games pl ayed i s 10
The f i r st pl ace t eami s The Ot t awa Senat or s have 2 wi ns, 0 l osses and 1 t i es.
The l ast pl ace t eami s The Washi ngt on Capi t al s have 0 wi ns, 1 l osses and 1 t i es.


Supplemental Information

There is an additional class called Vector which has the same functionality as the ArrayList
class. In fact, in most situations, you can simply replace the word ArrayList by Vector and
your code will still compile. There is a small difference between ArrayLists and Vectors.
They have the same functionality, but ArrayLists are faster because they have methods that
are not synchronized. Vectors allow multiple processes (or multiple "programs") to
access/modify them at the same time, so they have extra code in the methods to ensure that
the Vector is shared properly and safely between the processes. We will not talk any more
about this in this course. You should always use ArrayLists when creating simple
programs.



6.4 Car/Autoshow Example

Many agree that the best way of learning something is by
example. Therefore, let us consider another example that
uses ArrayLists. We will create an Autoshow that
contains many Car objects in an ArrayList. Although there
are many pieces of information that we may want to keep
track of for a Car, lets consider just keeping track of the
car’s make, model, color, topSpeed and whether or not it
has4Doors (we will assume that all cars are either 2-door or 4-door,
ignoring 3-door and 5-door cars).
Here is a simple class definition showing the attributes (i.e., instance variables), a constructor
and a toString() method …
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 161 -

class Car {
St r i ng make; / / name of company t hat makes i t ( e. g. , Honda)
St r i ng model ; / / name of car i t sel f ( e. g. , Ci vi c)
St r i ng col or ; / / col or ( e. g. , r ed)
int t opSpeed; / / f ast est t hat t he car goes
boolean has4Door s; / / t r ue i f i t has 4 door s, f al se ot her wi se

/ / Her e i s a const r uct or
Car ( St r i ng mk, St r i ng md, St r i ng cl , int t s, boolean f d) {
this. make = mk;
this. model = md;
this. col or = cl ;
this. t opSpeed = t s;
this. has4Door s = f d;
}

/ / Thi s met hod r et ur ns a st r i ng r epr esent i ng t he car i n t hi s f or mat :
/ / " Red 2-door Porsche 911 with top speed 340kmph"
public St r i ng t oSt r i ng( ) {
St r i ng s = this. col or ;
if ( this. has4Door s)
s += " 4- door " ;
el se
s += " 2- door " ;
return ( s + this. make + " " + this. model +
" wi t h t op speed " + this. t opSpeed + " kmph" ) ;
}
}


Now what about the Autoshow ? It will likely have a lot of information (e.g., name, start date,
end date, admission prices, etc..). However, we will keep things simple and just make use of
two attributes… a name and a list of cars:

import j ava. ut i l . Ar r ayLi st ;

class Aut oshow {
/ / These ar e t he i nst ance var i abl es
St r i ng name; / / Name of aut oshow ( e. g. , " Aut oRama 2009" )
Ar r ayLi st <Car > car s; / / The car s at t he show

/ / Her e i s a const r uct or
Aut oshow( St r i ng n) {
this. name = n;
this. car s = new Ar r ayLi st <Car >( ) ;
}
/ / Thi s met hod r et ur ns a st r i ng r epr esent i ng t he aut oshow
public St r i ng t oSt r i ng( ) {
return ( " Aut oshow " + this. name + " wi t h " +
this. car s. si ze( ) + " car s" ) ;
}
}
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 162 -
Notice that the cars list is specified to hold <Car> objects.

Suppose that we wanted to create a method that prints all the “cars with a given make” (e.g.,
print out all Honda cars). How can we do this ? Well, you should always begin by specifying
the name of the method, its parameters and its return type. We will call the method
carsWithMake() and it should take-in (as a parameter) a String which specifies the make of
the cars we are looking for.


void carsWithMake(String m) {

}


As you may have guessed, in order to make a list of all the companies that have cars at the
autoshow, we will need to check out all of the cars. So, we will need to loop through them.
Here is a strategy that prints out all the cars:


for ( Car c: this. car s) {
Syst em. out . pr i nt l n( c) ;
}


To print out ONLY those cars that have make m, we need to use an if statement. Remember,
we use the .equals() method to compare the makes since they are String objects, not
primitives:


void car sWi t hMake( St r i ng m) {
for ( Car c: this. car s) {
if ( c. make. equal s( m) )
Syst em. out . pr i nt l n( c) ;
}
}


Now instead of printing all the cars with a given make, how would we adjust the method to
return a list of all the cars with a specific make ? We would need to return a list of Car objects
… hence we need to change the return type from void to ArrayList<Car> as follows:


Ar r ayLi st <Car > car sWi t hMake( St r i ng m) {
...
}


Then, instead of printing the cars out, we would need to create a new list (to hold the answer),
fill that list up with all the cars that have the specified make, and then return that list …

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 163 -

Ar r ayLi st <Car > car sWi t hMake( St r i ng m) {
Ar r ayLi st <Car > answer ;

answer = new Ar r ayLi st <Car >( ) ; / / make t he empt y answer l i st
for ( Car c: this. car s) {
if ( c. make. equal s( m) )
answer . add( c) ; / / add t he car t o t he answer l i st
}
return answer ;
}


Now let us make another method that returns all of the "different makes of cars" at the auto
show. Of course, we want only a list of the different makes, so no duplicates are allowed.
Again we need to return an ArrayList, but this time it will contain String objects, since we
want the makes of the cars, not the cars themselves:

Ar r ayLi st <St r i ng> differentMakes() {
Ar r ayLi st <St r i ng> answer = new Ar r ayLi st <St r i ng>( ) ;
...
return answer ;
}


Again, we need to loop through all the cars. This time though, we need to get the make of the
car and add it to the list:

Ar r ayLi st <St r i ng> di f f er ent Makes( ) {
Ar r ayLi st <St r i ng> answer = new Ar r ayLi st <St r i ng>( ) ;

for ( Car c: this. car s) {
answer . add( c. make) ;
}

return answer ;
}


This code works. However, it will show duplicates. That is, if there are 10 cars made by
“Honda”, then “Honda” will appear 10 times in the answer. Likely, we only want a list of
unique makes. To do this, we need to determine whether or not the make has already been
added to the answer. If it has, then we don’t add it again.
The J AVA “guys” knew that there would often be a need to ask whether or not
an ArrayList contains a particular item already. So, they created a method
called contains() which allows us to ask whether or not a particular Object is
already in the list. The contains(Object x) method returns a boolean value
of true if the object x is in the ArrayList and false otherwise. Here is how
we can use it for our purposes …
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 164 -

Ar r ayLi st <St r i ng> di f f er ent Makes( ) {
Ar r ayLi st <St r i ng> answer = new Ar r ayLi st <St r i ng>( ) ;

for ( Car c: this. car s) {
if ( ! answer . cont ai ns( c. make) )
answer . add( c. make) ;
}
return answer ;
}


Notice that we ask the answer ArrayList if it contains Car c’s make. If it does not (i.e., notice
the ! character before the word answer), then the make is added to the list. This completes
our task.
How about writing a method to find the "fastest car" ? This would be the car with the maximum
topSpeed. Well, as before, we realize that we need to go through all the cars, so we start out
with the method signature and looping structure. The only difference is that the return type is
a single Car now, not an ArrayList. Of course, if there was a "tie" for "fastest car" (i.e., two or
more with the same topSpeed), then this method would only return one of the cars:

Car fastestCar() {
Car f ast est = null;

for ( Car c: this. car s) {
/ / . . . don' t know what goes her e yet . . .
}
return f ast est ;
}

To complete the method, we follow the same “template” that we used to determine the
firstPlaceTeam() in our League class. Do you remember ? We keep track of the fastest
car so far and replace it whenever we find a faster one. Here is the code:

Car f ast est Car ( ) {
Car f ast est = null;

for ( Car c: this. car s) {
if ( c. t opSpeed > f ast est . t opSpeed)
f ast est = c;
}
return f ast est ;
}

Remember though, that we must start off with a valid Car object, not with null …
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 165 -

Car f ast est Car ( ) {
if ( this. car s. i sEmpt y( ) )
return null;

Car f ast est = this. car s. get ( 0) ;

for ( Car c: this. car s) {
if ( c. t opSpeed > f ast est . t opSpeed)
f ast est = c;
}
return f ast est ;
}


Notice that there is an isEmpty() method for ArrayLists that returns true if
the list is empty and false otherwise. It has the same effect as asking
whether size() == 0.

Now let us see if we can print out the cars in order of their top speed … fastest car shown first.
Assume that cars with the same speed will appear in arbitrary order among themselves. Here
is what we will start with:

void printBySpeed() {
/ / . . . don' t know what goes her e yet . . .
}


You may feel that perhaps we can repeatedly find the fastest car... using our fastestCar()
method. But why won't the following code work:


void pr i nt BySpeed( ) {
for ( Car c: this. car s) {
Syst em. out . pr i nt l n( this.f ast est Car ( ) ) ;
}
}

If you guessed that the code above would always find the same car over and over again …
then you would be correct. What we need to do is find the fastest car, then remove it, then
find the fastest car again from the remaining ones ... doing this until there are no more cars.
ArrayLists happen to have a method called remove(x) that allows you to remove an object x
from the list, (provided that x is in the list to begin with). We can remove a car c from our cars
ArrayList by doing this: this.cars.remove(c). We can replace c by a call to this.fastestCar()
and obtain the following code …
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 166 -

void pr i nt BySpeed( ) {
Car f ;
for ( Car c: this. car s) {
f = this. f ast est Car ( ) ;
Syst em. out . pr i nt l n( f ) ;
this. car s. r emove( f ) ;
}
}


Intuitively, this would work fine. However, this code will not work. The reason is that J AVA
prevents us from removing from an ArrayList while we are looping through it. J AVA will
generate a ConcurrentModificationException. … which means that we are trying to modify
(i.e., remove from) the ArrayList while we are at the same time (i.e., concurrently) indexing
through it. The problem is that the for loop makes use of the indices of the items in the list
while it iterates through them … and when we remove an item in the middle of the list all the
items after it are shifted backwards, thereby changing their individual indices. This messes up
the iterative for loop process.
We can fix this however, by using a while loop since the while loop does not keep track of
indices. That is, we can repeatedly find and remove the fastest car while the list is not empty.
Here is the code:

void pr i nt BySpeed( ) {
Car f ;
while ( ! this. car s. i sEmpt y( ) ) {
f = this. f ast est Car ( ) ;
Syst em. out . pr i nt l n( f ) ;
this. car s. r emove( f ) ;
}
}


This code will work as expected … but it does something undesirable. It actually removes
ALL the cars from the autoshow! That means, after this method is called, our autoshow has
been ruined. One idea is to make a copy of the cars list and then loop through and remove
from the copy, thereby leaving the original list intact.
We can make a copy of an ArrayList by calling one of its constructors that allows us to pass in
an existing ArrayList as follows:

Ar r ayLi st <Car > copy;
copy = new Ar r ayLi st <Car >( this. car s) ;


This code will create a copy of the cars list that will contain the same cars as the original list …


COMP1005/1405 – Loops and ArrayLists Fall 2009

- 167 -



copy

copy of the original ArrayList
the original ArrayList
this.cars

Now if we remove cars from the copy, we are merely removing the pointers (or arrows) to the
cars, so the original cars list remains intact. Here is how we can adjust our code:


void pr i nt BySpeed( ) {
Car f ;
Ar r ayLi st <Car > copy;

copy = new Ar r ayLi st <Car >( this. car s) ;
while ( ! copy. i sEmpt y( ) ) {
f = this. f ast est Car ( ) ;
Syst em. out . pr i nt l n( f ) ;
copy. r emove( f ) ;
}
}


However, this does not work either! Why not ? Well, we are finding the fastest car from the
original list, but removing from the copy. We need to find the fastest car in the copy.
However, our fastestCar() method looks at the cars list, not the copy. So we cannot simply
call the fastestCar() method since it does not use the copy.

What we CAN do is extract the code from the fastestCar() method and past it in here,
changing references to this.cars into copy as follows …

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 168 -

void pr i nt BySpeed( ) {
Car f ;
Ar r ayLi st <Car > copy;

copy = new Ar r ayLi st <Car >( this. car s) ;
while ( ! copy. i sEmpt y( ) ) {
copy. get ( 0) ; f =
for ( Car c: copy) {
if ( c. t opSpeed > f . t opSpeed)
f = c;
}
Syst em. out . pr i nt l n( f ) ;
copy. r emove( f ) ;
}
}


Now we have the solution that we needed. Of course, you should realize that there are many
ways to approach this problem. I’m sure you may have your own ideas as to how to solve the
problem.

In a similar way, we can make a minor adjustment to write a method that "prints out all the cars
in alphabetical order of their make". Cars with the same make will appear in arbitrary order
amongst themselves. To do this, we need to know how to compare two strings to determine if
one comes alphabetically before another.
In J AVA’s String class, there is a method called s1.compareTo(s2) that compares string s1
with string s2. If s1 comes before s2 alphabetically, the result of this method is a negative
number. If s1 comes after s2 alphabetically, then the result is a positive number. Otherwise
if compareTo() returns zero, this indicates that s1 equals s2. Here is how we can use this
method on our previous code to solve our new problem:

void printByMake() {
Car f ;
Ar r ayLi st <Car > copy;

copy = new Ar r ayLi st <Car >( this. car s) ;
while ( ! copy. i sEmpt y( ) ) {
f = copy. get ( 0) ;
for ( Car c: copy) {
if ( c. make. compar eTo( f . make) < 0)
f = c;
}
Syst em. out . pr i nt l n( f ) ;
copy. r emove( f ) ;
}
}


COMP1005/1405 – Loops and ArrayLists Fall 2009

- 169 -
The solution is simple, provided that you knew that there was a compareTo() method available
to you. It is a good idea that you start looking around in the J AVA documentation for useful
methods whenever you are stuck on something or need some help in manipulating Strings or
ArrayLists or any type of object. J ust double click on a word (usually a class name) in your
J Creator editor and then select J DK Help under the Help menu. This will bring up the J AVA
documentation for the object that you clicked on … showing you various methods/tools that
you can apply to that object.
For a bigger challenge how could we write a method called mostCommonColor() that returns
the most common color of all the cars at the autoshow ? At first, it seems difficult, begin first
by getting the method signature correct, returning a String and inserting our for loop …
because we know that we need to check all of the cars:

St r i ng mostCommonColor() {
St r i ng best Col or SoFar = " " ;
for ( Car c: this. car s) {
/ / updat e t he bestColorSoFar when
/ / we f i nd a mor e common one . . .
}
return best Col or SoFar ;
}

Now how do we approach the problem ? Well think of real life. If your friend asked you to go
to the autoshow and come back and tell him/her what the most popular color was at the show,
how would YOU go about doing this ?
Assuming that there were hundreds of cars and that your memory is not perfect, you would
likely bring with you a piece of paper (perhaps on a clipboard) so that you can keep track of the
colors of cars that you find. When you enter the autoshow and see the first car, you would
look at its color and then likely write down the color on the paper and maybe put the number 1
beside it. Assume that you went to the next car and that it was a different color. You would
write that new color down too along with a count of 1. If you find a car with the same color
again, you would just increment its count. Below is a picture showing how your clipboard gets
updated as you encounter car colors in the order of red, white, white, blue, etc..:
etc..
etc..
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 170 -
If we were to take this approach in our programming solution, then we need to realize that
each color that we write down on our clipboard will have a single number associated with it at
all times (representing the count of the number of times that color appeared).
The clipboard/paper itself represents our list of colors so we would need an ArrayList to store
that. In fact, since we need a list of counts along with the list of colors, we will need two
ArrayLists … once to store the colors and one to store the counts. So, here is what we
have so far:

St r i ng most CommonCol or ( ) {
St r i ng best Col or SoFar = " " ;
Ar r ayLi st <St r i ng> col or s = new Ar r ayLi st <St r i ng>( ) ;
Ar r ayLi st <I nt eger > count s = new Ar r ayLi st <I nt eger >( ) ;

for ( Car c: this. car s) {
/ / updat e t he best Col or SoFar when we f i nd a mor e common one…
}
return best Col or SoFar ;
}

Now lets see how we can go through the cars and fill up the colors list. This is done exactly
the same way as we did the differentMakes() method … but checking the color attribute
instead of the make attribute:

St r i ng most CommonCol or ( ) {
St r i ng best Col or SoFar = " " ;
Ar r ayLi st <St r i ng> col or s = new Ar r ayLi st <St r i ng>( ) ;
Ar r ayLi st <I nt eger > count s = new Ar r ayLi st <I nt eger >( ) ;

for ( Car c: this. car s) {
if ( col or s. cont ai ns( c. col or ) ) {
/ / don’ t add i t t o t he l i st
}
else {
col or s. add( c. col or ) ; / / append col or t o colors l i st
}
}
return best Col or SoFar ;
}


Now we need to adjust our code so that it also updates the counter associated with the color
as we find it. In the case where it is a new color (i.e., the else code above), then this is easy
… we simply add a 1 to the end of the counts ArrayList:
else {
col or s. add( c. col or ) ; / / append col or t o colors l i st
count s. add( 1) ; / / append count val ue t o counts l i st
}
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 171 -
The above code adds the color/count pair each time a new color is found. This works fine in
our little example above when we find the 1
st
red car and then the 1
st
white car, but what
happens when we find the 2
nd
white car ? We need to go through our colors list and find the
color white, then change its corresponding counts value from 1 to 2. To do this, we will need
to make use of a couple of more ArrayList methods.

colors counts
As mentioned earlier, ArrayLists keep their values ordered by index … which is an integer
indicating the position of the value within the list. So, in our example, once we encounter the
first red car and then the first white car,
the two lists look as shown here


0
1
an ArrayList
“Red”
“White”

0
1
an ArrayList
1
1


Notice that the color red is at index 0 of
the colors ArrayList and white is at
index 1. In the counts list, the
corresponding count values are 1 and
1. When encountering the 2
nd
white
car, we need to determine the index of
white in the colors list (which is 1) and
then get the item at this index in the
counts list and increase it by 1.
We need to
increase this to 2.



ArrayLists have a useful method to get

0
1
an ArrayList
“Red”
“White”

0
1
an ArrayList
1
1
colors counts

y = counts.get(x)
2
counts.set(x, y+1)
x = colors.indexOf(“White”)
the index of a value called indexOf().
We can ask the colors list for the
indexOf(“ White” ) and it will return
the location of white in the list (in this
case 1).

Then we can use the get(1) method in
the counts list to get the current white
car count (in this case 1) and add 1 to
it to make it 2. However, in order to
make a change to the counts list we
have to make use of the ArrayList
set(x, y) method which will allow us
to set the value at position x in the list
to be y. By means of a diagram,
here is what we need to do

So, we are making use of the
indexOf(), get() and set() methods.

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 172 -
Here is what we now have:


St r i ng most CommonCol or ( ) {
int l oc;
St r i ng best Col or SoFar = " " ;
Ar r ayLi st <St r i ng> col or s = new Ar r ayLi st <St r i ng>( ) ;
Ar r ayLi st <I nt eger > count s = new Ar r ayLi st <I nt eger >( ) ;

for ( Car c: this. car s) {
if ( col or s. cont ai ns( c. col or ) ) {
l oc = col or s. i ndexOf ( c. col or ) ;
count s. set ( l oc, count s. get ( l oc) + 1) ;
}
else {
col or s. add( c. col or ) ;
count s. add( 1) ;
}
}
return best Col or SoFar ;
}

At this point, our code keeps track of all the unique colors found at the autoshow as well as the
corresponding count for that color. Now we just need to determine which is the most common
color.
I am sure you will agree that the answer is the color whose corresponding count is highest (in
the case of a tie, we will just return the first color encountered that has this highest value).
To do this, we take the approach that we used in our fastestCar() method … that is … keep
track of the highest count as we move along the autoshow, updating this highest value
whenever we encounter a higher count. Here is the code …

St r i ng most CommonCol or ( ) {
int l oc;
int best Count SoFar = 0;
St r i ng best Col or SoFar = " " ;
Ar r ayLi st <St r i ng> col or s = new Ar r ayLi st <St r i ng>( ) ;
Ar r ayLi st <I nt eger > count s = new Ar r ayLi st <I nt eger >( ) ;

for ( Car c: this. car s) {
if ( col or s. cont ai ns( c. col or ) ) {
l oc = col or s. i ndexOf ( c. col or ) ;
count s. set ( l oc, count s. get ( l oc) + 1) ;
if ( count s. get ( l oc) > best Count SoFar ) {
best Count SoFar = count s. get ( l oc) ;
best Col or SoFar = c. col or ;
}
}
COMP1005/1405 – Loops and ArrayLists Fall 2009

- 173 -
else {
col or s. add( c. col or ) ;
count s. add( 1) ;
if ( 1 > best Count SoFar ) {
best Count SoFar = 1;
best Col or SoFar = c. col or ;
}
}
}
return best Col or SoFar ;
}


We can simplify the code a little by noticing that the recently added code is similar for both
cases. If we add a newCount variable to hold the new updated count value for the item just
added to the list, then we can simplify the code as follows:

St r i ng most CommonCol or ( ) {
int l oc, newCount ;
int best Count SoFar = 0;
St r i ng best Col or SoFar = " " ;
Ar r ayLi st <St r i ng> col or s = new Ar r ayLi st <St r i ng>( ) ;
Ar r ayLi st <I nt eger > count s = new Ar r ayLi st <I nt eger >( ) ;

for ( Car c: this. car s) {
if ( col or s. cont ai ns( c. col or ) ) {
l oc = col or s. i ndexOf ( c. col or ) ;
newCount = count s. get ( l oc) + 1;
count s. set ( l oc, newCount ) ;
}
else {
col or s. add( c. col or ) ;
newCount = 1;
count s. add( newCount ) ;
}
if ( newCount > best Count SoFar ) {
best Count SoFar = newCount ;
best Col or SoFar = c. col or ;
}
}
return best Col or SoFar ;
}


Well, that is it. The code should work as planned … returning the most common color at the
autoshow.

We have written quite a few methods. Here is a test program to try them all out…

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 174 -

class Aut oshowTest Pr ogr am {
public static void mai n( St r i ng ar gs[ ] ) {
Aut oshow show = new Aut oshow( " Aut oRama 2009" ) ;

/ / Fi r st add l ot s of car s t o t he show
show. car s. add( new Car ( " Por sche" , " 959" , " Red" , 240, false) ) ;
show. car s. add( new Car ( " Pont i ac" , " Gr and- Am" , " Whi t e" , 160, true) ) ;
show. car s. add( new Car ( " For d" , " Must ang" , " Whi t e" , 230, false) ) ;
show. car s. add( new Car ( " Vol kswagon" , " Beet l e" , " Bl ue" , 140, false) ) ;
show. car s. add( new Car ( " Vol kswagon" , " J et t a" , " Si l ver " , 180, true) ) ;
show. car s. add( new Car ( " Geo" , " St or m" , " Yel l ow" , 110, true) ) ;
show. car s. add( new Car ( " Toyot a" , " MR2" , " Bl ack" , 220, false) ) ;
show. car s. add( new Car ( " For d" , " Escor t " , " Yel l ow" , 10, true) ) ;
show. car s. add( new Car ( " Honda" , " Ci vi c" , " Bl ack" , 220, true) ) ;
show. car s. add( new Car ( " Ni ssan" , " Al t i ma" , " Si l ver " , 180, true) ) ;
show. car s. add( new Car ( " BMW" , " 5" , " Gol d" , 260, true) ) ;
show. car s. add( new Car ( " Pr el ude" , " Honda" , " Whi t e" , 90, false) ) ;
show. car s. add( new Car ( " Mazda" , " RX7" , " Red" , 240, false) ) ;
show. car s. add( new Car ( " Mazda" , " MX6" , " Gr een" , 160, true) ) ;
show. car s. add( new Car ( " Pont i ac" , " G6" , " Bl ack" , 140, false) ) ;

/ / Now t est our f un met hods
Syst em. out . pr i nt l n( show) ;
Syst em. out . pr i nt l n( " Her e ar e t he Pont i ac car s: " ) ;
Syst em. out . pr i nt l n( show. car sWi t hMake( " Pont i ac" ) ) ;
Syst em. out . pr i nt l n( " Her e ar e t he For d car s: " ) ;
Syst em. out . pr i nt l n( show. car sWi t hMake( " For d" ) ) ;
Syst em. out . pr i nt l n( " Her e ar e t he di f f er ent makes: " ) ;
Syst em. out . pr i nt l n( show. di f f er ent Makes( ) ) ;
Syst em. out . pr i nt l n( " Thi s i s t he f ast est car : " ) ;
Syst em. out . pr i nt l n( show. f ast est Car ( ) ) ;
Syst em. out . pr i nt l n( " The most common col or i s " +
show. most CommonCol or ( ) ) ;
Syst em. out . pr i nt l n( " \ nHer e ar e t he car s sor t ed by t op speed: " ) ;
show. pr i nt BySpeed( ) ;
Syst em. out . pr i nt l n( " \ nHer e ar e t he car s sor t ed by make: " ) ;
show. pr i nt ByMake( ) ;
}
}

COMP1005/1405 – Loops and ArrayLists Fall 2009

- 175 -
Here are the test results:

Aut oshow Aut oRama 2009 wi t h 15 car s
Her e ar e t he Pont i ac car s:
[ Whi t e 4- door Pont i ac Gr and- Amwi t h t op speed 160kmph, Bl ack 2- door Pont i ac G6
wi t h t op speed 140kmph]
Her e ar e t he For d car s:
[ Whi t e 2- door For d Must ang wi t h t op speed 230kmph, Yel l ow 4- door For d Escor t wi t h
t op speed 10kmph]
Her e ar e t he di f f er ent makes:
[ Por sche, Pont i ac, For d, Vol kswagon, Geo, Toyot a, Honda, Ni ssan, BMW, Pr el ude,
Mazda]
Thi s i s t he f ast est car :
Gol d 4- door BMW5 wi t h t op speed 260kmph
The most common col or i s Whi t e


Her e ar e t he car s sor t ed by t op speed:
Gol d 4- door BMW5 wi t h t op speed 260kmph
Red 2- door Por sche 959 wi t h t op speed 240kmph
Red 2- door Mazda RX7 wi t h t op speed 240kmph
Whi t e 2- door For d Must ang wi t h t op speed 230kmph
Bl ack 2- door Toyot a MR2 wi t h t op speed 220kmph
Bl ack 4- door Honda Ci vi c wi t h t op speed 220kmph
Si l ver 4- door Vol kswagon J et t a wi t h t op speed 180kmph
Si l ver 4- door Ni ssan Al t i ma wi t h t op speed 180kmph
Whi t e 4- door Pont i ac Gr and- Amwi t h t op speed 160kmph
Gr een 4- door Mazda MX6 wi t h t op speed 160kmph
Bl ue 2- door Vol kswagon Beet l e wi t h t op speed 140kmph
Bl ack 2- door Pont i ac G6 wi t h t op speed 140kmph
Yel l ow 4- door Geo St or mwi t h t op speed 110kmph
Whi t e 2- door Pr el ude Honda wi t h t op speed 90kmph
Yel l ow 4- door For d Escor t wi t h t op speed 10kmph

Her e ar e t he car s sor t ed by make:
Gol d 4- door BMW5 wi t h t op speed 260kmph
Whi t e 2- door For d Must ang wi t h t op speed 230kmph
Yel l ow 4- door For d Escor t wi t h t op speed 10kmph
Yel l ow 4- door Geo St or mwi t h t op speed 110kmph
Bl ack 4- door Honda Ci vi c wi t h t op speed 220kmph
Red 2- door Mazda RX7 wi t h t op speed 240kmph
Gr een 4- door Mazda MX6 wi t h t op speed 160kmph
Si l ver 4- door Ni ssan Al t i ma wi t h t op speed 180kmph
Whi t e 4- door Pont i ac Gr and- Amwi t h t op speed 160kmph
Bl ack 2- door Pont i ac G6 wi t h t op speed 140kmph
Red 2- door Por sche 959 wi t h t op speed 240kmph
Whi t e 2- door Pr el ude Honda wi t h t op speed 90kmph
Bl ack 2- door Toyot a MR2 wi t h t op speed 220kmph
Bl ue 2- door Vol kswagon Beet l e wi t h t op speed 140kmph
Si l ver 4- door Vol kswagon J et t a wi t h t op speed 180kmph




Chapter 7
Organizing Classes to Use Inheritance


What is in This Chapter ?
Until now, we have been making simple objects that interact together to form an application.
Some of the more important concepts in Object-Oriented programming arise only when we
have many classes in our application, some classes being specializations of others. We will
discuss here how we can organize our classes and take advantage of the notion of
Inheritance which allows much of our code to be "shared" between similar classes called
subclasses. The proper use of inheritance will allow us to reduce the amount of code that we
need to write, resulting in quicker implementation time, less maintenance time and hence
reduced costs overall. We will discuss abstract vs. concrete classes as a means of forcing
users of our classes to be specific with respect to the kinds of objects that they use. Also, we
will discuss interfaces in J AVA as a means of forcing certain classes to have particular
behavior.



COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 177 -

7.1 Class Hierarchy

We have been defining a few objects throughout the course (i.e., Person, Address,
BankAccount, Team, League, Car, Autoshow, etc..). We created/defined a class for each
of these objects. In fact, a definition of the word ‘class’ in English is: "A collection of things
sharing a common attribute".

So, for example, when we created a Person class, we implied that all Person objects have
some attributes in common. Similarly, a Car class was created to define the common
attributes that Car objects have. In general, since Person and Car are different classes, their
list of attributes will differ.

In real life, however, there are some objects that “share” attributes in common. For example,
Person objects may have name and phoneNumber attributes, but so can Employee,
Manager, Customer and Company objects. Yet, there may be attributes of these other
objects that Person does not have. For example, an Employee object may maintain empID
information or a Company object may have a clientList attribute, whereas Person objects in
general do not keep such information.




phoneNumber
name
“ Hank Urchiff”
“ 1-613-555-2145”

phoneNumber
name
Employee object
empID
42516
“ Max Energy”
“ 1-613-555-1316”

phoneNumber
name
Company object
clientList
“ Perfect Store”
“ 1-613-555-5432”
Person object







Shared
Attributes















In addition to commonality between attributes, classes may also share common behavior.
That is, two or more objects may have the ability to perform the same function or procedure.
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 178 -
For example, if a Person, Car and Company are all insurable, then they may all have a
function called calculateInsurancePremium() that determines the pricing information for their
insurance plan.

All object-oriented languages (e.g., J AVA) allow you to organize your classes in a way that
allows you to take advantage of the commonality between classes. That is, we can define a
class with certain attributes (and/or behaviors) and then specify which other classes share
those same attributes (and/or behaviors). As a result, we can greatly reduce the amount of
duplicate code that we would be writing by not having to re-define the common attributes
and/or behaviors for all of the classes that share such common features.

J AVA accomplishes this task by arranging all of its classes in a "family-tree”-like
ordering called a class hierarchy. A class hierarchy is often represented as an
upside down tree (i.e., the root of the tree at the top). The more “general” kinds
of objects are higher up the tree and the more “specific” (or specialized) kinds of
objects are below them in the hierarchy. So, a child object defined in the tree is
a more specific kind of object than its parent or ancestors in the tree. Hence,
there is an "is a" (i.e., "is-a-kind-of") relationship between classes:


Each class is a subclass (i.e., a specialization) of some other class which is called its
superclass (i.e., a generalization). The direct superclass is the class right “above” it:

Whale Dog Lizard Snake
Mammal Reptile
Animal
Iguana Gecko
Reptile
is an
Animal
Snake
is a
Reptile
Gecko is a
Lizard which
is a Reptile
more
specific
Whale Dog Lizard Snake
Mammal Reptile
Animal
direct superclass
of Whale & Dog superclass of
Lizard, Snake,
Iguana &
Gecko
Iguana
subclass of Animal,
Reptile & Lizard
Gecko
direct subclass
of Reptile
more
general
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 179 -
Here, Snake, and Lizard are subclasses of Reptile (i.e., they are special kinds of reptiles).
Also Whale and Dog are subclasses of Mammal. All of the classes are subclasses of Animal
(except Animal itself). Animal is a superclass of all the classes below it, and Mammal is a
superclass of Whale and Dog. As we can see, we can go even deeper in the hierarchy by
creating subclasses of Lizard. Usually, when we use the term superclass, we are referring to
the class that is directly above a particular class (i.e., the direct superclass).

The Animal hierarchy above represents a set of classes that we may define ourselves. But
where do they fit-in with all the other pre-made J AVA classes like String, Date, ArrayList
etc... ? Well, all objects have one thing in common ... they are all Objects. Hence, at the very
top of the hierarchy is a class called Object. Therefore, all classes in J AVA are subclasses of
Object:



All of the classes that we created so far have been direct subclasses of Object. That means
that they did not share attributes with one another, but that they shared attributes only with
Object. However, we have the freedom to re-arrange our classes in a manner that will allow
them to share attributes with one other.

The way in which we arrange our classes will depend on how similar our objects are with
respect to their attributes. For example, a Car and a Truck have something in common ...
they are both drivable. Whereas an MP3Player and a BankAccount have little or nothing in
common with Car or Truck objects. So, intuitively, Car and Truck classes should somehow
be grouped together (i.e., placed nearby) in the hierarchy.

As an example, consider creating many kinds of bank accounts. We might arrange them in a
hierarchy like this:



SuperSavings PowerSavings BusinessChecking
SavingsAccount CheckingAccount
BankAccount
Object
PowerChecking
Object
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 180 -
Here are a few more examples of hierarchies of classes that we may create:


We will talk more about how and why we arrange these classes as above. But remember, a
class should only be a subclass of another class if it "is a kind of" its superclass.
Apple Orange Potato Carrot
Fruit Vegetable
Food
HomeImprovementLoan
Lease Mortgage
Loan
TownHome SingleFamilyHome Warehouse Office
Residential Commercial
BuildingStructure
Object Object
Object
Factory

Sometimes, students misunderstand the class hierarchy,
thinking that a class becomes a subclass
of another one if the superclass
"is made of" the subclasses.

That is, they mistakenly assume that
it is a "has a" relationship instead of
an "is a" relationship. Therefore, the
following hierarchies would be wrong

Distributor SparkPlug Hood Door
Engine Body
Car
Employee Office Customer
Company
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 181 -
In J AVA, in order to create a subclass of another class, use the extends keyword in our class
definition. For example, assume that we wanted to ensure that class A was placed in the
hierarchy as a subclass of class B as follows:



B


A
To make this happen, we simply write extends B immediately after we specify name of class
A as follows:
class A extends B {
. . .
}

If the extends keyword is not used (i.e., as we left it out from all our previous class definitions),
it is assumed that the class being defined extends the Object class. So, all the classes that
we defined previously were direct subclasses of Object.

How do we know how deep we should make the class hierarchy (i.e., tree) ?

Most of the time, any “ is a” relationship between objects should
certainly result in subclassing. Object-oriented code usually involves
a lot of small classes as opposed to a few large ones.

It is often the case that our class hierarchies become rearranged
over time, because we often make mistakes in deciding where to
place the classes. We make such mistakes because it is not always
easy to choose a hierarchy ... it depends on the application.
For example, hierarchies of classes representing students in a university may be arranged in
many different ways ... here are just 3 possibilities …
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 182 -

PartTimeStudent FullTimeStudent
Student
UndergradStudent GradStudent
Student
PartTimeUndergrad FullTimeUndergrad PartTimeGrad FullTimeGrad
UndergradStudent GradStudent
Student
PartTimeUndergrad PartTimeGrad FullTimeUndergrad
PartTimeStudent FullTimeStudent
Student
Object
Object
Object Object
FullTimeGrad
How do we know which one to use ? It will depend on the state (i.e., attributes) and behavior
(i.e., methods) that is common between the subclasses. If we find that the main differences in
attributes or behavior are between full time and part time students (e.g., fee payment rules),
then we may choose the top hierarchy. If however the main differences are between graduate
and undergraduate (e.g., privileges, requirements, exam styles etc..), then we may choose the
middle hierarchy. The bottom hierarchy further distinguishes between full and part time
graduate and undergraduate students, if that needs to be done. So ... the answer is ... we
often do not know which hierarchy to choose until we thought about which hierarchy allows
the maximum sharing of code.
The getClass() method in the Object class can be sent to any object. It returns a special
Class object that represents the class that an object actually belongs to. We can even use the
getName() method on a Class object to get a string representing the class name. Here is an
example of how to use these …
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 183 -

BankAccount a;
Cl ass c;

/ / Make a BankAccount obj ect and get i t s cl ass
a = new BankAccount ( ) ;
c = a. getClass();
Syst em. out . pr i nt l n( c) ; / / pr i nt s class BankAccount
Syst em. out . pr i nt l n( c. getName()) ; / / pr i nt s BankAccount


Keep in mind that the getClass() method can be sent to any object ... while the getName()
method can ONLY be sent to a Class object.
Similarly, we can use the instanceof keyword to determine whether or not an object belongs
to a particular class:
BankAccount a;

a = new BankAccount ( ) ;
if ( a instanceof BankAccount ) { / / same as a. get Cl ass( ) == BankAccount
. . .
}

As we will see later in this section, the instanceof keyword can be quite useful when you need
to figure out what kind of object you have (for example when you pull arbitrary objects out of a
“collection” and need to make a decision based on its type).

7.2 Inheriting Attributes

You may have heard the term inherit before which has various meanings in English such as:

• “to receive from a predecessor” or
• “to receive by genetic transmission”

Through birth, all of us have inherited traits and
behaviors from our parents. Something similar
happens in J AVA with regards to the class
hierarchy. A subclass (i.e., child) inherits the
attributes (i.e., instance variables) and behavior
(i.e., methods) from all of its superclasses (i.e.,
ancestors in the class hierarchy). So as a general
definition, in Object-Oriented Programming,
Inheritance is the act of receiving shared
attributes and behavior from more general types
of objects up the hierarchy.
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 184 -
This means that a subclass has the same "general" attributes/behaviors as its superclasses as
well as possibly some new additional attributes/behaviors which are specific for the subclass.

There are many advantages of using Inheritance:

• allows code to be shared between classes. This promotes software re-usability

• saves programming time since code is shared … less code needs to be written

• helps keep code simple since inheritance is natural in real life


Some languages (e.g., C++) allow Multiple Inheritance, which means that a class can inherit
state and behavior from more than one class. However, J AVA does not support multiple
inheritance. We can however, partially "fake" it (with respect to methods) through the use of
interfaces (which we will discuss later).
Consider making an object to represent an Employee in a company which maintains: name,
address, phoneNumber, employeeNumber and hourlyPay. We may make a single class
for this:

class Employee {
St r i ng name;
Addr ess addr ess;
St r i ng phoneNumber ;
int empl oyeeNumber ;
float hour l yPay;

}


Assume now that we have many employees in a company in which a few of them are
managers. If the managers are all essentially the same as employees, except perhaps that
the have a higher hourlyPay, then there is no need to create any new classes. The
Employee class is sufficient to represent them.

However, what if there were some more significant differences between managers and
employees ? Perhaps it would be beneficial to create a separate class for them. We would
need to determine what is different between these two classes with respect to their attributes
and behaviors. For example, a Manager may have:

• additional attributes (e.g., a list of duties, a list of employees that work for them, etc...)

• additional (or different) behavior (e.g., they may compute their pay differently, or have
different benefit packages, etc...)



COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 185 -
In these situations, a Manager may be considered as a special “kind of”
Employee. It would therefore make sense for the Manager to be
a subclass of Employee as follows:


class Employee {
St r i ng name;
Addr ess addr ess;
St r i ng phoneNumber ;
int empl oyeeNumber ;
float hour l yPay;

}

class Manager extends Employee {
Ar r ayLi st <St r i ng> dut i es;
Ar r ayLi st <Empl oyee> subor di nat es;

}


Notice here that Manager would inherit all of the attributes of the Employee class, so that
Employees have 5 attributes, while Managers have 7. All Employee behaviors would also
be inherited by Managers.

Now, what if we wanted to represent a Customer as well in our application ? Our application
may require keeping track of a customer’s name, address and phoneNumber. But these
attributes are also being used for our Employee objects. We could make two separate
unrelated classes ... one called Customer ... the other called Employee. We could define
Customer as follows:


class Customer {
St r i ng name;
Addr ess addr ess;
St r i ng phoneNumber ;

}


This would work fine. However, you will notice that both Employee and Customer have
some attributes in common. So, if we defined the Customer class in this manner, we would
need to repeat the same definitions, and perhaps some of the behaviors. It would be better if
we could somehow use inheritance to allow Customers to share attributes and behaviors that
are in common with Employees. So, we should perhaps have Customer inherit from
something. We have a few choices. We can have Customer inherit from Manager,
Employee inherit from Customer or Customer inherit from Employee as follows …
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 186 -

However, neither of these hierarchies will work according to the "is a" relationship because:
• a Customer is not always a Manager
• an Employee is not always a Customer
• a Customer is not always an Employee
One possible solution is to change the name Customer to Person. In this way, a customer is
simply represented by a Person object and we can use the following hierarchy:


class Person {
St r i ng name;
Addr ess addr ess;
St r i ng phoneNumber ;

}

class Employee extends Person {
int empl oyeeNumber ;
float hour l yPay;

}

class Manager extends Employee {
Ar r ayLi st <St r i ng> dut i es;
Ar r ayLi st <Empl oyee> subor di nat es;

}


COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 187 -
Now Employee inherits 3 attributes from Person, so it has 5 altogether, while Manager
inherits 3 from Person and 2 from Employee, making 7 altogether. Customers, are then
represented simply as Person objects.

This is a good solution as long as ALL of the attributes (e.g., name, address, phone number)
for a customer (i.e., Person object) is also shared with Employee and Manager. Also, there
must not be any attributes or behaviors in the Person class that do not apply to an Employee
and a Manager. For example, if the application required us to keep track of a list of items
purchased by the customer or perhaps even a purchase history, then such attributes may not
make sense for an Employee or Manager. So, if there is different behavior or attributes that is
unique to customers, then we must create a separate Customer class to define these
differences. In this case, we can still share the name, address and phoneNumber by
creating an extra Customer class to hold the common attributes. We can create the following
hierarchy:


class Person {
St r i ng name;
Addr ess addr ess;
St r i ng phoneNumber ;

}

class Employee extends Person {
int empl oyeeNumber ;
float hour l yPay;

}

class Customer extends Person {
Ar r ayLi st <St r i ng> i t emsPur chased;
Ar r ayLi st <Dat e> pur chaseHi st or y;

}

class Manager extends Employee {
Ar r ayLi st <St r i ng> dut i es;
Ar r ayLi st <Empl oyee> subor di nat es;

}


This will allow all common attributes (i.e., name, address, phoneNumber) to be shared by all
the classes while allowing Customer objects to have their own attributes and behaviors.

At this point, we should clarify the advantages of the attribute-related inheritance that is
occurring within our hierarchy. Here is a simple example piece of code showing the attributes
that are readily available to each type of object defined in our example …

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 188 -

Per son p = new Per son( ) ;
Empl oyee e = new Empl oyee( ) ;
Cust omer c = new Cust omer ( ) ;
Manager m= new Manager ( ) ;

p. name = " Hank Ur chi f f " ; / / own at t r i but e
p. addr ess = new Addr ess( ) ; / / own at t r i but e
p. phoneNumber = " 1- 613- 555- 2328" ; / / own at t r i but e

e. name = " Mi nni e Mumwage" ; / / at t r i but e i nher i t ed f r omPerson
e. addr ess = new Addr ess( ) ; / / at t r i but e i nher i t ed f r omPerson
e. phoneNumber = " 1- 613- 555- 1231" ; / / at t r i but e i nher i t ed f r omPerson
e. empl oyeeNumber = 232867; / / own at t r i but e
e. hour l yPay = 8. 75f ; / / own at t r i but e

c. name = " J i mCl ot hes" ; / / at t r i but e i nher i t ed f r omPerson
c. addr ess = new Addr ess( ) ; / / at t r i but e i nher i t ed f r omPerson
c. phoneNumber = " 1- 613- 555- 5675" ; / / at t r i but e i nher i t ed f r omPerson
c. i t emsPur chased. add( “Penci l Case”) ; / / own at t r i but e
c. pur chaseHi st or y. add( Dat e. t oday( ) ) ; / / own at t r i but e

m. name = " Max E. Mumwage" ; / / at t r i but e i nher i t ed f r omPerson
m. addr ess = new Addr ess( ) ; / / at t r i but e i nher i t ed f r omPerson
m. phoneNumber = " 1- 613- 555- 8732" ; / / at t r i but e i nher i t ed f r omPerson
m. empl oyeeNumber = 232867; / / at t r i but e i nher i t ed f r omEmployee
m. hour l yPay = 8. 75f ; / / at t r i but e i nher i t ed f r omEmployee
m. dut i es. add( " Phone Cl i ent s" ) ; / / own at t r i but e
m. subor di nat es. add( e) ; / / own at t r i but e


Notice that we use the inherited attributes just as if they were defined as part of that class
directly. For example, the Employee object e, Customer object c and Manager object m, all
access the name attribute as if it was defined in their class … even though it is actually defined
in the Person class … written in a different .java file!! You can see that through inheritance,
we do not have to re-define the name attribute in each of these classes. The same holds true
for the address and phoneNumber attributes, as well as any other inherited attributes in the
subclasses.

At this point, we only examined how to decide upon a class hierarchy based on the differences
in attributes. However, we would have to think in the same manner by examining the
behaviors of the individual classes. For example, even if managers did not have the duties
and subordinates attributes shown above, we may still want to make a separate class for
managers if there are behaviors that differ (e.g., different computePay() method). In the next
section, we will consider an example that shows how inheritance applies to behaviors within a
simple hierarchy of BankAccount objects.

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 189 -

7.3 Inheriting Behavior

Consider creating an application for a bank that maintains account information for its
customers. All bank accounts at this bank must maintain 3 common attributes (the owner’s
name, the account number and the balance of money remaining in the account). Also, an
account, by default, should have simple behaviors to deposit and withdraw from the account.
So, in its simplest form, a BankAccount object can be defined and used as follows:


class BankAccount {
static int LAST_ACCOUNT_NUMBER = 100000;

St r i ng owner ; / / per son who owns t he account
int account Number ; / / t he account number
float bal ance; / / amount of money cur r ent l y i n t he account

/ / Some const r uct or s
BankAccount ( ) {
this. owner = " " ;
this. account Number = LAST_ACCOUNT_NUMBER++;
this. bal ance = 0;
}
BankAccount ( St r i ng owner Name) {
Object
BankAccount
this. owner = owner Name;
this. account Number = LAST_ACCOUNT_NUMBER++;
this. bal ance = 0;
}

/ / Ret ur n a st r i ng r epr esent at i on of t he account
public St r i ng t oSt r i ng( ) {
return " Account #" + this. account Number + " wi t h $" + this. bal ance;
}

eposi t mo nt o t he account / / D ney i
void deposi t ( float amount ) {
this. bal ance += amount ;
}

/ / Wi t hdr aw money f r omt he account
void wi t hdr aw( float amount ) {
if ( this. bal ance >= amount )
this. bal ance - = amount ;
}
}


Now assume that the bank wants to distinguish between “savings” accounts and
“non-savings” accounts in that the customer cannot withdraw money from a
“savings” account once it has been deposited (i.e., to get the money out of the
account, the customer must close the account).
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 190 -
We would need to have a way of disabling the withdraw behavior for savings accounts. We
could do this through inheritance by creating a subclass of BankAccount to represent a
special “kind of” account … we will call it SavingsAccount:


class SavingsAccount extends BankAccount {
}


J ust by writing this simple “virtually empty” class definition in which SavingsAccount
extends BankAccount, we have “invented” a new type of bank account that inherits
all 3 attributes from BankAccount as well as the toString(), deposit() and
withdraw() methods. We could verify this by writing a simple piece of test code:


Savi ngsAccount s = new SavingsAccount();

Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $0. 0

s. deposit( 120) ;
Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $120. 0

s. withdraw( 20) ;
Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $100. 0


Something important to know, however, is that a subclass does not automatically inherit the
constructors in its superclass. So, SavingsAccount does not inherit the two constructors in
BankAccount … but it does get to use its own default constructor (i.e., zero-parameter
constructor) for free. We can verify this by altering the first line in our test code so read:

Savi ngsAccount s = new Savi ngsAccount ( " Bob" ) ;

If we made such an alteration to the code, our test code would no longer compile. We would
receive the following compile error:


cannot f i nd symbol const r uct or Savi ngsAccount ( j ava. l ang. St r i ng)


which is telling us that we don’t have a constructor in our SavingsAccount class that takes a
single String parameter. How then did our new Savi ngsAccount ( ) code work previously
since it seems to have properly initialized the account number ? Well, as it turns out, the
default constructor that we get for free actually looks as follows:


Savi ngsAccount ( ) {
super( ) ;
}

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 191 -
What does this mean ? What does the keyword super do ? The keyword
super is actually a special word that represents the superclass of this class.
In our case, the super class is BankAccount. So, it is essentially doing a
call to BankAccount() … which means it is calling the superclass
constructor. You may recall that previously in our constructors we used the
code this(…) to call a constructor in our own class. Well super(…) calls a
constructor too, but in the superclass instead. So, if we want to make use
of the attribute initialization code that is in a constructor in a superclass, we
can call the superclass constructor from our own. Hence we can write the following
constructor in our SavingsAccount class:


class SavingsAccount extends BankAccount {
Savi ngsAccount ( St r i ng aName) {
super( aName) ;
}
}


If we do this, then we can use the following code without
compile errors:

Savi ngsAccount s = new Savi ngsAccount ( " Bob" ) ;

Keep in mind, however, that the list of parameters (i.e., the types) supplied within the
super(…) call, must match the list of parameters (i.e., the types) of one of the constructors in
the superclass. In order to see the advantage of using constructor inheritance, here is what
the code would look like with and without using inherited constructors:

Without Inheritance (need to re-write the code) With Inheritance

Savi ngsAccount ( ) { {
this. owner = " " ;
this. account Number = LAST_ACCOUNT_NUMBER++;
this. bal ance = 0;
}

Savi ngsAccount ( St r i ng owner Name) {
this. owner = owner Name;
this. account Number = LAST_ACCOUNT_NUMBER++;
this. bal ance = 0;
}


Savi ngsAccount ( ) {
super( " " ) ;
}



Savi ngsAccount ( St r i ng aName) {
super( aName) ;
}


Again … the amount of code that needs to be written is reduced when using inheritance.
BankAccount
SavingsAccount
Object

So, we have SavingsAccount properly inheriting from BankAccount, however, the
SavingsAccount class still allows withdrawals. In order to disable this behavior, we need to
somehow “prevent” the withdraw method code from being used by savings accounts. The
simplest and most common way of doing this is to write a new withdraw() method in the
SavingsAccount class that simply does nothing as follows …

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 192 -

class SavingsAccount extends BankAccount {
/ / Const r uct or t o cal l t he super cl ass const r uct or
Savi ngsAccount ( St r i ng aName) { super( aName) ; }
Savi ngsAccount ( ) { super( " " ) ; }

/ / Pr event t he wi t hdr awal of money f r omt he account
void withdraw( float amount ) {
/ / Do not hi ng
}
}


Once we re-compile, we can test it out by running our test code again:


Savi ngsAccount s = new SavingsAccount();

Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $0. 0

s. deposit( 120) ;
Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $120. 0

s. withdraw( 20) ; / / t hi s wi l l do not hi ng now
Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $120. 0


Notice that the test code remains the same but now it no longer performs the withdrawal
calculation. What is actually happening here ? By writing the withdraw() method in the
SavingsAccount class, we are actually overriding the one that is in the BankAccount class.
That is, we are replacing the inherited behavior with our own unique behavior. So, we are
preventing or disabling the inheritance for this behavior.

The idea of overriding behavior is actually not new to us. Every time that we write a
toString() method, we are actually overriding the one that we would normally inherit from the
Object class.

At this point, we now have SavingsAccounts that cannot be withdrawn from and normal
BankAccounts that can be withdrawn from. Lets see another way that we can use overriding
… to modify inherited behavior.

Assume that the bank also wants to encourage depositing to savings accounts by
giving $0.50 to the customer for each $100 that they deposit into their
SavingsAccount (i.e., not for regular BankAccounts). For example, if they
deposit $354.23, then their account balance should immediately increase by
$355.73 … showing the extra $1.50 applied to the deposit amount.

To do this, we can completely override the deposit method from BankAccount by
writing the following method in SavingsAccount …

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 193 -

/ / Deposi t money i nt o t he account
void deposi t ( float amount ) {
this. bal ance += amount ;

Now add t he bonus nt s per $100 / / 50 ce
int whol eDol l ar s = (int)( amount / 100) ;
this. bal ance += whol eDol l ar s * 0. 50f ;
}


This method of overriding would work fine and would properly add the extra bonus deposit
incentive. However, the first line is a duplication of the BankAccount class’s deposit()
method. This duplication may seem insignificant in this simple example, but in a real bank
application there may actually be much more code devoted to the deposit process (e.g.,
logging the transaction). Hence, it would be better to make use of inheritance.

How though, can we inherit the deposit() method in BankAccount, while also incorporating
the additional bonus deposit behavior necessary for SavingsAccounts ? The answer makes
use of the super keyword again. Here is the solution:


/ / Deposi t money i nt o t he account
void deposi t ( float amount ) {
/ / Cal l t he deposi t ( ) met hod i n t he super cl ass
super. deposi t ( amount ) ;

Now add t he bonus nt s per $100 / / 50 ce
int whol eDol l ar s = (int)( amount / 100) ;
this. bal ance += whol eDol l ar s * 0. 50f ;
}


Notice that this time we use a dot . after the super keyword, followed by the method that we
want to call in the superclass. Here, the word super is used to tell J AVA to look for the
deposit() method in the superclass. J AVA will go and evaluate the superclass deposit()
method (which performs the “normal” depositing process) and then return here and complete
the behavior by adding the 50 cent bonus incentive. This method is still considered to
override the deposit() method in BankAccount. It is an example of a situation in which we
want to “borrow” a superclass’s behavior, but then add some additional behavior as well.

Alternatively, we could have combined the deposit amount with the 50 cent bonus incentive
before calling the superclass method as follows:


/ / Deposi t money i nt o t he account
void deposi t ( float amount ) {
int whol eDol l ar s = (int)( amount / 100) ;
super. deposi t ( amount + ( whol eDol l ar s * 0. 50f ) ) ;
}

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 194 -
or even simpler:


/ / D ney i eposi t mo nt o t he account
void deposi t ( float amount ) {
super. deposi t ( amount + (int)( amount / 100) * 0. 50f ) ;
}


I’m sure you will agree that the overriding can be quite powerful tool to save coding time.

J ust so you understand … what would happen if we used this instead of super as follows:


/ / Deposi t money i nt o t he account
void deposi t ( float amount ) {
this. deposi t ( amount + (int)( amount / 100) * 0. 50f ) ;
}


Well, we would be asking J AVA to call the deposit() method in this class, not the one in
BankAccount. Furthermore, since this code is written inside the deposit() method, we are
telling J AVA to call the method that we are actually trying to write! So the method will keep
calling itself forever … an infinite loop! We would get a pile of runtime error messages that
says something like this:


Except i on i n t hr ead " mai n" j ava. l ang. St ackOver f l owEr r or
at Savi ngsAccount . deposi t ( Savi ngsAccount . j ava: 13)
at Savi ngsAccount . deposi t ( Savi ngsAccount . j ava: 13)
at Savi ngsAccount . deposi t ( Savi ngsAccount . j ava: 13)
. . .
at Savi ngsAccount . deposi t ( Savi ngsAccount . j ava: 13)


OK. Now assume that the bank application needs to further distinguish between
accounts in that it also has a special “power savings” account that is a special
type of savings account that allows withdrawals, but there is a $1.25 service fee
each time a withdrawal is made. As before, this new type of account should
also have the 50 cent incentive for each $100 deposited.

Assuming that we call the new class PowerSavings, where do we put it in the
hierarchy ? We need it to inherit the deposit() method from SavingsAccount but the
withdraw() method from BankAccount. If we make PowerSavings a subclass of
SavingsAccount, we will inherit the deposit() behavior that we want, but would then need to
write a new withdraw() method, since the one in SavingsAccount does nothing. We could
do this …

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 195 -

class PowerSavings extends Savi ngsAccount {
/ / Const r uct or t o cal l t he super cl ass const r uct or
Power Savi ngs( St r i ng aName) { super( aName) ; }
Power Savi ngs( ) { super( " " ) ; }

/ / Wi t hdr aw money f r omt he account
void wi t hdr aw( float amount ) {
if ( this. bal ance >= ( amount + 1. 50f ) )
this. bal ance - = ( amount + 1. 50f ) ;
}
}


This code would work fine.

Again, we are using overriding by having the withdraw() method in PowerSavings override
the default behavior in SavingsAccount. We can test our new class with the following test
code:


Power Savi ngs s = new PowerSavings();

Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $0. 0

s. deposit( 320) ;
Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $321. 50

s. withdraw( 20) ;
Syst em. out . pr i nt l n( s) ; / / di spl ays Account #100000 wi t h $300. 0


Notice that the withdraw() method properly deducts the $1.50 fee.

However, again we are duplicating code. The code here is small, however in a large system,
there may be more complicated code for withdrawing money (e.g., transaction logging,
overdraft allowances, etc…). So, we do not want to duplicate this code. In fact, it would be
nice if we could do something like this to call the withdraw() method code up in
BankAccount:


class PowerSavings extends Savi ngsAccount {
/ / Const r uct or t o cal l t he super cl ass const r uct or
Power Savi ngs( St r i ng aName) { super( aName) ; }
Power Savi ngs( ) { super( " " ) ; }

/ / Wi t hdr aw money f r omt he account
void wi t hdr aw( float amount ) {
super. wi t hdr aw( amount + 1. 50f ) ;
}
}

BankAccount
SavingsAccount
Power Savings
Object
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 196 -
But this won’t work. Why not ? Because super refers to the SavingsAccount class here,
and so it calls the withdraw() method in SavingsAccount that does nothing. In a way, what
we want to do is something like this:

super.super. wi t hdr aw( amount + 1. 50f ) ; / / super - duper does not wor k

Unfortunately, we cannot skip over a class when looking up the class hierarchy for a method.
What can we do then ? The solution is to re-organize our hierarchy. We seem to need
common deposit behavior for savings accounts, but then differing withdrawal behavior. In
reality, we actually need to distinguish between the two kinds of savings accounts. We will
rename SavingsAccount to SuperSavings which will represent the previous savings account
behavior. Then we will create a new SavingsAccount class that will contain the shared
deposit behavior between the two types of savings accounts. Here is the new hierarchy:

SuperSavings PowerSavings
SavingsAccount
BankAccount
Object














Here is the code:


class SavingsAccount extends BankAccount {
Savi ngsAccount ( St r i ng aName) { super( aName) ; }
Savi ngsAccount ( ) { super( " " ) ; }

void deposi t ( float amount ) {
super. deposi t ( amount + (int)( amount / 100) * 0. 50f ) ;
}
}



class SuperSavings extends Savi ngsAccount {
Super Savi ngs( St r i ng aName) { super( aName) ; }
Super Savi ngs( ) { super( " " ) ; }

void withdraw( float amount ) { / * Do not hi ng */ }
}

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 197 -


class PowerSavings extends Savi ngsAccount {
Power Savi ngs( St r i ng aName) { super( aName) ; }
Power Savi ngs( ) { super( " " ) ; }

void wi t hdr aw( float amount ) {
super. wi t hdr aw( amount + 1. 50f ) ;
}
}


The code will work as we expect it to now, taking full advantage of inheritance.
You may have wondered about the toString() method. In all of our bank account subclasses,
we are able to inherit the toString() method that was written in the BankAccount class. So,
for example, PowerSavings accounts do not inherit the toString() method from its direct
superclass SavingsAccount, rather it inherits it from the BankAccount class further up the
hierarchy. That is because of J AVA’s strategy for looking up methods when you call them.

Whenever you call a method from a class directly (e.g., this.myMethod()), J AVA looks first to
see whether or not you have such a method in the class that you are calling it from. If it finds
it there, it evaluates the code in that method. Otherwise, J AVA tries to look for the method up
the hierarchy (never down the hierarchy) by checking the superclass. If not found there, J AVA
continues looking up the hierarchy until it either finds the method that you are trying to call, or
until it reaches the Object class at the top of the tree.

Here is the general strategy for all instance method lookup:

• If method myMethod()
exists in class H, then
it is evaluated.

• Otherwise, J AVA
checks the superclass
of H for myMethod (in
this case class F).

• If not found there,
J AVA continues looking
up the hierarchy until
Object is reached,
visiting additional
classes C, A and Object.

If not found at all during this search up to the Object class, the compiler will catch this and
inform you that it cannot find method myMethod() for the object you are trying to sending it to:

C: \ Test . j ava: 20: cannot r esol ve symbol
symbol : met hod myMet hod ( )

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 198 -
If there were many implementations of myMethod() along the path in the hierarchy
(e.g., classes F, C, and A all implement myMethod()), then J AVA will execute the first one that
it finds during its bottom-up search.
Notice the use of the keyword this in the picture. That tells J AVA to start looking for the
method in "this" class. Recall that we can leave out the keyword this in which case J AVA
assumes that you want to start looking for the method in this class anyway. As you saw
earlier, we can also use the keyword super. If we use the keyword super here (i.e.,,
super.myMethod()) then we are telling J AVA to start its search for the method in the
superclass. So, if we used super in the example above, J AVA would start looking for
myMethod() in class F first. If not found, it would then continue on up the tree looking for the
method as usual. In fact if there was an implementation of myMethod() in the H class, it
would not be called if we used super, since the search begins in the superclass, not in this
class. So, the use of super merely specifies "where the method lookup should begin the
search" ... nothing more.



COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 199 -

7.4 University Database Example
Consider implementing a university database system. Perhaps we might come up with the
following hierarchy showing the different types of people. The attributes are listed below the
classes as well.

Professor Secretary
PartTimeStudent
Employee Student
wor kl oad
schedul e
cour ses
of f i ceNumber
t enur ed
st udent Number
cour seLi st
gpa
maj or
FullTimeStudent
empl oyeeNumber
wor kPhoneNumber
sal ar y
name
addr ess
phoneNumber
emai l Addr ess
Person
From this example we can see that there are different specializations of people but each of
these special people share some common attributes (e.g., name, phoneNumber and
address). Notice that PartTimeStudents do not have any of their own attributes, but will
inherit 7 attributes from its superclasses Student and Person. (On a side note, do you know why
would we create a subclass that does not have any additional attributes ? How is a PartTimeStudent object
different from a Student object ? Well, PartTimeStudents may have different behavior as well which is
implemented in that class.)

Here are the class definitions showing the attributes defined in each class …
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 200 -

class Person {
St r i ng name
St r i ng addr ess;
St r i ng phoneNumber ;
St r i ng emai l Addr ess;
. . .
}



class Employee extends Person {
int empl oyeeNumber ;
St r i ng wor kPhoneNumber ;
float sal ar y;
. . .
}



class Professor extends Employee {
Ar r ayLi st <Cour se> cour ses;
St r i ng of f i ceNumber ;
boolean t enur ed;
. . .
}


class Secretary extends Employee {
Ar r ayLi st <St r i ng> wor kLoad;
Ar r ayLi st <Wor kTask> schedul e;
. . .
}



class Student extends Person {
int st udent Number ;
Ar r ayLi st <Cour se> cour seLi st ;
float gpa;
. . .
}



class FullTimeStudent extends Student {
St r i ng maj or ;
. . .
}



class PartTimeStudent extends Student {
. . .
}


Consider writing a getDescription() method for the Person, Employee, Professor, Secretary
and Student classes that returns a String showing information about the object as follows:
" Person
NAME: J i mCl ass
ADDRESS: 1445 Por t er St .
PHONE #: 613- 555- 3232"
" Employee
NAME: Rob Banks
ADDRESS: 789 Scot i aBank Road.
PHONE #: 613- 555- 2332
EMPLOYEE #: 88765
WORK #: 613- 555- 2433"
" Professor
NAME: Guy Smar t
EMPLOYEE #: 65445
WORK #: 613- 555- 3415
OFFI CE #: 5240 PA"
" Secretary
NAME: Ear l E. Bi r d
ADDRESS: 12 Knowher e Cr es.
PHONE #: 613- 555- 7854
EMPLOYEE #: 76845
WORK #: 613- 555- 3243"
" FullTimeStudent
NAME: May I . Passpl ease
ADDRESS: 5567 J ava Dr i ve
PHONE #: 613- 555- 8923
STUDENT #: 100156753
MAJ OR: Comput er Sci ence"

" PartTimeStudent
NAME: Al B. Back
ADDRESS: 23 Ret ur n Bl vd.
PHONE #: 613- 555- 9738
STUDENT #: 100134257"

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 201 -
Notice that the strings span multiple lines. Also, notice that the name of the class is in the
strings as well. Also note that only some of the object’s attributes are shown in each case.
For example, the courses were not displayed for Professors or Students. To obtain the
desired results for each class, we can simply write a separate getDescription() method for
each class that does what we want. However, this would be wasteful. Let us try to save
ourselves from writing a lot of code. We will try to "share" code among classes by making use
of inheritance and overriding by using the super keyword.
First, we notice that all classes show the name. This should definitely be done in the Person
class since all others are subclasses and can inherit the code. Next, we see that all classes
(except Professor) also show the phoneNumber and address. (I guess Professors do not want to
show everybody this information). Thus, we will allow all subclasses to display this information and
we will have to do something different for the Professor class. Let us begin by writing the
getDescription() method for the Person class:

class Per son {
. . .
St r i ng get Descr i pt i on( ) {
return ( " Per son \ n" +
" NAME: " + this. name + " \ n" +
" ADDRESS: " + this. addr ess + " \ n" +
" PHONE #: " + this. phoneNumer ) ;
}
}


It is quite straight forward. At this point, all of the subclasses will inherit this method and we
will obtain the following results:

/ / Resul t when cal l i ng getDesription()
/ / on Person obj ect
" Person
NAME: J i mCl ass
ADDRESS: 1445 Por t er St .
PHONE #: 613- 555- 3232"

/ / Resul t when cal l i ng getDesription()
/ / on Employee obj ect
" Person
NAME: Rob Banks
ADDRESS: 789 Scot i aBank Road.
PHONE #: 613- 555- 2332"

/ / Resul t when cal l i ng getDesription()
/ / on Professor obj ect
" Person
NAME: Guy Smar t
ADDRESS: 267 Lost Cr es.
PHONE #: 613- 555- 2378"


/ / Resul t when cal l i ng getDesription()
/ / on Secretary obj ect
" Person
NAME: Ear l E. Bi r d
ADDRESS: 12 Knowher e Cr es.
PHONE #: 613- 555- 7854"

/ / Resul t when cal l i ng getDesription()
/ / on FullTimeStudent obj ect
" Person
NAME: May I . Passpl ease
ADDRESS: 5567 J ava Dr i ve
PHONE #: 613- 555- 8923"

/ / Resul t when cal l i ng getDesription()
/ / on PartTimeStudent obj ect
" Person
NAME: Al B. Back
ADDRESS: 23 Ret ur n Bl vd.
PHONE #: 613- 555- 9738"

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 202 -
So by writing the one method in Person, all subclasses inherit it for free. However, we would
like the “type” of person displayed as the first word of the String … we do not always want
“Person” displayed. We can make use of this.getClass().getName() to extract the name of
the particular object. So we just need to make the following change in the method that we
wrote:

class Per son {
. . .
St r i ng get Descr i pt i on( ) {
return ( this. get Cl ass( ) . get Name( ) + " \ n" +
" NAME: " + this. name + " \ n" +
" ADDRESS: " + this. addr ess + " \ n" +
" PHONE #: " + this. phoneNumer ) ;
}
}


Now all of the subclasses will inherit this method and we will obtain the following results (note
the highlighted changes):
/ / Resul t when cal l i ng getDesription()
/ / on Person obj ect
" Person
NAME: J i mCl ass
ADDRESS: 1445 Por t er St .
PHONE #: 613- 555- 3232"

/ / Resul t when cal l i ng getDesription()
/ / on Employee obj ect
" Employee
NAME: Rob Banks
ADDRESS: 789 Scot i aBank Road.
PHONE #: 613- 555- 2332"

/ / Resul t when cal l i ng getDesription()
/ / on Professor obj ect
" Professor
NAME: Guy Smar t
ADDRESS: 267 Lost Cr es.
PHONE #: 613- 555- 2378"

/ / Res cal l i ng getDesription() ul t when
/ / on Secretary obj ect
" Secretary
NAME: Ear l E. Bi r d
ADDRESS: 12 Knowher e Cr es.
PHONE #: 613- 555- 7854"

/ / Resul t when cal l i ng getDesription()
/ / on FullTimeStudent obj ect
" FullTimeStudent
NAME: May I . Passpl ease
ADDRESS: 5567 J ava Dr i ve
PHONE #: 613- 555- 8923"

/ / Resul t when cal l i ng getDesription()
/ / on PartTimeStudent obj ect
" PartTimeStudent
NAME: Al B. Back
ADDRESS: 23 Ret ur n Bl vd.
PHONE #: 613- 555- 9738"

This is closer to what we want. Now what else is common within the remaining classes ? We
notice that all Employees (also Professors and Secretaries) show their employee number
and work number. Therefore, this can all be done in the Employee class.

In order to make sure that the name, address and phoneNumber are shown as well, we must
use the superclass method first. So we will override the getDescription() method in the
Person class with one in the Employee class that will make use of its superclass method and
then add additional code …

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 203 -

class Empl oyee extends Per son {
...
St r i ng get Descr i pt i on( ) {
return ( super. get Descr i pt i on( ) + " \ n" +
" EMPLOYEE #: " + this. empl oyeeNumber + " \ n" +
" WORK #: " + this. wor kNumer ) ;
}
}


Note the use of super.getDescription(). This allows us to inherit what the superclass did and
then add more information for this class. Here is what we have as results so far:

/ / Resul t when cal l i ng getDesription()
/ / on Person obj ect
" Person
NAME: J i mCl ass
ADDRESS: 1445 Por t er St .
PHONE #: 613- 555- 3232"

/ / Resul t when cal l i ng getDesription()
/ / on Employee obj ect
" Employee
NAME: Rob Banks
ADDRESS: 789 Scot i aBank Road.
PHONE #: 613- 555- 2332
EMPLOYEE #: 88765
WORK #: 613- 555- 2433"

/ / Res cal l i ng getDesription() ul t when
/ / on Professor obj ect
" Professor
NAME: Guy Smar t
ADDRESS: 267 Lost Cr es.
PHONE #: 613- 555- 2378
WORK #: 613- 555- 3415
OFFI CE #: 5240 PA"


/ / Resul t when cal l i ng getDesription()
/ / on Secretary obj ect
" Secretary
NAME: Ear l E. Bi r d
ADDRESS: 12 Knowher e Cr es.
PHONE #: 613- 555- 7854
EMPLOYEE #: 76845
WORK #: 613- 555- 3243"

/ / Resul t when cal l i ng getDesription()
/ / on FullTimeStudent obj ect
" FullTimeStudent
NAME: May I . Passpl ease
ADDRESS: 5567 J ava Dr i ve
PHONE #: 613- 555- 8923"

/ / Resul t when cal l i ng getDesription()
/ / on PartTimeStudent obj ect
" PartTimeStudent
NAME: Al B. Back
ADDRESS: 23 Ret ur n Bl vd.
PHONE #: 613- 555- 9738"

Notice how the Employee, Professor, and Secretary classes all have the additional
information now. In fact, we do not even need to write a getDescription() method in the
Secretary class since it inherits the one in Employee which already does what it needs to do.

Now what about the Student class ? It merely inherits from Person and also displays the
studentNumber, so we can use super again.


class St udent extends Per son {
. . .
St r i ng get Descr i pt i on( ) {
return ( super. get Descr i pt i on( ) + " \ n" +
" STUDENT #: " + this. st udent Number ) ;
}
}

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 204 -
We can append a little more for FullTimeStudent objects to include their major:


class Ful l Ti meSt udent extends St udent {
...
St r i ng get Descr i pt i on( ) {
return ( super. get Descr i pt i on( ) + " \ n" +
" MAJ OR: " + this. maj or ) ;
}
}


Here is the resulting change to results when calling getDescription() on a Student:

/ / Resul t when cal l i ng getDesription() / / Resul t when cal l i ng getDesription()
/ / on FullTimeStudent obj ect / / on PartTimeStudent obj ect
" FullTimeStudent " PartTimeStudent
NAME: May I . Passpl ease NAME: Al B. Back
ADDRESS: 5567 J ava Dr i ve ADDRESS: 23 Ret ur n Bl vd.
PHONE #: 613- 555- 8923 PHONE #: 613- 555- 9738
STUDENT #: 100156753 STUDENT #: 100134257"
MAJ OR: Comput er Sci ence"

Notice that we did not write any code in PartTimeStudent, as this simply inherits the method
from Student.

The Professor class however, will have problems. We cannot merely inherit from Employee
because the Employee method inherits from Person (which displays the phone number and
address, and professors do not want this information available). Therefore, we can do this
one without inheritance by completely overriding the Employee method:

class Pr of essor extends Empl oyee {
. . .
St r i ng get Descr i pt i on( ) {
return ( " Pr of essor \ n" +
" NAME: " + this. name + " \ n" +
" EMPLOYEE #: " + this. empl oyeeNumber + " \ n" +
" WORK #: " + this. wor kNumer + " \ n" +
" OFFI CE# : " + this. of f i ceNumber ) ;
}
}
Now we have the results that we want. It is too bad though, since we now have some
duplicate code in the Professor class. If only there was a way to make use of "some" of the
code in the Person class, but not all of it. But if we change the getDescription() method in
the Person class, then this will affect the getDescription() method in the Employee class.
We must be careful.

We can create two getDescription() methods in the Person class. One that displays the
name only, and the other one will display name, address and phoneNumber. Of course the
methods need to have different names:

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 205 -

class Per son {
. . .
St r i ng getDescription() {
return ( this. get Cl ass( ) . get Name( ) + " \ n" +
" NAME: " + this. name + " \ n" +
" ADDRESS: " + this. addr ess + " \ n" +
" PHONE #: " + this. phoneNumer ) ;
}
St r i ng shortGetDescription() {
return ( this. get Cl ass( ) . get Name( ) + " \ n" +
" NAME: " + this. name) ;
}
}


We want the Professor class to use the shortGetDescription() method … but how do we do
it ? If we call super.shortGetDescription(), it will display the name properly, but then we still
need to display the employeeNumber, workNumber and officeNumber:


class Pr of essor extends Empl oyee {
. . .
St r i ng get Descr i pt i on( ) {
return ( super. shor t Get Descr i pt i on( ) + " \ n" +
" EMPLOYEE #: " + this. empl oyeeNumber + " \ n" +
" WORK #: " + this. wor kNumer + " \ n" +
" OFFI CE# : " + this. of f i ceNumber ) ;
}
}


How can we make use of the getDescription() in the Employee class so that it displays the
employeeNumber and workNumber for us ? Well, we can make an additional
getDescription() in the Employee class as follows:


class Empl oyee extends Per son {
...
St r i ng get Descr i pt i on( ) {
return ( super. get Descr i pt i on( ) + " \ n" +
" EMPLOYEE #: " + this. empl oyeeNumber + " \ n" +
" WORK #: " + this. wor kNumer ) ;
}

St r i ng shor t Get Descr i pt i on( ) {
return ( super. shor t Get Descr i pt i on( ) + " \ n" +
" EMPLOYEE #: " + this. empl oyeeNumber + " \ n" +
" WORK #: " + this. wor kNumer ) ;
}
}


COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 206 -
But this duplicates code in the Employee class!!! We can fix this by extracting the common
code into a helper method as follows:

class Empl oyee extends Per son {
...
St r i ng commonString() {
return ( " \ n" + " EMPLOYEE #: " + this. empl oyeeNumber +
" \ n" + " WORK #: " + this. wor kNumer ) ;
}

D pt i on( ) { St r i ng get escr i
return( super. get Descr i pt i on( ) + this. commonString()) ;
}
St r i ng shor t Get Descr i pt i on( ) {
return( super. shor t Get Descr i pt i on( ) + this. commonString()) ;
}
}


Now the Professor class merely needs to call the shortGetDescription() method as before ...
but this time the super call finds the method in the Employee class, so that one is executed
(which in turn calls the one in Person). Here is the code:


class Pr of essor extends Empl oyee {
. . .
St r i ng get Descr i pt i on( ) {
return( super. shor t Get Descr i pt i on( ) + " \ n" +
" OFFI CE# : " + this. of f i ceNumber ) ;
}
}


This is nice and short now and makes use of shared code. In fact, we do not even need to
say super in the code above, and it will work the same. Do you know why ?


7.5 Abstract Classes & Methods

Recall our example in the previous section pertaining to the various types of bank accounts.
We had two types of accounts: SuperSavings and PowerSavings, which both inherited from
a more general class called SavingsAccount and indirectly from BankAccount a little further
up the hierarchy. Assume further that we distinguished between savings accounts and
chequing account … where chequing accounts allow their owners to write cheques.

Assume that the real bank actually has exactly 4 types of accounts so that
when someone goes to the bank teller to open a new account, they specify
whether or not they want to open a SuperSavings, PowerSavings,
BusinessChequing or PowerChequing account.
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 207 -
Here is a revised hierarchy …



SuperSavings PowerSavings
SavingsAccount
BankAccount
BusinessChequing
ChequingAccount
PowerChequing
Object
In our program, class hierarchy however, there are 7 account-related classes. The four
classes representing the accounts that we can actually open are called concrete classes. A
concrete class in J AVA is a class that we can make instances of directly by using the new
keyword. That is, throughout our code, we will find ourselves creating one of these 4 classes.
For example:

account 1 = new Super Savi ngs( …) ;
account 2 = new Power Savi ngs( …) ;
account 3 = new Busi nessChequi ng( …) ;
account 4 = new Power Chequi ng( …) ;

However, we will likely never need to create instances of the other 3 account-related classes:

account 5 = new BankAccount ( …) ;
account 6 = new Savi ngsAccount ( …) ;
account 7 = new Chequi ngAccount ( …) ;

Why not ? Well, put simply, these types of objects are not specific enough because they
cause ambiguity. For example, if you went to the bank teller and asked to open just “a bank
account”, the teller does not know which of the 4 types of accounts you actually want. The
teller would likely ask you questions to help you narrow down your choices, but ultimately, the
type of account that is opened (i.e., the account that is actually created) MUST be one of the 4
accounts that the bank offers. Likewise, in our program, if we were to create instances of
BankAccount, SavingsAccount and ChequingAccount, then these objects would not be
specific enough to define account behavior that matches one of the 4 real account types.

So in a sense, the BankAccount, SavingsAccount and ChequingAccount classes are not
concrete, they are more abstract in that they don’t exactly match the real-life objects.
In J AVA, we actually use the term abstract class to define a class that we do not want to
make instances of. So, BankAccount, SavingsAccount and ChequingAccount should all
abstract classes. We will draw abstract classes with dotted lines as follows …

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 208 -


SuperSavings PowerSavings
SavingsAccount
BankAccount
BusinessChequing
ChequingAccount
PowerChequing
Object
So, in J AVA, an abstract class is simply a class for which we cannot create instances. That
means, we can never call the constructor to make a new object of this type.

new BankAccount ( …) / / does not compi l e
new Savi ngsAccount ( …) / / does not compi l e
new Chequi ngAccount ( …) / / does not compi l e

All of the classes that we created so far in this course were concrete classes, although some
could have been easily made abstract. We define a class to be abstract simply by using the
abstract keyword in the class definition:

public abstract class BankAccount {
. . .
}

public abstract class Savi ngsAccount extends BankAccount {
. . .
}

public abstract class Chequi ngAccount extends BankAccount {
. . .
}
That is all that is involved in creating an abstract class. There really is nothing more to it. In
fact, the remainder of the code in that class definition may remain as is.
So, in fact, by making a class abstract, all we have done is to prevent the user of the class
from calling any of its constructors directly. This may raise an interesting question. If we
cannot ever create new objects of the abstract class, then why would we ever want to create
an Abstract class in the first place ?
Well why did we create the BankAccount and SavingsAccount classes in the first place ?
Inheritance was the key reason. These classes still contain the common attributes and
shared behavior for all of their subclasses. The BankAccount class, for example, contains
the 3 instance variables common to all accounts (owner, accountNumber and balance).
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 209 -
Also, the SavingsAccount, for example, contains the deposit() method that is shared
between SuperSavings and PowerSavings. Hence, you can see that even though a class
may be declared as abstract it is still useful and important in keeping our code organized
properly in our class hierarchy. Their attributes and behaviors are still being used by their
concrete subclasses.
How do we know which classes to make abstract and which ones to leave as concrete ? If we
are not sure, it is better to leave them as concrete. However, if we discern that a particular
class has subclasses that cover all of the possible concrete classes that we would ever need
to create in our application, then it would be reasonable to make the superclass abstract.
Is there any advantage of making a class abstract rather than simply leaving it concrete ? Yes.
By making a class abstract, you are informing the users of that class that they should not be
creating instances of that class. In a way, you are telling them “ If you want to use this
class, you should make your own concrete subclass of it.”. You are actually forcing them
to create a subclass if they want to use your abstract class. It forces the user of your class to
be more specific in his object creation, thereby reducing ambiguity in their code.
Here are a few more examples of class hierarchies that we already discussed, showing how
we could make some classes abstract:


PartTimeUndergrad PartTimeGrad FullTimeUndergrad
PartTimeStudent FullTimeStudent
Student
Object
FullTimeGrad
HomeImprovementLoan
Lease Mortgage
Loan
Object
TownHome SingleFamilyHome Warehouse Office
Residential Commercial
BuildingStructure
Factory
Object
Apple Orange Potato Carrot
Fruit Vegetable
Food
Employee Customer
Person
Object
Manager
Object
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 210 -
Abstract Methods:
In addition to having abstract classes, J AVA allows us to make abstract methods. An
abstract method is a method which has no code. That is, it is merely a specification of a
method’s signature (i.e., return type, name and list of parameters), but the body of the code
remains blank. To define an abstract method, we use the abstract keyword at the beginning
of the method’s signature. Here are a couple of examples:
abstract void deposi t ( float amount ) ;
abstract void wi t hdr aw( float amount ) ;
Notice that there are no braces { } to specify the method body … the method
signature simply ends with a semi-colon ;.
At this point you should be wondering: “Why would any sane person would
write a method that has no code in it ?”. That is certainly a reasonable
question since, after all, methods are called so that we can evaluate the code that
is in them.
Abstract methods are actually never called, so J AVA never attempts to evaluate
their code. J ust as an abstract class is used to force the user of that class to have
subclasses, an abstract method forces the subclasses to implement (i.e., to
write code for) that method. So, by defining an abstract method, you are really
just informing everyone that the concrete subclasses must write code for that
method. All concrete subclasses of an abstract class MUST implement the
abstract methods defined in their superclasses, there is no way around it.
When J AVA compiles an abstract method for a class (e.g., class A), it checks
to see whether or not all the subclasses of A have implemented the method
(i.e., that they have written a method with the same return type, name and
parameters). That is really all that happens in regard to the abstract methods.
For example, if we make deposit(float amount) and withdraw(float amount) methods
abstract in the BankAccount class, then, all of its concrete subclasses (SuperSavings,
PowerSavings, BusinessChequing and PowerChequing) would be forced to implement
those methods … complete with code as follows …
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 211 -

SuperSavings





SavingsAccount
BankAccount



ChequingAccount
PowerChequing





void deposi t ( float amount ) {
. . .
}
void wi t hdr aw( float amount ) {
. . .
}
BusinessChequing





void deposi t ( float amount ) {
. . .
}
void wi t hdr aw( float amount ) {
. . .
}
PowerSavings





void deposi t ( float amount ) {
. . .
}
void wi t hdr aw( float amount ) {
. . .
}
void deposi t ( float amount ) {
. . .
}
void wi t hdr aw( float amount ) {
. . .
}
abstract void deposi t ( float amount ) ;
abstract void wi t hdr aw( float amount ) ;
Object
Each of the 4 concrete subclasses would implement their deposit() and withdraw() code
according to the bank's rules for that type of account (i.e., apply certain fees, limit amount,
etc...).
Alternatively, we can take advantage of inheritance. If, for example, the SuperSavings and
PowerSavings accounts both deposit() in the same manner, instead of duplicating the code
we can implement a non-abstract deposit() method in the SavingsAccount class that
performs the required behavior. This method would then be shared (i.e., used) by both the
SuperSavings and PowerSavings subclasses through inheritance.
In this case, the SuperSavings and PowerSavings classes would NOT need to implement
the deposit() method, since it is inherited …
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 212 -

SavingsAccount



BankAccount



void deposi t ( float amount ) {
. . .
}
SuperSavings



void wi t hdr aw( float amount ) {
. . .
}
PowerSavings



void wi t hdr aw( float amount ) {
. . .
}
abstract void deposi t ( float amount ) ;
abstract void wi t hdr aw( float amount ) ;
ChequingAccount
Object
Only abstract classes are allowed to have such abstract methods. However, as you know, an
abstract class may have regular methods as well.
If we were to find that all 4 types of concrete accounts did the exact same thing when a
deposit() was made, then we would likely simply write the shared deposit() method in the
BankAccount class, INSTEAD OF making the abstract deposit() method in the first place.
This allows a kind of default deposit() behavior for all subclasses to inherit, not forcing any
classes to implement this method.
It is often the case that we define more than one
abstract method in a class. This allows us to
specify a set of “standard” behavior that ALL of
its subclasses MUST have. For example,
assume that we have the following hierarchy in
which an abstract Loan class has 3 specific
subclasses as shown here
Object
HomeImprovementLoan
Lease Mortgage
Loan

We may decide on some particular behavior that
all types of loans must exhibit. For example, we
may want to ensure that we have a way to
calculate a monthly payment for the loan, a way
to make payments on the loan, a way to re-finance
the loan and perhaps a way to extract the client’s information that pertains to the loan.
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 213 -
If this is the case, perhaps some of the behavior is similar for all loans (e.g., getting the client’s
information), while other behaviors may be unique depending on the type of loan (e.g., leases
and mortgages may be re-financed differently). Here is how we might define the Loan class:


abstract class Loan {
abstract float cal cul at eMont hl yPayment ( ) ;
abstract void makePayment ( float amount ) ;
abstract void r enew( int numMont hs) ;

Cl i ent get Cl i ent I nf o( ) { / / a non- abst r act met hod
. . .
}
. . . .
}


Notice that the getClientInfo() method is non abstract, so that we can write code in there that
is shared by all the subclasses. The other 3 methods shown are abstract … so the Lease,
Mortgage and HomeImprovementLoan classes MUST implement all 3 of these methods,
with the appropriate code. Remember … an abstract class is just like any other class in
regards to its attributes and behaviors. So there may be many more methods (abstract or
non-abstract) and/or attributes defined in the Loan class.

Do you see the benefit of defining abstract methods ? They allow you to define a set of
behaviors that all your subclasses must have while giving them the flexibility to specify their
own unique code for those behaviors. What would happen if we did not make any of the
methods abstract ?:


abstract class Loan {
float cal cul at eMont hl yPayment ( ) { }
void makePayment ( float amount ) { }
void r enew( int numMont hs) { }
Cl i ent get Cl i ent I nf o( ) { . . . }
. . . .
}


Two things would be different. First, the methods would need to have a body. We could
leave the code body blank or we could put in some default code of our choosing.

Second, the subclasses would not be “forced” to write these methods. So if the subclass did
not supply the method, then these methods here would be inherited. This is not such a “big
deal”, but if we simply forgot to implement these methods, then the inherited behavior may be
unexpected and in some cases undesirable. By making the 3 methods abstract, the compiler
will force us to write the methods, eliminating the possibility of us forgetting to implement them.



COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 214 -
7.6 Defining Interfaces

In the previous section, we showed how we can use a set of abstract methods to force a set
of specific behaviors in our subclasses. By doing that, we were able to ensure that all of the
subclasses of that abstract class have definitely implemented the required behavior.

Abstract methods are indeed quite useful for defining (and forcing) common behavior for
subclasses of an abstract class. How though, would we define (and force) common behavior
between seemingly unrelated classes in different parts of the class hierarchy ?

There is another mechanism in J AVA for doing this. In J AVA, an interface is a specification
(i.e., a list) of a set of behaviors. It is similar to the notion of a set of abstract methods, except
that the interface exists on its own, that is, it is defined by itself in its own file. We define this
list of behaviors/methods as if we were defining a new class, except that we use the keyword
interface instead of class:

interface InterfaceName {
...
}

J ust like classes, interfaces are types and are defined in their own .java files. So, the above
interface would be saved into a file called InterfaceName.java.

The methods themselves are defined like abstract methods, but without the word abstract.
Here is a comparison of the Loan class’s abstract methods and a similarly-defined interface:

abstract class Loan {
abstract float cal cul at eMont hl yPayment ( ) ;
abstract void makePayment ( float amount ) ;
abstract void r enew( int numMont hs) ;
. . .
}
interface Loanable {
float cal cul at eMont hl yPayment ( ) ;
void makePayment ( float amount ) ;
void r enew( int numMont hs) ;
}


There are some similarities between the two:

• both define a list of methods with no code.

• like abstract classes, we cannot create instances of interfaces. So,
we cannot use this code anywhere in our code: new Loan( )

There are also some differences between the two:

• we cannot declare/define any attributes nor static constants in
an interface, whereas an abstract class may have them

• we can only declare “empty” methods in an interface, we cannot supply code for them.
In contrast, an abstract class can have non-abstract methods with complete code.

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 215 -
Since interfaces are defined on their own (i.e., the interface does not belong to any particular
class), we must have a way to inform J AVA which objects will be implementing the methods
that are defined in the interface.

Consider defining an interface called Insurable that defined the common behavior that all
insurable objects MUST have as follows:


interface Insurable {
public int get Pol i cyNumber ( ) ;
public int get Cover ageAmount ( ) ;
public double cal cul at ePr emi um( ) ;
public j ava. ut i l . Dat e get Expi r yDat e( ) ;
}


In this example, we did not have any parameters for the methods, but you can have
parameters as well, just like any other method. The code above would need to be saved and
compiled before we can use it. Notice that the methods all have public in front of them.
Interfaces must be publicly accessible. We will talk more about this in an upcoming chapter.

Assume now that we want to have some classes in our hierarchy that are considered to be
insurable. Perhaps Person, Car and Company objects in our application are all considered
to be insurable objects.






We would want to make
sure that they all
implement the methods
defined in the Insurable
interface as shown here
Company Car
Person
Insurable
Employee
Manager
Object

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 216 -
To do this in J AVA, we simply add the keyword implements in the class definition, followed by
the name of the interface that the class will implement as follows:

abstract class Per son implements I nsur abl e {
...
}

class Company implements I nsur abl e {
...
}

class Car implements I nsur abl e {
...
}

By adding this to the top of the class definition, we are informing the whole world that these
objects are insurable objects. It represents a "stamp of approval" to everyone that these
objects are able to be insured. It provides a "guarantee" that these classes will have all the
methods required for insurable items (i.e., getPolicyNumber(), getCoverageAmount(),
calculatePremium(), getExpiryDate()). So then, for each of the implementing classes, we
must go and write the code for those methods:

class Car implements I nsur abl e {
. . .
public int getPolicyNumber() { / * wr i t e code her e */ }
public double calculatePremium() { / * wr i t e code her e */ }
public j ava. ut i l . Dat e getExpiryDate() { / * wr i t e code her e */ }
public int getCoverageAmount() { / * wr i t e code her e */ }
. . .
}


abstract class Per son implements I nsur abl e {
. . .
public int getPolicyNumber() { / * wr i t e code her e */ }
public double calculatePremium() { / * wr i t e code her e */ }
public j ava. ut i l . Dat e getExpiryDate() { / * wr i t e code her e */ }
public int getCoverageAmount() { / * wr i t e code her e */ }
. . .
}


class Company implements I nsur abl e {
. . .
public int getPolicyNumber() { / * wr i t e code her e */ }
public double calculatePremium() { / * wr i t e code her e */ }
public j ava. ut i l . Dat e getExpiryDate() { / * wr i t e code her e */ }
public int getCoverageAmount() { / * wr i t e code her e */ }
. . .
}

COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 217 -
Again, notice that the methods all have public in front of them. We will talk more about this in
an upcoming chapter. Remember that these classes may define their own attributes and
methods but somewhere in their class definition they must have ALL 4 methods listed in the
Insurable interface.

Interestingly, a class may implement more than one interface:



Here the Car object implements 3 interfaces. To allow this in our code, we just need to specify
each implemented interface in our class definition (in any order), separated by commas:


class Car implements Insurable, Drivable, Sellable {
. . .
}


Of course, the Car class would have to implement ALL of the methods defined in each of the
three interfaces. Like classes, interfaces can also be organized in a hierarchy:


Insurable
Object
Drivable
Car Company Product
Insurable
Company Car
Fixed
Insurable
Depreciating
Insurable
Person
Object
Sellable
COMP1005/1405 – Organizing Classes to Use Inheritance Fall 2009

- 218 -
As with classes, we form the interface hierarchy by using the extends keyword:


interface Insurable { ...
public int getPolicyNumber();
public int getCoverageAmount();
public double calculatePremium();
public j ava. ut i l . Dat e getExpiryDate();
}



interface DepreciatingInsurable extends Insurable {
public double comput eFai r Mar ket Val ue( ) ;
public void amor t i zePayment s( ) ;
}



interface FixedInsurable extends Insurable {
public int get Eval uat i onPer i od( ) ;
}


Classes that implement an interface must implement its "super" interfaces as well. So
Company and Person would need to implement the method in FixedInsurable as well as the
four in Insurable, while Car would have to implement the two methods in
DepreciatingInsurable and the four in Insurable as well.

In summary, how do interfaces help us ? They provide us with a way in which we can specify
common behavior between arbitrary objects so that we can ensure that those objects have
specific methods defined. There are many pre-defined interfaces in J AVA and you will see
them used often in the next course COMP1006/1406.


Chapter 8
Exception Handling


What is in This Chapter ?
It is never possible to predict accurately what the user of your software will do. While your
program is running, situations often arise in which some unexpected error occurs, perhaps due
to unexpected or corrupt data. We have to deal with these problems gracefully in our code so
that our code is robust, produces valid/correct results and does not crash. In this set of notes,
we will discuss Exceptions, which are J AVA's way of handling problems that occur in your
program. You will find out how to handle standard problems that occur in your code by using
the Exception classes and how to define your own types of Exceptions.




COMP1005/1405 – Exception Handling Fall 2009

- 220 -

8.1 Simple Debugging

We use the term bug in computer science to denote a problem with our program.
Unfortunately, much of our programming time may be spent on finding errors/bugs in the code
that we write. This can be VERY time consuming and frustrating. Sometimes we may fix one
bug only to find that another one appears. There are basically 3 types of errors (i.e., bugs):
1. Compile Errors occur when your code will not compile. They are
the easiest to since the compiler catches them and informs us of the
problem. Because J AVA is strongly typed, many “misunderstandings”
between method parameters and variables are eliminated. Once fixed,
compiler errors do not come back. Often though ... one error (such as a
missing semicolon) can lead to a whole slew of compile errors.
2. Runtime Errors cannot be determined at compile time. They "pop up"
when you run your code and usually represent a serious problem (e.g.,
divide by zero, stack overflow, out of memory). These errors may
sometimes require a re-design of your code (e.g., to reduce memory
usage). But often, the problem is less serious such as trying to send
messages to a null object (i.e., NullPointerException) or accessing past
available array boundaries (i.e., ArrayOutOfBoundsException)
3. Logic Errors pertain to the logistics of your program such as computing
wrong values or forgetting to handle certain “special situations” in your code.
J AVA cannot detect nor explain these errors. Sometimes the logic error
could lead to a runtime error which J AVA can then catch, but it certainly
cannot explain them. Logic errors are often VERY difficult to find since the
program could “appear” to be working. Rigorous testing is required to find
them. Logic errors typically require you to do some debugging.



Debugging is the processes of "figuring out errors" in your program and
"fixing" them. Actually, finding the error is usually the hard part. Fixing it is
often (but certainly not always) easy. One of the most common debugging
techniques is that of using "print" statements in your code. When there are
many logic errors, this is usually the simplest way to debug.


If your program is producing wrong answers, you can use print statements to display
intermediate calculations as follows …

COMP1005/1405 – Exception Handling Fall 2009

- 221 -

double comput eMor t gagePayment ( ) {
double mont hl yRat e = this. get I nt er est Rat e( ) / 12;
System.out.println("monthly rate = " + monthlyRate); / / debug

double amor t i zeRat e = ( 1- Mat h. pow( 1+mont hl yRat e, this. numMont hs*- 1) ) ;
System.out.println("amortize rate = " + amortizeRate); / / debug

return this. get HousePr i ce( ) * mont hl yRat e / amor t i zeRat e;
}


From the intermediate results, you should be able to narrow down where you went wrong.
Print statements can also be used to determine whether or not a certain point in your code is
being reached (or if a certain method is being called):


double comput eMor t gagePayment ( ) {
System.out.println("*** Got Here 1");
double mont hl yRat e = this. get I nt er est Rat e( ) / 12;
double amor t i zeRat e = ( 1- Mat h. pow( 1+mont hl yRat e, this. numMont hs*- 1) ) ;

System.out.println("*** Got Here 2");

return this. get HousePr i ce( ) * mont hl yRat e / amor t i zeRat e;
}


By doing this, we can get an idea as to where our program has stopped working and also find
out if J AVA is calling the methods that we think it is calling. Print statements can also be used
to show the order that certain pieces of code are evaluated in:


void deposi t ( float anAmount ) {
System.out.println("depositing $" + anAmount);
this. bal ance = this. bal ance + anAmount ;
}

boolean wi t hdr aw( float anAmount ) {
System.out.println("withdrawing $" + anAmount);
if ( amount <= this. bal ance) {
this. bal ance = this. bal ance - anAmount ;
return true;
}
return false;
}


In order to simplify the print statements, we can often print out whole objects …
COMP1005/1405 – Exception Handling Fall 2009

- 222 -

static void Test 1( ) {
BankAccount account = new BankAccount ( " J i m" ) ;
account . deposi t ( 120. 53f ) ;
account . wi t hdr aw( 20) ;
account . deposi t ( 400) ;
account . wi t hdr aw( 829. 31f ) ;
System.out.println(account);
}


As long as we have implemented the toString() method for our objects, we should get
descriptive output.
Although this debugging technique is effective, your code may become littered with
System.out.println statements which need to eventually be removed before you ship out your
code. However, the "print statement" remains one of the most popular and simplest methods
for debugging and this technique will usually help us narrow down the error that occurred.
In J AVA, it seems that the most common errors occur because we forgot to initialize something
or if unexpected data was given to us. In some cases we can write additional code to "expect
and handle" bad input data. This is called error-checking and it is the basis for Exceptions
in J AVA. We will not discuss debugging in this course, but will instead focus on how to deal
gracefully with unexpected errors that may arise in our programs.

8.2 Exceptions

There are many chances for errors to occur in a program when the programmer
has no control over information that is entered into the program from the
keyboard, files, or from other methods/classes/packages etc... Even worse ...
when such errors occur, it is not always clear how to handle the error.
Exceptions are errors that occur in your program.
They are J AVA’s way of telling you that something has gone wrong in your program. When an
exception occurs, J AVA forces us to do one of the following:
• Handle the exception (we must know when to do this and what to do)
• Declare that we want someone else to handle it.
Exception Handling is the strategy of handling errors which are generated during
program execution
We handle exceptions in order to allow our program to “quit gracefully" as opposed to having
J AVA spew out a bunch of exception messages.
COMP1005/1405 – Exception Handling Fall 2009

- 223 -
When should we handle exceptions ? When we do not know how to deal with the error … or
when it does not make sense to handle the error.

For example, in large software systems, an error may occur outside of the code that we wrote
(i.e., in someone else's code). We may not even have access to this code in order to fix the
error. Perhaps the error occurred in some module that was developed by another team of
programmers. Sometimes, it is an advantage to anticipate some possible errors and then we
can allow our program to handle the error gracefully. However, it is sometimes the case
where we do not know what to do at all when the error occurs. If our code can easily predict a
particular kind of error, then there is no need to use Exceptions, since we can deal with the
code on our own.

Furthermore, in software components such as methods, libraries, and classes that are likely to
be widely used, it is unclear as to what should be done when the error occurs. Our decision
as to how we handle the error may or may not be the best choice for the software as a whole.

To help you understand, consider this "real world" example in which an unexpected situation
occurs. Suppose that you ask your friend to go to McDonald's to get you a Big Mac and
Fries. You expect him to come back with food for you.
However, what could go wrong ?
1. he crashes his car and never arrives at McDonald's
2. he gets there, but the place is burnt down
3. he places his order but finds out there are no Big Macs
left anymore
4. he places the order but does not have enough money
5. he gets the food and drops/spills it on the ground on the
way back
As you can see, much can go wrong … but what would your friend do in each of these
situations ?
1. he informs you that he cannot handle your request
2. he either returns informing you of the problem, or drives to a different McDonald's or
nearby restaurant
3. he improvises and gets you two single hamburgers in the place of the Big Mac
4. he gets you an incomplete order
5. he tries to save money and simply wipes it off ;) ... or perhaps purchases replacements.
As you may well understand by now, we need to think along these lines. We need to always
ask ourselves:
• What can go wrong ?
• Should I handle it ?
• How do I handle it ?
As it turns out, J AVA has a nice mechanism for handling errors in a consistent manner. We
don’t necessarily ever need to use this mechanism in our code, but there are advantages:
COMP1005/1405 – Exception Handling Fall 2009

- 224 -
• Improves clarity of programs for large pieces of software
• Can be more efficient than "home-made" error checking code
• They apply to multi-threaded (more than one program) applications
• Programmers save time by using predefined Exceptions
In J AVA, exceptions are thrown (i.e., generated) by either:
• the J VM automatically
• your code at any time
Exceptions are always caught
(i.e., handled) by one of these:
• your own code (i.e., graceful decision)
• someone else's code (i.e., delegate the responsibility)
• the J VM (i.e., program halts)
J AVA has many predefined exceptions, and we can also create our own. In J AVA,
Exceptions are objects, so each one is defined in its own class. The Exception classes are
arranged in a hierarchy, and their position in the hierarchy can affect the way that they are
handled. There are also Error objects in J AVA … which represent more serious errors that
may occur in your program which would require the program to stop altogether since they are
considered unrecoverable:












Exception
VirtualMachineError
Error

StackOverflowError
OutOfMemoryError
RuntimeException
NullPointerException
ArithmeticException
ClassNotFoundException
DataFormatException


Throwable

IndexOutOfBoundsException
Object
COMP1005/1405 – Exception Handling Fall 2009

- 225 -
In regards to the Error classes, generally your application should not try to catch them. There
are many subclasses of Error, here are just a few:
• VirtualMachineError
o StackOverflowError ( e. g. , r ecur si on t oo deep)
o OutOfMemoryError ( e. g. , cannot cr eat e any mor e obj ect s)
• LinkageError
o NoClassDefFoundError ( e. g. , no cl ass wi t h a gi ven name)
o ClassFormatError ( e. g. , cl ass i s i ncompat i bl e)
The Exception class and its subclasses indicate a "less serious" problem. The exceptions
are either "checked" or "unchecked" by the compiler. “Checked” exceptions are pre-defined
types of errors that the J AVA compiler looks for in your code and forces you to deal with them
before it will compile your code. Generally, your applications will need to deal with these
types of Exceptions.
Here are just a few of the "checked" exceptions that we might need to catch in our code:
• ClassNotFoundException (e.g., tried to load an undefined class)
• CloneNotSupportedException (e.g., cannot make copy of object)
• DataFormatException (e.g., bad data conversion)
• IllegalAccessException (e.g., access modifiers prevent access)
• InstantiationException (e.g., problem creating an object)
• I OExcept i on
o EOFException (e.g., end of file exception)
o FileNotFoundException (e.g., cannot find a specified file)
Here are a few of the "unchecked" exceptions. Although you can check for (i.e., detect and
handle) these types of errors in your code, normally you will not do so. Instead, you will try to
write your code so that such exceptions cannot happen. The J AVA compiler will not force you
to handle these errors before compiling:
• Runt i meExcept i on
o ArithmeticException (e.g., bad computation such as divide by 0)
o ArrayStoreException (e.g., storing wrong type of object in array)
o ClassCastException (e.g., cannot typecast one class to another)
o IndexOutOfBoundsException (e.g., gone outside array bounds)
o NoSuchElementException (e.g., cannot find any more elements)
o NullPointerException (e.g., attempt to send message to null)
o NumberFormatException (e.g., trouble converting to a number)
Recall that when exception handling you must either (a) handle the
exception yourself, or (b) declare that someone else will handle it.
In the 2
nd
case, we are actually delegating the exception-handling
responsibility to someone else. We do this when we do not wish to handle an
error in our code. We actually delegate the responsibility to the "calling method" (i.e., the
method that called our method must handle the error). We do this by adding a throws clause
to our method declaration as follows …
COMP1005/1405 – Exception Handling Fall 2009

- 226 -

void openFi l e( St r i ng f i l eName) throws j ava. i o. Fi l eNot FoundExcept i on {
/ / code f or met hod
}


The throws keyword appears at the end of a method signature and is followed by an
Exception type. When compiling this method, J AVA will check all methods that call this
openFile() method to make sure that they deal with the FileNotFoundException in some way
(i.e., either by catching it, or declaring that they too will throw it, thereby delegating the
responsibility further up the chain of method calls).

You can actually specify multiple exception types with the throws clause by listing the
exceptions separated by commas:

void conver t Fi l e( St r i ng f i l eName) throws j ava. i o. Fi l eNot FoundExcept i on,
j ava. l ang. Cl assNot FoundExcept i on,
j ava. i o. I OExcept i on {
/ / code f or met hod
}


So, to clarify things a little, the throws clause is part of a method’s
declaration that is used to tell the compiler which exceptions the
method may throw back to its caller. The throws clause is
required if the code in the method "may" generate, but not handle,
a particular type of exception. You should think of the throws
clause as a “ sign” that the method holds up in order to tell the
whole world publicly that the code in that method may generate the
specified exception.

For example, if we consider the openFile() method mentioned
earlier, it declares to everyone in its signature that it may generate a
FileNotFoundException at any time. So, when we call the openFile() method from some
other method, say getCustomerInfo(), then the getCustomerInfo() method "may also"
declare that it throws the exception (if, for example, it did not want to handle it):

void get Cust omer I nf o( ) throws j ava. i o. Fi l eNot FoundExcept i on {
/ / do somet hi ng
this. openFi l e( " cust omer . t xt " ) ;
/ / do somet hi ng
}


Here, if the exception is thrown while in the openFile() method, the getCustomerInfo()
method will stop and it will then pass on the exception to "its" caller.
COMP1005/1405 – Exception Handling Fall 2009

- 227 -
The responsibility may be repeatedly delegated in this manner. It is as if everyone ignores the
error (like a hot potato). Nobody explicitly handles the error. The J VM will eventually catch it
and halt the program:

At any time during this process however, any method may catch the exception and handle it.
Once caught, propagation of the exception stops.

A method may catch an exception by specifying try and catch blocks. A "block" here refers to
a sequence of J AVA statements (i.e., code defined between brackets { }).
The "try block" represents the code for which you want to handle an Exception. We precede
this block with the try keyword. Similarly, the "catch block" represents the code that handles
a particular type of exception. We precede these blocks with the catch keyword.
COMP1005/1405 – Exception Handling Fall 2009

- 228 -
A catch block always appears right after a try block as follows:

...
try {
/ / some code t hat may cause an except i on
}
catch ( Fi l eNot FoundExcept i on ex) {
/ / some code t hat handl es t he except i on
}
...


Notice that the catch block requires a parameter which indicates the type of error to be
caught. This parameter can be accessed and used within the catch block (more on this
later). The getCustomerInfo() method in our previous example can decide to handle the
exception through use of try/catch blocks as follows:


void get Cust omer I nf o( ) {
try {
this. openFi l e( " cust omer . t xt " ) ;
}
catch (java.io.FileNotFoundException ex) {
Syst em. out . pr i nt l n( " Er r or : Fi l e not f ound" ) ; / / Handl e t he er r or her e
}
}


Notice that the method no longer needs to "throw" the exception any further (i.e., no throws
clause), since it caught and handled it.

More than one catch block may be used to catch one-of-many possible exceptions. We
simply list all catch blocks one after another:


void get Cust omer I nf o( ) {
try {
/ / do somet hi ng t hat may cause an Except i on
}
catch (java.io.FileNotFoundException ex) {
/ / Handl e t he er r or her e
}
catch (NullPointerException ex) {
/ / Handl e t he er r or her e
}
catch (ArithmeticException ex) {
/ / Handl e t he er r or her e
}
}


COMP1005/1405 – Exception Handling Fall 2009

- 229 -
Consider what happens when an exception occurs within a try block:

Here, an exception of type
ExceptionType2 occurs as
a result of the doSomething()

void get Cust omer I nf o( ) {
try {
. . .
method call. J AVA will
doSomet hi ng( ) ;
immediately stop running the
. . .
}
catch ( Except i onType1 ex) {
. . .
}
catch (ExceptionType2 ex) {
...
}
catch ( Except i onType3 ex) {
. . .
}
/ / cont i nue wi t h pr ogr am
. . .
}
1. Exception
occurs
2. Match
is found
4. Program
continues
code in the try block and
search through the catch
blocks for one whose
parameter type matches the
exception that occurred (i.e.,
3. Catch block
is executed
for one that takes
ExceptionType2 parameter
or a superclass of
ExceptionType2). When
one is found, it executes the
code within that catch block
and then continues to the
point in the program
immediately following the
catch blocks.


Note that J AVA does NOT
go back to the try block
once it completes the
catch block. So any code
remaining in the try block
after the location where
the exception had occurred
is not evaluated as shown
here

Does NOT go
back to the try
block again

void get Cust omer I nf o( ) {
try {
. . .
doSomet hi ng( ) ;
. . .
}
catch ( Except i onType1 ex) {
. . .
}
catch (ExceptionType2 ex) {
...
}

If no match is found when
J AVA looks for a matching
catch ( Except i onType3 ex) {
. . .
catch block, then the entire
getCustomerInfo() method
halts and the method throws
}
/ / cont i nue wi t h pr ogr am
the same exception to the
method that called this
. . .
}
getCustomerInfo()
method and that method
will then have to deal
with the exception in some
way.


COMP1005/1405 – Exception Handling Fall 2009

- 230 -
Since Exceptions are objects and are organized in a class hierarchy, then one Exception
may be a more specific kind of another one. That is, Exceptions in general may have
superclasses and subclasses. So, when J AVA goes looking through the catch blocks for a
match, it will look for the first match that either matches the Exception class exactly or
matches one of its superclasses. It is important to note that only one catch block (the one
that "first" matches the exception) will ever be evaluated. That means we need to be careful,
because the order of the catch blocks is important when we list them.

Consider the following portion of the J AVA class hierarchy:

Exception
IOException
Object
FileNotFoundException

The code below is problematic. Do you know why ?


void get Cust omer I nf o( ) {
try {
/ / do somet hi ng t hat may cause an except i on
}
catch ( Exception ex) {
/ / Cat ches al l except i ons
}
catch ( java.io.IOException ex) {
/ / Never r eached si nce above cat ches al l
}
catch ( java.io.FileNotFoundException ex) {
/ / Never r eached si nce above t wo ar e caught f i r st
}
}


Notice that we arranged the catch blocks so that the more general Exception is caught first.
But this is bad because ALL exceptions are subclasses of Exception. That means,
regardless of what type of exception occurs in the try block, the "first" catch block will
ALWAYS match and therefore ALWAYS be evaluated. The remaining to catch blocks will
never be evaluated. In fact, the J AVA compiler will catch this and tell you that the last two
catch blocks are "unreachable". To fix the problem, we can simply reverse the order of the
catch blocks.

COMP1005/1405 – Exception Handling Fall 2009

- 231 -
Optionally, a special finally block may be used after the catch blocks:

try {
. . .
}
catch ( j ava. i o. I OExcept i on ex) { . . . }
catch ( Except i on ex) { . . . }
finally {
/ / Code t o r el ease r esour ces
}


The finally block is used to release resources (e.g., closing files). It is always executed. That
is, if no exception occurs, it is executed immediately after the try block, even if the try block
has a return statement in it ! (i.e., it is executed just before returning). If an exception does
occur, it is executed immediately after the catch block is executed. If an exception occurs
and no catch block matches, the finally block is evaluated before the method returns with the
thrown exception.

Let us now look at what we can do inside our catch blocks. While inside the catch block, the
following messages can be sent to the incoming Exception (i.e., to the parameter of a catch
block):

• getMessage() - returns a String describing the exception. Typically, these
strings are short descriptions of the error.

• printStackTrace() - displays the sequence of method calls that led up to
the exception. This is what you see on the screen when the J VM catches an
exception. This is very useful for debugging purposes.

So we can do many different things inside catch blocks. Here are some examples:

try {
. . .
}
catch ( Except i onType1 ex) {
Syst em. out . pr i nt l n( " Hey! Somet hi ng bad j ust happened! " ) ;
}
catch ( Except i onType2 ex) {
Syst em. out . pr i nt l n( ex.getMessage()) ;
}
catch ( Except i onType3 ex) {
ex.printStackTrace();
}



COMP1005/1405 – Exception Handling Fall 2009

- 232 -
Consider the stack trace for this code:

class MyCl ass {
static void doSomet hi ng( int[ ] anAr r ay) {
doAnot her Thi ng( anAr r ay) ;
}
static void doAnot her Thi ng( int[ ] t heAr r ay) {
Syst em. out . pr i nt l n( t heAr r ay[ 0] ) ; / / Er r or i s gener at ed
}
public static void mai n( St r i ng[ ] ar gs) {
doSomet hi ng( null) ;
}
}


When we run this code, we get the following stack trace printed to the console window:

j ava. l ang. NullPointerException
at MyCl ass. doAnotherThing( MyCl ass. j ava: 7)
at MyCl ass. doSomet hi ng( MyCl ass. j ava: 5)
at MyCl ass. mai n( MyCl ass. j ava: 3)


Notice that the stack trace indicates:
1. the kind of Exception that was generated
2. the method that generated the exception and
3. the line number at which the exception occurred

8.3 Examples of Handling Exceptions

Let us now look at how we can handle (i.e., catch) a standard Exception in
J AVA. Consider a program that reads in two integers and divides the first one
by the second and then shows the answer. We will assume that we want the
number of times that the second number divides evenly into the first (i.e., ignore
the remainder). What problems can occur ? Well, we may get invalid data or we
may get a divide by zero error. Let us look at how we would have done this
previously …
COMP1005/1405 – Exception Handling Fall 2009

- 233 -
import j ava. ut i l . Scanner ;

class Except i onTest Pr ogr am1 {
public static void mai n( St r i ng ar gs[ ] ) {
int number 1, number 2, r esul t ;
Scanner keyboar d;

keyboar d = new Scanner ( Syst em. i n) ;
Syst em. out . pr i nt l n( " Ent er t he f i r st number : " ) ;
number 1 = keyboar d. next I nt ( ) ;

Syst em. out . pr i nt l n( " Ent er t he second number : " ) ;
number 2 = keyboar d. next I nt ( ) ;

Syst em. out . pr i nt ( number 2 + " goes i nt o " + number 1) ;
Syst em. out . pr i nt ( " t hi s many t i mes: " ) ;

r esul t = number 1 / number 2;
Syst em. out . pr i nt l n( r esul t ) ;
}
}


Here is the output if 143 and 24 are entered:

Ent er t he f i r st number :
143
Ent er t he second number :
24
24 goes i nt o 143 t hi s many t i mes: 5

What if we now enter 143 and ABC ?

Ent er t he f i r st number :
143
Ent er t he second number :
ABC
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:840)
at java.util.Scanner.next(Scanner.java:1461)
at java.util.Scanner.nextInt(Scanner.java:2091)
at java.util.Scanner.nextInt(Scanner.java:2050)
at ExceptionTestProgram1.main(ExceptionTestProgram1.java:13)

This is not a pleasant way for your program to end. By default, when exceptions occur, they
actually print out the stack trace (i.e., the sequence of method calls that led to the exception).
That is what we are seeing here. It is ugly, but good for debugging purposes.
Notice what happened. The first line of the stack trace indicates that an
InputMismatchException has occurred. The second line tells us that the error occurred at
COMP1005/1405 – Exception Handling Fall 2009

- 234 -
line 840 of the Scanner.java code from a method called throwFor(). This was not code that
we wrote … it is pre-existing code from J AVA’s Scanner class. The error, however, is not in
line 840 of the Scanner class code. That is just where the error surfaced.
By looking further down the stack trace, we can gain insight as to why our code caused the
Exception to occur. We just need to look down the stack trace until we find a method that we
wrote. Notice that most of the successive method calls were in the Scanner class. However,
right at the bottom we notice that the main method was called.
As it turns out, J AVA is telling us that the error occurred as a result of line 13 in our
ExceptionTestProgram1. That is the code that tries to obtain the next integer from the
Scanner. When it attempts to do this, we get an “Input Mismatch” because we entered ABC
when we ran the program … and ABC cannot be converted to an integer.
So now that we know WHY the error occurred, how can we gracefully handle the error ? We
certainly do not want to see the stack trace message !!!
In order to handle the entering of bad data (e.g., ABC instead of an integer) we would need to
do one of two things:
1. either modify the code in the Scanner class to detect and gracefully handle the error, or
2. catch the InputMismatchException within our code and gracefully handle the error.
Since it is not usually possible nor recommended to copy and start modifying the available
J AVA class libraries, our best choice would be to catch and handle the error from our own
code. We will have to "look for" (i.e., catch) the InputMismatchException by placing try/catch
blocks in the code appropriately as follows:

try {
Syst em. out . pr i nt l n( " Ent er t he f i r st number : " ) ;
number 1 = keyboar d. next I nt ( ) ;

Syst em. out . pr i nt l n( " Ent er t he second number : " ) ;
number 2 = keyboar d. next I nt ( ) ;
}
catch ( java.util.InputMismatchException e) {
Syst em. out . pr i nt l n( " Those wer e not pr oper i nt eger s! I qui t ! " ) ;
Syst em. exi t ( - 1) ;
}
Syst em. out . pr i nt ( number 2 + " goes i nt o " + number 1) ;
. . .


Notice in the catch block that we display an error message when the error occurs and then we
do: Syst em. exi t ( - 1) ; . This is a quick way to halt the program completely.
The value of -1 is somewhat arbitrary but when a program stops we need to supply some kind
of integer value. Usually the value is a code that indicates what happened. Often,
programmers will use -1 to indicate that an error occurred.

COMP1005/1405 – Exception Handling Fall 2009

- 235 -
Once we incorporate the try block, J AVA indicates to us the following compile errors:


var i abl e number 2 mi ght not have been i ni t i al i zed
var i abl e number 1 mi ght not have been i ni t i al i zed


It is referring to this line:

Syst em. out . pr i nt ( number 2 + " goes i nt o " + number 1) ;

Here we are using the number1 and number2 variables. However, because the try block
may generate an error, J AVA is telling us that there is a chance that we will never assign
values to these variables (i.e., they might not be initialized) and so we might obtain wrong data.
J AVA does not like variables that have no values … so it is forcing us to assign a value to
these two variables. It is perhaps the most annoying type of compile error in J AVA, but
nevertheless we must deal with it. The simplest way is to simply assign a value of 0 to each
of these variables when we declare them. Here is the updated version:

import j ava. ut i l . Scanner ;

class Except i onTest Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
int number 1 = 0, number 2 = 0, r esul t ;
Scanner keyboar d;

keyboar d = new Scanner ( Syst em. i n) ;
try {
Syst em. out . pr i nt l n( " Ent er t he f i r st number : " ) ;
number 1 = keyboar d. next I nt ( ) ;

Syst em. out . pr i nt l n( " Ent er t he second number : " ) ;
number 2 = keyboar d. next I nt ( ) ;
}
catch ( j ava. ut i l . I nput Mi smat chExcept i on e) {
Syst em. out . pr i nt l n( " Those wer e not pr oper i nt eger s! I qui t ! " ) ;
Syst em. exi t ( - 1) ;
}
Syst em. out . pr i nt ( number 2 + " goes i nt o " + number 1) ;
Syst em. out . pr i nt ( " t hi s many t i mes: " ) ;

r esul t = number 1 / number 2;
Syst em. out . pr i nt l n( r esul t ) ;
}
}


If we test it again with 143 and 24 as before, it still works the same. However, now when
tested with 143 and ABC, here is the output:


Ent er t he f i r st number :
COMP1005/1405 – Exception Handling Fall 2009

- 236 -
143
Ent er t he second number :
ABC
Those wer e not pr oper i nt eger s! I qui t !

What if we enter ABC as the first number ?

Ent er t he f i r st number :
ABC
Those wer e not pr oper i nt eger s! I qui t !

Woops! It appears that our error message is not grammatically correct anymore. Perhaps we
should change it to "Invalid integer entered!" … this should be clear enough.
Now lets test the code with values 12 and 0:

Ent er t he f i r st number :
12
Ent er t he second number :
0
0 goes i nt o 12 t hi s many t i mes: Exception in thread "main"
java.lang.ArithmeticException: / by zero
at ExceptionTestProgram2.main(ExceptionTestProgram2.java:23)

J AVA has detected that we tried to divide a number by zero … a big “no no” in the world of
mathematics. We can handle the ArithmeticException by adding additional try/catch blocks
around line 23 of our code:

try {
r esul t = number 1 / number 2;
}
catch (ArithmeticException e) {
Syst em. out . pr i nt l n( " Second number i s 0, cannot do di vi si on! " ) ;
Syst em. exi t ( - 1) ;
}
Syst em. out . pr i nt l n( r esul t ) ;


We can merge the two try blocks into one if we want to as follows…

COMP1005/1405 – Exception Handling Fall 2009

- 237 -
import j ava. ut i l . Scanner ;

class Except i onTest Pr ogr am3 {
public static void mai n( St r i ng ar gs[ ] ) {
int number 1 = 0, number 2 = 0, r esul t = 0;
Scanner keyboar d;

keyboar d = new Scanner ( Syst em. i n) ;
try {
Syst em. out . pr i nt l n( " Ent er t he f i r st number : " ) ;
number 1 = keyboar d. next I nt ( ) ;

Syst em. out . pr i nt l n( " Ent er t he second number : " ) ;
number 2 = keyboar d. next I nt ( ) ;

r esul t = number 1 / number 2;
}
catch ( j ava. ut i l . I nput Mi smat chExcept i on e) {
Syst em. out . pr i nt l n( " I nval i d i nt eger ent er ed! " ) ;
Syst em. exi t ( - 1) ;
}
catch ( Ar i t hmet i cExcept i on e) {
Syst em. out . pr i nt l n( " Second number i s 0, cannot do di vi si on! " ) ;
Syst em. exi t ( - 1) ;
}
Syst em. out . pr i nt ( number 2 + " goes i nt o " + number 1) ;
Syst em. out . pr i nt l n( " t hi s many t i mes: " + r esul t ) ;
}
}


Now when we enter 12 and 0 as input, we get the appropriate message:
Second number i s 0, cannot do di vi si on!

How can we adjust our code to repeatedly prompt for integers until valid ones were entered ?
We would need a while loop since we do not know how many times to keep asking.
Here is how we could do this to get a single number …

int number 1 = 0;
boolean got ANumber = false;

while ( !got ANumber ) {
try {
Syst em. out . pr i nt l n( " Ent er t he f i r st number " ) ;
number 1 = new Scanner ( Syst em. i n) . next I nt ( ) ;
got ANumber = true;
}
catch ( j ava. ut i l . I nput Mi smat chExcept i on e) {
Syst em. out . pr i nt l n( " I nval i d i nt eger . Pl ease r e- ent er " ) ;
}
}
COMP1005/1405 – Exception Handling Fall 2009

- 238 -
This code would repeatedly ask for a number until it was a valid integer. However, there is a
slight problem with the Scanner class. When the error is generated in the Scanner class
code due to the invalid integer being entered, the Scanner object is messed up and is no
longer ready read integers using nextInt(). The easiest way to fix this is to re-assign a new
Scanner object to the keyboard variable when the error occurs. Here is the completed code:

import j ava. ut i l . Scanner ;

class Except i onTest Pr ogr am4 {

public static void mai n( St r i ng ar gs[ ] ) {
int number 1 = 0, number 2 = 0, r esul t = 0;
boolean got ANumber = false;
Scanner keyboar d;

keyboar d = new Scanner ( Syst em. i n) ;
while( !got ANumber ) {
try {
Syst em. out . pr i nt l n( " Ent er t he f i r st number " ) ;
number 1 = keyboar d. next I nt ( ) ;
got ANumber = true;
}
catch ( j ava. ut i l . I nput Mi smat chExcept i on e) {
Syst em. out . pr i nt l n( " I nval i d i nt eger . Pl ease r e- ent er " ) ;
keyboar d = new Scanner ( Syst em. i n) ;
}
}
got ANumber = false;
while( !got ANumber ) {
try {
Syst em. out . pr i nt l n( " Ent er t he second number " ) ;
number 2 = keyboar d. next I nt ( ) ;
got ANumber = true;
}
catch ( j ava. ut i l . I nput Mi smat chExcept i on e) {
Syst em. out . pr i nt l n( " I nval i d i nt eger . Pl ease r e- ent er " ) ;
keyboar d = new Scanner ( Syst em. i n) ;
}
}
try {
r esul t = number 1 / number 2;
Syst em. out . pr i nt ( number 2 + " goes i nt o " + number 1) ;
Syst em. out . pr i nt l n( " t hi s many t i mes: " + r esul t ) ;
}
catch ( Ar i t hmet i cExcept i on e) {
Syst em. out . pr i nt l n( " Second number i s 0, cannot do di vi si on! " ) ;
}
}
}


COMP1005/1405 – Exception Handling Fall 2009

- 239 -
Here are the test results:


Ent er t he f i r st number
what
I nval i d i nt eger . Pl ease r e- ent er
Ent er t he f i r st number
help me
I nval i d i nt eger . Pl ease r e- ent er
Ent er t he f i r st number
ok, ok, here goes
I nval i d i nt eger . Pl ease r e- ent er
Ent er t he f i r st number
143
Ent er t he second number
did you say number 2 ?
I nval i d i nt eger . Pl ease r e- ent er
Ent er t he second number
40
40 goes i nt o 143 t hi s many t i mes: 3




8.4 Creating and Throwing Your Own Exceptions

You may throw an exception in your code at any time if you wish to inform everyone that an
error occurred in your code. Thus, you do not need to handle the error in your code, you can
simply delegate (i.e., transfer) the responsibility to whoever calls your method.

Exceptions are thrown with the throw statement. Basically, when we want to generate an
exception, we create a new Exception object by calling one of its constructors, and then
throw it as follows:
throw new j ava. i o. Fi l eNot FoundExcept i on( ) ;
throw new Nul l Poi nt er Except i on( ) ;
throw new Except i on( ) ;

Methods that throw these exceptions, must declare that they do
so in their method declarations, using the throws clause (as we
have seen before):

void your Met hod( ) throws anExcept i on {
. . .
}



COMP1005/1405 – Exception Handling Fall 2009

- 240 -
You may even catch an exception, partially handle it and then throw it again:

void your Met hod( ) throws Except i on {
try {
. . .
}
catch ( Except i on ex) {
. . . / / par t i al l y handl e t he except i on her e
throw ex; / / t hen t hr ow i t agai n
}
}



Catching and then throwing an exception again is useful, for example, if we want to:
• just keep an internal "log" of errors that were generated
• attach additional information to the exception message
• delay the passing on of the exception to the calling method
It is also possible to create "your own" types of exceptions. This would allow you to catch
specific types of problems in your code that J AVA would normally ignore. To make your own
exceptions, you simply need to create a subclass of an existing exception. If you are unsure
where to put it in the hierarchy, you should use Exception as the superclass.
Here are the steps to making your own Exception:
1. choose a meaningful class/exception name (e.g., WrongPasswordException)
2. specify the superclass under which this exception will reside (e.g., Exception)
3. optionally provide a constructor (for simplicity, this constructor may just call the super
constructor, passing in a string indicating the reason for the error).
Here is an example of a newly defined exception called MyExceptionName. We define it just
as we would any other class and then save it to a file called MyExceptionName.java. It must
also be compiled before it can be used in your program …
COMP1005/1405 – Exception Handling Fall 2009

- 241 -

class MyExceptionName extends Except i on {
MyExcept i onName( ) {
super( " Some st r i ng expl ai ni ng t he except i on" ) ;
}
}


Consider an example of how we could force the user to type in their name (i.e., not leave it
blank). We could do the following:

import j ava. ut i l . Scanner ;

class MyExcept i onTest Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
St r i ng name = " " ;
boolean got Val i dName = false;
Scanner keyboar d = new Scanner ( Syst em. i n) ;

while ( !got Val i dName) {
Syst em. out . pr i nt l n( " Ent er your name" ) ;
name = keyboar d. next Li ne( ) ;
if ( name. l engt h( ) > 0)
got Val i dName = true;
else
Syst em. out . pr i nt l n( " Er r or : Name must not be bl ank" ) ;
}
Syst em. out . pr i nt l n( " Hel l o " + name) ;
}
}


Here would be the output of such a program:


Ent er your name

Er r or : Name must not be bl ank
Ent er your name
Mark
Hel l o Mar k


Notice how the “error” is detected … we simply check the data for an empty string and use
if/else statements to determine whether or not the error has occurred and then display an
appropriate message.

In some programs, however, we may not want to print a message to the screen. For example,
we may want to bring up a dialog box. In fact, we may not know exactly what to do, as it
depends on our user interface as well as the context within our application. In such cases
(i.e., when we are not sure what to do), we could simply generate an exception and let the
method that called our code decide what to do.
COMP1005/1405 – Exception Handling Fall 2009

- 242 -
Lets generate a MissingNameException when the user does not enter a name. We can do
this by starting with our own exception definition as follows:


class MissingNameException extends Except i on {
Mi ssi ngNameExcept i on( ) {
super( " Name i s bl ank" ) ;
}
}


We need to save and compile that code in its own file. Now, how do we generate the
exception ? We simply call throw new Mi ssi ngNameExcept i on( ) at the right spot in the
code:

import j ava. ut i l . Scanner ;

class MyExcept i onTest Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) throws Mi ssi ngNameExcept i on {
St r i ng name = " " ;
boolean got Val i dName = false;
Scanner keyboar d = new Scanner ( Syst em. i n) ;

while ( !got Val i dName) {
Syst em. out . pr i nt l n( " Ent er your name" ) ;
name = keyboar d. next Li ne( ) ;
if ( name. l engt h( ) <= 0)
throw new Mi ssi ngNameExcept i on( ) ;
got Val i dName = true;
}
Syst em. out . pr i nt l n( " Hel l o " + name) ;
}
}


Notice that we must declare in our method that we now throw the exception. If we run the
code as before, we can see this new exception being generated:


Ent er your name

Exception in thread "main" MissingNameException: Name is blank
at MyExceptionTestProgram2.main(MyExceptionTestProgram2.java:12)


Congratulations to us … we have successfully created and generated our own exception.
How though can we handle the exception ? So that we may use the same example, let us
adjust the code a little by creating a method that will get the user input for us as follows …

COMP1005/1405 – Exception Handling Fall 2009

- 243 -

String get Name( ) throws Mi ssi ngNameExcept i on {
St r i ng name = new Scanner ( Syst em. i n) . next Li ne( ) ;
if ( name. l engt h( ) <= 0)
throw new Mi ssi ngNameExcept i on( ) ;
return name;
}


The above method gets the name from the user and returns it … unless the name is blank …
in which case it generates the MissingNameException.

Now we should catch the error from our main program as follows:

import j ava. ut i l . Scanner ;

class MyExcept i onTest Pr ogr am3 {

/ / Met hod t o et t he na e f r om g m t he user
static String getName() throws Mi ssi ngNameExcept i on {
St r i ng name = new Scanner ( Syst em. i n) . next Li ne( ) ;
if ( name. l engt h( ) <= 0)
throw new Mi ssi ngNameExcept i on( ) ;
return name;
}

t est out t he Mi ssi ng ameExcept i on / / Mai n met hod t o N
public static void mai n( St r i ng ar gs[ ] ) {
St r i ng name = " " ;
boolean got Val i dName = false;

while ( !got Val i dName) {
Syst em. out . pr i nt l n( " Ent er your name" ) ;
try {
name = getName();
got Val i dName = true;
}
catch ( Mi ssi ngNameExcept i on ex) {
Syst em. out . pr i nt l n( " Er r or : Name must not be bl ank" ) ;
}
}
Syst em. out . pr i nt l n( " Hel l o " + name) ;
}
}


The resulting output is the same as before (i.e., same as MyExcept i onTest Pr ogr am).

COMP1005/1405 – Exception Handling Fall 2009

- 244 -
As another example, let us take another look at the BankAccount object again ... more
specifically ... consider this withdraw() method:


boolean wi t hdr aw( float anAmount ) {
if ( anAmount <= this. bal ance) {
this. bal ance - = anAmount ;
return true;
}
return false;
}


When the user tries to withdraw more money than is actually in the account ... nothing
happens. Since the method returns a boolean, we can always check for this error where we
call the method:


public static void mai n( St r i ng ar gs[ ] ) {
BankAccount b = new BankAccount ( " Bob" ) ;
b. deposi t ( 100) ;
b. deposi t ( 500. 00f ) ;
if (!b.withdraw(25.00f))
Syst em. out . pr i nt l n( " Er r or wi t hdr awi ng money f r omaccount " ) ;
if (!b.withdraw(189.45f))
Syst em. out . pr i nt l n( " Er r or wi t hdr awi ng money f r omaccount " ) ;
b. deposi t ( 100. 00f ) ;
if (!b.withdraw(1000000))
Syst em. out . pr i nt l n( " Er r or wi t hdr awi ng money f r omaccount " ) ;
}


This form of error checking works fine, but it clearly clutters up the code! Let us see how we
can make use of an Exception. We will create a WithdrawalException object. Where would
it go in the Exception hierarchy ? Probably right under the Exception class again, since
there are no existing bank-related exception classes in J AVA. Here is the exception:


class WithdrawalException extends Except i on {
Wi t hdr awal Except i on( ) {
super( " Er r or maki ng wi t hdr awal " ) ;
}
}


Now how do we throw the exception from within the withdraw() method ?
Here is how we do it …

COMP1005/1405 – Exception Handling Fall 2009

- 245 -

void wi t hdr aw( float anAmount ) throws Wi t hdr awal Except i on {
if ( anAmount <= this. bal ance)
this. bal ance - = anAmount ;
else
throw new Wi t hdr awal Except i on( ) ;
}


Note that we must also instruct the compiler that this method may throw a
WithdrawalException by writing this as part of the method declaration. The addition of this
simple statement will force all methods that call the withdraw() method to deal with the
exception.
Also notice that we no longer need the boolean return type for the withdraw() method since
its purpose was solely for error checking. Now that we have the exception being generated,
this becomes our new form of error checking.

Now how do we change the code that calls the withdraw() method ? We just need to enclose
our withdrawal code in a try block:


public static void mai n( St r i ng ar gs[ ] ) {
BankAccount b = new BankAccount ( " Bob" ) ;
try {
b. deposi t ( 100) ;
b. deposi t ( 500. 00f ) ;
b. wi t hdr aw( 25. 00f ) ;
b. wi t hdr aw( 189. 45f ) ;
b. deposi t ( 100. 00f ) ;
b. wi t hdr aw( 1000000) ;
} catch ( Wi t hdr awal Except i on ex) {
Syst em. out . pr i nt l n( " Er r or wi t hdr awi ng money" ) ;
}
}


Notice how much simpler and cleaner the calling code becomes. Be aware however, that if
one error occurs early within the try block, none of the remaining code in the try block gets
evaluated!!! So an error in the first withdrawal attempt would prevent the two other
withdrawals and deposit being made on the account from happening. If we did not want this
behavior, we would need to make a separate try/catch block for each of the 3 withdraw()
method calls.

We can make our code even simpler by ignoring the error. To do this we would have to
indicate in the main() method that the WithdrawalException may occur as follows …
COMP1005/1405 – Exception Handling Fall 2009

- 246 -

public static void mai n( St r i ng ar gs[ ] ) throws Wi t hdr awal Except i on {
BankAccount b = new BankAccount ( " Bob" ) ;
b. deposi t ( 100) ;
b. deposi t ( 500. 00f ) ;
b. wi t hdr aw( 25. 00f ) ;
b. wi t hdr aw( 189. 45f ) ;
b. deposi t ( 100. 00f ) ;
b. wi t hdr aw( 1000000) ;
}


If we do this, however, then the program will stop and quit when the first
WithdrawalException occurs.
We can actually add more information to our exceptions. For example, there may be many
reasons why we cannot withdraw from a BankAccount. The bank account …
• may not have enough money in it,
• may not allow withdrawals (e.g., some kinds of SavingsAccounts), or
• may not have sufficient funds to cover transaction fees associated with the account

We do not need to make different types of exceptions, but can instead supply more information
when the WithdrawException is generated. The easiest way to do this is to modify the
constructor in our WithdrawalException class that takes a String parameter to describe the
error:


class Wi t hdr awal Except i on extends Except i on {
Wi t hdr awal Except i on( St r i ng descr i pt i on) {
super( descr i pt i on) ;
}
}


We can then use this new constructor instead by supplying different explanations as to why the
error occurred.

For example, the SuperSavingsAccount may have the following withdraw() method:

void wi t hdr aw( float anAmount ) throws Wi t hdr awal Except i on {
throw new Wi t hdr awal Except i on( " Wi t hdr awal s not al l owed f r omt hi s
account " ) ;
}


whereas the PowerSavingsAccount may have this method …
COMP1005/1405 – Exception Handling Fall 2009

- 247 -

void wi t hdr aw( float anAmount ) throws Wi t hdr awal Except i on {
if ( anAmount > this. bal ance)
throw new Wi t hdr awal Except i on( " I nsuf f i ci ent f unds i n account t o
wi t hdr aw speci f i ed amount " ) ;
if ( anAmount + WI THDRAW_FEE > this. bal ance) {
throw new Wi t hdr awal Except i on( " Not enough money t o cover
t r ansact i on f ee" ) ;
this. bal ance - = anAmount + WI THDRAW_FEE;
}


So, as can easily be seen, we can provide additional explanatory information for the user when
an exception does occur. Furthermore, we can do this with a single exception class (i.e., we
do not need to make a subclass of WithdrawalException for each specific situation).
We can extract this “additional explanation” from the Exception by sending the getMessage()
message to the exception within our catch blocks:


public static void mai n( St r i ng ar gs[ ] ) {
Power Savi ngs p = new Power Savi ngs( " Bob" ) ;
Super Savi ngs s = new Super Savi ngs( " Bet t y" ) ;
try {
p. deposi t ( 100) ;
s. deposi t ( 500. 00f ) ;
p. wi t hdr aw( 25. 00f ) ;
p. wi t hdr aw( 189. 45f ) ;
s. deposi t ( 100. 00f ) ;
s. wi t hdr aw( 1000000) ;
} catch ( Wi t hdr awal Except i on ex) {
Syst em. out . pr i nt l n( ex. get Message( ) ) ;
}
}


In this example, the catch block catches any errors for both bank accounts.




Chapter 9
Proper Coding Style


What is in This Chapter ?
In this chapter, we will discuss various ways to improve the code that we write. We begin by
discussing how we can simplify the way others will use our objects and we will find ways to
secure the attributes of our object by using access modifiers such as public, private and
protected. We will then discuss the use of get and set methods to access and modify our
objects in a safe way. The chapter then discuses ways to simplify our constructors as well as
reducing clutter in our code all by using or removing the this keyword. We then discuss in
detail how we can type-cast objects into more general types as a means of simplifying our
code greatly. Lastly, we briefly discuss ways in which we should test the code that we write.


COMP1005/1405 – Proper Coding Style Fall 2009

- 249 -

9.1 Protecting and Simplifying an Object

When creating and defining an object it is a good idea to keep it simple so that anybody who
uses that object in the future (including yourself) can remember how to use it. Often, there are
details about an object that we don’t need to know about in order to use the object. For
example, when we drive a car, we need to know simple things such as:

• starting/ stopping
• steering
• changing gears
• braking, etc..

However, we do not need to worry about things such as:

• assembling the carburetor
• adjusting the spark plug timing
• installing gas lines
• changing the muffler, etc..

Cars are clearly designed to be easy to drive, requiring a simple and easy-to-understand
interface. Similarly, it is important that we make our code easy to use and easy to
understand. Otherwise, making changes to the code, debugging it and extending it with new
features can quickly become very difficult and time consuming.

In order to keep our code simple, we need to make the interface (or
"outside view") of our objects as simple as possible. That means,
we need to “hide the details” of our object that most people
would not need to worry about. That is, we need to hide some of
the attributes (complicated parts) and methods (complicated
procedures) for our object “under the hood”, so to speak.

In addition to simplicity, there is another reason to hide some of the
details of our object. We would like to prevent outsiders from "messing
around with" the inner details of an object. For example we lock our car
doors and trunk so that people don't get in there are take things away or
damage them etc.. Similarly, for example, if we allow anyone to access
the attributes of our object and perform behaviors on it in the wrong
order, then this could lead to corrupt data and/or various types of errors
in our code.

The idea of hiding the "unnecessary details" of an object from general users is called
encapsulation in J AVA. Encapsulation involves enclosing our object with a kind of
“protective bubble” so that it cannot be accessed or modified without proper permission.

COMP1005/1405 – Proper Coding Style Fall 2009

- 250 -
In J AVA, we protect and hide attributes and behaviors by using something called an access
modifier. This is a big word for something quite simple. Basically, it allows us to set
permissions for our attributes and methods so that they will be visible/modifiable/usable) from
some places in our code but not from other places. As a result, when working with a team of
software developers on a large program, some developers will have the freedom to access or
modify attributes or methods from various objects, while others will not be allowed such
freedom to view or change portions the objects as they would like to.

Protecting Behaviors

We have already been using an access modifier called public when we wrote our public
main() method and toString() methods:

public static void mai n( St r i ng ar gs[ ] ) { …}
public St r i ng t oSt r i ng( ) { …}

The keyword public at the front of a method declaration means that the method is publicly
available to everyone, so that these methods may be called from anywhere. For all of our
other methods however, we did not write public, and so they had what is known as default
access … meaning that the methods may be called from any code that is in the same package
or folder that this method’s class is saved into. If we write all of our code in the same folder,
then default and public access means the same thing.
There are two other access modifier options available called private and protected. When
we declare a method as private, we would not be able to use this method from any class other
than the class in which it is defined. Protected methods are methods that may be called from
the method’s own class or from one of its subclasses. So here is a summary of the access
modifiers for methods:
• none - can be called from any class in the same folder
• public - can be called from anywhere
• private - can only be called from this class
• protected - can be called from this class or any subclasses
In this course, most of the methods that we write are public methods which allows the most
freedom to access and modify our objects. Usually, private methods are known as helper
methods since they are often created for the purpose of helping to reduce the size of a larger
public method or when a piece of code is shared by several methods.
For example, consider bringing in your car for repair. The publicly available method would be
called to repair() the car. However, many smaller sub-routines are performed as part of the
repair process (e.g., runDiagnostics(), disassembleEngine() etc...). From the point of view
of the user of the class, there is no need to understand the inner workings of the repair
process. The user of the class may simply need to know that the car can be repaired,
regardless of how it is done.
Here is an example of breaking up the repair problem into helper methods that do the sub-
routines as part of the repair …
COMP1005/1405 – Proper Coding Style Fall 2009

- 251 -

class Car {
public void r epai r ( ) {
this. r unDi agnost i cs( ) ;
this. di sassembl eEngi ne( ) ;
this. r epai r Br okenPar t s( ) ;
this. r eassembl eEngi ne( ) ;
this. r unDi agnost i cs( ) ;
}
private void r unDi agnost i cs( ) { . . . }
private void di sassembl eEngi ne( ) { . . . }
private void r epai r Br okenPar t s( ) { . . . }
private void r eassembl eEngi ne( ) { . . . }
}


Notice that the helper methods are private since users of this class probably do not need to
call them. Here is an example showing how we might try to call these methods from some
other class:


class SomeAppl i cat i onPr ogr am {
public static void mai n( St r i ng ar gs[ ] ) {
Car c = new Car ( ) ;
c. repair(); / / OK t o cal l t hi s met hod
c. disassenbleEngine(); / / Won’ t compi l e, si nce i t i s private
c. repairBrokenParts(); / / Won’ t compi l e, si nce i t i s private
}
}


To understand the protected modifier, we need to consider a class hierarchy. Recall the
Person/Employee/Manager/Customer example. Consider four methods within the
Employee class with various access modifiers as follows:



Employee



Customer
St r i ng get Empl oyeeNumber ( ) ;
public St r i ng get PhoneNumber ( ) ;
private St r i ng changePasswor d( St r i ng newOne) ;
protected Ar r ayLi st <St r i ng> j obsCompl et ed( ) ;
Person
Manager
COMP1005/1405 – Proper Coding Style Fall 2009

- 252 -
Now consider some code within the Manager class that attempts to access these methods:


class Manager extends Empl oyee {
void t r yThi ngsOut ( ) {
Syst em. out . pr i nt l n( this. getEmployeeNumber()) ; / / access al l owed
Syst em. out . pr i nt l n( this. getPhoneNumber()) ; / / access al l owed
Syst em. out . pr i nt l n( this. changePassword("12345678")) ; / / compi l e er r or
Syst em. out . pr i nt l n( this. jobsCompleted()) ; / / access al l owed
}
}


Notice that the only method not allowed to be accessed is the private method, since the
tryThingsOut() method is written in the Manager class, not in Employee.

Consider now the Customer class restrictions:


class Cust omer extends Per son {
void t r yThi ngsOut ( ) {
Syst em. out . pr i nt l n( this. getEmployeeNumber()) ; / / access al l owed
Syst em. out . pr i nt l n( this. getPhoneNumber()) ; / / access al l owed
Syst em. out . pr i nt l n( this. changePassword("12345678")) ; / / compi l e er r or
Syst em. out . pr i nt l n( this. jobsCompleted()) ; / / compi l e er r or
}
}


Now we can no longer call the jobsCompleted() method, since it has been declared
protected and Customer is not a subclass of Employee.

There really is not much more to the access modifiers when it comes to methods. However,
there is one more protective keyword that can be used with methods. We can declare a
method as final to prevent subclasses from modifying the behavior. That is, when we declare
a method as being final, J AVA prevents anyone from overriding that method. Hence no
subclasses can have a method with that same name and signature:


public final void wi t hdr aw( float amount ) {
. . .
}


Why would we want to do this ? Perhaps the behavior defined in the method is very critical
and overriding this behavior "improperly" may cause problems with the rest of the program.


COMP1005/1405 – Proper Coding Style Fall 2009

- 253 -
Protecting Attributes
Now what about protecting an object’s attributes ? Well, the public/private/protected and
default modifiers all work the same way as with behaviors. When used on instance variables,
it allows others to be able to access/modify them according to the specified restrictions.
So far, we have never specified any modifiers for our attributes, allowing them all default
access from classes within the same package or folder.
However, in real world situations, it is often best NOT to allow outside users
to modify the internal private parts of your object. The reason is that results
can often be disastrous. It is easy to relate to this because we well
understand how we hide our own private parts ☺.
As an example, consider the following code, which may appear in any class. It shows that we
can directly access the balance of a BankAccount. This is clearly undesirable since there is
little protection. Could you imagine if anyone could modify the balance of your bank account
directly ?

BankAccount myAccount = new BankAccount ( " Mi ne" ) ;


myAccount . bal ance = 1000000. 00f ; / / YAY : )


myAccount . bal ance = - 1000000. 00f ; / / WHY : (


In order to prevent direct access to important information we would need to prevent the code
above from compiling/running. If we were to declare the balance instance variable as private
within the BankAccount class, then the above code would not compile, thus solving the issue.
In general, while freedom to access/modify anything from anywhere seems like a friendly thing
to do, it is certainly dangerous. Anyone could "stomp" all over our instance variables
changing them at will. A general "rule-of-thumb" that should be followed is to declare all of
your instance variables as private as follows:

class Per son {
private St r i ng name;
private int age;
private float hei ght ;
private char gender ;
private boolean r et i r ed;

. . .
}

COMP1005/1405 – Proper Coding Style Fall 2009

- 254 -
Once we do this, then the following code will not work (when written in a class other than the
Person class):

class SomeAppl i cat i onPr ogr am {
public static void mai n( St r i ng ar gs[ ] ) {
Per son p = new Per son( ) ;
p.name = " Sandy Beach" ; / / wi l l NOT compi l e
p.age = 15; / / wi l l NOT compi l e
p.height = 5. 85f ; / / wi l l NOT compi l e
p.gender = ' M' ; / / wi l l NOT compi l e
p.retired = false; / / wi l l NOT compi l e
Syst em. out . pr i nt l n( p.name) ; / / wi l l NOT compi l e
Syst em. out . pr i nt l n( p.age) ; / / wi l l NOT compi l e
Syst em. out . pr i nt l n( p.height) ; / / wi l l NOT compi l e
Syst em. out . pr i nt l n( p.gender) ; / / wi l l NOT compi l e
Syst em. out . pr i nt l n( p.retired) ; / / wi l l NOT compi l e
}
}


What we have essentially done is to erect a wall around the object ... like
the wall around a city. We have encapsulated it with a protective bubble.
Although we are still able to create the object, we are prevented from
accessing or modifying its internals now due to privacy issues. By doing
this, we have protected the object so much that we cannot get information
neither into it nor out from it. We have kind of secluded the object from the
rest of the world by doing this. However, just as a walled city has gates or doors to allow
access, we too have a form of gated access by means of any publicly available methods.

We will grant access to "some" of our object's attributes (i.e., instance variables) by creating
methods known as get and set methods (also called getters and setters). The idea of
creating these gateways to our object’s data is common practice and is considered to be a
robust strategy when creating classes to be used in a large software application. In this
course, since we are only creating a few classes and since we are the only code writers, we
may not immediately see the benefits of declaring private attributes and then creating these
methods. However, in a larger/complicated system with hundreds of classes, the benefits
become quite clear:

• object attributes are easier to understand and use
• attributes are protected from external/unknown changes
• we are following proper and robust coding style

COMP1005/1405 – Proper Coding Style Fall 2009

- 255 -
Let us first consider get methods. They let
us look at information that is within the
object by getting the object’s attribute
values (i.e., get the values of the instance
variables):
false
'M'
0
0
null
retired
gender
height
age
name
false
'M'
0
0
Get methods
Person object

Get methods have:
null
• public access
• name matching attribute’s name
• return type matching attribute’s type
• code returning attribute’s value
Here is how we would write the standard
get methods for the Person class:

public class Per son {
private St r i ng name;
private int age;
private float hei ght ;
private char gender ;
private boolean r et i r ed;

/ / Get met hod f or name at t r i but e
public St r i ng getName() {
return this.name;
}
/ / Get met hod f or age at t r i but e
public int getAge() {
return this.age;
}
/ / Get met hod f or height at t r i but e
public float getHeight() {
return this.hei ght ;
}
/ / Get met hod f or gender at t r i but e
public char getGender() {
return this.gender ;
}
/ / Get met hod f or retired at t r i but e
public boolean isRetired() {
return this.r et i r ed;
}
}


Notice that all the methods look the same in structure. They are all public, all have return
types and names that match the attribute type, all have no parameters and all are one line
long. When we call the method to get the attribute value, the method simply returns the
attribute value to us. Its quite simple. By convention, all get methods start with “get” followed
by the attribute name, with the exception of attributes that are of type boolean. In that case,
we usually use “is” followed by the attribute name, as it makes the method call more natural.
COMP1005/1405 – Proper Coding Style Fall 2009

- 256 -
Now let us examine the set
methods. Set methods allow
us to put values into the
instance variables (i.e., to set
the object's attributes):

retired
gender
height
age
name
false
‘M’
0
0
Set methods
Person object

Set methods have:
null Sandy Beach
• public access
• void return type
15
• name matching
attribute’s name
5.85
• a parameter matching
attribute’s type
'M'
• code giving the attribute
a value
false

Here is how we would write the standard set methods for the Person class:

/ / Set met hod f or name at t r i but e
public void setName(String n) {
this.name = n;
}
/ / Set met hod f or age at t r i but e
public void setAge(int a) {
this.age = a;
}
/ / Set met hod f or height at t r i but e
public void setHeight(float h) {
this.hei ght = h;
}
/ / Set met hod f or gender at t r i but e
public void setGender(char g) {
this.gender = g;
}
/ / Set met hod f or retired at t r i but e
public void setRetired(boolean r) {
this.r et i r ed = r ;
}


The single line of code in a set method is quite simple also.
When we call the method to give the attribute a new value (i.e., we supply the new value as a
parameter to the method), the method simply takes that new attribute value and sets the
attribute to it using the = operator.

Normally, we write all the get and set methods together, and sometimes shorten them onto
one line. Also, they are often listed in the code right after the constructors as follows …

COMP1005/1405 – Proper Coding Style Fall 2009

- 257 -

class Person {
private St r i ng name;
private int age;
private float hei ght ;
private char gender ;
private boolean r et i r ed;

/ / Const r uct or s
public Per son( ) { / *. . . */ }
public Per son( St r i ng n, int a, float h, char g, boolean r ) { / *. . . */ }

/ / Get met hods
public St r i ng getName() { return this.name; }
public int getAge() { return this.age; }
public float getHeight() { return this.hei ght ; }
public char getGender() { return this.gender ; }
public boolean isRetired() { return this.r et i r ed; }

/ / Set met hods
public void setName(String n) { this.name = n; }
public void setAge(int a) { this.age = a; }
public void setHeight(float h) { this.hei ght = h; }
public void setGender(char g) { this.gender = g; }
public void setRetired(boolean r) { this.r et i r ed = r ; }
}


Here is how the get method works:

retired
gender
height
age
name
false
‘M’
0
0
aPerson.getGender ()
aPerson.isRetired()
0
0
aPerson.getAge()
aPerson.getName()
“Sandy Beach”
aPerson

Person object
aPerson.getHeight()
'M'
false















Notice that primitive attribute values are returned as simple values but object attribute values
are returned as pointers to the object. The Person object remains unchanged as a result of a
get method call. Here is how the set method works:

COMP1005/1405 – Proper Coding Style Fall 2009

- 258 -

Notice that primitive attribute values are simply replaced with the new value. For object
attribute values, after the set call, the attribute will point to the new object. The previous
object that the attribute used to point to is discarded (garbage collected) if no other objects are
holding on to it. Once we create these get/set methods, we can then access and modify the
object from anywhere in our program as before:

class Test Per sonPr ogr am {

public static void mai n( St r i ng ar gs[ ] ) {
Per son p = new Per son( ) ;

Syst em. out . pr i nt l n( " Bef or e Set t i ng . . . " ) ;
Syst em. out . pr i nt l n( p.getName()) ; / / was println(p.name);
Syst em. out . pr i nt l n( p.getAge()) ; / / was println(p.age);
Syst em. out . pr i nt l n( p.getHeight()) ; / / was println(p.height);
Syst em. out . pr i nt l n( p.getGender()) ; / / was println(p.gender);
Syst em. out . pr i nt l n( p.isRetired()) ; / / was println(p.retired);

p.setName(" Sandy Beach" ); / / was p.name = "Sandy Beach";
p.setAge(15); / / was p.age = 15;
p.setHeight(5.85f); / / was p.height = 5.85f;
p.setGender('F'); / / was p.gender = 'F';
p.setRetired(true); / / was p.retired = true;

Syst em. out . pr i nt l n( " \ nAf t er Set t i ng . . . " ) ;
Syst em. out . pr i nt l n( p.getName()) ; / / was println(p.name);
Syst em. out . pr i nt l n( p.getAge()) ; / / was println(p.age);
Syst em. out . pr i nt l n( p.getHeight()) ; / / was println(p.height);
Syst em. out . pr i nt l n( p.getGender()) ; / / was println(p.gender);
Syst em. out . pr i nt l n( p.isRetired()) ; / / was println(p.retired);
}
}


retired
gender
height
age
name
false
'M'
0
0
aPerson

Before

retired
gender
height
age
name
true
'F'
6.2
32
aPerson.setRetired(true)
aPerson.setGender('F')
aPerson.setHeight(6.2f)
aPerson.setAge(32)
aPerson.setName("Biffy")
“Biffy”
After
aPerson
“Sandy Beach”
COMP1005/1405 – Proper Coding Style Fall 2009

- 259 -
Here is what the output would be (however, initial values depend on the Person constructor):


Bef or e Set t i ng . . .
" UNKNOWN"
0
0. 0
?
f al se

Af t er Set t i ng . . .
" Sandy Beach"
15
5. 85
F
t r ue


Now if we think for a moment ... what did we really do by making all the get and set methods
? Really, we wrote a lot of code (e.g., 5 get methods and 5 set methods for the Person
class) but did not gain anything new. The code does the same thing as before. In fact, the
test code seems longer and perhaps slower (since we are calling a method to get/set the
instance variables for us instead of accessing them directly). So why did we do this ? Lets
review the advantages again:
1. First, get/set methods actually make life simpler for users of your
class because the user does not have to understand the “guts” of
the object being used. It allows them to treat the object as a “black
box”. The user does not need to know about all the instance
variables. Some are used to hold data that is temporary or
private. You should only create public get methods for the
instance variables that the user of the class would need to know
about.
2. Second, it prevents the users of a class from directly modifying the
object's internals. Recall, for example, that we should never be
able to directly change the balance of our bank account without
going through the proper transaction procedures such as
depositing and withdrawal. Of course, if we always create public
get/set methods for all our attributes, then we still would have no
such protection. So, it is important to create set methods only for the attributes that
you want the user of the class to be able to change directly. Therefore, you do not
always need to make set methods.

COMP1005/1405 – Proper Coding Style Fall 2009

- 260 -
Protecting Classes

In regards to class definitions, we are also allowed to indicate either default or public access to
the class. So far, all of our classes have been default access, but we can write public in front
of the class if we want this to be a truly publicly accessible object:


public class Manager { / / publ i c access f r omcl asses anywher e
. . .
}



class Empl oyee { / / def aul t access f r omcl asses wi t hi n package/ f ol der
. . .
}


Interestingly, we can also declare a class as final. This means that it CANNOT have
subclasses:


public final class Manager {
. . .
}


Why would we want to do this ? Perhaps the class has very weird code that the author does
not want you to inherit ... maybe because it is too complicated and may easily be misused.
Many of the J AVA classes (e.g., ArrayList) are declared as final which means that we cannot
make any subclasses of them. It is a kind of security issue to prevent us from "messing up"
the way those classes are meant to be used. It’s a shame, because often we would like to
have special types of ArrayLists and other similar objects .


9.2 Simplifying Constructors and Eliminating “ this”

You may have noticed that we have a lot of duplicated code in our 3 Person constructors that
we wrote earlier. Each attribute of the object was set explicitly with a line of code like this:
this. <at t r i but e> = <i ni t i al Val ue>;
Here was the code …
COMP1005/1405 – Proper Coding Style Fall 2009

- 261 -
/ / Thi s i s t he zer o- par amet er const r uct or
Per son( ) {
this. f i r st Name = " UNKNOWN" ;
this. l ast Name = " UNKNOWN" ;
this. gender = ' ?' ;
this. r et i r ed = false;
this. age = 0;
this. addr ess = null;
}

/ / Thi s i s a 4- par amet er const r uct or
Per son( St r i ng f n, St r i ng l n, char g, boolean r ) {
this. f i r st Name = f n;
this. l ast Name = l n;
this. gender = g;
this. r et i r ed = r ;
this. age = 0;
this. addr ess = null;
}

/ / Thi s i s a 6- par amet er cons ct or t r u
Per son( St r i ng f n, St r i ng l n, int a, char g, boolean r , Addr ess adr ) {
this. f i r st Name = f n;
this. l ast Name = l n;
this. age = a;
this. gender = g;
this. r et i r ed = r ;
this. addr ess = adr ;
}

We can actually reduce the amount of duplicated code by chaining our constructors together
(i.e., calling one constructor from another). We do this by making use of the this keyword
again. Notice the reduced code:

/ / Thi s const r uct or cal l s t he 6- par amet er one
Per son( ) {
this( " UNKNOWN" , " UNKNOWN" , 0, ' ?' , false, null) ;
}

/ / Thi s const r uct or cal l s t he 6- par amet er one
Per son( St r i ng f n, St r i ng l n, char g, boolean r ) {
this( f n, l n, 0, g, r , null) ;
}

/ / Thi s i s t he 6- par amet er co r uct nst or
Per son( St r i ng f n, St r i ng l n, int a, char g, boolean r , Addr ess adr ) {
this. f i r st Name = f n;
this. l ast Name = l n;
this. age = a;
this. gender = g;
this. r et i r ed = r ;
this. addr ess = adr ;
}
COMP1005/1405 – Proper Coding Style Fall 2009

- 262 -
Notice that the first 2 constructors simply call the last one which takes all 6 parameters as
arguments. You may also notice that there is no dot . operator after the this keyword. This
is a special use of the keyword this which only applies when calling one constructor from
another.

Notice as well in the above code that the keyword this is also used to access the object’s
attributes. As it turns out, whenever we are writing a constructor or instance method of a
class, we do not need to use the keyword this to access the attributes of the object or not call
another instance method in the same class. We can leave off this completely, and J AVA will
assume that we meant “this” object. Hence, the following constructor is equivalent to the 3
rd

one above:


Per son( St r i ng f n, St r i ng l n, int a, char g, boolean r , Addr ess adr ) {
f i r st Name = f n; / / same as this. f i r st Name = f n;
l ast Name = l n; / / same as this. l ast Name = l n;
age = a; / / same as this. age = a;
gender = g; / / same as this. gender = g;
r et i r ed = r ; / / same as this. r et i r ed = r ;
addr ess = adr ; / / same as this. addr ess = adr ;
}


Note however, that we cannot remove the keyword this in the chained constructors that use
this without the dot afterwards, so the first two constructors must remain the same.

In fact, we can go through our entire code and remove the code this. from our instance
methods since all instance methods have direct access to their own internal attributes. Here
are some methods that we wrote in the Person class. Notice how we can remove all
occurrences of this. from the code:

int mpu scount ( ) { co t eDi
if ( ( this. gender == ' F' ) && ( this. age < 13 | | this. r et i r ed) )
return 50;
else
return 0;
}

boolean i sOl der Than( Per son x) {
return ( this. age > x. age) ;
}

boolean i sOl der Than( Per son x, Per son y) {
if ( ( this. age > x. age) && ( this. age > y. age) )
return true;
else
return false;
}

COMP1005/1405 – Proper Coding Style Fall 2009

- 263 -
Person ol dest ( Per son x, Per son y) {
if ( ( this. age > x. age) && ( this. age > y. age) )
return this; / / cannot r emove t hi s her e t hough
else if ( ( x. age > this. age) && ( x. age > y. age) )
return x;
else
return y;
}

void swapNameWi t h( Per son x) {
St r i ng t empName;

tempName = this. f i r st Name;
this. f i r st Name = x. f i r st Name;
x. f i r st Name = tempName;

tempName = this. l ast Name;
this. l ast Name = x. l ast Name;
x. l ast Name = tempName;
}

public St r i ng t oSt r i ng( ) {
St r i ng answer = this. age + " year ol d " ;

if ( ! this. r et i r ed)
answer = answer + " non- " ;

return ( " r et i r ed per son named " +
this. f i r st Name + " " + this. l ast Name) ;
}


Recall as well in the Team/League example that we called one instance method from within
another. Here too, for each call that we made within the same class, we can remove the
references to this. since J AVA assumes automatically that we meant “this” class if we leave it
out. Here are some examples from the Team/League example showing where it can be
removed:

void r ecor dWi nAndLoss( St r i ng wi nner Name, St r i ng l oser Name) {
Team wi nner , l oser ;

wi nner = this. t eamWi t hName( wi nner Name) ;
l oser = this. t eamWi t hName( l oser Name) ;
this. r ecor dWi nAndLoss( wi nner , l oser ) ;
}

void r ecor dTi e( St r i ng t eamAName, St r i ng t eamBName) {
Team t eamA, t eamB;

t eamA = this. t eamWi t hName( t eamAName) ;
t eamB = this. t eamWi t hName( t eamBName) ;
this. r ecor dTi e( t eamA, t eamB) ;
}
COMP1005/1405 – Proper Coding Style Fall 2009

- 264 -
As a rule of thumb, you can ALWAYS remove “ this.” whenever it appears in your code since
J AVA will automatically assume that you are trying to access attributes for (or call a method
for) the receiver object when no object is specified. Until now, we have been using this.
throughout our code because it makes it easier to understand which object we are dealing
with. However, it is common practice to leave this. out of our code to reduce the clutter.



9.3 Type-Casting, Polymorphism and Double-Dispatching

Recall that we can type-cast primitives to convert a value from one type to another:

(int)871. 34354; / / r esul t s i n 871
(char)65; / / r esul t s i n ' A'
(long)453; / / r esul t s i n 453L

Remember that some type-casting is done automatically by J AVA where as in other cases we
can explicitly type-cast in order to simplify the data (e.g., from float to int) or for display
purposes (e.g., from byte to char).

In J AVA, we can also type-cast objects from one type to another type. However, type-casting
objects is different from type-casting primitives in that the objects are not converted or
modified in any way. Instead, when we type-cast an object variable, it is simply restricted with
respect to the kinds of behaviors that it is capable of doing from then on in our program.

It is important to understand the type-casting of objects because J AVA often type-casts objects
automatically. Therefore, we must understand how to type-cast and when it is done
automatically.
The answer of “How” to type-cast objects is simple, since it is done the same way (i.e, with the
round brackets) as with primitives. Here are a few examples:
p = (Person)anEmpl oyee;
c = (Customer)anAr r ayLi st . get ( i ) ;
b = (SavingsAccount)aBankAccount ;

Notice that there is an object type (i.e., class name) within the round brackets.

When we type-cast an object to another type we are not modifying it in any way.
Rather, we are simply causing the object to be “ treated” more generally from then on
in the program. As a result, the object will then be less flexible in that we can no longer
call some of the methods that we used to call on it. In a way, we are ignoring some of
the behavior that is available to the object.

This may sound strange, but we do this in real life. Lets consider a couple of examples.
COMP1005/1405 – Proper Coding Style Fall 2009

- 265 -
Consider meeting your professor with his family outside of class, perhaps at a local shopping
mall. Likely, you would “treat” your professor as a general/normal Person ...
not as your "professor". So, you might ask him questions that you would ask
anyone such as: “Is this your family?” or “What are you shopping for today?”.
However, you would likely not ask him a question like “What kind of questions
will be on the final exam?” and hopefully you would not pull out a laptop and
ask him to help you debug the code on your assignment. So, in a sense, you
have type-casted the Professor to a more general Person object by
restricting the behavior to behavior that is applicable to more general people,
avoiding any professor-specific behavior.
As another example, consider an Apple … normally
you may polish, peel or eat it ... but in a food fight,
you may type-cast (i.e., treat) your apple as a general
throwable projectile. Then, the apple takes on
different behavior such as throw, catch, splatter,
etc... The fact is ... it is still an Apple, but it is being
treated differently. You may even type-cast other
objects to be projectiles such as grapes, sandwiches,
pineapples (ouch), chairs, etc.. a dangerous food
fight.

Now lets look at a real coding example. Consider the following class hierarchy of Employee,
Person, Manager and Customer objects with some instance methods belonging to each
class as shown:



Object

getName()
getAddress()
getPhoneNumber()
getEmployeeNumber()
getHourlyPay()
getItemsPurchased()
getPurchaseHistory()
getDuties()
getSubordinates()
Manager
Employee
Customer
Person












Consider what happens when we create a single Employee object and then type-cast it to a
Person. Take note of the methods that are available for use and those which will not compile.
Note that we create 2 variables, yet both point to the same object …

COMP1005/1405 – Proper Coding Style Fall 2009

- 266 -

employee variable person variable
Per son per son;

“ Earl”
address
name
Empl oyee empl oyee;

empl oyee = new Empl oyee( " Ear l " ) ;
Employee object
empl oyee. get Name( ) ;
empl oyee. get Addr ess( ) ;
empl oyee. get PhoneNumber ( ) ;
empl oyee. get Empl oyeeNumber ( ) ;
empl oyee. get Hour l yPay( ) ;

/ / now t r eat Ear l l i ke a per son
phoneNumber
employeeNumber
10012
hourlyPay
8.50f
per son = (Person)empl oyee;
per son. get Name( ) ;

per son. get Addr ess( ) ;
per son. get PhoneNumber ( ) ;

/ / t hese t wo wi l l not compi l e
per son. get Empl oyeeNumber ( ) ;
per son. get Hour l yPay( ) ;

/ / t ype- cast back and al l i s ok
( (Employee)per son) . get Empl oyeeNumber ( ) ;
( (Employee)per son) . get Hour l yPay( ) ;


You will notice that once the type-cast to (Person) occurs, we are no longer able to use the
getEmployeeNumber() and getHourlyPay() methods since they are Employee-specific
methods and we are now treating Earl as simply a Person. However, the person variable is
still pointing to Earl … the exact same object. When we type-cast the person variable back to
(Employee) again, and then try the same two methods, they work fine because we are now
treating Earl as an Employee again.

Notice what we are not able to do:

Empl oyee empl oyee;
Manager manager ;
Cust omer cust omer ;

empl oyee = new Empl oyee( " Ear l " ) ;
manager = (Manager)empl oyee; / / Type- cast i s not al l owed
cust omer = (Customer)empl oyee; / / Type- cast i s not al l owed
We are only allowed to use class type-casting to generalize an object. Therefore we can only
type-cast to classes up the hierarchy (e.g., Person and Object) but not down the hierarchy
(e.g., Manager) or across the hierarchy (e.g., Customer) from the original object class (e.g.,
Employee). In summary, objects may ONLY be type-casted to:
• a type which is one of its superclasses
• an interface which the class implements
• or back to their own class again
COMP1005/1405 – Proper Coding Style Fall 2009

- 267 -
In the following example, an Employee object can only be type-casted to (or stored in a
variable of type) Employee, Person, Object or Insurable:


Employee Customer
Object
Person
Car Company
Manager
Insurable
Attempts to type-cast to anything else will generate a ClassCastException. So Employees
CANNOT be type-casted to Manager, Customer, Company or Car. Such restrictions make
sense, after all, why would we "treat" a Manager as a Company or a Car.
Why would we want to do type-casting in the first place ? It seems that all we are doing is
restricting the object in some way. Would it not be better (i.e., more flexible) to simply allow
the object’s methods to be used at any time ? These are valid questions. However, there are
reasons for type-casting.

Perhaps the main advantage of type-casting is that it allows for
polymorphism which is the ability to use the same behavior for
objects of different types. That is, it allows different objects to
respond to the exact "same" messages (i.e., methods). The
result is that we have much less to remember when we go to use
the object. That is, by using polymorphism, we just need to
understand a few commonly used methods that all these objects
understand. For example:

• we can ask all Person objects what their name is. This is independent as to whether
or not they are instances of Employees, Managers, Customers etc...

• and, we can deposit to any BankAccount, independent of its type.
• all Objects understand the toString() method, so we do not have to remember
additional method names.
And so … by treating an object more generally (i.e., type-casting it), we are simplifying the way
that we will use the object by restricting its usage to a few well understood methods. As a
result, our code becomes easier to understand, more intuitive and quicker to write since the
programmer does not need to remember as many methods.
COMP1005/1405 – Proper Coding Style Fall 2009

- 268 -
Some coding advantages arise through implicit or automatic type-casting. Sometimes J AVA
will automatically type-cast an object, even if we do not explicitly do so with the brackets ().
There are two main situations in which automatic type-casting occurs:
1. when we assign an object to a variable with a more general type:

Per son per son;
Empl oyee empl oyee;

empl oyee = new Empl oyee( " Ear l " ) ;
person = employee; / / same as person = (Person)employee;

2. when we pass in the object as a parameter to a method which has a more general type:

Empl oyee empl oyee;

empl oyee = new Empl oyee( " Ear l " ) ;
doSt andar dHi r i ngPr ocess( employee) ;
...


public void doSt andar dHi r i ngPr ocess( Per son p) {
/ / employee obj ect i s t ype- cast ed t o Person upon ent er i ng met hod
. . .
}

In both cases, you should be aware that an automatic type-cast has taken place. In fact, it
usually does not matter if you “know” that the type-casting is taking place, because the
compiler will tell you. However, it tells you this by means of a compile error … which is
somewhat unpleasant, as you well know. Also, sometimes the compiler message is not
straightforward to understand.
Let us now look at a simple example to see
how much we can reduce our code through
the use of automatic type-casting. Consider a
hierarchy of shape-related objects as shown
here. We can create a Circle, a Triangle
and a Rectangle and all three can be stored
into a variable of type Shape:
Circle Triangle
Shape
Object
Rectangle
Shape s;
Ci r cl e c = new Ci r cl e( 20) ;
Tr i angl e t = new Tr i angl e( 10, 20, 30) ;
Rect angl e r = new Rect ange( 10, 10, 20, 20) ;
s = c; / / s poi nt s t o obj ect c
s = t ; / / s poi nt s t o obj ect t
s = r ; / / s poi nt s t o obj ect r
COMP1005/1405 – Proper Coding Style Fall 2009

- 269 -
Notice that we did not make any explicit type-cast to Shape (although we
could have done so). Here we simply re-assigned s to have three different
values corresponding to three different types of objects. The example code
itself is pointless, but it helps us to see how we can use automatic type-
casting.

Assume now that we want to draw a shape and that the Circle, Triangle
and Rectangle classes all have an appropriate method for drawing themselves called draw ():

class Circle extends Shape {
...
public void dr aw( ) { . . . }
}

class Triangle extends Shape {
...
public void dr aw( ) { . . . }
}

class Rectangle extends Shape {
...
public void dr aw( ) { . . . }
}

Consider now our Shape variable s which can hold any kind of shape:
Shape s = . . . ;
At any given time, we may not know exactly which kind of shape is currently stored in the
aShape variable. How then do we know which draw() method to call ? Well, we could check
the type of the object, perhaps with the instanceof keyword and then use some if statements
as follows:

if ( s instanceof Circle)
s. dr aw( ) ;

if ( s instanceof Triangle)
s. dr aw( ) ;

if ( s instanceof Rectangle)
s. dr aw( ) ;


However, looking at the code, it is clear that regardless of the type of shape we have, we just
need to call draw(). Since we called all of the methods draw(), this is an example of
polymorphism … that is … all shape objects understand the draw() method. For this to
compile though, there should also be a draw() method defined in the Shape class, which may
be blank.
COMP1005/1405 – Proper Coding Style Fall 2009

- 270 -
As a result, because of polymorphism and the explicit type-cast, we don't even need the IF
statements. Our code can be simplified to:

s.draw();

Incredible!!! What a reduction in code! But why does this work ? How does J AVA know
which draw() method to call ? Well, remember, whatever we store in the Shape variable s
does not change its type. The compiler will look at the kind of object that we put in there and
call the appropriate method accordingly by starting its method lookup in the class
corresponding to that object type (i.e., either Circle, Triangle or Rectangle, depending on
what was stored in s). As you can see, polymorphism can be quite powerful.
Now consider a Pen object which is capable of drawing shapes. We would like to use code
that looks something like this:

Pen aPen = new Pen( ) ;

aPen. draw( aCi r cl e) ;
aPen. draw( aTr i angl e) ;
aPen. draw( aRect angl e) ;


However, this is not so straight forward. We would have to define a draw() method in the Pen
class for each kind of shape in order to satisfy the compiler with regards to the particular type
of the parameter:


class Pen {
...
public void draw( Ci r cl e aCi r cl e) {
/ / code t hat dr aws a Ci r cl e
}
public void draw( Tr i angl e aTr i angl e) {
/ / code t hat dr aws a Tr i angl e
}
public void draw( Rect angl e aRect angl e) {
/ / code t hat dr aws a Rect angl e
}
}


Since the drawing code is likely different for all 3 shapes we will need the 3 different pieces of
code to do the drawing. However, all of the shape-drawing code must appear here in the
Pen class. This is somewhat intuitive in regards to real life, since Pen’s draw shapes.

COMP1005/1405 – Proper Coding Style Fall 2009

- 271 -
However, if we had other drawing classes such as Pencil, Marker or Chalk, we would need to
go to all these classes and insert shape-specific code for each kind of shape. Even worse, if
we wanted to add shapes (e.g., Ellipse, Diamond, Parallelogram, Rhombus, etc..) then we
would have to go to the Pen, Pencil, Marker and Chalk classes to add the appropriate shape-
drawing code.

This is quite terrible since our code is not modular … the adding of one simple Shape class
would require us to recompile 4 other classes.



There must be a better way to do this!

The answer is to use a technique known as double-dispatching. The idea behind double
dispatching is to dispatch a J AVA message two times. That is, when we call a method in
J AVA, this is the same as sending a message to the object. Through double dispatching, we
force a second message to be sent (i.e., we call another method) in order to accomplish the
task.

Before we do the double-dispatch, we need to adjust our code a little. We can simplify the
draw() methods in the Pen, Pencil, Marker and Chalk classes by combining them all in one
method. The new method will take a single parameter of type Shape. Hence, through type-
casting, we can pass in any subclass of Shape to the method. Here is the code …


class Pencil {
...
public void draw( Ci r cl e
aCi r c e) {
/ / code t hat dr aws a Ci r cl e
}
p
aTr i a

Tr i an
}
p
aRect
l

ublic void draw( Tr i angl e
ngl e
/ / code s a
gl e

ublic
angl

t hat dr aw
void draw( Rect angl e
/ / code t hat dr aws a

class Chalk {
...
public void draw( Ci r cl e
aCi r cl e
/ / code t hat dr aws a
Ci r cl
}
p
aTr i a
/ / code t hat dr aws a
Tr i an
}
p
e

ublic void draw( Tr i angl e
ngl e
gl e

ublic

void draw( Rect angl e
aRect angl e) {

class Marker {
...
public void draw( Ci r cl e aCi r
/ / code t hat dr aws a
Ci r cl
}
p
aTr i a

Tr i an
}
p
aRect
e

ublic void draw( Tr i angl e
ngl e) {
/ / code t hat dr aws a
gl e

ublic void draw( Rect angl e
angl e) {
/ / code t hat dr aws a

class Pen {
...
public void draw( Ci r cl e aCi r cl e) {
/ / code t hat dr aws a Ci r cl e
}
public void draw( Tr i angl e aTr i angl e) {
/ / code t hat dr aws a Tr i angl e
}
public void draw( Rect angl e aRect angl e) {
/ / code t hat dr aws a Rect angl e
}

public void draw( El l i pse anEl l i pse) {
/ / code t hat dr aws an El l i pse
}

}
class El l i pse {
...
}
COMP1005/1405 – Proper Coding Style Fall 2009

- 272 -

class Pen {
...
public void dr aw( Shape anyShape) {
if ( anyShape instanceof Ci r cl e)
/ / Do t he dr awi ng f or ci r cl es
if ( anyShape instanceof Tr i angl e)
/ / Do t he dr awi ng f or t r i angl es
if ( anyShape instanceof Rect angl e)
/ / Do t he dr awi ng f or r ect angl es}
}
}


At this point, we still have to decide how to draw the different Shapes. So then when new
Shapes are added, we still need to come into the Pen class and make changes. However,
we can correct this problem by shifting the drawing responsibility to the individual shapes
themselves, as opposed to it being the Pen's responsibility. This "shifting" (or flipping) of
responsibility is where the notion of double dispatching comes in. It is similar to the
expression "passing-the-buck" in English. In other words, we are saying: " I'm not going to
do it ... you do it yourself" .
We perform double-dispatching by making a method in each of the specific Shape subclasses
that allows the shape to draw itself using a given Pen object:

class Ci r cl e extends Shape {
...
public void drawWith(Pen aPen) { . . . }
}



class Tr i angl e extends Shape {
...
public void drawWith(Pen aPen) { . . . }
}



class ect angl e extends Shape { R
...
public void drawWith(Pen aPen) { . . . }
}


COMP1005/1405 – Proper Coding Style Fall 2009

- 273 -
Then, we do the double dispatch itself by calling the drawWith() method from the Pen class:


class Pen {
...
public void dr aw( Shape aShape) {
aShape. drawWith(this);
}
}


Notice that the code is incredibly simple. When the Pen is asked to draw a Shape, it basically
says: "No way! Let the shape draw itself using ME!". That is the second message call, which
itself does the real drawing work. We would write a similar one-line method in the Pencil,
Chalk and Marker classes. In order for this to compile, you must also have a
drawWith(Pen aPen) method declared in class Shape even if that method does nothing.

Do you see the tremendous advantages here ? Regardless of the kind of Shape that we may
add in the future, we NEVER have to go into the Pen class to make changes. This code
remains intact. Instead, we simply write a drawWith() method in the new Shape class to do
the drawing of itself. And who would know better how to draw the shape than itself. The
code is much more modular and has a nice clean separation. Furthermore, the code is logical
and easy to understand.
Type-casting also provides advantages when multiple unrelated classes implement the same
interface. Objects can be type-casted to an interface type, provided that the class implements
that interface. In the hierarchy below, we can type-cast any instances of Car, Company,
Customer, Employee or Manager to Insurable.



Employee Customer
Insurable
Object
Person
Car Company
Manager
COMP1005/1405 – Proper Coding Style Fall 2009

- 274 -
Assume that Insurable has a method defined called getPolicyNumber() and that the Car
class has a getMileage() method. Notice the type-casting as follows:


Car j et t a = new Car ( ) ;
I nsur abl e i t em= (Insurable)j et t a;

i t em. getPolicyNumber(); / / OK si nce Insurable
j et t a. get Mi l eage( ) ; / / OK ( assumi ng i t i s a Car met hod)
i t em. get Mi l eage( ) ; / / Compi l e Er r or
( (Car)i t em) . get Mi l eage( ) ; / / OK now


Notice the compile error when calling getMileage() on item. Even though item is actually a
Car object, it has been type-casted to Insurable, and so only methods that are defined in the
Insurable interface can be used on it.

What is the advantage of type-casting to an interface ? Well, we can treat “seemingly
unrelated” objects the same way. This is often useful when we have a collection of such
items. Consider adding a variety of Insurable items to an ArrayList and then listing all of the
policies and totaling the amounts of all the policies:


float t ot al = 0;
Ar r ayLi st <Insurable> i nsur abl eI t ems;

i nsur abl eI t ems = new Ar r ayLi st <Insurable>( ) ;
i nsur abl eI t ems. add( new Car ( " Por shce" , " Car er r a" , " Red" , 340) ) ;
i nsur abl eI t ems. add( new Cust omer ( " Guy Ri ch" ) ) ;
i nsur abl eI t ems. add( new Company( " El mo’ s Edi bl es" , 2009) ) ;
i nsur abl eI t ems. add( new Empl oyee( " J i mSocks" ) ) ;
i nsur abl eI t ems. add( new Manager ( " Ti mBur r " ) ) ;

Syst em. out . pr i nt l n( " Her e ar e t he pol i ci es: " ) ;
for ( Insurable i t em: i nsur abl eI t ems) {
Syst em. out . pr i nt l n( " " + i t em. getPolicyNumber()) ;
t ot al += i t em. getPolicyAmount();
}
Syst em. out . pr i nt l n( " Tot al pol i ci es amount i s $" + t ot al ) ;


In the above example, all 5 unique objects are automatically type-casted to Insurable when
added to the ArrayList. Then when listing the policies, we simply use the common
getPolicyNumber() method (which must be defined in Insurable and implemented by all the
classes). Similarly, we total all the policy amounts by using the common getPolicyAmount()
method.

What would the code look like without having the Insurable interface ? Well, in order to store
the items in the same ArrayList we would still need to know what they have in common.
COMP1005/1405 – Proper Coding Style Fall 2009

- 275 -
Without the Insurable interface, the only other thing that all the objects have in common is that
they are subclasses of Object. So we would have to make an ArrayList<Object> of general
objects.

Ar r ayLi st <Object> i nsur abl eI t ems = new Ar r ayLi st <Object>( ) ;

This will affect the type defined for our FOR loop as well:

for ( Object i t em: i nsur abl eI t ems) { . . . }

Once we make these changes, then the compiler will prevent us from calling the
getPolicyNumber() or getPolicyAmount() methods because it assumes that the item
extracted in the FOR loop is a general Object … but general objects do not have such
methods. Therefore, we would be forced to check the type of every object, beforehand …
implying that we knew all the different types that would ever be placed in the ArrayList. Our
code would be longer, more complicated, messier and non-modular:


...
for ( Object i t em: i nsur abl eI t ems) {
if ( i t eminstanceof Car )
Syst em. out . pr i nt l n( " " + ( ( Car ) i t em) . getPolicyNumber()) ;
t ot al += ( ( Car ) i t em) . getPolicyAmount();
}
else if ( i t eminstanceof Empl oyee)
Syst em. out . pr i nt l n( " " + ( ( Empl oyee) i t em). getPolicyNumber()) ;
t ot al += ( ( Empl oyee) i t em) . getPolicyAmount();
}
else if ( . . . )

. . .
}


Of course, an alternative to using the shared interface would be to have all insurable objects
extend (i.e., inherit from) a common abstract class, perhaps called Insurable as well. We
could then define the getPolicyNumber() and getPolicyAmount() methods as abstract
methods, forcing all subclasses to implement them. Then, we could use the same identical
code that worked with the Insurable interface.

The big disadvantage though of doing things this way, is that we are restricting the inheritance
of Insurable objects to be insurable-related. That means, we cannot take advantage of other
kinds of inherited attributes and behaviors.

Here is a diagram showing how we could get such shared behavior either with interfaces or
with abstract methods …

COMP1005/1405 – Proper Coding Style Fall 2009

- 276 -
Shared Behavior Using a Common Interface
int getPolicyNumber();
float getPolicyAmount();
Employee Customer
Object
Person
Car Company
Manager
Insurable


Shared Behavior Using Abstract Methods
Employee Customer
Object
Person
Car Company
Manager
Insurable
abstract int getPolicyNumber();
abstract float getPolicyAmount();
As another more tangible example, consider defining a Controllable interface for objects that
can be controlled via remote control. The interface may look as follows:

interface Controllable {
void t ur nLef t ( ) ;
void t ur nRi ght ( ) ;
void moveFor war d( ) ;
void moveBackwar d( ) ;
}


COMP1005/1405 – Proper Coding Style Fall 2009

- 277 -
Now, consider a Robot object which is Controllable and implements this interface:


class Robot implements Controllable {
int bat t er yLevel ;
Ar r ayLi st <Behavi or > behavi or s;

/ / These ar e t he Controllable- r el at ed met hods
void t ur nLef t ( ) { . . . }
void t ur nRi ght ( ) { . . . }
void moveFor war d( ) { . . . }
void moveBackwar d( ) { . . . }

/ / Ther e wi l l l i kel y al so be some ot her met hods
/ / whi ch ar e r obot - speci f i c
Behavior comput eDesi r edBehavi or ( ) { . . . }
int r eadSensor ( Sensor x) { . . . }
. . .
}


Now, what about a ToyCar, or even a Lawnmower ? We can implement the
Controllable interface for each of these as well. In fact, suppose that we
want to set up a handheld remote control for Controllable objects. We can
then treat all of the objects (Robots, ToyCars, Lawnmowers, etc...) as a
single type of object ... a Controllable object:


class RemoteControl {
Controllable machi ne;

Remot eCont r ol ( Controllable m) {
machi ne = m;
}

void andl eBut t onPr ess( int but t onNumber ) { h
if ( but t onNumber == 1)
m. moveFor war d( ) ;
else if ( but t onNumber == 2)
m. moveBackwar d( ) ;
else if ( but t onNumber == 3)
m. t ur nLef t ( ) ;
else
m. t ur nRi ght ( ) ;
}
. . .
}

Notice that the remote control constructor is supplied with any object that is of type
Controllable (i.e., a Robot, ToyCar, Lawnmower, etc..) Therefore, as can be seen in the
handleButtonPress() method, the code for controlling the machine from the remote is
independent of the type of object being controlled.
COMP1005/1405 – Proper Coding Style Fall 2009

- 278 -
This is a nice clean separation of code in that any new Controllable object that is developed
in the future can be controlled by this RemoteControl object. The programmer would not
need to make any changes to the RemoteContrrol class code whatsoever:


ToyPl ane aPl ane = new ToyPl ane( ) ;
ToyBoat aBoat = new ToyBoat ( ) ;

Remot eCont r ol pl aneRemot e = new Remot eCont r ol ( aPl ane) ;
Remot eCont r ol boat Remot e = new Remot eCont r ol ( aBoat ) ;




9.4 Proper Testing
As you know now, object-oriented programming requires you to define
and implement many objects and to get them to work together in
meaningful ways. Often, the object class definitions that you write
can be re-used in many applications. It is therefore a good idea to
ensure that these objects are robust and that their methods provide
proper results. To do this, you should perform proper testing of your
objects.
Unfortunately, testing is often tedious. It is therefore poorly done and
ignored by many programmers. Companies that hire programmers
do not like laziness … and even worse … they hate code with bugs
or errors in it. To avoid disappointing your boss, possibly losing your job, and just to feel good
about the quality of your work … you should properly test your code.
Normally, it is not common to test your constructors nor get/set methods, but it is certainly
important to test methods that perform computations, search, sort, etc… For problems that
require numerical parameters, it is a good idea to test different values that could potentially
cause problems. For example, if we were to fully test the deposit() method for the
BankAccount class, we would want to test depositing the following amounts:
• 0. 0 / / deposi t not hi ng
• 0. 67 / / a cent s amount
• 100. 57 / / a t ypi cal posi t i ve amount
• 100. 2234343 / / an amount wi t h many deci mal pl aces
• - 34 / / an i nval i d amount
We could create a simple test program to do this, making sure that we properly display the
results to confirm that they are correct as follows …
COMP1005/1405 – Proper Coding Style Fall 2009

- 279 -

class BankAccount Test Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
BankAccount acc;

acc = new BankAccount ( " Rust y Can" ) ;
Syst em. out . pr i nt l n( " Account at st ar t : " + acc) ;
acc. deposi t ( 0. 0f ) ;
Syst em. out . pr i nt l n( " Account af t er deposi t i ng $0. 00: " + acc) ;
acc. deposi t ( 0. 67f ) ;
Syst em. out . pr i nt l n( " Account af t er deposi t i ng $0. 67: " + acc) ;
acc. deposi t ( 100. 57f ) ;
Syst em. out . pr i nt l n( " Account af t er deposi t i ng $100. 57: " + acc) ;
acc. deposi t ( 100. 2234343f ) ;
Syst em. out . pr i nt l n( " Account af t er deposi t i ng $100. 2234343: " + acc) ;
acc. deposi t ( - 34) ;
Syst em. out . pr i nt l n( " Account af t er deposi t i ng $- 34: " + acc) ;
}
}

Here is the output:

Account at st ar t : Account #100000 wi t h $0. 0
Account af t er deposi t i ng $0. 00: Account #100000 wi t h $0. 0
Account af t er deposi t i ng $0. 67: Account #100000 wi t h $0. 67
Account af t er deposi t i ng $100. 57: Account #100000 wi t h $101. 24
Account af t er deposi t i ng $100. 2234343: Account #100000 wi t h $201. 46344
Account af t er deposi t i ng $- 34: Account #100000 wi t h $167. 46344

Notice the careful use of System.out.println() in the program to provide a kind of “log”
showing exactly what we tested and the order that things were tested in. If you were to read
the output, you should be able to follow along as the deposit transactions were made to
confirm the correct balance each time.
From the output, you may notice a few things that you would like to change. First, you may
feel that the toString() method would be better if it displayed the money amounts properly with
2 decimal places. You can then change your code accordingly and re-run the test:

Account at st ar t : Account #100000 wi t h $0. 00
Account af t er deposi t i ng $0. 00: Account #100000 wi t h $0. 00
Account af t er deposi t i ng $0. 67: Account #100000 wi t h $0. 67
Account af t er deposi t i ng $100. 57: Account #100000 wi t h $101. 24
Account af t er deposi t i ng $100. 2234343: Account #100000 wi t h $201. 46
Account af t er deposi t i ng $- 34: Account #100000 wi t h $167. 46

Second, you may decide to disallow depositing negative amounts of money. You might do
this by changing the code to generate an exception or perhaps simply perform a check and
ignore deposits of negative amounts.
COMP1005/1405 – Proper Coding Style Fall 2009

- 280 -
It really depends on the application and is tied in with the user interface. For example, at a
bank machine, it is impossible to deposit a negative amount of money because the machine
does not allow you to enter a negative sign. In such a situation, you may choose simply to
ignore the problem altogether, since it would never occur. However, a simple check may be
best, in case you port your code into a different program:

void deposi t ( float amount ) {
if ( amount > 0)
bal ance += amount ;
}

Then we would re-run the same test code to see whether or not it worked:

Account at st ar t : Account #100000 wi t h $0. 00
Account af t er deposi t i ng $0. 00: Account #100000 wi t h $0. 00
Account af t er deposi t i ng $0. 67: Account #100000 wi t h $0. 67
Account af t er deposi t i ng $100. 57: Account #100000 wi t h $101. 24
Account af t er deposi t i ng $100. 2234343: Account #100000 wi t h $201. 46
Account af t er deposi t i ng $- 34: Account #100000 wi t h $201.46

Now this was a simple test program which is often known as a “Test Unit”. In larger, more
complicated, real-word programs, in order to keep organized, it would be necessary to create
multiple simple test units that test particular aspects of the program. For example,

class BankAccount Test Uni t 1 {
public static void mai n( St r i ng ar gs[ ] ) {
BankAccount acc = new BankAccount ( " Rust y Can" ) ;

Syst em. out . pr i nt l n( " Account bef or e deposi t i ng $100. 57: " + acc) ;
acc. deposi t ( 100. 57f ) ;
Syst em. out . pr i nt l n( " Account af t er deposi t i ng $100. 57: " + acc) ;
}
}



class BankAccount Test Uni t 2 {
public static void mai n( St r i ng ar gs[ ] ) {
BankAccount acc = new BankAccount ( " Rust y Can" ) ;

Syst em. out . pr i nt l n( " Account bef or e wi t hdr awi ng $100. 57: " + acc) ;
acc. wi t hdr aw( 100. 57f ) ;
Syst em. out . pr i nt l n( " Account af t er wi t hdr awi ng $100. 57: " + acc) ;
}
}

COMP1005/1405 – Proper Coding Style Fall 2009

- 281 -
In fact, it is often the case that we would like to perform transactions and test cases on a
particular bank account. In this case, we can breakdown the separate test units as test
methods in a larger test program:

class BankAccount Test Uni t 3 {
static void deposi t 1( BankAccount acc) {
Syst em. out . pr i nt l n( " Account bef or e deposi t i ng $100. 57: " + acc) ;
acc. deposi t ( 100. 57f ) ;
Syst em. out . pr i nt l n( " Account af t er deposi t i ng $100. 57: " + acc) ;
}
static void deposi t 2( BankAccount acc) {
Syst em. out . pr i nt l n( " Account bef or e deposi t i ng $0. 01: " + acc) ;
acc. deposi t ( 0. 01f ) ;
Syst em. out . pr i nt l n( " Account af t er deposi t i ng $0. 01: " + acc) ;
}
static void wi t hdr aw1( BankAccount acc) {
Syst em. out . pr i nt l n( " Account bef or e wi t hdr awi ng $100. 57: " + acc) ;
acc. wi t hdr aw( 100. 57f ) ;
Syst em. out . pr i nt l n( " Account af t er wi t hdr awi ng $100. 57: " + acc) ;
}
static void wi t hdr aw2( BankAccount acc) {
Syst em. out . pr i nt l n( " Account bef or e wi t hdr awi ng $0. 01: " + acc) ;
acc. wi t hdr aw( 0. 01f ) ;
Syst em. out . pr i nt l n( " Account af t er wi t hdr awi ng $0. 01: " + acc) ;
}

public static void mai n( St r i ng ar gs[ ] ) {
BankAccount acc;

acc = new BankAccount ( " Rust y Can" ) ;
deposi t 1( acc) ;
deposi t 2( acc) ;
wi t hdr aw1( acc) ;
wi t hdr aw2( acc) ;

acc = new BankAccount ( " Rust y Door " , 10. 00f ) ;
deposi t 1( acc) ;
deposi t 2( acc) ;
wi t hdr aw1( acc) ;
wi t hdr aw2( acc) ;

acc = new BankAccount ( " Rust y Pai l " , - 200. 00f ) ;
deposi t 1( acc) ;
deposi t 2( acc) ;
wi t hdr aw1( acc) ;
wi t hdr aw2( acc) ;
}
}

There are actually principles and guidelines for writing test cases for large systems. However,
it is beyond the scope of this course. You will learn more about proper testing next year.

Chapter 10
Code Efficiency


What is in This Chapter ?
In this set of notes, we concentrate on aspects of programming that can make our code more
efficient. We begin by explaining how to exit our loops when we have found what we needed
from them. Then we discuss how to simplify our IF statements through a proper
understanding of booleans. We further discuss enumerated types and when they should be
used. Further, we discuss the notion of objects equality vs. identity and how understanding
the differences can allow us to efficiently share objects without problems, thereby reducing
store space and run time. This topic brings up the need to write an equals() method to
compare two objects. Finally, we discuss arrays and how they can be used to write code that
is a little more efficient than ArrayLists.



COMP1005/1405 – Code Efficiency Fall 2009

- 283 -

10.1 Exiting Loops Efficiently

Consider a piece of code that loops through an ArrayList of Strings to find a specific item.
We may need to do this, for example, when we look up someone’s name in a list to see
whether or not they are registered. Here is a simple method that does such a search:

boolean i sRegi st er ed( St r i ng sear chName) {
for ( St r i ng name: r egi st er edLi st ) {
if ( name. equal s( sear chName) )
return true;
}
return false;
}


Notice that the method returns true the moment it finds the name that it is searching for. In
the case where the name is not in the list, then the FOR loop must iterate through the entire
list, returning false afterwards. This code is efficient, because it stops searching as soon as it
finds what it is looking for. However, consider now a method that contains this search loop,
but then needs to do something afterwards:


void doSomet hi ng( St r i ng sear chName) {
boolean f ound = false;

for ( St r i ng name: r egi st er edLi st ) {
if ( name. equal s( sear chName) )
f ound = true;
}
if ( f ound)
do somet hi ng . . . / /
else
/ / do somet hi ng el se . . .
}


This looping code is no longer time-efficient. Do you know why ? Imagine that the
registeredList has 100,000 names in it. Assume that the first name is the one that matches
the searchName. The FOR loop will still continue checking the remaining 99,999 names,
even though it has already found what it is looking for!

There is an efficient way to exit a loop when we know that we no longer need to keep
checking. The break keyword in J AVA will “break out of” (i.e., exit) the loop that it is inside of.
Hence, we can insert into the above code a break statement as follows …

COMP1005/1405 – Code Efficiency Fall 2009

- 284 -

void doSomet hi ng( St r i ng sear chName) {
boolean f ound = false;

for ( St r i ng name: r egi st er edLi st ) {
if ( name. equal s( sear chName) ) {
f ound = true;
break;
}
}
if ( f ound)
J umps out of loop.
do somet hi ng . . . / /
else
/ / do somet hi ng el se . . .
}


Another situation that we may want to exit from a loop is when a certain condition occurs. For
example, we may want to loop until the user enters a valid number:

int num= 0;
bool ean val i d = false;

while( !val i d) {
Syst em. out . pr i nt l n( " Ent er t he per cent age: " ) ;
num= new Scanner ( Syst em. i n) . next I nt ( ) ;
if ( ( num>= 0) && ( num<= 100) )
val i d = true;
else
Syst em. out . pr i nt l n( " I nval i d Ent r y" ) ;
}


In the code above, we use the valid boolean variable to indicate whether or not the number
entered by the user was in the valid range of 0 to 100. The while loop simply repeats until this
boolean (known as a “flag”) changes its state from false to true.
We can actually eliminate the need for the boolean variable by making use of the break
statement as follows:

int num= 0;

while( true) {
Syst em. out . pr i nt l n( " Ent er t he per cent age: " ) ;
num= new Scanner ( Syst em. i n) . next I nt ( ) ;
if ( ( num>= 0) && ( num<= 100) )
break;
Syst em. out . pr i nt l n( " I nval i d Ent r y" ) ;
}

COMP1005/1405 – Code Efficiency Fall 2009

- 285 -
Notice that the code above uses a potentially infinite while loop. However, the loop will exit
when the break statement is reached, provided that the user enters a valid number. This is
equally efficient in terms of the number of loops made, but certainly the use of the break
statement reduces and simplifies the code.

In addition to the break statement (which exits a loop), J AVA offers a continue statement
which does not exit the loop, but instead goes back to the top of the loop to do another
iteration. This is useful if we need to do something like ignore certain entries in a list. For
example, assume that we wanted to go through a list of people and process information for all
people in the list that are 21 years of age or older:


for ( Per son p: peopl e) {
if ( p. age < 21)
continue;
else
/ / do some pr ocessi ng. . .
}


In the above code, the continue statement goes back and gets the next person, evaluating the
loop again. Of course, we could have done this without the continue as follows:


for ( Per son p: peopl e) {
if ( p. age >= 21) {
/ / do some pr ocessi ng. . .
}
}


However, sometimes the code is more complicated. For example, assume that we want to
ignore all people under 21 or whose health risk factor is below 50%:


for ( Per son p: peopl e) {
if ( p. age < 21)
continue;
else {
Syst em. out . pr i nt ( " Adul t f ound, comput i ng heal t h r i sk . . . " ) ;
Goes back to top
float r i sk = p. cal cul at eHeal t hRi sk( ) ;
if ( r i sk < 50) {
Syst em. out . pr i nt l n( " l ow r i sk, i gnor ed. " ) ;
continue;
}
else {
Syst em. out . pr i nt l n( " hi gh r i sk, f ut her pr ocessi ng . . . " ) ;
/ / do some mor e pr ocessi ng. . .
}
}
}

COMP1005/1405 – Code Efficiency Fall 2009

- 286 -
Notice above that the continue statement may be used as many times as necessary to
abandon this particular “round” through the loop and move onto the next person. This saves
processing time, which may be significant if the list of people is large.

We can also add labels in J AVA to indicate which loop we want to get out of in the case of
multiple/nested loops. Consider searching a table with rows and columns of potentially large
amounts of data at each (row/column) cell. We may want to abandon the processing of a cell
in the table if we find that further processing of the cell, row, or column is not needed:


r ows:
for ( int r ow=0; r ow<1000; r ow++) {

col ns: um
for ( int col =0; col <1000; col ++) {
. . .
if ( someCondi t i onOccur sThat MakesThi sCol umnUsel ess( ) )
continue col umns;

if ( someCondi t i onOccur sThat MakesThi sRowUsel ess( ) )
continue r ows; / / same as break columns;

if ( someCondi t i onOccur sThat Requi r esSt oppi ngAl t oget her ( ) )
break r ows;
. . .
}
}



10.2 Proper Use of Booleans

Another aspect of proper coding style pertains to simplifying your code by
making proper use of booleans. Although the if statement is quite easy to use,
it is often the case that some students do not fully understand its logic when
booleans are being used. As a result, many students end up writing overly
complex and inefficient code. Also, the if statement is often used when it is not
even required !!

To illustrate this, consider the following examples of "BAD" coding style. Try to determine why
the code is inefficient and how to improve it. If it is your desire to be a good programmer, pay
careful attention to these examples.

COMP1005/1405 – Code Efficiency Fall 2009

- 287 -

Example 1:

boolean mal e = . . . ;

if ( mal e == true) {
Syst em. out . pr i nt l n( " mal e" ) ;
else
Syst em. out . pr i nt l n( " f emal e" ) ;


Here, the boolean value of male is already true or false, we can make use of this fact:

boolean mal e = . . . ;

if ( mal e) {
Syst em. out . pr i nt l n( " mal e" ) ;
else
Syst em. out . pr i nt l n( " f emal e" ) ;


Example 2:

boolean adul t = . . . ;

if ( adul t == false)
di scount = 3. 00;


Here is a similar situation as above, but with a negated boolean. Below is better code.

boolean adul t = . . . ;

if ( ! adul t ) {
di scount = 3. 00;


Example 3:
boolean t i r ed = . . . ;

if ( t i r ed)
r esul t = true;
else
r esul t = false;


Above, we are actually returning the identical boolean as tired. No if statement is
needed:
COMP1005/1405 – Code Efficiency Fall 2009

- 288 -

boolean t i r ed = . . . ;

r esul t = t i r ed;


Example 4:

boolean di scount ;

if ( ( age < 6) | | ( age > 65) )
di scount = true;
else
di scount = false;


The discount is solely determined by the age. No if statement is needed:

boolean di scount ;

di scount = ( age < 6) | | ( age > 65) ;


Example5:

boolean f ul l Pr i ce;

if ( ( age < 6) | | ( age > 65) )
f ul l Pr i ce = false;
else
f ul l Pr i ce = true;


J ust like above, we do not need the if statement:

boolean f ul l Pr i ce;

f ul l Pr i ce = ! ( ( age < 6) | | ( age > 65) ) ;


or …

boolean f ul l Pr i ce;

f ul l Pr i ce = ( age >= 6) && ( age <= 65) ;




COMP1005/1405 – Code Efficiency Fall 2009

- 289 -

10.3 Enumerated Types

Consider writing a program that evaluated certain code depending on the day of the week:


St r i ng dayOf Week = . . . ;

if ( dayOf Week. equal s( " SATURDAY" ) | | dayOf Week. equal s( " SUNDAY" ) )
/ / do somet hi ng . . .
else
/ / do somet hi ng el se . . .


Each time we make a String comparison using the equals() method, this takes time because
J AVA needs to compare the characters from the strings. However, comparing integers is
fast. So we could define some integer constants for the days of the week, store our day of
the week as an integer and then and simply compare the constants using == as follows:


static final int MONDAY = 1;
static final int TUESDAY = 2;
static final int WEDNESDAY = 3;
static final int THURSDAY = 4;
static final int FRI DAY = 5;
static final int SATURDAY = 6;
static final int SUNDAY = 7;

. . .
. . .
int dayOf Week = . . . ;

if ( ( dayOf Week == SATURDAY) | | ( dayOf Week == SUNDAY) )
/ / do somet hi ng . . .
else
/ / do somet hi ng el se . . .


This comparison is very fast since it merely compares two integers as opposed to a sequence
of characters. We should use this strategy whenever we have a set of fixed constant values
such as the days of the week, planets in the solar system, choices on a menu, command-line
flags, etc..

Rather than clutter up our code with these constants, we can define them in their own publicly
accessible class as follows …

COMP1005/1405 – Code Efficiency Fall 2009

- 290 -

public class Day {
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
public static final int WEDNESDAY = 3;
public static final int THURSDAY = 4;
public static final int FRI DAY = 5;
public static final int SATURDAY = 6;
public static final int SUNDAY = 7;
}


Then we can make use of this class in our code:

. . .
int dayOf Week = . . . ;

if ( ( dayOf Week == Day.SATURDAY) | | ( dayOf Week == Day.SUNDAY) )
/ / do somet hi ng . . .
else
/ / do somet hi ng el se . . .


J AVA provides a useful keyword called enum that can be used for this exact situation. The
enum keyword can be used in place of the class keyword to define a set of constant symbols
such as the days of the week. It makes the code much simpler:


public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRI DAY, SATURDAY, SUNDAY
}


I’m sure you will agree that this is much shorter than the class definition. In fact, we can
define as many of these enumerated types as we would like … each in their own file:

public enum Gender {
MALE, FEMALE
}

public enum Size {
TI NY, SMALL, MEDI UM, LARGE, HUGE
}

public enum Di r ect i on {
NORTH, NORTH_EAST, EAST, SOUTH_EAST, SOUTH,
SOUTH_WEST, WEST, NORTH_WEST
}

COMP1005/1405 – Code Efficiency Fall 2009

- 291 -
These would be saved, compiled and then used in our programs just as we would use any
other class or interface definition. Once the type is defined, we can even define variables with
these types, assigning them one of the values in the list specified in the enum declaration:


Gender gender ;
Size or der Si ze;
Weekday payday;
Direction homePosi t i on;

gender = Gender. MALE;
or der Si ze = Size. MEDI UM;
payday = Weekday. FRI DAY;
homePosi t i on = Direction. NORTH_WEST;


Notice that the variable type matches the enum type. Also notice that when assigning a value
to the variable, we must again specify the enum type followed by the dot . operator and then
by one of the fixed constant values.

If we wanted to, we could have defined the enum types right inside of our class (but then they
may not be declared public and would only be useable within that class’s code). It would
however, allow us to reduce the number of classes that we write. To use the enum types in
your own class definition, you should place them directly under the class definition (or above
it). You are not allowed to place the declarations in the executable (i.e., running) part of your
program (i.e., in the main method, nor in other methods). Note that when you display an enum
type value (as in the code above), the symbol value is printed out just as it appears in the type
declaration.


public class EnumTest Pr ogr am{

enum Gender {MALE, FEMALE};
enum Size {TI NY, SMALL, MEDI UM, LARGE, HUGE};
enum Weekday {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRI DAY,
SATURDAY, SUNDAY};
enum Direction {NORTH, NORTH_EAST, EAST, SOUTH_EAST,
SOUTH, SOUTH_WEST, WEST, NORTH_WEST};

public static void mai n( St r i ng ar gs[ ] ) {
Gender gender = Gender. MALE;
Size or der Si ze = Size. MEDI UM;
Weekday payday = Weekday. FRI DAY;
Direction homePosi t i on = Direction. NORTH_WEST;

Syst em. out . pr i nt l n( gender ) ; / / di spl ays MALE
Syst em. out . pr i nt l n( or der Si ze) ; / / di spl ays MEDIUM
Syst em. out . pr i nt l n( payday) ; / / di spl ays FRIDAY
Syst em. out . pr i nt l n( homePosi t i on) ; / / di spl ays NORTH_WEST
}
}

COMP1005/1405 – Code Efficiency Fall 2009

- 292 -
10.4 Equality Vs. Identity

From childhood, all of us have been taught to share with one another. Not only does this
instill proper moral values in us, but it also saves on costs (e.g., one toy can be shared by
many children, instead of buying them each their own). Sharing is even a good idea when
programming. Since created objects use up memory space, it is often desirable to share
objects (or information) whenever possible.
Since objects can be shared, it is important to know whether or not the same object is being
used in two or more places. This brings up the notion of equality vs. identity. In J AVA, two
objects are considered to be equal if they are of the "same type" and have the "same attribute
values". That is, if the values of the instance variables of the two objects are the same, then
the objects are equal. Two objects are identical if and only if they are the exact same
object.
jimsCar dadsCar

jimsCar

dadsCar


For example, here is how equality and identity differ in the situations where J im and his dad
share a family car, buy two cars of the same kind or buy two different kinds of cars:




jimsCar is

identical & equal
to dadsCar




jimsCar is
equal but not identical
to dadsCar


jimsCar dadsCar





jimsCar is
not equal & not identical

to dadsCar
COMP1005/1405 – Code Efficiency Fall 2009

- 293 -
So the word identical means the "exact same object" in J AVA (i.e., the two objects point to the
same memory location). Note that this is different from the English
language definition of identity.

Equality actually is very subjective. For example, if you lose a 5
year old child's teddy bear and then buy her/him a new one of the
same type, the child will likely agree that they are not equal. In
J AVA, we will spend a lot of time defining our own objects, so we
are the ones that will decide what it actually means for two of our
objects to be equal.
Consider this code which creates two people:
Per son p1, p2, p3;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p3 = new Per son( " Hol l y" , " Day" , 67, ' F' , true) ;


If we want to ask whether or not these two people are equal, intuitively we would use the ==
operator as follows:


if ( p1 == p2)
Syst em. out . pr i nt l n( " p1 and p2 ar e t he same t wo peopl e" ) ;
else
Syst em. out . pr i nt l n( " p1 and p2 ar e di f f er ent peopl e" ) ;

if ( p1 == p3)
Syst em. out . pr i nt l n( " p1 and p32 ar e t he same t wo peopl e" ) ;
else
Syst em. out . pr i nt l n( " p1 and p3 ar e di f f er ent peopl e" ) ;


But if we evaluate the following code, we would get the following result:

p1 and p2 ar e di f f er ent peopl e
p1 and p3 ar e di f f er ent peopl e


Why is p1 not equal to p2 ? Clearly both people are equal since they have the same
firstName, lastName, age, gender and retirement status. However, in J AVA, when the ==
operator is used with objects, it means "identity" ... not "equality". That is, the == operator
determines whether or not the two variables are pointing to the exact same object. Here is a
diagram showing how the variables are related to the objects …

COMP1005/1405 – Code Efficiency Fall 2009

- 294 -


Person object
“ Hank”
p3
“ Urchif”
“ Hank”
false retired
'M' gender
19 age

lastName
firstName

p1

“ Day”
true retired
'F' gender
67 age

lastName
firstName


Person object
“ Holly”
“ Urchif”
p2
false retired
'M' gender
19 age
firstName
lastName

Person object
Notice that p1 and p2 are pointing to different objects, although the objects have the same
attribute values. As it turns out, whenever we make a new instance of an object, we actually
get a unique object every time, thus that new object can only be identical to itself.

So what kind of coding situation would allow (p1 == p2) to return true ? Well, we would have
to have code that allows the two variables to refer to the same object as follows:


“ Urchif”
false retired
'M' gender
19 age

lastName
firstName
p1

p2

Person object
“ Hank”

COMP1005/1405 – Code Efficiency Fall 2009

- 295 -
We can do this with the following code:

Per son p1, p2;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = p1;


Now p1 is identical to p2 and so (p1 == p2) will return true.

The above code is just an example to illustrate how to make two variables point to the same
object. A more realistic example may be to copy over the contents of an ArrayList. For
example, consider the following method which creates and returns a copy of an ArrayList
containing Person objects:

public Ar r ayLi st <Per son> makeCopy( Ar r ayLi st <Per son> peopl e) {
Ar r ayLi st <Per son> newLi st = new Ar r ayLi st <Per son>( ) ;

for( Per son p: peopl e)
newLi st . add( p) ;

return newLi st ;
}


Consider testing this method as follows:

Ar r ayLi st <Per son> p, copy;

p = new Ar r ayLi st <Per son>( ) ;
p. add( new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ) ;
p. add( new Per son( " Hol l y" , " Day" , 67, ' F' , true) ) ;
p. add( new Per son( " Bobby" , " Socks" , 8, ' M' , false) ) ;

copy = makeCopy( p) ;


Here is what we accomplish with this code …
COMP1005/1405 – Code Efficiency Fall 2009

- 296 -


Notice that when we add the Person objects to the copy, they are actually shared between the
two ArrayLists. This is known as a shallow copy. Normally this is not a problem when writing
code as long as we know that the items are shared. Consider what happens here:

2 1 0
copy


firstName
“Hank”
…etc …

firstName
“Holly”
…etc …

firstName
“Bobby”
…etc …

0 1 2
p

Per son l ast ;
Ar r ayLi st <Per son> p, copy;

p = new Ar r ayLi st <Per son>( ) ;
p. add( new Per son( " Hank" , " Ur chi f " , 19, ' M' false) ) ; ,
p. add( new Per son( " Hol l y" , " Day" , 67, ' F' , true) ) ;
p. add( l ast = new Per son( " Bobby" , " Socks" , 8, ' M' , false) ) ;

copy = makeCopy( p) ;

copy. set ( 1, new Per son( " J en" , " Tul l " , 42, ' F' , false) ) ;
copy. get ( 0) . set Fi r st Name( " St eve" ) ;
copy. r emove( l ast ) ;


Notice the use of the set() method for ArrayLists. This method will replace the object in the
list at the given index (specified by the first parameter .. .in this case 1) with the new object
(given as the second parameter … in this case Jen). When replacing Holly with Jen in the
copy list, the original list remains unchanged. However, when modifying Hank from the copy,
then the original list is affected since the object that they were sharing has now been altered.
Finally, when we remove Bobby from the copy, the original remains unchanged.
COMP1005/1405 – Code Efficiency Fall 2009

- 297 -
Here is the diagram showing what happened:

So you can see, by replacing, adding or removing items to a copied list, the original list
remains intact. However, when we access a shared object from the copy and go into it to
make changes to its attributes, then the original list will be affected.
To make a more separated copy, we could make what is known as a deep copy of the
ArrayList so that the Person objects are not shared between them (i.e., each list had their
own copies of the Person objects), then we would have to create new Person objects with the
same attributes as follows:

public Ar r ayLi st <Per son> makeCopy( Ar r ayLi st <Per son> peopl e) {
Ar r ayLi st <Per son> newLi st = new Ar r ayLi st <Per son>( ) ;

for( Per son p: peopl e) {
Per son x = new Per son( ) ;
x.firstName = p.firstName;
x.lastName = p.lastName;
x.age = p.age;
x.gender = p.gender;
x.retired = p.retired;
newLi st . add( x) ;

return newLi st ;
}


copy

0 1

firstName
“Steve”
…etc …

firstName
“Holly”
…etc …

firstName
“Bobby”
…etc …

0 1 2


firstName
“J en”
…etc …
p
COMP1005/1405 – Code Efficiency Fall 2009

- 298 -
Here is what the ArrayLists would look like if we did copy = makeCopy(p);

Notice that the Strings are still shared! To make a truly separated copy of the list, we would
need to copy the strings as well like this:
x. f i r st Name = new String(p. f i r st Name);
x. l ast Name = new String(p. l ast Name);
In fact, in order to make a truly deep copy, we would need to ensure that all objects of all
attributes are themselves copied.
You should now understand what it means for two objects to be identical and you should
understand how to make two or more variables point to identical objects as well as how to
make sure that they point to unique objects by copying them.
Recall, that we use == to determine whether or not two objects are identical:
Per son p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
Per son p2 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;

if ( p1 == p2)
Syst em. out . pr i nt l n( " p1 and p2 ar e t he same t wo peopl e" ) ;
else
Syst em. out . pr i nt l n( " p1 and p2 ar e di f f er ent peopl e" ) ;


2 0 1

copy


firstName
“Hank”
…etc …

firstName
“Holly”
…etc …

firstName
“Bobby”
…etc …


0 1 2

firstName
…etc …
firstName
…etc …
firstName
…etc …
p
COMP1005/1405 – Code Efficiency Fall 2009

- 299 -
If we want to ask whether or not these two people are equal, instead of identical, we replace
the == operator with a call to equals() as follows:


Per son p1, p2;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;

if ( p1.equals(p2))
Syst em. out . pr i nt l n( " p1 and p2 ar e equal " ) ;
else
Syst em. out . pr i nt l n( " p1 and p2 ar e not equal " ) ;


As it turns out, all objects understand the equals(Object x) method in J AVA. That is, there is
an equals() method defined in the Object class that all objects will inherit. However, in the
above code, the call to equals() still returns false, even though all of the attributes are the
same for both Person objects!! Why ? Well, here is what the code does inside the equals()
method that all objects inherit:

public boolean equal s( Obj ect x) {
return this == x;
}


So, when we call equals(),J AVA simply returns true if the objects are identical and false
otherwise. So, the default equals() method checks whether two objects are identical, not just
equal. But this does not help us at all since it is no different from simply using the == operator.
As a standard programming convention, when we define our own objects, we should also
define our own equals() method for that object which overrides the default equals() method
from the Object class. Many existing J AVA classes already have a different implementation
of the equals() method that properly checks for equality.
Let us look at how to write an equals() method for our Person object. First of all, the equals()
method MUST be written in the Person class, it must be public, it must return a boolean and
it must take a single Object parameter as follows:

public class Per son {
. . .
public boolean equal s( Obj ect x) {
. . .
}
. . .
}


COMP1005/1405 – Code Efficiency Fall 2009

- 300 -
If we make a mistake in the method’s signature (e.g., we make a mistake in the return type,
name or parameter), then the method will not be called by J AVA and it will use the old (i.e.,
default) one instead of ours.

So, now what do we write in the method ? Well, it must return true if the objects are equal
and false otherwise. Notice that the parameter is of type Object. That means, we can pass
in ANY type of object whatsoever. So, we can actually ask if Person objects are equal to
other Person objects or Car objects or Apple objects or String objects etc...

Per son p1;
Appl e a;
Car c;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
a = new Appl e( " Red" ) ;
c = new Car ( " Red" , 1991, " Por sche" , " 959" ) ;

if ( p1. equals( a) ) {. . . } // false
if ( p1. equals( c) ) {. . . } // false
if ( p1. equals( " Hank" ) ) {. . . } // false


Of course, it should always return false if the parameter object is not a Person object. We
would need to first check to make sure that the object passed in is a Person. We can do this
by using the instanceof keyword in J AVA:
public boolean equal s( Obj ect x) {
if ( ! ( x instanceof Per son) )
return false;
. . .
}

Notice that the 'o' in "of" is not capitalized. The instanceof keyword actually checks whether
or not the object on the left (i.e., the x parameter in this case) is the same type as a particular
class which is specified on the right (i.e., Person in this case). As it turns out, objects that
belong to a subclass of Person will also work here, so you should be aware of that if you feel
that special subclasses of person should not be considered equal for some reason.

The next step is to type-cast the parameter object into a Person object. This will tell the
J AVA compiler that we would like to treat the incoming object as a Person object. That is, we
want to call methods for it from the Person class:

public boolean equal s( Obj ect x) {
if ( ! ( x instanceof Per son) )
return false;

Per son p = (Person)x;
. . .
}

COMP1005/1405 – Code Efficiency Fall 2009

- 301 -
At this point, we have ensured that the incoming object is indeed a Person object and we now
have a variable (i.e., p) that represents this Person. We now just need to decide what we think
it means for two Person objects to be equal. To do this, we need to look at the attributes of
our Person object. Our Person objects all have an firstName, lastName, age, gender and
retired status. The equality of two people depends on the application.
For example, if we are searching for a particular person in a list, it may be sufficient just to
confirm that the first and last names are equal to the one we are looking for. However, in
some cases, two people may have the same name but different ages and retired status (e.g.,
J ohn Doe, J ane Doe). In that case we should check for more information. As a rule of
thumb, it may be best to simply check all attributes for equality.
So in our method, we check each attribute and only return true if all attributes are equal:

public boolean equal s( Obj ect x) {
if ( ! ( x instanceof Per son) )
return false;

Per son p = ( Per son) x;
if ( ( this. f i r st Name. equal s( p. f i r st Name) ) &&
( this. l ast Name. equal s( p. l ast Name) ) &&
( this. age == p. age) &&
( this. gender == p. gender ) &&
( this. r et i r ed == p. r et i r ed) )
return true;
else
return false;
}


Notice that we check each attribute from the receiver Person (i.e., this, which represents the
Person for which we call the equals() method) to make sure that it is equal to the attribute
from the parameter Person (i.e., p). We use && to ensure that ALL attributes are equal
before we return true. Notice that the age, gender and retired attributes are all checked
using the == operator. This is because they are primitives, not objects. The firstName and
lastName attributes are compared using the equals() method because they are String objects
and == would only check identity … which is not what we want. Of course, we want to reduce
and simplify the code above by noticing the poor use of booleans in the if statement:
public boolean equal s( Obj ect x) {
if ( ! ( x instanceof Per son) )
return false;

Per son p = ( Per son) x;
return ( ( this. f i r st Name. equal s( p. f i r st Name) ) &&
( this. l ast Name. equal s( p. l ast Name) ) &&
( this. age == p. age) &&
( this. gender == p. gender ) &&
( this. r et i r ed == p. r et i r ed) ) ;
}

COMP1005/1405 – Code Efficiency Fall 2009

- 302 -
Assuming that the above method is implemented within the Person class, then the following
code will now return "p1 and p2 ar e equal " :

Per son p1, p2;

p1 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;
p2 = new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ;

if ( p1.equals(p2))
Syst em. out . pr i nt l n( " p1 and p2 ar e equal " ) ;
else
Syst em. out . pr i nt l n( " p1 and p2 ar e not equal " ) ;


Now you should understand how to write an equals() method for your own objects.
The equals() method is often called automatically by J AVA when you are searching for
something in a collection. For example, consider searching for a particular Person in an
ArrayList of Person objects. Assume that we wrote a method that takes an
ArrayList<Person> as well as a particular Person to search for within the list. The method
may look as follows:


boolean cont ai ns( Ar r ayLi st <Per son> peopl e, Per son per sonToLookFor ) {
for ( Per son p: peopl e) {
if ( p. equal s( per sonToLookFor ) )
return true;
}
return false;
}


Notice that the code makes use of the equals() method to find a Person object in the list that
matches the one we are looking for. As it turns out, the standard contains() method for
ArrayLists does exactly this. Hence, for example, if we wanted to write a method that added
a particular person p to the people list, as long as p was not already there, we could do it as
follows:


void add( Ar r ayLi st <Per son> peopl e, Per son p) {
if ( ! peopl e. contains( p) )
peopl e. add( p) ;
}


If we do not write an equals() method in the Person class (i.e., we inherit the default one from
Object), then the above method will allow multiple people to be added to the list with the same
attribute values.

COMP1005/1405 – Code Efficiency Fall 2009

- 303 -
However, if we write our own equals() method, then no two people in the people list will have
the same attributes … they will all be unique. Hence, it is important for you to understand that
other methods (such as contains()) will make use of the equals() method, whether or not you
created one. The results will be different.


10.5 Arrays

We have already discussed a collection class called an ArrayList which is capable of storing
multiple objects. The objects are all stored in order and we can access or modify the contents
of the list through various methods by referring to the objects inside it or by their integer index
location.




"Hello"
25

0 1 2 3
an ArrayList
myList
In J AVA, there is a more restrictive kind of list called an Array. In general, arrays are more
efficient in terms of run-time usage, but they are less flexible in the way that they are used.
We will discuss here the similarities and differences between arrays and ArrayLists.

Like an ArrayList, an array is a collection of elements (i.e., data types or objects) which are
stored in a particular order and accessible by their index. However, unlike ArrayLists, the
elements of an array must ALL be of the same type (e.g., all Strings, ints, Person objects,
Car objects, etc…) That is, all the elements must have a common type. In real life, an array
could represent a grouped collection of similar "things" such as:
• a list of names
• a list of grades
• a set of hockey cards
• a shelf of books
• etc..

COMP1005/1405 – Code Efficiency Fall 2009

- 304 -
In the picture to the left, there are three
array variables called names, cars and
friends. Array variables are declared
just like any other variable, except that
we need to include square brackets [ ]
alongside the type as follows:
names

an Array
"Bob"
0 1 2
"Jen" "Max"

St r i ng[ ] names;
Car [ ] car s;
Per son[ ] f r i ends;
cars



an Array
The type can either be an object (as the
three above variables show) or a data
type. So we can make arrays of ints,
double, booleans etc..:


0 1 2 3
int[ ] si ckDays;
double[ ] wei ght s;
boolean[ ] r et i r ed;
friends
Alternatively, we could put the brackets
after the variable's name:


an Array


0 1 2 3
int si ckDays[ ] ;
double wei ght s[ ] ;
boolean r et i r ed[ ] ;
St r i ng names[ ] ;
Car car s[ ] ;
Per son f r i ends[ ] ;
4


All of the above 6 variables here are object variables in that they all point to an Array object.
Looking at the code above, it may seem like sickDays, weights and balances each store "a
data type", but actually they each store "an array of data types". So the following two lines of
code have very different meanings:

int si ckDays; / / Var i abl e hol ds a si ngl e int pr i mi t i ve
int[] si ckDays; / / Var i abl e hol ds an array obj ect t hat contains ints

Now these array variable declarations simply reserve space to store array objects, but it
actually does not create the array object. So, the following code would print out null because
the variable is not yet initialized:

int[] si ckDays;
Syst em. out . pr i nt l n( si ckDays) ;

Notice above that we did not use the square brackets [] when you are using the array variable
in your code ... you only use the brackets when we define the variable.
COMP1005/1405 – Code Efficiency Fall 2009

- 305 -
To use this variable (which is just like any other variable), we must give it a value. What kind
of value should it have ? An array of course. But it may surprise you to know that we do not
call a constructor to create Array objects. Instead, we use a special (similar) syntax as
follows:
new Per son[ 10]

This will create an array that can hold up to 10 Person objects. Unlike ArrayLists, arrays are
fixed size. That means we cannot enlarge them later. So if you are unsure how many items
we want to put into the array, you should chose an over-estimate (i.e., a maximum bound) for
its size. Here are some examples of how to give a value to our 6 array variables by creating
some fixed-size arrays.

int[] si ckDays;
double[] wei ght s;
St r i ng[ ] names;
Car [ ] car s;
Per son[ ] f r i ends;

si ckDays = new int[ 30] ; / / cr eat es ar r ay t hat can hol d 30 ints
wei ght s = new double[ 100] ; / / cr eat es ar r ay t hat can hol d 100 doubles
names = new St r i ng[ 3] ; / / cr eat es ar r ay t hat can hol d 3 String obj ect s
r ent al s = new Car [ 500] ; / / cr eat es ar r ay t hat can hol d 500 Car obj ect s
f r i ends = new Per son[ 50] ; / / cr eat es ar r ay t hat can hol d 50 Person obj ect s

Once we create arrays using the code above, the arrays themselves simply reserve enough
space to hold the number of objects (or data types) that you specified. However, it does not
create any of those objects!! Newly created arrays are filled with:
• 0 for numbered arrays
• character 0 (i.e., the null character) for char arrays
• false for boolean arrays
• null for Object-type arrays
So, when you create an array to
hold objects, only the array
itself is created. The array is
NOT initialized with new objects
in each location.
sickDays
an Array
0 0 0
0 1 2
Hence, the following code
produces the result shown in
the picture
cars
an Array

null null null null

0 1 2 3
int[ ] si ckDays;
friends
St r i ng[ ] names;
an Array
Per son[ ] f r i ends;

null null null null null
si ckDays = new int[ 3] ;
names = new t r i ng[ 4] ; S
f r i ends = new Per son[ 5] ;
0 1 2 3 4
COMP1005/1405 – Code Efficiency Fall 2009

- 306 -
Notice that the arrays which hold object types are simply filled with null values … there are no
Car objects nor Person objects created in the above code.

At any time, if we would like to ask an array how big it is (i.e., its size or capacity), we can
access one of its special attributes called length as follows:

Syst em. out . pr i nt l n( si ckDa length) ; / / di spl ays 3 ys.
Syst em. out . pr i nt l n( names. length) ; / / di spl ays 4
Syst em. out . pr i nt l n( f r i ends. length) ; / / di spl ays 5

Note that the length attribute is NOT a method (i.e., notice that there are no brackets). Also,
the length attribute ALWAYS returns the exact same size value for the array regardless of the
number of elements that you put into the array.

How do we put things into the array ? Values are actually assigned to an array using the =
operator just as with other variables. However, since the array holds many objects together,
we must also specify the location in the array that we want to access or modify. As with
ArrayLists, the index numbering starts with 0 as the being first item and (length -1) is the
location of the last item.

Here is an example of how to fill in our arrays:
sickDays

int[ ] si ckDays;
St r i ng[ ] names;
34 65 12
Per son[ ] f r i ends;
0 1 2
si ckDays = new int[ 3] ; cars
names = new St r i ng[ 4] ;


f r i ends = new Per son[ 5] ;
null null
si ckDays[ 0] = 34;
si ckDays[ 1] = 12;
0 1 2 3
si ckDays[ 2] = 65;
car s[ 1] = new Car ( " Red" ) ;
car s[ 3] = new r ( " Bl ue" ) ; Ca
f r i ends[ 0] = new Per son( . . . ) ;
friends


f r i ends[ 1] = new Per son( . . . ) ;
0 1 2 3 4
null null f r i ends[ 2] = new Per son( . . . ) ;

The picture to the right shows the result from
this code. Notice that we can insert an object
at any location in the array, provided that the
index is valid.

The following two lines of code would produce
an ArrayIndexOutOfBoundsException:

si ckDays[ 3] = 87; / / Er r or : i ndex 3 i s out of r ange
car s[ 10] = new Car ( " Yel l ow" ) ; / / Er r or : i ndex 10 i s out of r ange

COMP1005/1405 – Code Efficiency Fall 2009

- 307 -
A very common mistake made when learning to use arrays is to declare the array variable, but
to forget to create the array and then try to use it. For example, look at this code:

Per son[ ] f r i ends = new Per son[ 100] ;
Syst em. out . pr i nt l n( f r i ends[ 0] . f i r st Name) ; / / Er r or !
Syst em. out . pr i nt l n( f r i ends[ 0] . age) ; / / Er r or !
Syst em. out . pr i nt l n( f r i ends[ 0] . gender ) ; / / Er r or !

Although the above code does create an array that can hold Person objects, it actually never
creates any Person objects. Hence, the array is filled with 100 values of null. The code will
produce a NullPointerException because friends[0] is null here and we are trying to access
the attributes of a null object. We are really doing this: null.firstName … which makes no
sense.
How do we remove something from an array ? Well, as with a program
recorded on a videotape, we cannot actually remove the item but we can
overwrite it (i.e., replace it) with a new value.
So, to delete information from an array, you will replace its value with 0 or null. The array will
stay the same size, but the data will be deleted. For example, to remove the sick day at
position 2 in sickDays from our previous example, we would replace it with 0, -1, or anything
else that tells us that the data is invalid. To remove the blue car from the cars array, we
would replace it with null.
si ckDays[ 2] = - 1;
car s[ 3] = null;

Here is the result:



What happened to the blue car ? In the array, the Car object has been replaced by null, and
so the array is no longer pointing to the Car object. Therefore, the Car object is garbage
collected (unless some other object is still pointing to it).
sickDays
an Array


1 2
3
null null
an Array
cars
1 0
34
2
-1 12

null
0

COMP1005/1405 – Code Efficiency Fall 2009

- 308 -
Lets get back to adding items to an array. Since arrays are of fixed size and cannot grow,
what do we do if we run out of room ? For example, the code below will keep asking for
integers from the user, adding them to the array as they come in until -1 is entered:


int aNumber = 0;
int cur r ent I ndex = 0;
int[ ] number s = new int[ 5] ;
Scanner keyboar d = new Scanner ( Syst em. i n) ;

/ / Get cont ent s f or t he ar r ay, one number at a t i me unt i l - 1 ent er ed
while( aNumber ! = - 1) {
aNumber = keyboar d. next I nt ( ) ;
if ( aNumber ! = - 1)
number s[ cur r ent I ndex++] = aNumber ;
}

/ / ow N pr i nt t he ar r ay out
for( int i =0; i <cur r ent I ndex; i ++)
Syst em. out . pr i nt l n( number s[ i ] +" , " ) ;



Notice that each number coming in is added to the array according to the currentIndex
position, which is incremented each time a valid number arrives. When the loop has
completed, currentIndex represents the number of integers that were entered and added to
the array. Can you foresee any problems with the above code ?

When attempting to add the 6
th
number to the array, an ArrayIndexOutOfBoundsException
will occur, because the array has only been declared to hold 5 integers. The array has filled
up and we must either stop adding to it or make more room for the new numbers.

However, we cannot make more room with the existing array. Rather, a new bigger array must
be created and all elements must be copied into the new array. But how much bigger ? Its
up to us.

Here is how we could change the code to increase the array size by 5 each time it gets full …

COMP1005/1405 – Code Efficiency Fall 2009

- 309 -

int aNumber = 0;
int cur r ent I ndex = 0;
int[ ] number s = new int[ 5] ;
Scanner keyboar d = new Scanner ( Syst em. i n) ;

/ / Get cont ent s f or t he ar r ay, one number at a t i me unt i l - 1 ent er ed
while( aNumber ! = - 1) {
aNumber = keyboar d. next I nt ( ) ;
if ( aNumber ! = - 1) {
/ / I f at max capaci t y, make new ar r ay and copy cont ent s over
if ( cur r ent I ndex == number s. l engt h) {
int[ ] t emp = new int[ number s. l engt h + 5] ;
for( int i =0; i <number s. l engt h; i ++)
t emp[ i ] = number s[ i ] ;
number s = t emp;
}
number s[ cur r ent I ndex++] = aNumber ;
}
}
/ / ow N pr i nt t he ar r ay out
for( int i =0; i <cur r ent I ndex; i ++)
Syst em. out . pr i nt l n( number s[ i ] +" , " ) ;



Of course, we can increase by any amount each time, perhaps even doubling the size. In a
similar situation, it may be that less space is needed in the array, for example if elements are
removed from the array and we don’t need as much space anymore. In this case, we could
similarly copy the elements over into a smaller array and discard the original array.

Assigning individual values to an array like this can be quite tedious, especially when the array
is large. Sometimes, in fact, we already know the numbers that we want to place in an array
(e.g., we are using some fixed table or matrix of data that is pre-defined). In this case, to save
on coding time J AVA allows us to assign values to an array at the time that we create it. This
is done by using braces { }. In this case, neither the new keyword, the type nor the size of
the array are specified. Instead, we supply the values on the same line as the declaration of
the variable. Here are some examples:

int[ ] ages = {34, 12, 45};
double[ ] wei ght s = {4. 5, 23. 6, 84. 124, 78. 2, 61. 5};
boolean[ ] r et i r ed = {true, false, false, true};
St r i ng[ ] names = {" Bi l l " , " J enni f er " , " J oe" };
char[ ] vowel s = {' a' , ' e' , ' i ' , ' o' , ' u' };
Here, the array’s size is automatically determined by the number of values that you specify
within the braces, each value separated by a comma. So the sizes of the arrays above are 3,
5, 4, 3 and 5, respectively.
Objects may also be created and assigned during this initialization process as follows …
COMP1005/1405 – Code Efficiency Fall 2009

- 310 -
Per son[ ] peopl e = {new Per son( " Hank" , " Ur chi f " , 19, ' M' , false) ,
new Per son( " Don" , " Beef or dusk" , 23, ' M' , false) ,
new Per son( " Hol l y" , " Day" , 67, ' F' , true) ,
null,
null};

Here we are actually creating three specific Person objects and inserting them into the first
three positions in the people array. Notice that you can even supply a value of null, for
example if you wish to leave some extra space at the end for future information. Hence the
people array above has a capacity (i.e., length) of 5.

As an example, here is an interesting method that uses an array to convert an integer
representing a day of the week into the name of the day of the week. For example, the 3rd
day of the week is Tuesday (assuming that we start the week on a Sunday).

public static St r i ng dayOf Week( int dayNumber ) {
St r i ng days[ ] = {" Sunday" , " Monday" , " Tuesday" , " Wednesday" ,
" Thur sday" , " Fr i day" , " Sat ur day" };
if ( ( dayNumber < 1) | | ( dayNumber > 7) )
return " I nval i d" ;

return days[ dayNumber - 1] ;
}


Do you understand why we are subtracting 1 from the dayNumber ? I hope so. To use this
method, we would have to write it in some class and then call it ... although the details have
been left out here.
Lets look at some simple examples that use arrays to make sure that you understand how to
use them. First of all, given an array of integers, here is a program to find their average:


int nums[ ] = {23, 54, 88, 98, 23, 54, 7, 72, 35, 22};
int sum= 0;

for ( int i =0; i <nums. l engt h; i ++)
sum+= nums[ i ] ;

Syst em. out . pr i nt l n( " The aver age i s " + sum/ ( double) nums. l engt h) ;


Recall that we need to divide by a float or double in order to get a more accurate result,
hence the type cast to double here. If we would have left out the type cast, the result would
have discarded the fractional portion of the computation, giving only a truncated integer result.

What if we were to create a method to compute the average of an arbitrary array ? We would
need to place this code in some class, perhaps called ArrayCalculator as follows …
COMP1005/1405 – Code Efficiency Fall 2009

- 311 -

public class Ar r ayCal cul at or {
static double calculateAverage( int[ ] nums) {
int sum= 0;
for ( int i =0; i <nums. l engt h; i ++)
sum+= nums[ i ] ;
return sum/ ( double) nums. l engt h;
}
}


Notice that the method takes an int array as a parameter and returns a double as the result.
Here is a test program that we can use to try out the method:

public class Ar r ayCal cul at or Test Pr ogr am{
public static void mai n( St r i ng ar gs[ ] ) {
int ages[ ] = {23, 54, 88, 98, 23, 54, 7, 72, 35, 22};
Syst em. out . pr i nt ( " The aver age i s " ) ;
Syst em. out . pr i nt l n( Ar r ayCal cul at or . cal cul at eAver age( ages) ) ;
}
}


Notice that when passing an array as a parameter, we DO NOT use the square brackets:

ar r ayCal c. cal cul at eAver age( ages[ ] ) ; / / WRONG! !

and we DO NOT specify the type either:

ar r ayCal c. cal cul at eAver age( int ages[ ] ) ; / / WRONG! !

Here is another example. Given an array of 10 numbers, how do we write a method that can
find the maximum ? The method should take in an int array parameter and return an int as
the result:

public static int findMaximum( int[ ] nums) {
int max = 0;
for ( int i =0; i <nums. l engt h; i ++) {
if ( nums[ i ] > max)
max = nums[ i ] ;
}
return max;
}


Notice that the code goes through the numbers in the array and simply replaces the value of
the max variable with any number that it finds to be larger. It is important to note that the
return must occur after the loop, that is, after you have checked all of the numbers. A
common mistake by students is to put the return in the IF statement (as shown below) but this
would not work. Do you know why ?
COMP1005/1405 – Code Efficiency Fall 2009

- 312 -

public static int f i ndMaxi mum( int[ ] nums) {
int max = 0;
for ( int i =0; i <nums. l engt h; i ++) {
if ( nums[ i ] > max)
return nums[ i ] ; / / WRONG!
}
}

In the above example, the initial value for max was set to 0. Could you foresee any situations
in which this could pose a problem ?
Well, if all of the integers are negative, then the code will return 0 as the maximum, which is an
error. If however, you are certain that at least one number is positive or 0, then the code will
work fine. However, a safer way to do this is to set the value of max initially to
Integer.MIN_VALUE, which is a constant which represents the smallest int that can be
stored in J AVA. Another option is to set max to be the first element in the array (i.e.,
nums[0]), which would always produce correct results as well.

Let us write one more method. This time we will write one that modifies the elements of an
array. We will write a method, called scale() that will take an array of integers and multiply
them by the some scale factor, which will also be passed as a parameter.

public static void scale( int[ ] nums, int f act or ) {
for ( int i =0; i <nums. l engt h; i ++)
nums[ i ] = nums[ i ] * f act or ;
}


The method is quite straight forward. Each element of the array is simply replaced by that
same element multiplied by the factor. As a result, the original array that was passed in would
now be changed. We can actually simplify the code a little by making use of the “for each”
loop that we used with ArrayLists. Here is how we could use it on the calculateAverage()
and findMaximum() methods …
public static double calculateAverage( int[ ] nums) {
int sum= 0;
for ( int i : nums)
m+= i su ;
return sum/ ( double) nums. l engt h;
}

public static int findMaximum( int[ ] nums) {
int max = 0;
for ( int i : nums) {
if ( i > max)
max = i ;
}
return max;
}
COMP1005/1405 – Code Efficiency Fall 2009

- 313 -
Notice that i now represents the number from the array, NOT its index! Interestingly, we
cannot use the “for each” loop for the scale() method:


static void scale( int[ ] nums, int f act or ) {
for ( int i : nums)
i = i * f act or ; / / WRONG!
}


Why not ? This will not work because i is a temporary loop variable that points to the item in
the array. The code above simply re-assigns the value of i * factor to the loop variable i, but
does not affect the element from the array. So, we would need to use the regular “for” loop
where i represents the index, so that we can do nums[i] = nums[i] * factor;

Multi-Dimensional Arrays

J AVA allows arrays of multiple dimensions (2 or more). 2-dimensional (i.e., 2D) arrays are
often used to represent data tables, game boards, mazes, pictures, terrains, displays etc…




In these cases, the information in the array is arranged by rows and columns. Hence, to
access or modify something in the table/grid, we need to specify which row and column the
item lies in. Therefore, instead of using just one index as with simple arrays, a 2D array
requires that we always supply two indices … row and column.

Therefore, in J AVA, we specify a 2D array by using two sets of square brackets [ ] [ ].
Therefore, our variables should have both sets of brackets when we declare them:

int[ ] [ ] schedul e; / / a 2D ar r ay
int[ ] l i st ; / / a 1D ar r ay
int age; / / not an ar r ay, j ust an int

COMP1005/1405 – Code Efficiency Fall 2009

- 314 -
Also, when creating the arrays, we must specify the number of rows as well as the number of
columns. Remember, the arrays cannot grow, so these should represent the maximum
number of rows and columns that we want to have:

schedul e = new int[ 10] [ 10] ; / / t abl e wi t h 10 r ows and 10 col umns
i mage = new Pi xel [ 1024] [ 768] ; / / a 1024x768 pi xel i mage
par ki ngLot = new Car [ 12] [ 4] ; / / 12x4 l ot t hat can hol d 48 car s

Usually, intuitively, the first length given represents the number of rows while the second
represents the number of columns, but this need not be the case. For example, the following
line of code creates an array that can hold 15 items:

gr i d1 = new int[ 3] [ 5] ;

You can think of it as being either a 3x5
array or a 5x3 array. It is up to you as
to whether the 3 is the rows or the 5 is
the rows. You just need to make sure
that you are consistent.

As with regular arrays, the elements each have a location. However,
for 2D arrays, the location is a pair of indices instead of a single index.
3 columns
5
rows
3
rows
5 columns
The rows and columns are all numbered starting with 0. Therefore, we access and modify
elements from the array by specifying both row and column indices as follows:

schedul e[ 0] [ 0] = 34; / / r ow 0, col umn 0
schedul e[ 0] [ 1] = 15; / / r ow 0, col umn 1
schedul e[ 1] [ 3] = 26; / / r ow 1, col umn 3

Sometimes, there is confusion, for example, when we create
grids with (x,y) coordinates because when dealing with
coordinates we always specify x before y. But visually,
x represents the number of columns in a grid, while y
represents the number of rows … hence (x,y) corresponds
to (columns,rows) which seems counter-intuitive.

poi nt s[ 0] [ 0] = 34; / / ( x, y) =( 0, 0) = r ow 0, col umn 0
poi nt s[ 0] [ 1] = 15; / / ( x, y) =( 0, 1) = r ow 1, col umn 0
poi nt s[ 1] [ 3] = 26; / / ( x, y) =( 1, 3) = r ow 3, col umn 1
y
x

COMP1005/1405 – Code Efficiency Fall 2009

- 315 -
As an example that uses 2D arrays, let us write code that will create and display the following
maze:

0
1
2
3
4
5
6
7
0 1 2 3 4 5 6 7 8 9

We could represent this maze by using an array of ints indicating whether or not there is a wall
at each location in the array (i.e., 1 for wall, 0 for open space). Notice how we can do this by
using the quick array declaration with the braces { } as we did with 1D arrays:
int[ ] [ ] maze = {{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,0,1,1,1,0,1,1},
{1,0,0,0,1,0,1,1,1,1},
{1,0,1,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}};

Notice that there are more brace characters than with 1D arrays. Each row is specified by its
own unique braces and each row is separated by a comma. In fact, each row is itself a 1D
array. Interestingly, the length field of a multi-dimensional array returns the length of the first
dimension only. Consider this code:

int[ ] [ ] ages = new int[ 4] [ 7] ;
Syst em. out . pr i nt l n( ages. l engt h) ; / / di spl ays 4, not 28!
In fact, we can actually access the separate arrays for each dimension:
int[ ] [ ] ages = new int[ 4] [ 7] ;
int[ ] f i r st Ar r ay = ages[ 0] ; / / get s 1st r ow f r omages ar r ay
Syst em. out . pr i nt l n( ages. l engt h * f i r st Ar r ay. l engt h) ; / / di spl ays 28
Therefore, as you can see, a 2D array is actually an array of 1D arrays.
Iterating through a 2D array is similar to a 1D array except that we usually use 2 nested for
loops. Here is some code to print out the maze that we created above:
for ( int r ow=0; r ow<8; r ow++) {
for ( int col =0; col <10; col ++)
Syst em. out . pr i nt ( maze[ r ow] [ col ] ) ;
COMP1005/1405 – Code Efficiency Fall 2009

- 316 -
Syst em. out . pr i nt l n( ) ;
}
And here would be the result:

1111111111
1001000001
1011101101
1010001001
1010111011
1000101111
1010000001
1111111111


Of course, we can make the maze look however we want. Here is how we can display a
different wall character. Notice how we switched the loops (just for fun) to use the “for each”
style:
for ( int[ ] r ow: maze) {
for ( int i t em: r ow) {
if ( i t em== 1)
yst em. out . pr i nt ( ' *' ) ; S
else
Syst em. out . pr i nt ( ' ' ) ;
}
Syst em. out . pr i nt l n( ) ;
}

And here would be the result:

**********
* * *
* *** ** *
* * * *
* * *** **
* * ****
* * *
**********





PRACTICE: Assuming that you set one location in the
array to 2 and another location to 3, could you write a
program that would determine how long it would take
(i.e., steps) to get from 2 to 3 ?

1 1 1 1 1 1 1 1
1 0 0 0
1 0 1 0
1 0 1 1
0 0 0 0
1 1 1 1
1 2 0 1
1 0 1 1
1 0 1 0
1 1 1 1
0 0 0 0
1 0 1 1
0 0 1 0
1
1
0
1
0
0
0
1
1
1
1
1
1
1
1
1 3 1 1 1 0 1 0 1
0
1
2
3
4
5
6
7
0 1 2 3 4 5 6 7 8 9
COMP1005/1405 – Code Efficiency Fall 2009

- 317 -
Interestingly, you can create even higher dimensional arrays. For example, an array as follows
may be used to represent a cube of colored blocks:
int cube[ ] [ ] [ ] = new Col or [ 3] [ 3] [ 3] ;

Notice that there are now 3 sets of square brackets. Using 3D arrays works the same way as
with 2D arrays except that we now use 3 sets of brackets and 3 indices when referring to the
elements of the array.

3-dimensional arrays are often used in the real world to model various objects:





Chapter 11
Saving and Loading Information


What is in This Chapter ?
In computer science, all data eventually gets stored onto storage devices such as hard drives,
diskettes, USB flash drives, CDs, DVDs, etc... This set of notes explains how to save
information from your program to a file that sits on one of these backup devices. It also
discusses how to load that information back into your program. The saving/loading of data
from files can be done using different formats. We discuss here the notion of text vs. binary
formats. Note as well that the techniques presented here also apply to sending and receiving
information from Streams (e.g., networks). We will look at the way in which Stream objects
are used to do data I/O in J AVA. We will also look at how to use ObjectStreams to read/write
entire objects easily and finally investigate the File class which is useful for querying files and
folders on your computer.

COMP1005/1405 – Saving and Loading Information Fall 2009

- 319 -

11.1 Introduction to Files and Streams

File processing is very important since eventually, all data must be stored externally from the
machine so that it will not be erased when the power is turned off. Here are some of the terms
related to file processing:

Field
File
A group of related records
(e.g., all employees in a
company, products at a
store)
A group of characters that
reflects the value of a
single object attribute
(e.g., name, phone
number, age, gender).
Database
A group of possibly
unrelated files (e.g., police
database containing all
criminals, dmv records,
phone records)
Record
A composition of several
related fields. (e.g.,
represents group of all
attribute values for a
particular object such as
a single employee’s file).

In J AVA, we can store information from our various objects by extracting their attributes and
saving these to the file. To use a file, it must be first opened. When done with a file, it MUST
be closed. We use the terms read to denote getting information from a file and write to
denote saving information to a file. The contents of a file is ultimately reduced to a set of
numbers from 0 to 255 called bytes.
In J AVA, files are represented as Stream objects. The idea is that
data “streams” (or flows) to/from the file … similar to the idea of
streaming video that you may have seen online. Streams are
objects that allow us to send or receive information in the form of
bytes. The information that is put into a stream, comes out in the
same order.

It is similar to those scrolling signs where the letters scroll from right
to left, spelling out a sentence:






COMP1005/1405 – Saving and Loading Information Fall 2009

- 320 -
Streams are actually very general in that they provide a way to send or receive information to
and from:
• files
• networks
• different programs
• any I/O devices (e.g., console and keyboard)
When we first start executing a J AVA program, 3 streams are automatically created:
• System.in // for inputting data from the keyboard
• System.out // for outputting data to the screen
• System.err // for outputting error messages to the screen
In fact, there are many stream-related classes in J AVA. We will look at a few and how they
are used to do file I/O. The various Streams differ in the way that data is “entered into” and
“extracted from” the stream. As with Exceptions, Streams are organized into different
hierarchies. J AVA contains four main stream-related hierarchies for transferring data as binary
bytes or as text bytes:
Object
InputStream OutputStream Reader Writer
Binary Text (e.g., ASCII)

It is interesting to note that there is no common Stream class from which these main classes
inherit. Instead, these 4 abstract classes are the root of more specific subclass hierarchies.
A rather large number of classes are provided by J AVA to construct streams with the desired
properties. We will examine just a few of the common ones here.
Typically I/O (i.e., input/output) is a bottleneck in many applications. That is, it is very time
consuming to do I/O operations when compared to internal operations. For this reason,
buffers are used. Buffered output allows data to be collected for output before it is actually
sent to the output device. Only when the buffer gets full does the actual data get sent. This
reduces the amount of actual output operations, but each output operation would usually send
more data.
COMP1005/1405 – Saving and Loading Information Fall 2009

- 321 -
(Note: The flush() command can be sent to buffered streams in order to empty the buffer and cause the data to
be sent "immediately" to the output device. Input data can also be buffered.)
By the way, what is System.in and System.out exactly ? We can determine their respective
classes with the following code:

System.out.print("System.in is an instance of ");
System.out.println(System.in.getClass());
System.out.print("System.out is an instance of ");
System.out.println(System.out.getClass());


This code produces the following output:


System.in is an instance of class java.io.BufferedInputStream
System.out is an instance of class java.io.PrintStream


So we have been using these streams for displaying information and getting information from
the user through the keyboard. We will now look at how the classes are arranged in the
different stream sub-hierarchies.



11.2 Reading and Writing Binary Data

First, let us examine a portion of J AVA's OutputStream sub-hierarchy:
Object
OutputStream
FilterOutputStream FileOutputStream ObjectOutputStream …

DataOutputStream PrintStream …
Output bytes
to a file
System.out is
one of these
Output primitives
to a file
Output objects
to a file
COMP1005/1405 – Saving and Loading Information Fall 2009

- 322 -
The streams in this sub-hierarchy are responsible for outputting binary data. That is, data
which is the form of bytes or data types. OutputStreams have a write() method that allows
us to output a single byte of data at a time.
To open a file for binary writing, we can create an instance of FileOutputStream using one of
the following constructors:
new FileOutputStream(String fileName);
new FileOutputStream(String fileName, boolean append);
The first constructor opens a new file output stream with that name. If one exists already with
that name, it is overwritten (i.e., erased). The second constructor allows you to determine
whether you want an existing file to be overwritten or appended to. If the file does not exist, a
new one with the given name is created. If the file already exists prior to opening then the
following rules apply:
• if append =false the existing file's contents is discarded and the file will be overwritten.
• if append =true the new data to be written to the file is appended to the end of the file.
We can output simple bytes to a FileOutputStream by using the write() method, which takes
a single byte (i.e., a number from 0 to 255) as follows:

FileOutputStream out;

out = new FileOutputStream("myFile.dat");
out.write('H');
out.write(69);
out.write(76);
out.write('L');
out.write('O');
out.write('!');
out.close();


This code outputs the characters HELLO! to a file called " myFile.dat" . The file will be
created (if not exiting already) in the current directory/folder (i.e., the directory/folder that your
J AVA program was run from). Alternatively, you can specify where to create the file by
specifying the whole path name instead of just the file name as follows:


FileOutputStream out;
out = new FileOutputStream("F:\\My Documents\\myFile.dat");


Notice the use of "two" backslash characters within the String constant (because the backslash
character is a special character which requires it to be preceded by a backslash ... just like \n
is used to create a new line).

COMP1005/1405 – Saving and Loading Information Fall 2009

- 323 -
Using this strategy, we can output either characters or positive integers in the range from 0 to
255. Notice in the code that we closed the file stream when done. This is important to
ensure that the operating system (e.g., Windows VISTA) releases the file handles correctly).
When working with files in this way, two exceptions may occur:
• opening a file for reading or writing may generate a java.io.FileNotFoundException
• reading or writing to/from a file may generate a java.io.IOException
You should handle these exceptions with appropriate try/catch blocks:
import java.io.*;

public class FileOutputStreamTestProgram {
public static void main(String args[]) {
try {
FileOutputStream out;
out = new FileOutputStream("myFile.dat");
out.write('H'); out.write(69);
out.write(76); out.write('L');
out.write('O'); out.write('!');
out.close();

} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for writing");
} catch (IOException e) {
System.out.println("Error: Cannot write to file");
}
}
}


Since all streams are all a part of the java.io package we need to import them.

The code above allows us to output any data as long as it is in byte format. This can be
tedious. For example, if we have the integer 7293901 and we want to output it, we have a few
choices:
• break up the integer into its 7 digits and output these digits one at a time (very tedious)
• output the 4 bytes corresponding to the integer itself (recall that an int is stored as 4
bytes)
Either way, these are not fun. Fortunately, J AVA provides a DataOutputStream class which
allows us to output whole primitives (e.g., ints, floats, doubles) as well as whole Strings to a
file! Here is an example of how to use it to output information from a BankAccount object as
follows …
COMP1005/1405 – Saving and Loading Information Fall 2009

- 324 -
import java.io.*;

public class DataOutputStreamTestProgram {
public static void main(String args[]) {
try {
BankAccount aBankAccount;
DataOutputStream out;

aBankAccount = new BankAccount("Rob Banks");
aBankAccount.deposit(100);

out = new DataOutputStream(new FileOutputStream("myAcc.dat"));
out.writeUTF(aBankAccount.getOwner());
out.writeInt(aBankAccount.getAccountNumber());
out.writeFloat(aBankAccount.getBalance());
out.close();

} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for writing");
} catch (IOException e) {
System.out.println("Error: Cannot write to file");
}
}
}


The DataOutputStream acts as a “wrapper” class around the FileOutputStream. It takes
care of breaking our primitive data types and Strings into separate bytes to be sent to the
FileOutputStream.

There are methods to write each of the primitives as well as Strings:

writeUTF(String aString)
writeInt(int anInt)
writeFloat(float aFloat)
writeLong(long aLong)
writeDouble(double aDouble)
writeShort(short aShort)
writeBoolean(boolean aBool)
writeByte(int aByte)
writeChar(char aChar)

The output from a DataOutputStream is not very nice to look at (i.e., it is in binary format).
The myAcc.dat file would display as follows if we tried to view the it with Windows Notepad:


Rob Banks † BÈ

This is the binary representation of the data, which usually takes up less space than text files.
The disadvantage of course, is that we cannot make sense of the data if we try to read it with
our eyes as text. However, rest assured that the data is saved properly.
Let us now examine how we could read that information back in from the file with a different
program. To start, we need to take a look at the InputStream sub-hierarchy as follows …
COMP1005/1405 – Saving and Loading Information Fall 2009

- 325 -
Object
InputStream
ObjectInputStream …
FilterInputStream FileInputStream

Notice that it is quite similar to the OutputStream hierarchy. In fact, its usage is also very
similar. We can read back in the byte data from our file by using FileInputStream now as
follows:
import java.io.*;

public class FileInputStreamTestProgram {
public static void main(String args[]) {
try {
FileInputStream in = new FileInputStream("myFile.dat");
while(in.available() > 0)
System.out.print(in.read() + " ");
in.close();

} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for reading");
} catch (EOFException e) {
System.out.println("Error: EOF encountered, file may be corrupt");
} catch (IOException e) {
System.out.println("Error: Cannot read from file");
}
}
}


Notice that we now use read() to read in a single byte from the file. Notice as well that we can
use the available() method which returns the number of bytes available to be read in from the
file (i.e., the file size minus the number of byte as already read in).

Also, notice now that we are forced to handle an EOFException which can occur if the file is
corrupted and the end of the file character is reached before the number of available bytes has
been read in.

DataInputStream BufferedInputStream …
Input bytes
from a file
System.in is
one of these
Input primitives
from a file
Input objects
from a file
COMP1005/1405 – Saving and Loading Information Fall 2009

- 326 -
Recall that the order in which you catch your exceptions is important! Catch the most specific
ones first. Since IOException is a superclass of EOFException, it must go afterwards.

The code reads the data back in from our file (i.e., the characters HELLO! ) and outputs their
ASCII (i.e., byte) values to the console:


72 69 76 76 79 33


Try changing in.read() to (char)in.read() (i.e., type-cast the byte to a char) and see what
happens...

That was fairly simple ... but what about getting back those primitives ? You guessed it! We
will use DataInputStream:
import java.io.*;

public class DataInputStreamTestProgram {
public static void main(String args[]) {
try {
BankAccount aBankAccount;
DataInputStream in;

in = new DataInputStream(new FileInputStream("myAccount.dat"));

String name = in.readUTF();
int acc = in.readInt();
float bal = in.readFloat();

aBankAccount = new BankAccount(name, bal, acc);
System.out.println(aBankAccount);
in.close();

} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for reading");
} catch (EOFException e) {
System.out.println("Error: EOF encountered, file may be corrupt");
} catch (IOException e) {
System.out.println("Error: Cannot read from file");
}
}
}


Notice that we re-create a new BankAccount object and "fill-it-in" with the incoming file data.
Note, that in order for the above code to compile, we would need to write a public constructor
for the BankAccount class that takes an owner name, balance and account number (i.e.,
previously, in our BankAccount class, we had no way of specifying the accountNumber
since it was set automatically) …
COMP1005/1405 – Saving and Loading Information Fall 2009

- 327 -

BankAccount(String initName, float initBal, int num) {
ownerName = initName;
accountNumber = num;
balance = initBal;
}


As with output, there are methods to read the other primitives:

String readUTF()
int readInt()
float readFloat()
long readLong()
double readDouble()
short readShort()
boolean readBoolean()
int readByte()
char readChar()



11.3 Reading and Writing Text Data

Here is the Writer class sub-hierarchy which is used for writing text data to a stream:
Object
Writer
OutputStreamWriter BufferedWriter PrintWriter …
FileWriter
Output lines of
text to a file
Output characters
to a file
Output objects to a
file using toString()

Notice that there are 3 main classes we will use for writing characters, lines of characters
and general objects to a text file. When objects are written to the text file, the toString()
method for the object is called and the resulting String is saved to the file.

We can output (in text format) to a file using simply the print() or println() methods with the
PrintWriter class as follows …
COMP1005/1405 – Saving and Loading Information Fall 2009

- 328 -
import java.io.*;

public class PrintWriterTestProgram {
public static void main(String args[]) {
try {
BankAccount aBankAccount;
PrintWriter out;

aBankAccount = new BankAccount("Rob Banks");
aBankAccount.deposit(100);

out = new PrintWriter(new FileWriter("myAccount2.dat"));

out.println(aBankAccount.getOwner());
out.println(aBankAccount.getAccountNumber());
out.println(aBankAccount.getBalance());
out.close();

} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for writing");
} catch (IOException e) {
System.out.println("Error: Cannot write to file");
}
}
}


Wow! Outputting text to a file is as easy as outputting it to the console window ! But what
does it look like ? If we opened the file with Windows Notepad, we would notice that the result
is a “pleasant looking” text format:


Rob Banks
100000
100.0


In fact, we can actually write any object using the println() method. J AVA will use that
object's toString() method. So if we replaced this code:

out.println(aBankAccount.getOwner());
out.println(aBankAccount.getAccountNumber());
out.println(aBankAccount.getBalance());

with this code:

out.println(aBankAccount);

we would end up with the following saved to the file:

Account #100000 with $100.0

COMP1005/1405 – Saving and Loading Information Fall 2009

- 329 -
So it actually does behave just like the System.out console. We would need to be careful
though, because you will notice that the BankAccount’s toString() method in the example
above did not display the owner’s name. So the file does not record that owner’s name and
therefore we could never read that name back in again … it would be lost forever. Notice as
well how the PrintWriter wraps the FileWriter class just as the DataOutputStream wrapped
the FileOutputStream.
It is also easy to read back in
the information that was
saved to a text file. Here is
the hierarchy of classes for
reading text files
Object
Reader
BufferedReader …
FileReader
InputStreamReader
Notice that we can only read
in characters and lines of
characters from the text file,
but NOT general objects.
We will see later how we
can read the objects.

Most of the time, we will make
use of the BufferedReader
class by using the readLine()
method as follows:
Input lines of text Input characters
from a file from a file

import java.io.*;

public class BufferedReaderTestProgram {
public static void main(String args[]) {
try {
BankAccount aBankAccount;
BufferedReader in;

in = new BufferedReader(new FileReader("myAccount2.dat"));
String name = in.readLine();
int acc = Integer.parseInt(in.readLine());
float bal = Float.parseFloat(in.readLine());

aBankAccount = new BankAccount(name, bal, acc);
System.out.println(aBankAccount);
in.close();

} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for reading");
} catch (EOFException e) {
System.out.println("Error: EOF encountered, file may be corrupt");
} catch (IOException e) {
System.out.println("Error: Cannot read from file");
}
}
}
COMP1005/1405 – Saving and Loading Information Fall 2009

- 330 -
Note the use of "primitive data type" wrapper classes to read data types. We could have used
the Scanner class here to simplify the code:

import java.io.*;
import java.util.*; // Needed for use of Scanner and NoSuchElementException

public class BufferedReaderTestProgram2 {
public static void main(String args[]) {
try {
BankAccount aBankAccount;

Scanner in = new Scanner(new FileReader("myAccount2.dat"));
String name = in.nextLine();
int acc = in.nextInt();
float bal = in.nextFloat();
aBankAccount = new BankAccount(name, bal, acc);
System.out.println(aBankAccount);
in.close();

} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for reading");
} catch (NoSuchElementException e) {
System.out.println("Error: EOF encountered, file may be corrupt");
} catch (IOException e) {
System.out.println("Error: Cannot read from file");
}
}
}


Notice here that we now catch a NoSuchElementException instead of an EOFException.
This is how the Scanner detects the end of the file. The main advantage of using this
Scanner class is that we do not have to use any wrapper classes to convert the input strings
to primitives.



11.4 Reading and Writing Whole Objects

So far, we have seen ways of saving and loading bytes and characters to a file. Also, we
have seen how DataOutputStream/DataInputStream and PrintWriter/BufferedReader
classes can make our life simpler since they deal with larger (more manageable) chunks of
data such as primitives and Strings. We also looked at how we can save a whole object (i.e.,
a BankAccount) to a file by extracting its attributes and saving them individually.
Now we will look at an even simpler way to save/load a whole object to/from a file using the
ObjectInputStream and ObjectOutputStream classes:
COMP1005/1405 – Saving and Loading Information Fall 2009

- 331 -

OutputStream
ObjectOutputStream
InputStream
ObjectInputStream
Object
These classes allow us to save or load entire J AVA objects with one method call, instead of
having to break apart the object into its attributes. Here is how we do it:

import java.io.*;

public class ObjectOutputStreamTestProgram {
public static void main(String args[]) {
try {
BankAccount aBankAccount;
ObjectOutputStream out;

aBankAccount = new BankAccount("Rob Banks");
aBankAccount.deposit(100);

out = new ObjectOutputStream(new FileOutputStream("myAcc.dat"));
out.writeObject(aBankAccount);
out.close();

} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for writing");
} catch (IOException e) {
System.out.println("Error: Cannot write to file");
}
}
}


Wow! It is VERY easy to write out an object. We simply supply the object that we want to
save to the file as a parameter to the writeObject() method. Notice that the
ObjectOutputStream class is a wrapper around the FileOutputStream. That is because
ultimately, the object is reduced to a set of bytes by the writeObject() method, which are then
saved to the file. The process of breaking down the object into bytes is called serialization.
Thus, when an object is saved to a file, it is automatically de-constructed into bytes, these
bytes are then saved to a file, and then the bytes are read back in later and the object is re-
constructed again. This is all done automatically by J AVA, so we don’t have to be too
concerned about it.

In order to be able to save an object to a file using the ObjectOutputStream, the object must
be serializable (i.e., able to be serialized…or reduced to a set of bytes). To do this, we need
to inform J AVA that our object implements the java.io.Serializable interface as follows …

COMP1005/1405 – Saving and Loading Information Fall 2009

- 332 -

public class BankAccount implements java.io.Serializable {
...
}


This particular interface does not actually have any methods within it that we need to
implement. Instead, it merely acts as a “flag” that indicates your permission for this object to
be serialized. It allows a measure of security for our objects (i.e., only serializable objects
are able to be broken down into bytes and sent to files or over the network).

Most standard J AVA classes are serializable by default and so they can be saved/loaded
to/from a file in this manner. When allowing our own objects to be serialized, we must make
sure that all of the “pieces” of the object are also serializable. For example, assume that our
BankAccount is defined as follows:

public class BankAccount implements java.io.Serializable {
Customer owner;
float balance;
int accountNumber;
...
}


In this case, since owner is not a String but a Customer object, then we must make sure that
Customer is also Serializable:


public class Customer implements java.io.Serializable
{
...
}


We would need to then check whether Customer itself uses other
objects and ensure that they too are serializable … and so on.
To understand this, just think of a meat grinder. If some hard
marbles were placed within out meat, we cannot expect it to come
out through the grinder since they cannot be reduced to a smaller
form. Similarly, if we have any non-serializable objects in our original object, we cannot
properly serialize the object.

So what does a serialized object look like anyway ? Here is what the file would look like from
our previous example if opened in Windows Notepad:


¬í sr BankAccount“ÈSòñ úä I accountNumberF balanceL ownert
  Ljava/lang/String;xp † BÈ t Rob Banks

COMP1005/1405 – Saving and Loading Information Fall 2009

- 333 -
Weird … it seems to be a mix of binary and text. As it turns out, J AVA saves all the attribute
information for the object, including their types and values, as well as some other information.
It does this in order to be able to re-create the object when it is read back in.

The object can be read back in by using the readObject() method in the ObjectInputStream
class as follows:

import java.io.*;

public class ObjectInputStreamTestProgram {
public static void main(String args[]) {
try {
BankAccount aBankAccount;
ObjectInputStream in;

aBankAccount = new BankAccount("Rob Banks");
aBankAccount.deposit(100);

in = new ObjectInputStream(new FileInputStream("myAcc.dat"));
aBankAccount = (BankAccount)in.readObject();

System.out.println(aBankAccount);
in.close();

} catch (ClassNotFoundException e) {
System.out.println("Error: Object'c class does not match");
} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for writing");
} catch (IOException e) {
System.out.println("Error: Cannot read from file");
}
}
}


Note, that the ObjectInputStream wraps the FileInputStream. Also, notice that once read in,
the object must be type-casted to the appropriate type (in this case BankAccount). Also, if
there is any problem trying to re-create the object according to the type of object that we are
loading, then a ClassNotFoundException may be generated, so we have to handle it.
Finally, in order for this to work, you must also make sure that your object (i.e., BankAccount)
has a zero-parameter constructor, otherwise an IOException will occur when J AVA tries to
rebuild the object. Although not shown in our example above, you may also make use of the
available() method to determine whether or not the end of the file has been reached.

Although this method is extremely easy to use, there is a potentially disastrous
disadvantage. The object that is saved to the file using this strategy is actually
saved in binary format which depends on the class name, the object’s attribute
types and names as well as the method signatures and their names. So if you
change the class definition after it has been saved to the file, it may not be able
to be read back in again !!! Some changes to the class do not cause problems
such as adding an attribute or changing its access modifiers.
COMP1005/1405 – Saving and Loading Information Fall 2009

- 334 -
So as a warning, when saving objects to a file using this strategy, you should always keep a
backed-up version of all of your code so that you will be able to read these files with this
backed-up code in the future.

Supplemental Information (Disguising Serialized Data)
You can actually write your own methods for serializing your objects. One
reason for doing this may be to encrypt some information beforehand (such as a
password). You can decide which parts of the object will be serialized and which parts will not.
You can declare any object attribute as being transient (which means that it will not be
serialized) as follows:

transient String password;

This will tell J AVA that you do not want the password saved automatically upon serialization.
That way you can write your own method to encrypt it before it is serialized.

To do this, you would need to write two methods called writeObject(ObjectOutputStream)
and readObject(ObjectInputStream). These methods will automatically be called by J AVA
upon serialization and they override the default writing behavior. In fact, there are
defaultWriteObject() and defaultReadObject() methods which do the default serialization
behavior (i.e., the serializing before you decided to do your own). Here are examples of what
you can do:
void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// ... do extra stuff here to append to end of file
out.writeObject(myField.encrypt());
}
void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
// ... do extra stuff here to read from end of file
myField = ((myFieldType)in.readObject()).decrypt();
}




11.5 Saving and Loading the Autoshow

Let us now consider a real example that shows how to save and load information from the
Autoshow example that we implemented earlier in the course. We will save the autoshow’s
information in a text file so that we can print it out or read it easily. So, we will be using the
PrintWriter and BufferedReader classes. We need to decide how to format the text in the
file.
COMP1005/1405 – Saving and Loading Information Fall 2009

- 335 -
The Autoshow class had these attributes:

public class Autoshow {
String name;
ArrayList<Car> cars;
...
}

where as the Car object had these attributes:

public class Car {
String make;
String model;
String color;
int topSpeed;
boolean has4Doors;
...
}

So, to save the autoshow, we need to save the name of the autoshow as well as the individual
cars in the show. For each car, the file should show the make, model, color, topSpeed and
has4Doors. Perhaps we will separate the cars by a blank line to indicate that one car's data
ends and another's begins as follows:


AutoRama 2009

Porsche
959
Red
240
false

Pontiac
Grand-Am
White
160
true

Ford
Mustang
White
230
false




This seems like a reasonable way to save the autoshow so that the data is readable in a text
program. You will notice that each Car object is saved the same way. Hence, it would be
good to start with methods that can save/load Car objects.

We can write the following method in the Car class to begin…

COMP1005/1405 – Saving and Loading Information Fall 2009

- 336 -

public void saveTo(PrintWriter aFile) {
...
}


Notice that the method will take a single parameter which is a PrintWriter object to represent
the file that we are saving to. Where does this file come from ? It actually does not matter.
When writing this method, we should just assume that someone opened a file and handed it to
us and now it is our job to write the Car information to the file specified through this incoming
parameter. Note as well, that since we did not open the file (i.e., the PrintWriter was handed
to us), we should also not close the file. It is the opener’s responsibility to close it.

So, now how do we write the Car information to the file ? We simply do it as if we were
writing to the System console:


public void saveTo(PrintWriter aFile) {
aFile.println(make);
aFile.println(model);
aFile.println(color);
aFile.println(topSpeed);
aFile.println(has4Doors);
}


That was easy. Remember though, that in order for J AVA to recognize the PrintWriter object,
we will need to import java.io.PrintWriter at the top of our Car class. In fact, as you will see
soon, we will need more classes from the java.io package, so it would be best to simply
import java.io.*;

The method for loading a Car back in from the file is also quite easy. Again, it should read
from a file (i.e., a BufferedReader object) that is passed in as a parameter, not a file that we
open or close. Then all we need to do is to read the information from the file. But what do we
“do” with the information once it has been read in ? Probably, we return it from the method so
that whoever called this “load” method can decide what to do with the loaded car information.
So, the method should probably return a Car object. Here is the method that we will write:

public static Car loadFrom(BufferedReader aFile) {
...
}

Notice that the method is static. This is not required, but it allows us to call the method as
follows:
Car c = Car.loadFrom(aFile);
instead of doing this:
Car c = new Car().loadFrom(aFile);
COMP1005/1405 – Saving and Loading Information Fall 2009

- 337 -
That is the only difference. The static version is more logical. So … now … what goes into
the method ? Well, we need to at least create and return a new Car object, so we can start
with that:


public static Car loadFrom(BufferedReader aFile) {
Car loadedCar = new Car();
// ...
return loadedCar;
}


To read in the car, we should recall that we use readLine() to read a single line of text from a
BufferedReader file. We need to read the lines of text in the same order that they were
outputted (i.e., make, model, color, topSpeed and then has4Doors). Then we can set the
attributes of the Car to the data that was read in. Here is the code:


public static Car loadFrom(BufferedReader aFile) throws IOException {
Car loadedCar = new Car();

loadedCar.make = aFile.readLine();
loadedCar.model = aFile.readLine();
loadedCar.color = aFile.readLine();
loadedCar.topSpeed = Integer.parseInt(aFile.readLine());
loadedCar.has4Doors = Boolean.parseBoolean(aFile.readLine());

return loadedCar;
}


Notice that the method may throw an IOException (due to the fact that J AVA's readLine()
method declares that it throws an IOException). We could have caught the exception here
and handled it. However, since this method is just a helper method in a larger application, we
are unsure what to do here if an error occurs. Therefore, by declaring that this method throws
an IOException, we will be forced to handle that exception from the place where we call this
loadFrom() method. Also notice that we are calling the zero-parameter constructor for the
Car here … we would need to make sure that such a constructor is available.

Now we will write some test code to see if it works. Notice in the following code how we
separated the write and read tests …

COMP1005/1405 – Saving and Loading Information Fall 2009

- 338 -
import java.io.*;

public class CarSaveLoadTestProgram {
private static void writeTest() throws IOException {
PrintWriter file1, file2;
Car car1, car2;

file1 = new PrintWriter(new FileWriter("car1.txt"));
file2 = new PrintWriter(new FileWriter("car2.txt"));
car1 = new Car("Pontiac", "Grand-Am", "White", 160, true);
car2 = new Car("Ford", "Mustang", "White", 230, false);
car1.saveTo(file1);
car2.saveTo(file2);
file1.close();
file2.close();
}

private static void readTest() throws IOException {
BufferedReader file1, file2;
Car car1, car2;

file1 = new BufferedReader(new FileReader("car1.txt"));
file2 = new BufferedReader(new FileReader("car2.txt"));
car1 = Car.loadFrom(file1);
car2 = Car.loadFrom(file2);
System.out.println(car1);
System.out.println(car2);
file1.close();
file2.close();
}

public static void main(String args[]) throws IOException {
writeTest();
readTest();
}
}


Notice that we simply ignored handling any IOExceptions by declaring that the test methods
and the main() method all throw the IOException. If we now look at the "car1.txt" and
"car2.txt" files, we see that it seems to have saved properly:

car1.txt looks like this:


Pontiac
Grand-Am
White
160
true


car2.txt looks like this:


Ford
Mustang
White
230
false

COMP1005/1405 – Saving and Loading Information Fall 2009

- 339 -
Now for the fun part. Lets make this work with the Autoshow. We will make saveTo() and
loadFrom() methods in the Autoshow class as well. This time, we need to save ALL the Car
objects from the autoshow's cars list.
Recall that we need to first save the autoshow’s name to the file:

public void saveTo(PrintWriter aFile) {
aFile.println(name);
...
}


Now we can iterate through the cars and save them one by one, leaving a blank line in
between each, to make the file more readable:


public void saveTo(PrintWriter aFile) {
aFile.println(name);
for (Car c: cars) {
aFile.println(); // Leave a blank line before writing the next one
c.saveTo(aFile);
}
}


Notice that we are making use of the Car class's saveTo() method. This is important, since it
makes good use of pre-existing code and is more modular. Again, we should import java.io.*
at the top of our Autoshow class.

The method for loading an Autoshow from the file is also quite easy. It should create and
return an Autoshow object whose name is the name that is the first line of the file. We will
make it a static method as well:


public static Autoshow loadFrom(BufferedReader aFile) throws IOException {
Autoshow aShow = new Autoshow(aFile.readLine());
...
return aShow;
}


Again, we will need to make sure that a zero-parameter constructor is available in the
Autoshow class. Now we now need to read in the name at the top of the file and then read in
each car individually. How do we know how many cars to read ? Well, we can simply read
until there are no more cars left. The ready() method in the BufferedReader class returns
true as long as there is another line to be read in the file, otherwise it returns false. We can
simply keep reading in cars until we get a !ready() as follows …

COMP1005/1405 – Saving and Loading Information Fall 2009

- 340 -
public static Autoshow loadFrom(BufferedReader aFile) throws IOException {
Autoshow aShow = new Autoshow(aFile.readLine());

while (aFile.ready()) { //read until no more available (i.e., not ready)
aFile.readLine(); //read the blank line
aShow.getCars().add(Car.loadFrom(aFile)); //read & add the car
}
return aShow;
}

Notice that each time we load a new car from the file, we must not forget to add it to the new
autoshow object’s cars collection. Here is a method for testing out our saving and loading:

import java.io.*;
public class AutoshowSaveLoadTestProgram {
s the writing autoshow to a file // This method test of an
private static void writeTest() throws IOException {
// First make an Autoshow and add lots of cars to the show
Autoshow show = new utoshow("AutoRama 2009"); A
show.getCars().add(new Car("Porsche", "959", "Red", 240, false));
show.getCars().add(new Car("Pontiac", "Grand-Am", "White", 160, true));
show.getCars().add(new Car("Ford", "Mustang", "White", 230, false ));
show.getCars().add(new Car("Volkswagon", "Beetle", "Blue", 140, false));
show.getCars().add(new Car("Volkswagon", "Jetta", "Silver", 180, true));
show.getCars().add(new Car("Geo", "Storm", "Yellow", 110, true ); )
show.getCars().add(new Car("Toyota", "MR2", "Black", 220, false));
show.getCars().add(new Car("Ford", "Escort", "Yellow", 10, true));
show.getCars().add(new Car("Honda", "Civic", "Black", 220, true ));
show.getCars().add(new Car("Nissan", "Altima", "Silver", 180, true));
show.getCars().add(new Car("BMW", "5", "Gold", 260, true));
show.getCars().add(new Car("Prelude", "Honda", "White", 90, false));
show.getCars().add(new Car("Mazda", "RX7", "Red", 240, false));
show.getCars().add(new Car("Mazda", "MX6", "Green", 160, true));
show.getCars().add(new Car("Pontiac", "G6", "Black", 140, false));

// Now open the file and save the autoshow
PrintWriter aFile;
aFile = new PrintWriter(new FileWriter("autoshow.txt"));
show.saveTo(aFile);
aFile.close();
}

s the readin n autoshow from a file // This method test g of a
private static void readTest() throws IOException {
BufferedReader aFile;

aFile = new BufferedReader(new FileReader("autoshow.txt"));
Autoshow aShow = Autoshow.loadFrom(aFile);
aShow.printByMake();
aFile.close();
}

public static void main(String args[]) throws IOException {
writeTest(); // Write an autoshow to the file
readTest(); // Read an autoshow from the file
}
}
COMP1005/1405 – Saving and Loading Information Fall 2009

- 341 -
From running the test, we can see that the Autoshow does indeed load properly.

Storing Data on a Single Line:

What if we want to store the Car data on a single line as follows:


Porsche 959 Red 240 false
Pontiac Grand-Am White 160 true
Ford Mustang White 230 false


Well, saving a Car is easy:


public void saveTo(PrintWriter aFile) {
aFile.println(make + " " + model + " " + color + " " +
topSpeed + " " + has4Doors);
}


For reading however, we will need to alter the load method to use a StringTokenizer to
extract the pieces:


public static Car loadFrom(BufferedReader aFile) throws IOException {
Car loadedCar = new Car();

StringTokenizer wholeLine = new StringTokenizer(aFile.readLine());
loadedCar.make = wholeLine.nextToken();
loadedCar.model = wholeLine.nextToken();
loadedCar.color = wholeLine.nextToken();
loadedCar.topSpeed = Integer.parseInt(wholeLine.nextToken());
loadedCar.has4Doors = Boolean.parseBoolean(wholeLine.nextToken());

return loadedCar;
}


The methods for saving and loading the Autoshow would not change much, except that the
blank line need not be written nor read in after each Car.

But what if the Car make has two words like this ?


PT Cruiser Chrysler Silver 120 true


Now its tougher since the name requires two tokens, not one. We can save and load using
commas as our delimiters and then extract the pieces of data one at a time …
COMP1005/1405 – Saving and Loading Information Fall 2009

- 342 -

PT Cruiser,Chrysler,Silver,120,true


This solves the problem.


11.6 The File Class

The File class allows us to retrieve information about a file or folder on our
computer. However, it has nothing to do with reading and writing
information to and from the files.
To make a File object, there are three commonly used constructors:
File file1 = new File("C:\\Data\\myFile.dat");
File file2 = new File("C:\\Data\\", "myFile.dat");
File file3 = new File(new File("."), "myFile.dat");
In the first constructor, we supply the entire file name as a string which includes the path to the
file. (A path is a sequence of folders on the computer that lead to the file … starting with the
root drive letter).
In the second constructor, we can supply the pathname as a separate string from the file
name. The third constructor actually uses another File object as a parameter which must
represent a folder/directory on the computer. The “ .” as a filename indicates the current
directory/folder. The “ ..” as a filename indicates the directory/folder above the current
directory/folder. Alternatively we could supply and pathname here.
Once we create this File object, there are a set of methods that we can use to ask questions
about this file or folder. Here are just some of the available methods:

boolean canRead()
Returns whether or not a file is readable.
boolean canWrite()
Returns whether or not a file is writable.
boolean exists()
Returns whether or not a file or directory exists in the specified path.
boolean isFile()
Returns whether or not this represents a file (as opposed to a directory/folder).
COMP1005/1405 – Saving and Loading Information Fall 2009

- 343 -

boolean isDirectory()
Returns whether or not this represents a directory/folder (as opposed to a file).
boolean isAbsolute()
Returns whether or not this represents an absolute path to a file or directory.
Here are other methods for accessing components (i.e., filenames and pathnames) of a file or
directory:
String getAbsolutePath()
Return a String with the absolute path of the file or directory.
String getName()
Return a String with the name of the file or directory.
String getPath()
Return a String with the path of the file or directory.
String getParent()
Return a String with the parent directory of the file or directory.
Here are some other user useful methods:
long length()
Return the length of the file in bytes. If the File object is a directory, return 0.
long lastModified()
Return a system-dependent representation of the time at which the file or
directory was last modified. The value returned is only useful for comparison
with other values returned by this method.

String[] list()
Return an array of Strings representing the contents of a directory.
For the purpose of demonstration, here is a program that gives a directory listing of the files
and folders on the root of your C: drive, but it does not go into each folder recursively …
COMP1005/1405 – Saving and Loading Information Fall 2009

- 344 -
import java.io.*;

public class FileClassTestProgram {
public static void main(String args[]) {
// The dot means the current directory
File currDir = new File("C:\\");
System.out.println("The directory name is: " + currDir.getName());
System.out.println("The path name is: " + currDir.getPath());
System.out.println("The actual path name is: " +
currDir.getAbsolutePath());

System.out.println("Here are the files in the current directory: ");
String[] files = currDir.list();
for (int i=0; i<files.length; i++) {
if (new File("C:\\", files[i]).isDirectory())
System.out.print("*** ");
System.out.println(files[i]);
}
}
}


Here is the output (which differs of course depending where you run your code from):


The directory name is:
The path name is: C:\
The actual path name is: C:\
Here are the files in the current directory:
*** 1eceb6cd306883b5737c1dbf5404e4
a-1049-1-7C23.zip
AUTOEXEC.BAT
BOOT.BAK
boot.ini
*** Config.Msi
CONFIG.SYS
*** CtDriverInstTemp
*** Documents and Settings
*** Downloaded Videos
*** Downloads
*** drivers
hiberfil.sys
*** i386
INFCACHE.1
IO.SYS
IPH.PH
*** java

… etc …

*** System Volume Information
*** temp
*** TempArchive
*** Users
*** WINDOWS

COMP1005/1405 – Saving and Loading Information Fall 2009

- 345 -
File Separators:
Depending on which type of computer you have, folders are specified in different ways. For
example, windows uses a ‘\’ character to separate folders in a pathname, whereas Unix/Linux
uses ‘/’ and Classic Mac OS uses “:”.
If we were to hard-code out pathnames into our programs, then our code would not be portable
to different machines. For example, this pathname:
String fileName = "C:\\FunInc\\models\\BankAccount.java";
would be ok for a windows-based machine, but for a Unix/Linux machine, the pathname would
be invalid. We would need to use something like this for Linux:
String fileName = "usr/FunInc/models/BankAccount.java";
… and further…something like this for Mac OS:
String fileName = "C:FunInc:models:BankAccount.java";
In order to make our code portable, J AVA has defined a static constant called separator in
the File class which will represent the appropriate file separation character depending on the
machine that our code is running on. Hence, the following code will be portable for all
machines:
String fileName = File.separator + "FunInc" + File.separator +
"models" + File.separator + "BankAccount.java";
If you do this in your programs, your code will always be portable and you will save time when
porting your code to other machines.

Chapter 12
Some Useful Tools


What is in This Chapter ?
There are many useful classes in J AVA. We take a look here at some of the commonly used
ones. It is important to know some of these available classes so that we don’t end up re-
creating a method that already exists in J AVA. We begin with a discussion of the String and
StringBuilder classes and describe how to use some of their methods. Then we discuss the
Date and Calendar classes and how they can be used to represents time and date
information. Lastly, we discuss the use of Iterators as a means of traversing through items in
a list. Obviously, there are many more classes in J AVA and you should refer to the API to get
more information.



COMP1005/1405 – Some Useful Tools Fall 2009

- 347 -

12.1 The String Class

Strings are one of the most commonly used concepts
in all programming languages. They are used to
represent text characters and are fundamental in
allowing a user to interact with the program. In
J AVA, Strings are actually objects, not primitives and
any text between double quotes represents a literal
String in our programs:

St r i ng name = " St an Dupp" ;
St r i ng empt y = " " ;

However, since Strings also objects, we can also
create one by using one of many available
constructors. Here are two examples:
St r i ng not hi ng = new St r i ng( ) ; / / makes an empt y St r i ng
St r i ng copy = new St r i ng( name) ; / / makes copy of t he name St r i ng

A String has a length corresponding to the number of characters in the String. We can ask a
String for its length by using the length() method:


St r i ng name = " St an Dupp" ;
St r i ng empt y = " " ;
name. length(); / / r et ur ns 9
empt y. length(); / / r et ur ns 0


This length remains unchanged for the string at all times. That is, once a string has been
created we cannot change the size of the string, nor can we append to the string.

Even though we cannot append to a String, we can still make use of the + operator to join two
of them together. Recall, for example, the use of the + operator within the toString() method
for the Person class:


public St r i ng t oSt r i ng( ) {
return ( this. age + " year ol d Per son named " +
this. f i r st Name + " " + this. l ast Name) ;
}


Here, we are actually combining 5 String objects to form a new String object containing the
result … the original 5 String objects remain unaltered.

COMP1005/1405 – Some Useful Tools Fall 2009

- 348 -
Each character in a String is assigned an imaginary integer index that represents its order in
the sequence. The first character in the String has an index of 0, the second character has
an index of 1, and so on. We can access any character from a String by using the charAt()
method which requires us to specify the index of the character that we want to get:


St r i ng name = " Hank Ur chi f " ;
name. charAt( 0) ; / / r et ur ns char act er ' H'
name. charAt( 1) ; / / r et ur ns char act er ' a'
name. charAt( name. l engt h( ) - 1) ; / / r et ur ns char act er ' f '
name. charAt( name. l engt h( ) ) ; / / causes StringIndexOutOfBoundsException
name. charAt( 100) ; / / causes StringIndexOutOfBoundsException


There are also some methods in the String class that allow us to extract a sequence (or
range) of characters from the String. The substring(s,e) method does just that. It takes two
parameters s and e where s specifies the starting character index and e specifies one more
than the ending character index:


St r i ng name = " Hank Ur chi f " ;
name. substring( 0, 4) ; / / r et ur ns char act er " Hank"
name. substring( 5, 11) ; / / r et ur ns char act er " Ur chi f f "
name. substring( 5, name. l engt h( ) ) ; / / r et ur ns char act er " Hank"
name. substring( 3, 6) ; / / r et ur ns char act er " k U"


In all cases above, the resulting String is a new
object, the original name object remaining unchanged.
There is also a very useful method for eliminating unwanted leading and trailing characters
(e.g., spaces, tabs, newlines and carriage returns). This can be useful when writing programs
that get String input (e.g., name, address, email etc..) from the user through text fields on
windows. The trim() method returns a new String object that represents the original string
object but with no leading and trailing space, tab, newline or carriage return characters.

St r i ng s1 = " I need a shave " ;
St r i ng s2 = " " ;
s1. trim() ; / / r et ur ns " I need a shave"
s2. trim() ; / / r et ur ns empt y st r i ng " "


Also, sometimes when getting input from the user we would like to force the information to be
formatted as either uppercase or lowercase characters. Two useful methods called
toUppercase() and toLowercase() will generate a copy of the string but with all alphabetic
characters converted to uppercase or lowercase, respectively. The methods only alter the
alphabetic characters … all other characters remain the same.

COMP1005/1405 – Some Useful Tools Fall 2009

- 349 -

St r i ng s = " Tea For 2! " ;
s. toUpperCase() ; / / r et ur ns " TEA FOR 2! "
s. toLowerCase() ; / / r et ur ns " t ea f or 2! "

A final important area that we will discuss regarding strings is that of comparing strings with
one another. String comparison is a fundamental tool used in many programs. For example,
whenever we want to search for a person’s name in a list, we must compare the name of the
person (i.e., a String) with all of the names in a list of some sort.
J AVA has two useful methods for comparing Strings. The equals(s) method compares one
String with another String, s, and then returns true if the two strings have the exact same
characters in them and false otherwise. A similar comparison method called
equalsIgnoreCase(s) is used to compare the two strings but in a way such that lowercase and
uppercase characters are considered equal.


St r i ng appl e1 = " appl e" ;
St r i ng appl e2 = " APPLE" ;
St r i ng appl e3 = " appl es" ;
St r i ng or ange = " or ange" ;

appl e1. equals( or ange) ; / / r et ur ns false
appl e1. equals( appl e2) ; / / r et ur ns false
appl e1. equals( appl e3) ; / / r et ur ns false
appl e1. equals( appl e2. t oLower case( ) ) ; / / r et ur ns true

appl e1. equalsIgnoreCase( appl e2) ; / / r et ur ns true

In regards to sorting strings, the compareTo(s) method will compare one string with another
(i.e., s) and return information about their respective alphabetical ordering. The method
returns an integer which is:
• negative if the first string is alphabetically before s
• positive if the first string is alphabetically after s, or
• zero if the first string equals s


St r i ng appl e = " Appl e" ;
St r i ng or ange = " Or ange" ;
St r i ng banana = " Banana" ;

banana. compareTo( or ange) ; / / r et ur ns - 13, Banana comes bef or e Orange
banana. compareTo( appl e) ; / / r et ur ns 1, Banana comes af t er Apple
appl e. compareTo( " Appl e" ) ; / / r et ur ns 0, Apple equal s Apple
" Zebr a" . compareTo( " appl e" ) ; / / r et ur ns - 7, uppercase chars are before lower!
" appl e" . compareTo( " Appl e" ) ; / / r et ur ns 32, lowercase chars are after upper!


COMP1005/1405 – Some Useful Tools Fall 2009

- 350 -
You may notice, in the last two cases, that uppercase characters always come alphabetically
before lowercase characters. You should always take this into account when sorting data.
To avoid sorting problems, it may be best to use toUpperCase() on each String before
comparing them:

if ( s1. toUpperCase(). compareTo( s2. toUpperCase()) < 0)
/ / s1 comes f i r st
else
/ / s2 comes f i r st

Another very useful method in the String class is the split() method because it allows you to
break up a String into individual substrings (called tokens) based on some separation
criteria. For example, we can extract
• words from a sentence, one by one
• fields from a database or text file, separated by commas or other chars
The term delimiter is used to indicate the character(s) that separate the tokens (i.e., individual
words or data elements).
Consider for example, the following String data which has been read in from a file:
" Mar k, Lant hi er , 41, M, f al se"

Perhaps this is data for a particular person and we want to extract the information from the
string in a way that we can use it. If we consider the comma ',' character as the only
delimeter, then we can use the split method to obtain an array of Strings which we can then
parse one by one to extract the needed data:

St r i ng s1 = " Mar k, Lant hi er , 41, M, f al se" ;

St r i ng[ ] t okens = s1. spl i t ( " , " ) ;
for( St r i ng t oken: t okens)
Syst em. out . pr i nt l n( t oken) ;

The code above will produce the following output:

Mar k
Lant hi er
41
M
f al se


Each token is an individual String that can be used afterwards. If, for example, we wanted to
have just the 3
rd
piece of data (i.e., 41) and use it in a math expression, we could split the
string and access just that piece of data, converting it to an integer as necessary …

COMP1005/1405 – Some Useful Tools Fall 2009

- 351 -
St r i ng s1 = " Mar k, Lant hi er , 41, M, f al se" ;
St r
int age;
i ng[ ] t okens;

t okens = s1. spl i t ( " , " ) ;
age = I nt eger . par seI nt ( t okens[ 2] ) ;
if ( age > 21) . . .

The " ," parameter to the split() method above indicates that the ',' character is the delimeter.
If we had the following String, however, we may want to include the ':' character as a delimeter
as well:
" Mar k, Lant hi er : 41: M, f al se"
We cannot simply use the parameter string ",:" because that will only consider consecutive
comma colon characters as delimeters (i.e., a 2-char delimiter). We want to allow the comma
OR the colon to be delimiters, but not necessarily together. To accomplish this, the
expression in the string becomes more complex. We basically have to indicate that we want
all non-alphanumeric characters to be part of the tokens and everything else to be delimeters.
So the following code would do what we want:

St r i ng s1 = " Mar k, Lant hi er : 41: ' M' , f al se" ;

St r i ng[ ] t okens = s1. spl i t ( " [ ^a- zA- Z0- 9] " ) ;
for( St r i ng t oken: t okens)
Syst em. out . pr i nt l n( t oken) ;

Notice the square brackets [ ] in the parameter string. Thins indicates that we are about to list
a sequence of characters to be the delimeters. The ^ character negates the list of characters
to indicate that we are about to list all the non-delimeter characters (i.e., the token characters).
Then we list the alphanumeric ranges a-z, A-Z and 0-9 to indicate that any alphanumeric
character is part of a token, while everything else is to be considered a delimeter.

The parameter string is considered to be a regular expression (not discussed here) and can
be quite complex. You may look in J AVA’s API for more information. In some cases, the
token strings will be of size 0. For example, consider the following code:

St r i ng s1 = " Mar k, Lant hi er , 41 , , , M , f al se" ;

St r
for( St r i ng t oken: t okens)
i ng[ ] t okens = s1. spl i t ( " [ , ] " ) ; / / comma or space del i mi t er
Syst em. out . pr i nt l n( t oken) ;

The following output would be obtained …

COMP1005/1405 – Some Useful Tools Fall 2009

- 352 -


Mar k



Lant hi er






41







M




f al se

Notice that there are many spaces in between. These spaces are empty strings. We should
check for the empty strings in our code:

St r i ng s1 = " Mar k, Lant hi er , 41 , , , M , f al se" ;

St r i ng[ ] t okens = s1. spl i t ( " [ , ] " ) ; / / comma or space del i mi t er
for( St r i ng t oken: t okens)
if ( t oken. l engt h( ) > 0)
Syst em. out . pr i nt l n( t oken) ;

Then we obtain the output as before:


Mar k
Lant hi er
41
M
f al se



COMP1005/1405 – Some Useful Tools Fall 2009

- 353 -

Supplemental Information (StringTokenizers)
There is another (perhaps simpler) way of extracting tokens from a String through use of the
StringTokenizer class (imported from the java.util package). However, for some reason,
the J AVA guys “suggest” that you use the split() method instead.
St r i ng s = " Mar k, Lant hi er , 41 , , , M , f al se" ;

St r i ngTokeni zer t okens = new St r i ngTokeni zer ( s, " , " ) ;
Syst em. out . pr i nt l n( " The st r i ng has " + t okens. countTokens() + " t okens" ) ;

while( t okens. hasMoreTokens()) {
Syst em. out . pr i nt l n( t okens. nextToken()) ;
}
This code will produce the same result as above, but with an extra line of output indicating the
number of tokens in total, which is 5 in this example.
Interestingly, the Scanner class that we used for getting keyboard input can also be used to
get tokens from a String. The list of delimeters however is actually a pattern sequence, not a
list of separate delimiter characters. That means, whatever is listed as the delimiter string
must match exactly (i.e., in the example below, a single comma must be followed by a single
space character):
St r i ng sent ence = " Banks, Rob, 34, Ot t awa, 12. 67" ;
Scanner s = new Scanner ( sent ence) . useDel i mi t er ( " , " ) ;
Syst em. out . pr i nt l n( s. next ( ) ) ;
Syst em. out . pr i nt l n( s. next ( ) ) ;
Syst em. out . pr i nt l n( s. next I nt ( ) ) ;
Syst em. out . pr i nt l n( s. next ( ) ) ;
Syst em. out . pr i nt l n( s. next Fl oat ( ) ) ;
s. cl ose( ) ;
Notice that the Scanner should be closed, we did not do this earlier but it is common
practice.



12.2 The StringBuilder & Character Classes

Strings cannot be changed once created. Instead, when we try to
manipulate them, we always get back a "brand new" String object.
This is not normally a problem in most cases when programming,
however, sometimes we would like to be able to modify a String by
inserting/removing characters. For example, when we open a file in
a text editors or word processor, we usually append, cut and insert
text “on the fly”.
COMP1005/1405 – Some Useful Tools Fall 2009

- 354 -
It would be memory-inefficient and time-inefficient to continually create new strings and copy
over characters from an old string to a new one.

The StringBuilder class in J AVA is useful for such a purpose. You may think of it simply as a
String that can be modified. The StringBuilder methods run a little slower that their String
equivalent methods, so if you plan to create strings that will not need to change, use String
objects instead.

Here are two constructors for the StringBuilder class:

new St r i ngBui l der ( ) ;
new St r i ngBui l der ( St r i ng s) ;
The first creates a StringBuilder with no characters to begin with and the second creates one
with the characters equal to the ones in the given String s.
As with Strings, the length() method can be used to return the number of characters in the
StringBuilder as follows:

St r i ngBui l der sb1, sb2;

sb1 = new St r i ngBui l der ( ) ;
sb2 = new St r i ngBui l der ( " hel l o t her e" ) ;
sb1. length(); / / r et ur ns 0
sb2. length(); / / r et ur ns 11


Unlike Strings, you can actually modify the length of the StringBuilder to any particular length
by using a setLength(int newLength) method. If the newLength is less than the current
length, the characters at the end of the StringBuilder are truncated. If the size is greater, null
characters are used to fill in the extra places at the end as follows:


St r i ngBui l der sb;

sb = new St r i ngBui l der ( " hel l o t her e" ) ;
sb. setLength( 9) ;
Syst em. out . pr i nt l n( sb) ; / / di spl ays " hel l o t he"

As with Strings, the charAt(int index) method is used to access particular characters based
on their index position (which starts at position 0). Unlike Strings though, the setCharAt(int
index, char c) method is available which allows you to change the character at the given index
to become the specified character c.
Here are how these methods work …
COMP1005/1405 – Some Useful Tools Fall 2009

- 355 -

St r i ngBui l der name;

name = new St r i ngBui l der ( " Chi p El ect r oni c" ) ;
name. charAt( 3) ; / / r et ur ns ' p'
name. setCharAt( 4, ' +' ) ;
Syst em. out . pr i nt l n( name) ; / / di spl ays "Chip+Electronic"

However, a more commonly used method in the StringBuilder class is the append(Object x)
method which allows you to append a bunch of characters to the end of the StringBuilder. If
x is a String object, the entire string is appended to the end. If x is any other object, J AVA
will call the toString() method for that object and append the resulting String to the end of the
StringBuilder:

St r i ngBui l der sb = new StringBuilder() ;
sb. append( " Mar k has " ) ;
sb. append( new BankAccount ( " Mar k" ) ) ;
Syst em. out . pr i nt l n( sb) ; / / di spl ays " Mar k has Account #10000 wi t h $0. 0"

The resulting output may differ, of course, depending on the BankAccount’s toString()
method. Similar methods also exist for appending an int, long, float, double, boolean or
char as follows:
append(int x), append(long x), append(float x),
append(double x), append(boolean x), append(char x)

The final two methods that we will mention allow you to remove characters from the
StringBuilder. The deleteCharAt(int index) method will remove the character at the given
index while the delete(int start, int end) method will delete all the characters within the indices
ranging from start to end-1 as follows:


St r i ngBui l der sb;

sb = new St r i ngBui l der ( " Mi l es Per l yt er " ) ;
sb. del et e( 3, 11) ; / / changes sb t o " Milter"
sb. del et eChar At ( sb. l engt h( ) - 1) ; / / changes sb t o " Milte"
sb. del et eChar At ( sb. l engt h( ) - 1) ; / / changes sb t o " Milt"


Sometimes, it is useful to use a StringBuilder to go through a String and make changes to it.
For example, consider using a StringBuilder to remove all the non-letter characters from a
String as follows (of course the result would have to be a new String, since the original
cannot be modified) …

COMP1005/1405 – Some Useful Tools Fall 2009

- 356 -

St r i ng or i gi nal , r esul t = " " ;
St r i ngBui l der sb;
Char act er c;

or i gi nal = " Hel l o, my 1st name . . . i s Mar k ! ! " ;
sb = new St r i ngBui l der ( ) ;
for ( int i =0; i <or i gi nal . l engt h( ) ; i ++) {
c = or i gi nal . char At ( i ) ;
if ( Char act er . i sLet t er ( c) )
sb. append( c) ;
}
r esul t = new St r i ng( sb) ;
Syst em. out . pr i nt l n( r esul t ) ;


Notice a couple of things from this code. First, the StringBuilder is used as a
temporary object for creating the result string but is no longer useful after the
method has completed. We use one of the String class’ constructors to create
the new String … passing in the StringBuilder. Second, we are checking for
non-letter characters by using Character.isLetter(). Here, isLetter() is a static
function in the Character class that determines whether or not the given
character is alphabetic or not.
Side note: Character is a class in J AVA known as a wrapper class because it is an object wrapper for
the char primitive. Essentially, the class can be used to “convert” (i.e., wrap up) a char into an object that
can then be used as a regular object. There is a wrapper class for each of the primitives in J AVA (i.e.,
Integer, Long, Float, Double, Character, Boolean, Short and Byte). Since J AVA 1.5, primitives are
automatically wrapped into objects, and so we need not worry about this.
There are other useful methods in the Character class. Here are just a few:
Char act er . i sLet t er ( c) / / checks i f c i s a l et t er i n t he al phabet
Char act er . i sDi gi t ( c) / / checks i f c i s a di gi t ( i . e. , ' 0' - ' 9' )
Char act er . i sLet t er Or Di gi t ( c) / / …t hi s one i s obvi ous …
Char act er . i sWhi t eSpace( c) / / checks i f c i s t he space char act er
Char act er . i sLower Case( c) / / checks i f c i s l ower case ( e. g. , ‘ a’ )
Char act er . i sUpper Case( c) / / checks i f c i s upper case ( e. g. , ‘ A’ )
Char act er . t oLower Case( c) / / r et ur ns l ower case equi val ent of c
Char act er . t oUpper Case( c) / / r et ur ns upper case equi val ent of C

Here are some examples of how they are used:

Char act er . i sLet t er ( ' A' ) / / r et ur ns true
Char act er . i sDi gi t ( ' 6' ) / / r et ur ns true
Char act er . i sLet t er Or Di gi t ( ' @' ) / / r et ur ns false
Char act er . i sWhi t eSpace( ' ' ) / / r et ur ns true
Char act er . i sLower Case( ' a' ) / / r et ur ns true
Char act er . i sUpper Case( ' A' ) / / r et ur ns true
Char act er . t oLower Case( ' B' ) / / r et ur ns ' b'
Char act er . t oUpper Case( ' b' ) / / r et ur ns ' B'
COMP1005/1405 – Some Useful Tools Fall 2009

- 357 -
Note that none of these methods require you to make an instance of a Character object. They
are all static/class methods that take a char as a parameter (int in some cases) and return
another primitive.

12.3 The Date and Calendar Classes

It is often necessary to use dates and times when programming. Let us take a look at the Date
class provided in the java.util package. The Date class allows us to make
data objects that incorporate time as well. The java.util.Date class is used to
represent BOTH date and time. Dates are stored simply as a number, which
happens to be the number of milliseconds since J anuary 1, 1970, 00:00:00
GMT.
New dates are created with a call to a constructor as follows:
Dat e t oday = new Date();
The result is an object that represents the current date and time and it looks something like this
when displayed (of course it will vary depending on the day you run your code):

Thu Mar 26 14: 39: 17 EDT 2009


Notice that it shows the day, month, day-of-month, hours, minutes, seconds, timezone
and year of the Date object. This is default behavior for this class. There are only three other
useful methods in the Date class:

• getTime() - Returns a long representing this time in milliseconds.
• after(Dat e d) - Returns whether or not receiver date comes after the given date d.
• before(Dat e d) - Returns whether or not receiver date comes before the given date d.

Most other methods have been deprecated (which means they should not be used anymore).

In the class Date itself, there is no easy way to create a specific date (e.g., Feb. 13, 1992).
Instead, we must use a different class to do this. In the current version of J AVA,
Calendar objects are used to represent dates, instead of Date objects. Calendar is
an abstract base class for converting between a Date object and a set of integer
fields such as YEAR, MONTH, DAY, HOUR, and so on.

Although this Calendar class has many useful constants and methods (as you will soon see),
we cannot make instances of it (i.e., we cannot say new Calendar()). Instead, the more
specific kind of calendar called a GregorianCalendar is used.
The java.util.GregorianCalendar class is used to query and manipulate dates. Here are some
of the available constructors …
COMP1005/1405 – Some Useful Tools Fall 2009

- 358 -
new GregorianCalendar() / / t oday’ s dat e
new GregorianCalendar(1999, 11, 31) / / year , mont h, day
new GregorianCalendar(1968, 0, 8, 11, 55) / / year , mont h, day, hour s, mi ns
Notice that:
• the year is specified as 4-digits (e.g., 1968)
• months are specified from 0 to 11 (J anuary being 0)
• days must be from 1 to 31
• hours and minutes are at the end of the constructor
Calendars do not display well. Here is what you would see if you tried displaying a
GregorianCalendar:

j ava. ut i l . Gr egor i anCal endar [ t i me=1178909251343, ar eFi el dsSet =t r ue,
ar eAl l Fi el dsSet =t r ue, l eni ent =t r ue, zone=sun. ut i l . cal endar . ZoneI nf o[ i d=
" Amer i ca/ New_Yor k" , of f set =- 18000000, dst Savi ngs=3600000, useDayl i ght =t r ue,
t r ansi t i ons=235, l ast Rul e=j ava. ut i l . Si mpl eTi meZone[ i d=Amer i ca/ New_Yor k,
of f set =- 18000000, dst Savi ngs=3600000, useDayl i ght =t r ue, st ar t Year =0,
st ar t Mode=3, st ar t Mont h=3, st ar t Day=1, st ar t DayOf Week=1, st ar t Ti me=7200000,
st ar t Ti meMode=0, endMode=2, endMont h=9, endDay=- 1, endDayOf Week=1, endTi me=
7200000, endTi meMode=0] ] , f i r st DayOf Week=1, mi ni mal DaysI nFi r st Week=1, ERA=1,
YEAR=2007, MONTH=4, WEEK_OF_YEAR=19, WEEK_OF_MONTH=2, DAY_OF_MONTH=11,
DAY_OF_YEAR=131, DAY_OF_WEEK=6, DAY_OF_WEEK_I N_MONTH=2, AM_PM=1, HOUR=2,
HOUR_OF_DAY=14, MI NUTE=47, SECOND=31, MI LLI SECOND=343, ZONE_OFFSET=
- 18000000, DST_OFFSET=3600000]


Obviously, this is not pleasant. To display a Calendar in a friendlier manner, we must used
the getTime() method, which actually returns a Date object (... not very intuitive … I know).
Consider these examples:
Syst em. out . pr i nt l n( new Gr egor i anCal endar ( ) . getTime()) ; / / t oday
Syst em. out . pr i nt l n( new Gr egor i anCal endar ( 1999, 11, 31) . getTime()) ;
Syst em. out . pr i nt l n( new Gr egor i anCal endar ( 1968, 0, 8, 11, 55) . getTime()) ;

Here is the output (which of course varies with the current date):

Thu Mar 26 14: 48: 40 EDT 2009
Fr i Dec 31 00: 00: 00 EST 1999
Mon J an 08 11: 55: 00 EST 1968


The isLeapYear(int year) method returns whether or not the given year is a
leap year for this calendar:

new Gr egor i anCal endar ( ) . isLeapYear(2008)) ; / / r et ur ns true
new Gr egor i anCal endar ( ) . isLeapYear(2009)) ; / / r et ur ns false

There are many other methods that we can use to query or alter the date which are inherited
from the Calendar class.
COMP1005/1405 – Some Useful Tools Fall 2009

- 359 -
For example, the get(int field) method is used along with some static constants to access
information about the particular calendar date. For example, at the time of updating these
notes the date was:
Thu Mar 26 15:05:35 EDT 2009
Consider the results (shown to the right) of each get method call in the code below. You
should use import java.util.Calendar at the top of your code so that you can use these
constants:
Cal endar t oday = Cal endar . get I nst ance( ) ;

t oday. get( Cal endar . YEAR) ; / / 2009
t oday. get( Cal endar . MONTH) ; / / 2
t oday. get( Cal endar . DAY_OF_MONTH) ; / / 26
t oday. get( Cal endar . DAY_OF_WEEK) ; / / 5
t oday. get( Cal endar . DAY_OF_WEEK_I N_MONTH) ; / / 4
t oday. get( Cal endar . DAY_OF_YEAR) ; / / 85
t oday. get( Cal endar . WEEK_OF_MONTH) ; / / 4
t oday. get( Cal endar . WEEK_OF_YEAR) ; / / 13
t oday. get( Cal endar . HOUR) ; / / 3
t oday. get( Cal endar . AM_PM) ; / / 1
t oday. get( Cal endar . HOUR_OF_DAY) ; / / 15
t oday. get( Cal endar . MI NUTE) ; / / 5
t oday. get( Cal endar . SECOND) ; / / 35

The value returned from the get(int field) method can be compared with other Calendar
constants. For example,

if ( aCal endar . get( Cal endar . MONTH) == Cal endar . APRI L) {. . . }
if ( aCal endar . get( Cal endar . DAY_OF_WEEK) == Cal endar . SATURDAY) {. . . }

Here are some of the useful constants:


Cal endar . SUNDAY
Cal endar . MONDAY
Cal endar . TUESDAY
Cal endar . WEDNESDAY
Cal endar . THURSDAY
Cal endar . FRI DAY
Cal endar . SATURDAY

Cal endar . J ANUARY
Cal endar . FEBRUARY
Cal endar . MARCH
Cal endar . APRI L
Cal endar . MAY
Cal endar . J UNE
Cal endar . AM

Cal endar . J ULY
Cal endar . AUGUST
Cal endar . SEPTEMBER
Cal endar . OCTOBER
Cal endar . NOVEMBER
Cal endar . DECEMBER
Cal endar . PM


There is also a set(int field, int value) method that can be used to set the values for certain
date fields:

aCal endar . set( Cal endar . MONTH, Cal endar . J ANUARY) ;
aCal endar . set( Cal endar . YEAR, 1999) ;
aCal endar . set( Cal endar . AM_PM, Cal endar . AM) ;
Other set methods allow the date and time to be changed …
COMP1005/1405 – Some Useful Tools Fall 2009

- 360 -
aCal endar . set( 1999, Cal endar . AUGUST, 15) ;
aCal endar . set( 1999, Cal endar . AUGUST, 15, 6, 45) ;

We can also format dates when we want to print them nicely. There is a SimpleDateFormat
class (in the java.text package) that formats a Date object using one of many predefined
formats. It does this by generating a String representation of the date. The constructor
takes a String which indicates the desired format:

new Si mpl eDat eFor mat ( " MMM dd, yyyy" ) ;

The parameter in the method is a format string that specifies “how you want the date to look”
when it is printed. By using different characters in the format string, you get different output
for the date. The format(Date d) method in the SimpleDataFormat class is then used to
actually do the work by applying the format to the given date. Here is an example:

import j ava. t ext . Si mpl eDat eFor mat ;
/ / . . .

Si mpl eDat eFor mat dat eFor mat t er = new Si mpl eDat eFor mat ( " MMM dd, yyyy" ) ;
Dat e t oday = new Dat e( ) ;
St r i ng r esul t = dat eFor mat t er . format( t oday) ;

Syst em. out . pr i nt l n( r esul t ) ;


Here is the result (which would vary, depending on the date):

Mar 26, 2009


Here are examples of format Strings and their effect on the date April 30th 2001 at 12:08 PM:

Format String Resulting output

without formatting

" yyyy/ MM/ dd"
" yy/ MM/ dd"
" MM/ dd"
" MMM dd, yyyy"
" MMMM dd, yyyy"
" EEE. MMMM dd, yyyy"
" EEEE, MMMM dd, yyyy"
" h: mma"
" MMMM dd, yyyy ( hh: mma) "

Tue Apr 10 15: 07: 52 EDT 2001

2001/ 04/ 30
01/ 04/ 30
04/ 30
Apr 30, 2001
Apr i l 30, 2001
Mon. Apr i l 30, 2001
Monday, Apr i l 30, 2001
12: 08 PM
Apr i l 30, 2001 ( 12: 08PM)


For additional formatting information, check out the J AVA API specification. Here is a simple
example that creates two dates. One representing today, the other representing a future date:

COMP1005/1405 – Some Useful Tools Fall 2009

- 361 -
import j ava. ut i l . *;
import j ava. t ext . Si mpl eDat eFor mat ;

public class Dat eTest Pr ogr am{
public static void mai n ( St r i ng ar gs[ ] ) {

Cal endar t oday = Cal endar . get I nst ance( ) ;
Cal endar f ut ur e;
int di f f er ence;

/ / Di spl ay I nf or mat i on about t oday' s dat e and t i me
Syst em. out . pr i nt l n( " Her e i s t oday: " ) ;
Syst em. out . pr i nt l n( t oday. get Ti me( ) ) ;
Syst em. out . pr i nt l n( t oday. get ( Cal endar . YEAR) ) ;
Syst em. out . pr i nt l n( t oday. get ( Cal endar . MONTH) ) ;
Syst em. out . pr i nt l n( t oday. get ( Cal endar . DAY_OF_MONTH) ) ;

/ / Di spl ay I nf or mat i on about a f ut ur e day' s dat e and t i me
f ut ur e = Cal endar . get I nst ance( ) ;
f ut ur e. set ( 2010, Cal endar . MARCH, 5) ;
Syst em. out . pr i nt l n( " Her e i s t he f ut ur e: " ) ;
Syst em. out . pr i nt l n( f ut ur e. get Ti me( ) ) ;
Syst em. out . pr i nt l n( f ut ur e. get ( Cal endar . YEAR) ) ;
Syst em. out . pr i nt l n( f ut ur e. get ( Cal endar . MONTH) ) ;
Syst em. out . pr i nt l n( f ut ur e. get ( Cal endar . DAY_OF_MONTH) ) ;

/ / Test t he f or mat t i ng
Dat e aDat e = new Dat e( ) ;
Syst em. out . pr i nt l n( aDat e) ;
Syst em. out . pr i nt l n( new Si mpl eDat eFor mat ( " yyyy/ MM/ dd" ) . f or mat ( aDat e) ) ;
Syst em. out . pr i nt l n( new Si mpl eDat eFor mat ( " yy/ MM/ dd" ) . f or mat ( aDat e) ) ;
Syst em. out . pr i nt l n( new Si mpl eDat eFor mat ( " MM/ dd" ) . f or mat ( aDat e) ) ;
Syst em. out . pr i nt l n( new Si mpl eDat eFor mat ( " MMM dd, yyyy" ) . f or mat ( aDat e) ) ;
Syst em. out . pr i nt l n( new Si mpl eDat eFor mat ( " MMMM dd, yyyy" ) . f or mat ( aDat e) ) ;
}
}

Here is the output from running this code on March 26, 2009:

Her e i s t oday:
Thu Mar 26 15: 24: 11 EDT 2009
2009
2
26
Her e i s t he f ut ur e:
Fr i Mar 05 15: 24: 11 EST 2010
2010
2
5
Thu Mar 26 15: 24: 11 EDT 2009
2009/ 03/ 26
09/ 03/ 26
03/ 26
Mar 26, 2009
Mar ch 26, 2009


COMP1005/1405 – Some Useful Tools Fall 2009

- 362 -
Notice that the months start at 0, and so March is month #2.
Although we can create and display simple dates, we have not done any manipulation at all.
For instance, we may want to know how many working days there are between two dates.
There are many more functions in the Calendar and Date classes, but we will not discuss
them any further here. You would have to look at the API for the Date, Calendar,
GregorianCalendar and SimpleDateFormat classes.

Supplemental Information (Formatting Dates with Strings)
We can also use the String.format() method to format dates and times. There are many
flags that can be used (see the API for details) but here are some commonly used ones for
displaying dates and times:
Dat e aDat e = new Dat e( ) ;

Syst em. out . pr i nt l n( St r i ng. f or mat ( " %t c" , aDat e) ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %t F" , aDat e) ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %t R" , aDat e) ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %t r " , aDat e) ) ;
Syst em. out . pr i nt l n( St r i ng. f or mat ( " %t D" , aDat e) ) ;
Here was the output ran on March 26, 2009 at 3:26pm:
Thu Mar 26 15: 26: 56 EDT 2009
2009- 03- 26
15: 26
03: 26: 56 PM
03/ 26/ 09



12.4 Iterators

The simplest way to traverse elements of a collection using a FOR EACH loop is as follows:
for ( Per son p: peopl e) {
Syst em. out . pr i nt l n( p. get Name( ) ) ;
}

Sometimes however, we want to remove certain elements of a collection (e.g., ArrayList) as
we traverse through it. For example, consider removing from an ArrayList all people who are
under the age of 21 as follows …
COMP1005/1405 – Some Useful Tools Fall 2009

- 363 -
import j ava. ut i l . Ar r ayLi st ;

public class I t er at or Test Pr ogr am1 {
public static void mai n( St r i ng ar gs[ ] ) {
Ar r ayLi st <Per son> peopl e = new Ar r ayLi st <Per son>( ) ;
peopl e. add( new Per son( " Pet e" , " Zar i a" , 12, ' M' , false) ) ;
peopl e. add( new Per son( " Ri t a" , " Book" , 20, ' F' , false) ) ;
peopl e. add( new Per son( " Wi l l i e" , " Mayki t " , 65, ' M' , true) ) ;
peopl e. add( new Per son( " Pat t y" , " O' Fur ni t ur e" , 41, ' M' , false) ) ;
peopl e. add( new Per son( " Sue" , " Per mann" , 73, ' F' , true) ) ;
peopl e. add( new Per son( " Si d" , " Down" , 19, ' M' , false) ) ;
peopl e. add( new Per son( " J ack" , " Pot " , 4, ' M' , false) ) ;

for ( Per son p: peopl e) {
if ( p. get Age( ) < 21)
peopl e. r emove( p) ;
}
for ( Per son p: peopl e) {
Syst em. out . pr i nt l n( p) ;
}
}
}


If we were to run the above code, this would be the output:

Except i on i n t hr ead " mai n" j ava. ut i l . ConcurrentModificationException
at j ava. ut i l . Abst r act Li st $I t r . checkFor Comodi f i cat i on( Abst r act Li st . j ava: 372)
at j ava. ut i l . Abst r act Li st $I t r . next ( Abst r act Li st . j ava: 343)
at I t er at or Test Pr ogr am1. mai n( I t er at or Test Pr ogr am1. j ava: 15)


A ConcurrentModificationException occurs on line 13 (i.e., when we do remove(p)). Why
? The exception’s name indicates that we are trying to modify something at the same time as
doing something else (i.e., concurrently). As it turns out, J AVA does NOT want us removing
elements from a collection while we are looping through it using a FOR EACH loop. But why
does J AVA do this ? Well, consider using a regular FOR loop in place of the FOR EACH loop
as in the following code …
public class I t er at or Test Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
Ar r ayLi st <Per son> peopl e = ...;
...
for ( int i =0; i <peopl e. si ze( ) ; i ++) {
Per son p = peopl e. get ( i ) ;
if ( p. get Age( ) < 21)
peopl e. r emove( p) ;
}
for ( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;
}
}
COMP1005/1405 – Some Useful Tools Fall 2009

- 364 -

If we were to run the above code, it would generate this output:

20 year ol d non- r et i r ed per son named Ri t a Book
65 year ol d r et i r ed per son named Wi l l i e Mayki t
41 year ol d non- r et i r ed per son named Pat t y O' Fur ni t ur e
73 year ol d r et i r ed per son named Sue Per mann
4 year ol d non- r et i r ed per son named J ack Pot


Notice that it properly removed Pete Zaria (who was under 21), but did not remove Rita Book,
nor Jack Pott who were both under 21. What happened ?
When we remove an element from our list, all elements after it in the list are shifted up in the
list. That is, they move locations so that their index is one smaller than it used to be.
However, our FOR loop specifies the index to look at each time through the loop. Since we
removed the element at index i, the item at position i+1 is moved into that location so that it
now has index i. We would need to re-check position i next time through the loop so as not to
skip over the item that was just shifted into that position. Therefore, we would need to adjust i
accordingly as follows:
...

for ( int i =0; i <peopl e. si ze( ) ; i ++) {
Per son p = peopl e. get ( i ) ;
if ( p. get Age( ) < 21) {
peopl e. r emove( p) ;
i - - ;
}
}


Then our output will be correct. However, this is a little messy and sometimes hard to catch.
J AVA therefore provides us with a way of removing elements from a
collection of which we are traversing, by use of an Iterator. Iterators are
"middle-man" objects that are used to help us to traverse through the objects
of a collection in an organized "in-order" manner. It is similar to the idea of a
doorman at a night club who lets one person into the club at a time when
instructed by someone inside to do so. It makes sure that nobody “slips
through the cracks” as the expression goes. Or, the iterator can be thought
of as someone at the front of the line at a bank, instructing individuals to go to the teller, one at
a time when instructed to do so.
Basically, an iterator actually works by "handing you" one
object at a time from the collection until there are no more
remaining. It also allows you to discard an object from the
collection, as long as it was the object that was just handed
to you. The iterators are meant to traverse (i.e., enumerate
through) the elements of a collection exactly one time only.
COMP1005/1405 – Some Useful Tools Fall 2009

- 365 -
Many methods in J AVA return Iterators instead of collections like ArrayLists. Hence,
Iterators are widely used in J AVA.
Iterators need a collection of objects to iterate through. It should not be surprising then that to
make an iterator, we simply call the iterator() method on an ArrayList (or any other Collection
type). This method returns an Iterator object that can be stored in an Iterator type variable:


Ar r ayLi st <Per son> peopl e;
I t er at or <Per son> l i neup;
. . .
l i neup = peopl e. iterator();


Notice that we did not call any constructors to make the Iterator object. Remember, that you
must import java.util.ArrayList to use the ArrayList class and you must also import
java.util.Iterator in order to use the Iterator object type. You can simply do import
java.util.*; to import both at the same time.
Notice as well that we specified the type of object that the Iterator will loop through (i.e.,
Person). This is not required, but it prevents us from having to type-cast everything when we
take them out later. Once we have the iterator, there are three methods that we can use on it:
• hasNext() ... returns a boolean indicating whether there are any more items left.
• next() ... removes & returns next item (automatically type-casted to specified type)
• remove() ... removes the latest item that was obtained from the last call to next().
To use the iterator, we just need to make successive calls to next() to obtain the elements
from the ArrayList. Normally we use a while loop with hasNext() as the sole condition. Here
is the “iterator version” of our previous example:
import j ava. ut i l . I t er at or ;
import j ava. ut i l . Ar r ayLi st ;

public class I t er at or Test Pr ogr am3 {
public static void mai n( St r i ng ar gs[ ] ) {
Ar r ayLi st <Per son> peopl e;
I t er at or <Per son> l i neup;

peopl e = ... / / same code as bef or e …omi t t ed t o save space

l i neup = peopl e. iterator();
while ( l i neup. hasNext()) {
Per son p = l i neup. next();
if ( p. get Age( ) < 21)
l i neup. remove();
}
for ( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;
}
}
COMP1005/1405 – Some Useful Tools Fall 2009

- 366 -
Notice a few things here. First, we do not need to type-cast to Person once we retrieve the
next item from the iterator, provided that we declared the Iterator object to use Person objects
(i.e., Iterator<Person>). Also, in order to remove the item from the ArrayList, we actually call
the Iterator's remove() method, not the ArrayList's remove()
method!!! This is not so intuitive. You may think of it as follows.
Assume that you asked the doorman at the nightclub to let the next
person through, but then you decide for some reason that this person
is not allowed in (perhaps under age). You then ask the doorman to
remove that person, we don’t remove the person by ourselves.
In J AVA, it is the same situation. we don't specify anywhere the
actual object that we want removed because J AVA will actually remove (from the ArrayList),
the item that was last obtained from the Iterator. So we cannot remove arbitrary objects, only
the last one that we just looked at from using the next() method.
A common mistake when using iterators is to call next() twice during a single pass through the
loop. For example, when printing out the items using an iterator:

while ( l i neup. hasNext()) {
Syst em. out . pr i nt l n( l i neup. next(). get Fi r st Name( ) + " " +
l i neup. next(). get Last Name( ) ) ;
}


The above code causes two items to be extracted each time through the loop, which usually
causes the Iterator to run out of objects too quickly and may also result in mixed data (e.g.,
first name of one person displayed with second name of a different person). You would get a
NoSuchElementException with the above code if the number of people is odd. Try to be
careful that you do not do this.

Even in situations where we simply want to iterate through the elements of a collection, there is
another advantage of using an Iterator as opposed to just looping through the elements. An
Iterator maintains indexing information about the collection (i.e., it remembers the position that
it last looked at in the collection). Therefore, we need not iterate through the entire collection
in one shot. We could iterate through a few items and then stop (e.g., if interrupted) and
continue later on in our program, provided that we still have the iterator object.

For example, imagine processing, in some way, the Person objects in an ArrayList. Perhaps
while processing, we find a situation that requires us to stop processing (i.e., an exception
occurred or something arose with a higher priority).

With iterators, the code may look as follows …

COMP1005/1405 – Some Useful Tools Fall 2009

- 367 -

public boolean doPr ocessi ng( I t er at or <Per son> l i st ) {
try {
while ( l i st . hasNext ( ) )
pr ocess( l i st . next ( ) ) ;
return true; / / r et ur n true si nce done now
}
catch( Somet hi ngBadHappenedExcept i on ex) {
return false; / / r et ur n false si nce not done yet
}
}


Notice that the method returns a boolean that simply indicates whether or not we were done
processing the list of people. We could then examine this boolean value and decide later
whether or not to call the method again to do further processing.

We could accomplish the same thing without using an Iterator. However, a bit more “book
keeping” is involved, in that we must remember ourselves the position in the list that we left off
at when we are interrupted:


public int doPr ocessi ng( Ar r ayLi st <Per son> l i st , int st ar t i ngPosi t i on) {
try {
for ( int i =st ar t i ngPosi t i on; i <l i st . si ze( ) ; i ++) {
pr ocess( ( Per son) l i st . get ( i ) ) ;
}
return l i st . si ze( ) ; / / al l done, r et ur n maxi mumi ndex
}
catch( Somet hi ngBadHappenedExcept i on ex) {
return i +1; / / r et ur n t he i ndex of t he next i t emt o pr ocess
}
}


Notice that we must now return an integer to represent the position that we were at when we
quit the method. We would then need to examine the position to see if it reached the end of
the list. Notice that we must also pass in this startingPosition to the method each time so
that we start in the correct spot. So, the code is more complex, but certainly do-able. The
Iterator solution is simpler and cleaner.

Supplemental Information (Enumerations)

There is an older interface type in J AVA called an Enumeration, which works similarly to the
Iterator, but without a remove() method. It has two similar methods available:
• hasMoreElements() ... are there any more left ?
• nextElement() ... get me the next one


Chapter 13
Other Collections


What is in This Chapter ?
We have already looked collections called ArrayLists and Arrays. There are actually other
collections in J AVA which are useful for certain purposes. We will look here at the
organization of some of the collection-related classes as well as explain the differences
between the various List classes. We will look at two examples of how to use Stacks as well
as how to Sort objects in a collection easily using the tools provided in J AVA’s Collections
class. Finally, we will show how we can remove duplicate objects by using Sets.






COMP1005/1405 – Other Collections Fall 2009

- 369-

13.1 Collection Organization

Collections are a vital part of any programming language. As we have already seen with
ArrayLists and Arrays, they allow many objects to be collected together, stored and passed
around as one object (i.e., the collection itself). J ust about every useful application of any kind
requires collections for situations such as:
• storing products on shelves in a store
• maintaining information on customers
• keeping track of cars for sale, for rental or for servicing
• a personal collection of books, CDs, DVDs, cards, etc...
• maintaining a shopping cart of items to be purchased from a website





In J AVA, there is a small variety of collection-related classes that can be used to represent
these various programming needs. These collections are located in the java.util.Collection
package, along with some other useful tools. In this set of notes, we investigate (very briefly)
some of these J AVA collections in a way that will help a programmer understand which
collection is best for their particular programming application.

Collections are organized into a “seemingly complicated” hierarchy of J AVA interfaces and
classes. Here is a diagram showing part of this hierarchy:



















Vector
Stack
AbstractCollection
SortedSet
LinkedList
Set
List
AbstractList
AbstractSet
AbstractSequentialList ArrayList
HashSet TreeSet
Queue
Deque AbstractQueue
PriorityQueue ArrayDeque
Interface
Abstract Class
Concrete Class
Collection
COMP1005/1405 – Other Collections Fall 2009

- 370-
Notice that there are 8 concrete classes, and that all of them indirectly implement the
Collection interface. Recall that an interface just specifies a list of method signatures ... not
any code. That means, all of the concrete collection classes have something in common and
that they all implement a common set of methods. The main commonality between the
collection classes is that they all store objects called their elements, which may be
heterogeneous objects (i.e., the elements may be a mix of various (possibly unrelated)
objects). Storing mixed kinds of objects in a Collection is allowed, but not often done unless
there is something in common with the objects (i.e., they extend a common Abstract class or
implement a common Interface).

The Collection interface defines common methods for querying (i.e., getting information from)
and modifying (i.e., changing) the collection in some way. Here is a list of some of the
common functionality available from all collections:
Querying methods (returns some kind of value):
size() returns the number of elements in the collection

isEmpty() returns whether or not si ze( ) == 0

contains(Object obj) returns whether or not given object obj is in the collection
(uses equals() method for comparison)

containsAll(Collection c) same as above but looks for ALL elements specified
in the given collection c.

Modifying methods (changes the collection in some way):

add(Object obj) adds the given object as an element to the collection
(its location is not specified)

addAll(Collection c) same as above but adds ALL elements specified in the
given collection c.

remove(Object obj) removes the given object from the collection
(uses equals() method for comparison)

removeAll(Collection c) same as above but removes ALL elements specified in
the given collection c.

retainAll(Collection c) same as above but removes all elements EXCEPT
THOSE specified in the given collection c.

clear() empties out the collection by removing all elements.
Now although the various collection types implement these methods, there are various
restrictions for each of the collection classes that when followed, produce more efficient code.
COMP1005/1405 – Other Collections Fall 2009

- 371-
For example, lets consider the differences between a List, a Queue, a Deque and a Stack:

Insert/remove Insert/remove anywhere anytime
from top
23
23 15 9 12 … 37 83
23 15 9 12 … 37 83
23 15 9 12 … 37 83
83
37

12
9
15
STACK
QUEUE
DEQUE
LIST
Insert/remove from front or back
Insert at back, remove from front









A List (e.g., an ArrayList) allows us to access any of its elements at any time as well as
insert or remove elements anywhere in the list at any time. The list
will automatically shift the elements around in memory to make the
needed room or reduce the unused space. The general List is the
most flexible kind of list in terms of its capabilities. We use a
general List whenever we have elements coming in and being
removed in a random order. For example, when we have a shelf of
library books, we may need to remove a book from anywhere on the
shelf and we may insert books back into their proper location when
they are returned. Typical methods for Lists are:

• add(int index, Object x)
• remove(int index)
• get(int index)
• set(int index, Object x)


Consider the Queue. A queue is used to store elements
in a First-in-First-out (FIFO) order. In other words, the first
element to be added to the queue is the first element to be
taken out of the queue. This is analogous to a line-up that
we see every day. The first person in line is the first
person served (i.e., first-come-first-served). When people
come, they go to the back of the line. People get served
COMP1005/1405 – Other Collections Fall 2009

- 372-
from the front of the line first. Therefore, with a queue, we add to the back and remove from
the front. We are not allowed to insert or remove elements from the middle of the queue.
Why is this restriction a good idea ? Well, depending on how the queue is implemented, it can
be more efficient (i.e., faster) to insert and remove elements since we know that all such
changes will occur at the front or back of the queue. Removing from the front may then simply
require moving the “front-of-the-line pointer” instead of shifting elements over. Also, adding to
the back may require extending the “back-of-the-line pointer”. Typical methods for Queues
are:

• add(Object x)
• remove()
• peek()


What about a Deque ? A deque is a “Double-Ended-QUEue”. It allows us to add/remove
from the front or the back of the queue at any time, but no modifications to the middle. It has
the same advantages of a regular single-ended Queue, but is a
little more flexible in that it allows removal from the back of the
queue and insertion at the front. An example of where we
might use a deque is when we implement “Undo” operations
in a piece of software. Each time we do an operation, we
add it to the front of the deque. When we do an undo, we
remove it from the front of the deque. Since undo
operations usually have a fixed limit defined somewhere in the
options (i.e., maximum 20 levels of undo), we remove from
the back of the deque when the limit is reached. Typical methods for Deques are:

• addFirst(Object x)
• addLast(Object x)
• removeFirst()
• removeLast()
• getFirst()
• getLast()

Finally, a Stack is used to store elements in a Last-in-First-out
(LIFO) order. That is, we store information like a stack of items one on
top of the other. When a new item comes in, we place it on the top of
the stack and when we want to remove an item, we take the top one
from the stack. Stacks are used for many applications in computer
science such as syntax parsing, memory management, reversing data,
backtracking, etc.. Typical methods for Stacks are:
• push(Object x)
• pop()
• isEmpty()
• peek()
COMP1005/1405 – Other Collections Fall 2009

- 373-
It is not the purpose of this course to describe in detail various kinds of collections and data
structures. You will gain a deeper understanding of the advantages and disadvantages
between the various data structures in your second year COMP2402 course. For fun
however, we will do a couple of examples to help you understand how to use a couple of these
J AVA classes.


13.2 Example: Bracket Matching

Lets look at an example of using a data structure to
solve a simple problem of matching brackets. Consider
a math expression that contains numbers, operators
and parentheses (i.e., round brackets). How could we
write a program that takes a String representing a
math expression and then determines whether or not
the brackets match properly (i.e., each opening bracket
has a matching closing bracket in the right order) ?

" ((23 + 4 * 5) - 34) + (34 - 5))" / / no mat ch
" ((23 + 4 * 5) - 34) + ((34 - 5)" / / no mat ch
" ((23 + 4 * 5) - 34) + (34 - 5)" / / mat ch

How would we approach solving this problem? Well, we need to understand the process. I’m
sure that you realize that we need to look at all the String’s characters. Perhaps from start to
end with a loop, but then what do we do ?

Lets assume that we are not interested in determining whether the formula makes sense but
rather that each opening bracket is matched by a closing bracket. Therefore, we are
interested in the bracket characters ( and ), but not the other characters. When encountering
an open bracket as we go through the characters of the string, we need to do something. We
might think right away of trying to find the matching closing bracket for each open bracket, but
that is not as easy as it sounds. There are many special cases that can be tricky.
A simpler approach would be to make sure that whenever we find a closing bracket, we just
need to make sure that we already encountered an open bracket to match with it. This can
be done by keeping a count of the number of open brackets. When encountering an opening
bracket we increment the counter and when encountering a closing bracket we decrement the
counter. If, when all done, the counter is not zero, there is no match. Otherwise the brackets
match. Consider these cases:
" ()" / / count er = 0, mat ch
" ()(" / / count er = 1, no mat ch
" (((" / / count er = 3, no mat ch
" ((())())" / / count er = 0, mat ch
" (()))" / / count er = - 1, no mat ch
" " / / count er = 0, mat ch

COMP1005/1405 – Other Collections Fall 2009

- 374-
There is a special case that we did not consider. If the counter ever becomes negative before
we are done, then we must have encountered a closing bracket before an open bracket … and
there is no match:

" )(" / / count er = - 1, no mat ch
" ())(" / / count er = - 1, no mat ch

So, how do we write the code ? We can use a FOR loop and some IF statements to check for
brackets as follows:


public static boolean br acket sMat ch( St r i ng s) {
int count = 0;
char c;

for ( int i =0; i <s. l engt h( ) ; i ++) {
c = s. char At ( i ) ;
if ( c == ' ( ' ) count ++;
if ( c == ' ) ' ) count - - ;
if ( count < 0)
return false;
}
return count == 0;
}


Here is a test program to try it out:

import j ava. ut i l . *;

public class Br acket Mat chi ngTest Pr ogr am{

public static boolean br acket sMat ch( St r i ng s) { / * code as above */ }

public static void mai n( St r i ng ar gs[ ] ) {
Scanner keyboar d = new Scanner ( Syst em. i n) ;
St r i ng aSt r i ng;

do {
Syst em. out . pr i nt l n( " Pl ease ent er expr essi on: ( <cr > t o qui t ) " ) ;
aSt r i ng = keyboar d. next Li ne( ) ;

if ( br acket sMat ch( aSt r i ng) )
Syst em. out . pr i nt l n( " The br acket s mat ch" ) ;
else
Syst em. out . pr i nt l n( " The br acket s do not mat ch" ) ;
} while ( aSt r i ng. l engt h( ) > 0) ;
}
}

COMP1005/1405 – Other Collections Fall 2009

- 375-
Here are some testing results:


Pl ease ent er expr essi on: ( <cr > t o qui t )
((23 + 4 * 5) - 34) + (34 - 5))
The br acket s do not mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
((23 + 4 * 5) - 34) + ((34 - 5)
The br acket s do not mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
((23 + 4 * 5) - 34) + (34 - 5)
The br acket s mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
()
The br acket s mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
()(
The br acket s do not mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
(((
The br acket s do not mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
((())())
The br acket s mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
(()))
The br acket s do not mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
)(
The br acket s do not mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )
())(
The br acket s do not mat ch
Pl ease ent er expr essi on: ( <cr > t o qui t )

The br acket s mat ch

I think that our solution works fine. The bracket-matching example above is not very difficult,
but what if we have 3 kinds of brackets (, [, and { ? Consider this example:
" {2 +( 3 *[ 4 - 5}] ) " / / not supposed t o mat ch
Maybe we can keep 3 counters ? If we just keep three counters separately, we cannot tell
whether the brackets are well formed with respect to one another. We somehow need to
know the ordering of each type of bracket so that we can ensure the reverse ordering when we
find the closing brackets.

The need for backtracking may seem a little clearer if we consider a different application of the
bracket matching program.

Suppose that we want to match the brackets in our J AVA code…

COMP1005/1405 – Other Collections Fall 2009

- 376-
public class PrintWriterTestProgram {
public static void main(String args[]) {
try {
BankAccount aBankAccount;
PrintWriter out;

aBankAccount =new BankAccount("Rob Banks");
aBankAccount.deposit(100);
out =new PrintWriter(new FileWriter("myAccount2.dat"));
out.println(aBankAccount.getOwner());
out.println(aBankAccount.getAccountNumber());
out.println(aBankAccount.getBalance());
out.close();
} catch (FileNotFoundException e) {
System.out.println("Error: Cannot open file for writing");
} catch (IOException e) {
System.out.println("Error: Cannot write to file");
}
}
}

Here we see, for example, that the portion of code inside the class definition must have all of
its brackets matching, and that involves matching the code inside the main method’s body and
then within the try block etc… The compiler does this kind of bracket matching to make sure
that your code is well-formed.

The stack data structure is designed for this purpose. It allows us to back-track … which is
essentially what we need to do when finding a closing bracket. Here is how we can use a
stack. When we find an open bracket, we put it on the top of the stack, regardless of its type.
When we find a closing bracket, we take the top opening bracket from the stack and check to
see if it is the same type as the closing bracket. If not, the brackets are in the wrong order,
otherwise all is fine and we continue onwards. If, when encountering a closing bracket, we
find that the stack is empty, then there is no match either.

Lets look at the code. In J AVA, we make a Stack by simply calling its constructor:

St ack aSt ack = new St ack( ) ;

However, in our case, we are going to be placing bracket character on the Stack. Therefore
we should specify this as follows:

St ack<Char act er >aSt ack = new St ack<Char act er >( ) ;

Then, we need to use the following Stack methods:
• push(Object obj) – to place a given object on the top of the stack
• pop() – to remove and return the top element of the stack.
• empty() – to determine whether or not there are any elements in the stack.
COMP1005/1405 – Other Collections Fall 2009

- 377-
Here is the resulting code:

public static boolean br acket sMat ch( St r i ng s) {
St ack<Char act er > aSt ack;
char c, t op;

aSt ack = new St ack<Char act er >( ) ;
for ( int i =0; i <s. l engt h( ) ; i ++) {
c = s. char At ( i ) ;

if ( ( c == ' ( ' ) | | ( c == ' [ ' ) | | ( c == ' {' ) ) / / got open br acket
aSt ack. push(c);

if ( ( c == ' ) ' ) | | ( c == ' ] ' ) | | ( c == ' }' ) ) { / / got cl osed br acket
if ( aSt ack. empty())
return false; / / no open br acket f or t hi s cl osed one

t op = aSt ack. pop(); / / get t he l ast openi ng br acket f ound
if ( ( ( c == ' ) ' ) && ( t op ! = ' ( ' ) ) | |
( ( c == ' ] ' ) && ( t op ! = ' [ ' ) ) | |
( ( c == ' }' ) && ( t op ! = ' {' ) ) )
return false; / / wr ong cl osi ng br acket f or l ast opened one
}
}
return aSt ack. empty(); / / No mat ch i f br acket s ar e l ef t over
}

Notice in the above code that it never has return true anywhere. In fact, it is only at the very
end, once we have checked all characters that there is a chance for the method to return true.
This will happen if the stack is empty (i.e., all open brackets have been matched with closing
ones). If desired, you can simplify the above code by replacing the IF statements with a
SWITCH statement as follows:

switch( c) {
case ' ( ' :
case ' [ ' :
case ' {' :
aSt ack. push(c); / / got open br acket
break;
case ' ) ' :
if ( aSt ack. empty() | | ( aSt ack. pop() ! = ' ( ' ) )
return false;
break;
case ' ] ' :
if ( aSt ack. empty() | | ( aSt ack. pop() ! = ' [ ' ) )
return false;
break;
case ' }' :
if ( aSt ack. empty() | | ( aSt ack. pop() ! = ' {' ) )
return false;
break;
}

COMP1005/1405 – Other Collections Fall 2009

- 378-
Here are some testing results that we would obtain if we replaced our previous
bracketsMatch() method with this new method:
Here are some testing results that we would obtain if we replaced our previous
bracketsMatch() method with this new method:


Pl ease ent er t he expr essi on: ( j ust <cr > t o qui t )
](){}[
The br acket s do not mat ch
Pl ease ent er t he expr essi on: ( j ust <cr > t o qui t )
()[]{}
The br acket s mat ch
Pl ease ent er t he expr essi on: ( j ust <cr > t o qui t )
{{(([[]]))}}
The br acket s mat ch
Pl ease ent er t he expr essi on: ( j ust <cr > t o qui t )
{{{{{{
The br acket s do not mat ch
Pl ease ent er t he expr essi on: ( j ust <cr > t o qui t )
}}}}}}
The br acket s do not mat ch
Pl ease ent er t he expr essi on: ( j ust <cr > t o qui t )
((()[]{})[()[]{])
The br acket s do not mat ch
Pl ease ent er t he expr essi on: ( j ust <cr > t o qui t )
The br acket s mat ch


Challenge: Could you adjust the code above to read in a J AVA file instead of using a fixed
string and have it ensure that the brackets match ?


13.3 Example: Maze Search

Recall the practice question from our discussion of 2D arrays. We had a maze, represented
as an array of integers (where 0 meant an open space and 1 meant a wall was there). We
wanted to know if we could find a path from a start location in the maze to an end location:


1 1 1 1 1 1 1 1
1 0 0 0
1 0 1 0
1 0 1 1
0 0 0 0
1 1 1 1
1 2 0 1
1 0 1 1
1 0 1 0
1 0 1 0
1 1 1 1
0 0 0 0
1 0 1 1
0 0 1 0
1 1 1 3
0 1 2 3 4 5 6
1
1
0
1
0
0
0
1
1
1
1
1
1
1
1
1
7 8 9
0
1
2
3
4
5
6
7
6
7













I’m sure you will agree if you were in the maze searching for the goal location, that you would
occasionally encounter a dead-end and have to back track a little bit, trying another direction.
COMP1005/1405 – Other Collections Fall 2009

- 379-
Since we are doing backtracking, could we use a stack to find a solution ? What would we
even put in the stack ?

Well, the stack could keep track of the locations that we’ve been at. So, we
could put locations on the stack. A location could be stored as an (x,y)
Point.

There is a Point class in the java.awt package that we could use.
Therefore, the start location in the above picture (i.e., the green 2) would be
represented as ( 1, 1) and the end location (i.e., the red 3) would be
( 7, 4) .

Here are our starting variables:

int[ ] [ ] maze = {{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,0,1,1,1,0,1,1},
{1,0,0,0,1,0,1,1,1,1},
{1,0,1,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}};
Poi nt cur r ent Loc = new Poi nt ( 1, 1) ;
Poi nt goal Loc = new Poi nt ( 4, 7) ; / / Her e x=r ows, y=col s
St ack<Poi nt > possi bi l i t i es = new St ack<Poi nt >( ) ;

The maze variable maintains information about open space and walls.
The currentLoc represents our current location in the maze. The
goalLoc represents the location that we are trying to reach.

The possibilities stack will contain all the possible locations that we
have been close to, but have not yet explored. Here is how it should
work:

We start by placing the starting location currentLoc onto the stack. Then we go through a
continuous loop that does the following:

REPEAT {
I F ( t he st ack i s empt y) THEN
We cannot r each t he goal , qui t .
Get a l ocat i on f r omt he t op of t he st ack and move t o i t .
I F ( t he l ocat i on i s t he goal l ocat i on) THEN
We have r eached t he goal , qui t .
OTHERWI SE
Check al l “open” l ocat i ons ar ound t he cur r ent l ocat i on
and add t hemt o t he st ack.
}

COMP1005/1405 – Other Collections Fall 2009

- 380-
The above code will keep adding potential paths to the stack, but there is a problem. It is
possible that we will add the same location to the stack multiple times. That is, we might end
up circling back to the same locations over and over again. We need to add some
breadcrumbs so that we don’t get stuck on the same locations.


To do this, we need to change the values in the maze array so that we can distinguish
between an “open” location that we have never been to and an “open” spot that we have
been to before.

Perhaps we can use -1 to mark a location as having been visited.
So, here are the changes to the algorithm:

REPEAT {
I F ( t he st ack i s empt y) THEN
We cannot r each t he goal , qui t .
Get a l ocat i on f r omt he t op of t he st ack and move t o i t .
Mar k cur r ent l ocat i on as havi ng been vi si t ed ( br eadcr umb)
I F ( t he l ocat i on i s t he goal l ocat i on) THEN
We have r eached t he goal , qui t .
OTHERWI SE
Check al l “open” l ocat i ons ar ound t he cur r ent l ocat i on
t hat have not been vi si t ed and add t hemt o t he st ack.
}

How do we leave a breadcrumb in the maze ?

We mark the current location in the array with a -1 as follows:

maze[ cur r ent Loc. x] [ cur r ent Loc. y] = - 1;

How do we add potential paths (i.e., the OTHERWISE part of the algorithm) ?
COMP1005/1405 – Other Collections Fall 2009

- 381-
We just check the locations above, below, left and right of the current location to see if there is
an unvisited location (i.e., with a value of 0 in that location of the maze) . Here is how we
would handle checking the location above the current location:

if ( maze[ cur r ent Loc. x- 1] [ cur r ent Loc. y] == 0)
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x- 1, cur r ent Loc. y) ) ;

Can you come up with the other 3 cases ?

Be aware that the rows of the array correspond to the x values of the locations …and the
columns correspond to the y values of the locations.

Here is the resulting code:


possi i l i t b i es. push( cur r ent Loc) ;
while( true) {
if ( possi bi l i t i es. i sEmpt y( ) ) {
Syst em. out . pr i nt l n( " Cannot Reach Goal " ) ;
break;
}
cur r ent Loc = possi bi l i t i es. pop( ) ; / / get t he next l ocat i on
pr i nt Maze( maze, cur r ent Loc, goal Loc) ;
maze[ cur r ent Loc. x] [ cur r ent Loc. y] = - 1; / / pl ace t he br eadcr umb
if ( cur r ent Loc. equal s( goal Loc) ) {
Syst em. out . pr i nt l n( " Reached Goal " ) ;
break;
}
if ( maze[ cur r ent Loc. x- 1] [ cur r ent Loc. y] == 0) / / above
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x- 1, cur r ent Loc. y) ) ;
if ( maze[ cur r ent Loc. x+1] [ cur r ent Loc. y] == 0) / / bel ow
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x+1, cur r ent Loc. y) ) ;
if ( maze[ cur r ent Loc. x] [ cur r ent Loc. y- 1] == 0) / / l ef t
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x, cur r ent Loc. y- 1) ) ;
if ( maze[ cur r ent Loc. x] [ cur r ent Loc. y+1] == 0) / / r i ght
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x, cur r ent Loc. y+1) ) ;
}


You should be able to understand this code as it directly follows from the algorithm. For
debugging purposes, a call to printMaze() has been inserted. This method will display the
maze so that we can see how the program runs each time through the loop. It is similar to the
code we used when we discussed 2D arrays.

Here is all of the code as it appears in a test program…

import j ava. awt . Poi nt ;
import j ava. ut i l . St ack;

public class MazeTest Pr ogr am{


COMP1005/1405 – Other Collections Fall 2009

- 382-
static void pr i nt Maze( int[ ] [ ] maze, Poi nt cur r ent Loc, Poi nt goal Loc) {
for ( int r ow=0; r ow<maze. l engt h; r ow++) {
for ( int col =0; col <maze[ 0] . l engt h; col ++) {
if ( ( cur r ent Loc. x == r ow) && ( cur r ent Loc. y == col ) )
Syst em. out . pr i nt ( ' C' ) ;
else if ( ( goal Loc. x == r ow) && ( goal Loc. y == col ) )
Syst em. out . pr i nt ( ' G' ) ;
else if ( maze[ r ow] [ col ] == 1)
Syst em. out . pr i nt ( ' *' ) ;
else if ( maze[ r ow] [ col ] == 0)
Syst em. out . pr i nt ( ' ' ) ;
else
Syst em. out . pr i nt ( ' . ' ) ;
}
Syst em. out . pr i nt l n( ) ;
}
Syst em. out . pr i nt l n( ) ;
}
public static void mai n( St r i ng ar gs[ ] ) {
int[ ] [ ] maze = {{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 0, 0, 1, 0, 0, 0, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 1, 1, 0, 1}, {1, 0, 1, 0, 0, 0, 1, 0, 0, 1},
{1, 0, 1, 0, 1, 1, 1, 0, 1, 1}, {1, 0, 0, 0, 1, 0, 1, 1, 1, 1},
{1, 0, 1, 0, 0, 0, 0, 0, 0, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
Poi nt cur r ent Loc = new Poi nt ( 1, 1) ;
Poi nt goal Loc = new Poi nt ( 4, 7) ; / / Her e x=r ows, y=col s
St ack<Poi nt > possi bi l i t i es = new St ack<Poi nt >( ) ;

possi bi l i t i es. push( cur r ent Loc) ;
while( true) {
if ( possi bi l i t i es. i sEmpt y( ) ) {
Syst em. out . pr i nt l n( " Cannot Reach Goal " ) ;
break;
}
cur r ent Loc = possi bi l i t i es. pop( ) ; / / get next l ocat i on
pr i nt Maze( maze, cur r ent Loc, goal Loc) ;
maze[ cur r ent Loc. x] [ cur r ent Loc. y] = - 1; / / pl ace br eadcr umb
if ( cur r ent Loc. equal s( goal Loc) ) {
Syst em. out . pr i nt l n( " Reached Goal " ) ;
break;
}
if ( maze[ cur r ent Loc. x- 1] [ cur r ent Loc. y] == 0) / / above
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x- 1, cur r ent Loc. y) ) ;
if ( maze[ cur r ent Loc. x+1] [ cur r ent Loc. y] == 0) / / bel ow
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x+1, cur r ent Loc. y) ) ;
if ( maze[ cur r ent Loc. x] [ cur r ent Loc. y- 1] == 0) / / l ef t
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x, cur r ent Loc. y- 1) ) ;
if ( maze[ cur r ent Loc. x] [ cur r ent Loc. y+1] == 0) / / r i ght
possi bi l i t i es. push( new Poi nt ( cur r ent Loc. x, cur r ent Loc. y+1) ) ;
}
}
}

Here is the output when we run the code (placed in a table to save space, read left to right):
COMP1005/1405 – Other Collections Fall 2009

- 383-
The * represent the walls, the small dots . represent the “breadcrumbs” (i.e., visited locations).
The C represents the current location and the G represents the goal location. The numbers
in the maze from 1 through 4 indicate the locations that are on the stack after each round
through the loop. A 1 indicates the top of the stack (i.e., the next to be popped off), while the
largest number indicates the bottom of the stack. You will notice how the stack grows and
shrinks, with 2 items left on it (i.e., 2 unexplored path portions) after the program completes.


**********
*C1* *
*2*** ** *
* * * *
* * ***G**
* * ****
* * *
**********
St ack:
( 1, 2)
( 2, 1)
**********
*. C* *
*1*** ** *
* * * *
* * ***G**
* * ****
* * *
**********
St ack:
( 2, 1)
**********
*. . * *
*C*** ** *
*1* * *
* * ***G**
* * ****
* * *
**********
St ack:
( 3, 1)
**********
*. . * *
* ** * . ***
*C* * *
*1* ***G**
* * ****
* * *
**********
St ack:
( 4, 1)
**********
*. . * *
*. *** ** *
* * * * .
*C* ***G**
*1 * ****
* * *
**********
St ack:
( 5, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. * ***G**
*C1 * ****
*2* *
**********
St ack:
( 5, 2)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. * ***G**
*. C1* ****
*2* *
**********
St ack:
( 5, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. *2***G**
*. . C* ****
*3*1 *
**********
St ack:
( 6, 3)
( 4, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. *2***G**
*. . * **** .
*3*C1 *
**********
St ack:
( 6, 4)
( 4, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. *2***G**
*. . . **** *
*3*. C1 *
**********
St ack:
( 6, 5)
( 4, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. *3***G**
*. . . * 2****
*4*. . C1 *
**********
St ack:
( 6, 6)
( 5, 5)
( 4, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. *3***G**
*. . . *2 *** *
*4*. . . C1 *
**********
St ack:
( 6, 7)
( 5, 5)
( 4, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. *3***G**
*. . . *2****
*4*. . . . C1*
**********
St ack:
( 6, 8)
( 5, 5)
( 4, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. *2***G**
*. . . *1****
*3*. . . . . C*
**********
St ack:
( 5, 5)
( 4, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. * * *
*. *1***G**
*. . . *C****
*2*. . . . . . *
**********
St ack:
( 4, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. *1 * *
*. *C***G**
*. . . *. ****
*2*. . . . . . *
**********
St ack:
( 3, 3)
( 6, 1)
**********
*. . * *
*. *** ** *
*. *C1 * *
*. *. ***G**
*. . . *. ****
*2*. . . . . . *
**********
St ack:
( 3, 4)
( 6, 1)
**********
*. . * *
*. *** ** *
*. *. C1* *
*. *. ***G**
*. . . *. ****
*2*. . . . . . *
**********
St ack:
( 3, 5)
( 6, 1)
**********
*. . * *
*. ***1** *
*. *. . C* *
*. *. ***G**
*. . . *. ****
*2*. . . . . . *
**********
St ack:
( 2, 5)
( 6, 1)
**********
*. . * 1 *
*. ***C** *
*. *. . . * *
*. *. ***G**
*. . . *. ****
*2*. . . . . . *
**********
St ack:
( 1, 5)
( 6, 1)
**********
*. . *2C1 *
*. ***. ** *
*. *. . . * *
*. *. ***G**
*. . . *. ****
*3*. . . . . . *
**********
St ack:
( 1, 6)
( 1, 4)
( 6, 1)
**********
*. . *2. C1 *
*. ***. ** *
*. *. . . * *
*. *. ***G**
*. . . *. ****
*3*. . . . . . *
**********
St ack:
( 1, 7)
( 1, 4)
( 6, 1)
**********
*. . *2. . C1*
*. ***. ** *
*. *. . . * *
*. *. ***G**
*. . . *. ****
*3*. . . . . . *
**********
St ack:
( 1, 8)
( 1, 4)
( 6, 1)
**********
*. . *2. . . C*
*. ***. **1*
*. *. . . * *
*. *. ***G**
*. . . *. ****
*3*. . . . . . *
**********
St ack:
( 2, 8)
( 1, 4)
( 6, 1)
**********
*. . *2. . . . *
*. ***. **C*
*. *. . . * 1*
*. *. ***G**
*. . . *. ****
*3*. . . . . . *
**********
St ack:
( 3, 8)
( 1, 4)
( 6, 1)
**********
*. . *2. . . . *
*. ***. ** * .
*. *. . . *1C*
*. *. ***G**
*. . . *. ****
*3*. . . . . . *
**********
St ack:
( 3, 7)
( 1, 4)
( 6, 1)
**********
*. . *2. . . . *
*. ***. * . * *
*. *. . . *C. *
*. *. ***1**
*. . . *. ****
*3*. . . . . . *
**********
St ack:
( 4, 7)
( 1, 4)
( 6, 1)
**********
*. . *1. . . . *
*. ***. **. *
*. *. . . *. . *
*. *. ***C**
*. . . *. ****
*2*. . . . . . *
**********
St ack:
( 1, 4)
( 6, 1)
Reached Goal
COMP1005/1405 – Other Collections Fall 2009

- 384-
13.4 Sorting Objects
So far, we have examined some List classes and we did a couple of examples that used a
Stack. In each of these cases, we added objects to a collection in some manner that was
predictable according to the order that the items were added in. For some data structures, this
is not the case. One example of this is the PriorityQueue.
A PriorityQueue is a Queue in which the elements also maintain a priority. That is, we
still add to the back of the queue and remove from the front, but elements with higher priority
are automatically shifted closer to the front before lower priority elements.

As a real life example, when we go to the hospital for an
“emergency”, we wait in line (6 to 8 hours typically). We
normally get served in the order that we came in at. However,
if someone comes in after us who is bleeding or unconscious,
they automatically get bumped up ahead of us since their injuries
are likely more serious and demand immediate attention. We
may think of a PriorityQueue as a sorted queue.

Typical methods for PriorityQueues are:

• add(int priority, Object x) / / j ust add(Object x) i n J AVA
• remove()
• isEmpty()

In a PriorityQueue, when we add items, they usually get shuffled around inside according to
their priority. Therefore, we may not necessarily know the order of the items afterwards …
except that they will be in some sort of prioritized order. Consider adding some Person
objects to an ArrayList:

import j ava. ut i l . *;

public class Sor t Test Pr ogr am1 {
public static void mai n( St r i ng ar gs[ ] ) {
Ar r ayLi st <Per son> peopl e = new Ar r ayLi st <Per son>( ) ;
peopl e. add( new Per son( " Pet e" , " Zar i a" , 12, ' M' , false) ) ;
peopl e. add( new Per son( " Ri t a" , " Book" , 20, ' F' , false) ) ;
peopl e. add( new Per son( " Wi l l i e" , " Mayki t " , 65, ' M' , true) ) ;
peopl e. add( new Per son( " Pat t y" , " O' Fur ni t ur e" , 41, ' M' , false) ) ;
peopl e. add( new Per son( " Sue" , " Per mann" , 73, ' F' , true) ) ;
peopl e. add( new Per son( " Si d" , " Down" , 19, ' M' , false) ) ;
peopl e. add( new Per son( " J ack" , " Pot " , 4, ' M' , false) ) ;

for ( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;
}
}

COMP1005/1405 – Other Collections Fall 2009

- 385-
The people are displayed in the order that they are added:

12 year ol d non- r et i r ed per son named Pet e Zar i a
20 year ol d non- r et i r ed per son named Ri t a Book
65 year ol d r et i r ed per son named Wi l l i e Mayki t
41 year ol d non- r et i r ed per son named Pat t y O' Fur ni t ur e
73 year ol d r et i r ed per son named Sue Per mann
19 year ol d non- r et i r ed per son named Si d Down
4 year ol d non- r et i r ed per son named J ack Pot


What would happen if we added them to a priority queue instead ? Assume that we changed
the variable type from ArrayList to PriorityQueue as follows:


Pr i or i t yQueue <Per son> peopl e = new Pr i or i t yQueue<Per son>( ) ;


If we tried running the code, we would get the following Exception:

j ava. l ang. Cl assCast Except i on: Per son cannot be cast t o j ava. l ang. Compar abl e

The problem is that J AVA does not know how to compare Person objects in order to be able
to sort them. It is telling us that Person must implement the Comparable interface. Instead
of supplying a priority when we add the objects to the queue, the items are sorted by means of
a Comparator interface. That means, each object that we store in the PriorityQueue,
must implement methods compare() and equals() … which are used determine the sort order
(i.e., priority).

So, we should add implements Comparable<Person> to the Person class definition:

public class Per son implements Compar abl e<Per son> {
. . .
}

Interestingly, the additional <Person> at the end of Comparable indicates to J AVA that we will
only be comparing Person objects, not Person objects with other types of objects.

Then we can easily write an equals() method … we did this previously when we discussed
“Code Efficiency”. But how do we write a compare() method ? It is similar to the equals()
method in that it takes a single object parameter:

public int compareTo( Per son p) { . . . }
However, you will notice that the return type is not a boolean, but an
int instead. This integer reflects the ordering between the receiver
and t he parameter. If a negative value is returned from the method,
this informs J AVA that the receiver has higher priority (i.e., comes
before in the ordering) than the incoming parameter object.
COMP1005/1405 – Other Collections Fall 2009

- 386-
Likewise, a positive value indicates lower priority and a zero value indicates that they are equal
priority.
Lets now give it a try for Person objects. If we want to sort by means of their increasing ages
(i.e., younger first), this would be the compareTo() method:

public int compareTo( Per son p) {
return ( this. age – p. age) ;
}


Assume now that we ran the following program:

import j ava. ut i l . *;

public class Sor t Test Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
Pr i or i t yQueue<Per son> peopl e = new Pr i or i t yQueue<Per son>( ) ;
peopl e. add( new Per son( " Pet e" , " Zar i a" , 12, ' M' , false) ) ;
peopl e. add( new Per son( " Ri t a" , " Book" , 20, ' F' , false ; ) )
peopl e. add( new Per son( " Wi l l i e" , " Mayki t " , 65, ' M' , true) ) ;
peopl e. add( new Per son( " Pat t y" , " O' Fur ni t ur e" , 41, ' M' , false) ) ;
peopl e. add( new Per son( " Sue" , " Per mann" , 73, ' F' , true) ) ;
peopl e. add( new Per son( " Si d" , " Down" , 19, ' M' , false) ) ;
peopl e. add( new Per son( " J ack" , " Pot " , 4, ' M' , false) ) ;

/ / Di spl ay t he queue
Syst em. out . pr i nt l n( " Her e i s what t he queue l ooks l i ke: " ) ;
for( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;

/ / Ext r act t hemf r omt he queue
Syst em. out . pr i nt l n( " \ nHer e ar e i t ems as ext r act ed f r omqueue: " ) ;
while( ! peopl e. i sEmpt y( ) )
Syst em. out . pr i nt l n( peopl e. r emove( ) ) ;
}
}

Interestingly, the output from the for loop is as follows:

Her e i s what t he queue l ooks l i ke:
4 year ol d non- r et i r ed per son named J ack Pot
20 year ol d non- r et i r ed per son named Ri t a Book
12 year ol d non- r et i r ed per son named Pet e Zar i a
41 year ol d non- r et i r ed per son named Pat t y O' Fur ni t ur e
73 year ol d r et i r ed per son named Sue Per mann
65 year ol d r et i r ed per son named Wi l l i e Mayki t
19 year ol d non- r et i r ed per son named Si d Down

COMP1005/1405 – Other Collections Fall 2009

- 387-
Notice that the items do not seem sorted at all ! That is because a PriorityQueue does not
actually sort the items, it simple makes sure that the item at the front of the queue is the one
with highest priority. In this case, that is the youngest person … which is indeed at the front of
the queue.
To get the items in sorted order, we simply extract them from the queue one at a time as
shown in the while loop from the code above. Here is the output from the while loop:

Her e ar e i t ems as ext r act ed f r omqueue:
4 year ol d non- r et i r ed per son named J ack Pot
12 year ol d non- r et i r ed per son named Pet e Zar i a
19 year ol d non- r et i r ed per son named Si d Down
20 year ol d non- r et i r ed per son named Ri t a Book
41 year ol d non- r et i r ed per son named Pat t y O' Fur ni t ur e
65 year ol d r et i r ed per son named Wi l l i e Mayki t
73 year ol d r et i r ed per son named Sue Per mann

Indeed, you can see that as we extract the items one at a time, they are sorted. More about
these kinds of queue will be discussed in a later course.
What if we wanted to sort the people by their last names ? To do this, we would need to alter
the compareTo() method to compare names, not ages. Lets make a subclass of Person
called AlphaPerson that has a different compareTo() method:

public class Al phaPer son extends Per son {
/ / Thi s i s a 5- par amet er const r uct or
public Al phaPer son( St r i ng f n, St r i ng l n, int a, char g, boolean r ) {
super( f n, l n, a, g, r ) ;
}

/ / Used t o compar e Per sons by al phabet i cal or der of l ast names
public int compar eTo( Per son p) {
return this. l ast Name. compar eTo( p. l ast Name) ; / / assumes t hat lastName
} / / i s decl ar ed protected
} / / i n t he Person cl ass


Notice that the parameter is Person, not AlphaPerson. This is because Person implements
the Comparable<Person> interface, which specifies type Person and AlphaPerson inherits
from Person.

Consider the output then from the following program …

COMP1005/1405 – Other Collections Fall 2009

- 388-
import j ava. ut i l . *;

public class Sor t Test Pr ogr am3 {
public static void mai n( St r i ng ar gs[ ] ) {
Pr i or i t yQueue<Al phaPer son> peopl e;
peopl e = new Pr i or i t yQueue<Al phaPer son>( ) ;

peopl e. add( new Al phaPer son( " Pet e" , " Zar i a" , 12, ' M' , false) ) ;
peopl e. add( new Al phaPer son( " Ri t a" , " Book" , 20, ' F' , false) ) ;
peopl e. add( new Al phaPer son( " Wi l l i e" , " Mayki t " , 65, ' M' , true) ) ;
peopl e. add( new Al phaPer son( " Pat t y" , " O' Fur ni t ur e" , 41, ' M' , false) ) ;
peopl e. add( new Al phaPer son( " Sue" , " Per mann" , 73, ' F' , true) ) ;
peopl e. add( new Al phaPer son( " Si d" , " Down" , 19, ' M' , false) ) ;
peopl e. add( new Al phaPer son( " J ack" , " Pot " , 4, ' M' , false) ) ;

Syst em. out . pr i nt l n( " \ nHer e ar e i t ems as ext r act ed f r omqueue: " ) ;
while ( ! peopl e. i sEmpt y( ) )
Syst em. out . pr i nt l n( peopl e. r emove( ) ) ;
}
}

Here is the output now … notice that they are sorted by their last names:

Her e ar e i t ems as ext r act ed f r omqueue:
20 year ol d non- r et i r ed per son named Ri t a Book
19 year ol d non- r et i r ed per son named Si d Down
65 year ol d r et i r ed per son named Wi l l i e Maykit
41 year ol d non- r et i r ed per son named O'Furniture Pat t y
73 year ol d r et i r ed per son named Sue Permann
4 year ol d non- r et i r ed per son named J ack Pot
12 year ol d non- r et i r ed per son named Pet e Zaria

So, we can decide how to sort the items and make the appropriate compareTo() method.
As you will learn in a later course, the PriorityQueue is very efficient. However, the above
code required us to extract all the items from the queue in order to get them in sorted order …
thereby leaving the queue empty. This is often undesirable. Sometimes we just want to
leave the elements in the collection and sort them so that we can then use the collection as we
normally would, but maintain all items in sorted order.
J AVA provides a nice tool-kit class called Collections that contains a bunch of useful methods
that we can take advantage of. One of these is a sort() method which will sort an arbitrary
collection.
Examine the following code to see how easy it is to sort our ArrayList of Person objects using
this sort() method …
COMP1005/1405 – Other Collections Fall 2009

- 389-
import j ava. ut i l . *;

public class Sor t Test Pr ogr am4 {
public static void mai n( St r i ng ar gs[ ] ) {
Ar r ayLi st <Per son> peopl e = new Ar r ayLi st <Per son>( ) ;

peopl e. add( new Per son( " Pet e" , " Zar i a" , 12, ' M' , false) ) ;
peopl e. add( new Per son( " Ri t a" , " Book" , 20, ' F' , false) ) ;
peopl e. add( new Per son( " Wi l l i e" , " Mayki t " , 65, ' M' , true) ) ;
peopl e. add( new Per son( " Pat t y" , " O' Fur ni t ur e" , 41, ' M' , false) ) ;
peopl e. add( new Per son( " Sue" , " Per mann" , 73, ' F' , true) ) ;
peopl e. add( new Per son( " Si d" , " Down" , 19, ' M' , false) ) ;
peopl e. add( new Per son( " J ack" , " Pot " , 4, ' M' , false) ) ;

Collections.sort(people); / / do t he sor t i ng

for ( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;
}
}

The output is as expected with all people sorted by their age.
Of course, we could use AlphaPerson to sort them alphabetical instead, if so desired. For
the above code to work, we still need to have the compareTo() methods written. Hopefully
you noticed how easy this sort() method is to use.
There is also a class called Arrays which has some useful methods for manipulating arrays.
For example, if our code had arrays of Person objects instead of ArrayLists, here is what the
code would look like to sort:
import j ava. ut i l . *;

public class Sor t Test Pr ogr am4b {
public static void mai n( St r i ng ar gs[ ] ) {
Per son[ ] peopl e = {new Per son( " Pet e" , " Zar i a" , 12, ' M' , false) ,
new Per son( " Ri t a" , " Book" , 20, ' F' , false) ,
new Per son( " Wi l l i e" , " Mayki t " , 65, ' M' , true) ,
new Per son( " Pat t y" , " O' Fur ni t ur e" , 41, ' M' , false) ,
new Per son( " Sue" , " Per mann" , 73, ' F' , true) ,
new Per son( " Si d" , " Down" , 19, ' M' , false) ,
new Per son( " J ack" , " Pot " , 4, ' M' , false) };

Arrays.sort(people); / / do t he sor t i ng

for ( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;
}
}

COMP1005/1405 – Other Collections Fall 2009

- 390-
There are similar sort methods for the primitive data types, so you can sort simple arrays of
numbers such as this:

int[ ] nums = {23, 54, 76, 1, 29, 89, 45, 76};

Arrays.sort(nums); / / do t he sor t i ng

Interestingly, there are other useful methods in the Collections class such as reverse(),
shuffle(), max() and min(). Can you guess what they do by looking at the output of the
following program ?
import j ava. ut i l . *;

public class Sor t Test Pr ogr am5 {
public static void mai n( St r i ng ar gs[ ] ) {
Ar r ayLi st <Per son> peopl e = new Ar r ayLi st <Per son>( ) ;

peopl e. add( new Per son( " Pet e" , " Zar i a" , 12, ' M' , false) ) ;
peopl e. add( new Per son( " Ri t a" , " Book" , 20, ' F' , false) ) ;
peopl e. add( new Per son( " Wi l l i e" , " Mayki t " , 65, ' M' , true) ) ;
peopl e. add( new Per son( " Pat t y" , " O' Fur ni t ur e" , 41, ' M' , false) ) ;
peopl e. add( new Per son( " Sue" , " Per mann" , 73, ' F' , true) ) ;
peopl e. add( new Per son( " Si d" , " Down" , 19, ' M' , false) ) ;
peopl e. add( new Per son( " J ack" , " Pot " , 4, ' M' , false) ) ;

Syst em. out . pr i nt l n( " The l i st r ever sed: " ) ;
Collections.reverse(people);
for( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;

uf f d: " ) ; Syst em. out . pr i nt l n( " \ nThe l i st sh l e
Collections.shuffle(people);
for( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;

Syst em. out . pr i nt l n( " \ nThe l i st shuf f l ed agai n: " ) ;
Collections.shuffle(people);
for( Per son p: peopl e)
Syst em. out . pr i nt l n( p) ;

Syst em. out . pr i nt l n( " \ nOl dest per son: " + Collections.max(people)) ;
Syst em. out . pr i nt l n( " Youngest per son: " + Collections.min(people)) ;
}
}

Here is the output … was it as you expected? …
COMP1005/1405 – Other Collections Fall 2009

- 391-


The l i st r ever sed:
4 year ol d non- r et i r ed per son named J ack Pot
19 year ol d non- r et i r ed per son named Si d Down
73 year ol d r et i r ed per son named Sue Per mann
41 year ol d non- r et i r ed per son named Pat t y O' Fur ni t ur e
65 year ol d r et i r ed per son named Wi l l i e Mayki t
20 year ol d non- r et i r ed per son named Ri t a Book
12 year ol d non- r et i r ed per son named Pet e Zar i a

The l i st shuf f l ed:
12 year ol d non- r et i r ed per son named Pet e Zar i a
4 year ol d non- r et i r ed per son named J ack Pot
19 year ol d non- r et i r ed per son named Si d Down
20 year ol d non- r et i r ed per son named Ri t a Book
41 year ol d non- r et i r ed per son named Pat t y O' Fur ni t ur e
73 year ol d r et i r ed per son named Sue Per mann
65 year ol d r et i r ed per son named Wi l l i e Mayki t

The l i st shuf f l ed agai n:
4 year ol d non- r et i r ed per son named J ack Pot
73 year ol d r et i r ed per son named Sue Per mann
19 year ol d non- r et i r ed per son named Si d Down
12 year ol d non- r et i r ed per son named Pet e Zar i a
41 year ol d non- r et i r ed per son named Pat t y O' Fur ni t ur e
20 year ol d non- r et i r ed per son named Ri t a Book
65 year ol d r et i r ed per son named Wi l l i e Mayki t

Ol dest per son: 73 year ol d r et i r ed per son named Sue Per mann
Youngest per son: 4 year ol d non- r et i r ed per son named J ack Pot

There are additional methods in the Collections class. Have a look at the API and see if you
find anything useful.

13.5 Removing Duplicates
One more interesting tool for collections that we will look at is one that will allow us to remove
duplicates. In the real world, it is often necessary to reduce the amount of data you have by
eliminating the duplicate information. We have already had some practice at preventing
duplicate information when we created the differentMakes() method in the Autoshow class
when we discussed ArrayLists:
Ar r ayLi st <St r i ng> di f f er ent Makes( ) {
Ar r ayLi st <St r i ng> answer = new Ar r ayLi st <St r i ng>( ) ;

for ( Car c: this. car s) {
if ( ! answer . cont ai ns( c. make) )
answer . add( c. make) ;
}
return answer ;
}
COMP1005/1405 – Other Collections Fall 2009

- 392-
Recall that we simply checked (with the contains() method) to see whether or not the make of
the car was already in the answer collection before we added it. If already there, we did not
add it. The code was straight forward. However, sometimes we want to have duplicates in
our collections, we just may not want to show them all of the time. For example, a video store
might be interested in producing a list of all its unique moves … nothing is gained by listing a
move 15 times if the store, for example, has 15 copies of that movie.
Consider the following code which simulates some inventory at a video store. The code
makes use of a simple Movie object, which contains only one attribute corresponding to its
title. The code adds 10 movies from among 5 unique titles … hence many duplicates. The
code makes us of Math.random() so that the inventory is different each time we run the
program.
import j ava. ut i l . *;

public class Set Test Pr ogr am1 {
public static void mai n( St r i ng ar gs[ ] ) {
Movi e[ ] dvds = {new Movi e( " Bol t " ) ,
new Movi e( " Monst er s Vs. Al i ens" ) ,
new Movi e( " Mar l ey & Me" ) ,
new Movi e( " Hot el For Dogs" ) ,
new Movi e( " The Day t he Ear t h St ood St i l l " ) };

Ar r ayLi st <Movi e> i nvent or y = new Ar r ayLi st <Movi e>( ) ;

/ / Add 20 r andommovi es f r omt he l i st of dvds
for ( int i =0; i <10; i ++) {
i nvent or y. add( dvds[ ( int) ( Mat h. r andom( ) *5) ] ) ;
}

for ( Movi e m: i nvent or y)
Syst em. out . pr i nt l n( m) ;
}
}



public class Movi e {
private St r i ng t i t l e;

public Movi e( St r i ng t ) { t i t l e = t ; }
public St r i ng get Ti t l e( ) { return t i t l e; }
public St r i ng t oSt r i ng( ) { return " Movi e: \ " " + t i t l e + " \ " " ; }
}


Here is an example of the output (will differ each time you run though) …

COMP1005/1405 – Other Collections Fall 2009

- 393-


Movi e: " Hot el For Dogs"
Movi e: " Hot el For Dogs"
Movi e: " Hot el For Dogs"
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " Bol t "
Movi e: " Mar l ey & Me"
Movi e: " Hot el For Dogs"
Movi e: " Mar l ey & Me"
Movi e: " Hot el For Dogs"
Movi e: " Bol t "

Can we adjust the for loop so that it only displays unique movies ? No. We would have to do
some extra work of making a new list with the duplicates removed. So, we could replace the
for loop with the following:

Ar r ayLi st <Movi e> uni queLi st = new Ar r ayLi st <Movi e>( ) ;

for ( Movi e m: i nvent or y) {
if ( ! uni queLi st . cont ai ns( m) )
uni queLi st . add( m) ;
}
for ( Movi e m: uni queLi st ) {
Syst em. out . pr i nt l n( m) ;
}

This would produce the following output (according to the earlier results):

Movi e: " Hot el For Dogs"
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " Bol t "
Movi e: " Mar l ey & Me"

However, there is an easier way to do this … by making use of the Set classes in J AVA.
A Set is a collection that does not allow duplicates. That is, there
cannot be two elements e
1
and e
2
such that e
1
.equals(e
2
). Any attempt
to add duplicate elements is ignored. Sets differ from Lists in that the
elements are not kept in the same order as when they were added.
Sets are generally unordered, which means that the particular location
of an element may change according to the particular set implementation.
Typical methods for Sets are:

• add(Object x)
• remove(Object x)
COMP1005/1405 – Other Collections Fall 2009

- 394-
There are two Set classes in J AVA. A HashSet is a set in which the order of the items is
arbitrary, whereas a TreeSet keeps the items in sorted order (according to how the
compareTo() method is written).

Consider what would happen if we changed the inventory from an ArrayList to a HashSet as
follows:

HashSet<Movi e> i nvent or y = new HashSet<Movi e>( ) ;
Our code would produce the following output (according to the earlier results):

Movi e: " Hot el For Dogs"
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " Bol t "
Movi e: " Mar l ey & Me"


Notice that the duplicates were removed. The HashSet prevented any duplicates from being
added. Therefore, we have lost all duplicate copies from our inventory, which can be bad.
Perhaps it would be better to only use a HashSet when displaying the inventory, so that we
don’t destroy the duplicate movies. This is easily done by creating an extra HashSet variable
(displayList in this case) and using the HashSet constructor that takes a Collection
parameter:

import j ava. ut i l . *;

public class Set Test Pr ogr am2 {
public static void mai n( St r i ng ar gs[ ] ) {
Movi e[ ] dvds = {new Movi e( " Bol t " ) ,
new Movi e( " Monst er s Vs. Al i ens" ) ,
new Movi e( " Mar l ey & Me" ) ,
new Movi e( " Hot el For Dogs" ) ,
new Movi e( " The Day t he Ear t h St ood St i l l " ) };

Ar r ayLi st <Movi e> i nvent or y = new Ar r ayLi st <Movi e>( ) ;

/ / Add 10 r andommovi es f r omt he l i st of dvds
for ( int i =0; i <10; i ++) {
i nvent or y. add( dvds[ ( int) ( Mat h. r andom( ) *5) ] ) ;
}

Syst em. out . pr i nt l n( " Her e ar e t he uni que movi es: " ) ;
HashSet <Movi e> di spl ayLi st = new HashSet <Movi e>( i nvent or y) ;
for ( Movi e m: di spl ayLi st )
Syst em. out . pr i nt l n( m) ;

Syst em. out . pr i nt l n( " \ nHer e i s t he whol e i nvent or y: " ) ;
for ( Movi e m: i nvent or y)
Syst em. out . pr i nt l n( m) ;
}
}
COMP1005/1405 – Other Collections Fall 2009

- 395-
Notice the parameter to the HashSet constructor. This constructor will ensure to add all the
elements from the inventory collection to the newly create HashSet. Then, in the for loop, we
use this new HashSet for display purposes, while the original inventory remains unaltered.
Here is the output:

Her e ar e t he uni que movi es:
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " Bol t "
Movi e: " Monst er s Vs. Al i ens"
Movi e: " Hot el For Dogs"

Her e i s t he whol e i nvent or y:
Movi e: " Bol t "
Movi e: " Bol t "
Movi e: " Bol t "
Movi e: " Monst er s Vs. Al i ens"
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " Hot el For Dogs"
Movi e: " Bol t "
Movi e: " Monst er s Vs. Al i ens"
Movi e: " Monst er s Vs. Al i ens"
Movi e: " Bol t "


So, it is easy to remove duplicates from any collection … we simply create a new HashSet
from the collection and it removes the duplicates for us.

However, there is one point that should be mentioned. In the above code, the duplicates
movies all represented the same exact object in memory … that is … all duplicates were
identical to one another. However, it is more common to have two equal movies which are not
identical.

So, consider this code … notice the equal (but not identical) movies:

import j ava. ut i l . *;

public class Set Test Pr ogr am3 {
public static void mai n( St r i ng ar gs[ ] ) {
Movi e[ ] dvds = {new Movi e( " Bol t " ) ,
new Movi e( " Monst er s Vs. Al i ens" ) ,
new Movi e( " Mar l ey & Me" ) ,
new Movi e( " Monst er s Vs. Al i ens" ) ,
new Movi e( " Hot el For Dogs" ) ,
new Movi e( " Hot el For Dogs" ) ,
new Movi e( " Monst er s Vs. Al i ens" ) ,
new Movi e( " The Day t he Ear t h St ood St i l l " ) ,
new Movi e( " The Day t he Ear t h St ood St i l l " ) ,
new Movi e( " The Day t he Ear t h St ood St i l l " ) ,
new Movi e( " The Day t he Ear t h St ood St i l l " ) };

Ar r ayLi st <Movi e> i nvent or y = new Ar r ayLi st <Movi e>( ) ;


COMP1005/1405 – Other Collections Fall 2009

- 396-
/ / Add 10 r andommovi es f r omt he l i st of dvds
for ( int i =0; i <10; i ++) {
i nvent or y. add( dvds[ ( int) ( Mat h. r andom( ) *11) ] ) ;
}

Syst em. out . pr i nt l n( " Her e ar e t he uni que movi es: " ) ;
HashSet <Movi e> di spl ayLi st = new HashSet <Movi e>( i nvent or y) ;
for ( Movi e m: di spl ayLi st )
Syst em. out . pr i nt l n( m) ;

Syst em. out . pr i nt l n( " \ nHer e i s t he whol e i nvent or y: " ) ;
for ( Movi e m: i nvent or y)
Syst em. out . pr i nt l n( m) ;
}
}


Here is the result:


Her e ar e t he uni que movi es:
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " Bol t "
Movi e: " Mar l ey & Me"
Movi e: " Monst er s Vs. Al i ens"
Movi e: " Monst er s Vs. Al i ens"

Her e i s t he whol e i nvent or y:
Movi e: " Mar l ey & Me"
Movi e: " Monst er s Vs. Al i ens"
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " Monst er s Vs. Al i ens"
Movi e: " Bol t "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "


Notice that there are many duplicates still in the Set. What if we were to add the following
equals() method to the Movie class:


public boolean equal s( Obj ect obj ) {
if ( ! ( obj instanceof Movi e) ) return false;
return t i t l e. equal s( ( ( Movi e) obj ) . t i t l e) ;
}


Logically, that should solve the problem. However, it does not quite.
COMP1005/1405 – Other Collections Fall 2009

- 397-
As it turns out, in J AVA, Sets make use of a programming technique called hashing.
Hashing is used as a way of quickly comparing and sorting objects because it
quickly identifies objects that cannot be equal to one another, without needing
to go deep down inside the object to make comparisons. For example, if
you had an apple and a pineapple, they are clearly not equal. You need
not compare them closely because a simple quick glance tells you that they
are not the same.

In real life, hashing is used by post offices when sorting mail at
various levels. First, they look at the destination country and
make two piles … domestic mail vs. international mail. That is a
quick “hash” in that the postmen do not need to examine any
further details at that time … such as street names and recipient
names etc… Then they hash again later by using the postal
code to determine "roughly" and “quickly” the area of a city that
your mail needs to be delivered to. This allows them to make a
pile of mail for all people living in the same area. At each level
of “sorting” the mail (i.e., country, city, postal code, street), the
postmen must make a quick decision as to which pile to place
the mail item into. This quick decision is based on something
called a hash function (or hash code).
In J AVA, for Sets to work properly, we must also write a hashCode() method for our objects.
These methods return an int which represents the “pile” that the object belongs to. Similar
objects will have similar hash codes, and therefore end up in the same “pile”. Here is the
hashCode() method for our Movie object:

public int hashCode( ) {
return t i t l e. hashCode( ) ;
}

It must be public, return an int and have no parameters. The code simply returns the hash
code of the title string for the movie. We do not wish to go into details here as to “how” to
produce a proper hash code. Instead, let us simply use this “rule of thumb”: the hash code for
our objects should return a sum of all the hash codes of its attributes. If an attribute is a
primitive, just convert it to an integer in some way and use that value in the hashCode()
method’s total value.
By adding the above method, therefore, the HashSet will work properly to eliminate the
duplicates. You will notice however, that the HashSet does not sort the items. Also, the
items don’t even appear in the order that they were added. Instead, the order seems
somewhat random and arbitrary.
If you want the items in sorted order, you can use a TreeSet instead of a HashSet:
Tr eeSet <Movi e> di spl ayLi st = new Tr eeSet <Movi e>( i nvent or y) ;
COMP1005/1405 – Other Collections Fall 2009

- 398-
Of course, as we did with PriorityQueues, we will need to make sure that our Movie class
implements the Comparable<Movie> interface and thus has a compareTo() method. Here is
the completed Movie class that will work with both HashSet and TreeSet:

public class Movi e implements Compar abl e<Movi e> {
private St r i ng t i t l e;

public Movi e( St r i ng t ) { t i t l e = t ; }
public St r i ng get Ti t l e( ) { return t i t l e; }
public St r i ng t oSt r i ng( ) { return " Movi e: \ " " + t i t l e + " \ " " ; }

public boolean equal s( Obj ect obj ) {
if ( ! ( obj instanceof Movi e) ) return false;
return t i t l e. equal s( ( ( Movi e) obj ) . t i t l e) ;
}

public int hashCode( ) {
return t i t l e. hashCode( ) ;
}

public int compar eTo( Movi e m) {
return t i t l e. compar eTo( m. t i t l e) ;
}
}

Here is the output from our SetTestProgram3 when using TreeSet instead of HashSet:

Her e ar e t he uni que movi es:
Movi e: " Bol t "
Movi e: " Hot el For Dogs"
Movi e: " Mar l ey & Me"
Movi e: " Monst er s Vs. Al i ens"
Movi e: " The Day t he Ear t h St ood St i l l "

Her e i s t he whol e i nvent or y:
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " The Day t he Ear t h St ood St i l l "
Movi e: " Mar l ey & Me"
Movi e: " Monst er s Vs. Al i ens"
Movi e: " Hot el For Dogs"
Movi e: " Monst er s Vs. Al i ens"
Movi e: " Mar l ey & Me"
Movi e: " Bol t "

Notice the sorted order of the movies from the TreeSet.
TO BE CONTINUED…

Sponsor Documents

Or use your account on DocShare.tips

Hide

Forgot your password?

Or register your new account on DocShare.tips

Hide

Lost your password? Please enter your email address. You will receive a link to create a new password.

Back to log-in

Close