Java Book
Java Book
First:
An Introduction to
Computer Programming
using Java and BlueJ
Copyright 2008
Rick Gee
Table of Contents
Table of Contents ................................................................................................................. i
Preface to the reader.......................................................................................................... xii
To students .................................................................................................................... xii
To teachers ................................................................................................................... xiv
To everyone ................................................................................................................. xvi
Acknowledgements......................................................................................................... xvii
Notes ............................................................................................................................... xvii
Chapter 0 Introduction ..................................................................................................... 1
Learning objectives:........................................................................................................ 1
What is computer science?.............................................................................................. 1
Hardware......................................................................................................................... 1
Software .......................................................................................................................... 2
Programming languages.................................................................................................. 3
Summary ......................................................................................................................... 4
Chapter 1 Classes and objects identity, state, and behaviour ....................................... 5
Learning objectives:........................................................................................................ 5
Introduction..................................................................................................................... 5
Definitions....................................................................................................................... 5
Classes............................................................................................................................. 7
The Student class ............................................................................................................ 8
Class diagrams ............................................................................................................ 9
Datatypes................................................................................................................... 10
The Professor class ....................................................................................................... 12
Tradeoffs of storing the name as one field versus several ............................................ 12
Behaviours .................................................................................................................... 14
A first look at Java ........................................................................................................ 15
Documentation.......................................................................................................... 17
Programming Style documentation........................................................................ 18
Class declaration ....................................................................................................... 18
Programming Style class names ............................................................................ 18
Instance variables...................................................................................................... 19
Programming Style instance variables................................................................... 19
Constructor(s) ........................................................................................................... 20
Programming Style constructors............................................................................ 22
Getters and setters ..................................................................................................... 23
Programming Style getters and setters................................................................... 24
toString...................................................................................................................... 24
Programming Style toString .................................................................................. 25
Creating another class, the College............................................................................... 26
Summary ....................................................................................................................... 28
Exercises ....................................................................................................................... 29
Chapter 2 Introducing BlueJ .......................................................................................... 31
Learning objectives:...................................................................................................... 31
Introduction................................................................................................................... 31
BlueJ ............................................................................................................................. 31
Creating a new project .............................................................................................. 32
Virtual machines and bytecode ................................................................................. 35
Testing the Student class........................................................................................... 35
Inspecting an object .................................................................................................. 37
Unit testing - definition............................................................................................. 39
Unit testing with BlueJ.............................................................................................. 40
Unit testing the results ........................................................................................... 46
Smoke testing................................................................................................................ 47
The Professor class ....................................................................................................... 48
The College class .......................................................................................................... 48
Summary ....................................................................................................................... 48
Exercises ....................................................................................................................... 50
Chapter 3 Making decisions........................................................................................... 53
Learning objectives:...................................................................................................... 53
The if statement............................................................................................................. 53
Boolean algebra ........................................................................................................ 53
Boolean algebra an example .................................................................................. 55
A revised toString method ........................................................................................ 55
Using the Java documentation .............................................................................. 56
Programming style if statements............................................................................ 58
Simpler tests.................................................................................................................. 59
More complicated tests ................................................................................................. 59
Summary ....................................................................................................................... 62
Exercises ....................................................................................................................... 63
Chapter 4 Inheritance ..................................................................................................... 67
Learning objectives:...................................................................................................... 67
Abstract classes ............................................................................................................. 67
The Person class........................................................................................................ 68
toString in an abstract class................................................................................... 69
The Person class, continued ...................................................................................... 71
The Student class a derived class........................................................................... 72
The Professor class ................................................................................................... 73
Summary ....................................................................................................................... 73
Exercises ....................................................................................................................... 74
Chapter 5 An Address class ........................................................................................... 77
Learning objectives:...................................................................................................... 77
Introduction................................................................................................................... 77
Adding an address......................................................................................................... 77
The Address class ..................................................................................................... 78
ii
vi
vii
Introduction................................................................................................................. 369
Threads - definition..................................................................................................... 369
Threads and Swing...................................................................................................... 370
Inner classes ................................................................................................................ 371
Refactoring.................................................................................................................. 373
public static void main(String[] args) ......................................................................... 373
Output and input ......................................................................................................... 375
Summary ..................................................................................................................... 375
Exercises ..................................................................................................................... 376
In Conclusion .................................................................................................................. 377
References....................................................................................................................... 379
Printed Reference materials ........................................................................................ 379
Online references ........................................................................................................ 380
Appendix 1 Sample lab assignments ........................................................................... 381
Introduction................................................................................................................. 381
Lab Assignment 0 - Getting to know you................................................................... 382
Lab Assignment 1 - Players ........................................................................................ 387
Lab Assignment 2 - Teams and Leagues .................................................................... 389
Lab Assignment 3 - Reporting.................................................................................... 391
Lab Assignment 4 - Data Entry Screens ..................................................................... 393
Lab Assignment 5 - And now for something completely different ............................ 394
Lab Assignment 6 - Mathematics I............................................................................. 396
Lab Assignment 7 - Mathematics II............................................................................ 397
Subsequent lab assignments........................................................................................ 399
Index ............................................................................................................................... 401
Index ............................................................................................................................... 401
xi
To students
What is computer science? How does this textbook help you study computer science?
Much, but not all, of computer science involves using a computer to solve a problem. The
instructions to solve the problem are written as a program or programs, instruction which
the computer performs.
In this book we gloss over some of the details of how the program you write is translated
into something which the computer can carry out, and we focus on how to write good
programs. By good, we mean clear, easy-to-read, well-documented, efficient, and
tested.
Clear, easy-to-read, and well-documented go together. Can someone else read your
program and understand what it does? If not, you have failed in creating something which
can be used. This is because programs are continually changing. The problem they are
solving changes, or a better solution becomes available. If you or someone else can not
understand what you have written, then how can you improve or change it? How can you
fix it if it contains an error?
Efficient means that the program carries out its task quickly, or with a small amount of
computer resources. Often these two characteristics are in conflict; a program may work
faster when you give it more memory. But a detailed discussion of efficiency is beyond
the scope of this course.
Tested means that you can trust the results the program produces. We emphasize unit
testing using a tool named JUnit, which allows us to test each portion of the program
separately, and together. It allows us to save the tests and reuse them whenever we make
a change to the programs.
When we write programs, we use the Java language. This is a modern language which I
find very enjoyable and easy to use. The language itself is very simple, and gains much of
xii
its power from libraries which people have created to carry out specialised tasks. There
are many other languages available so you will need to learn many languages during your
career in computing. Java is a good language with which to start.
A significant part of learning Java is becoming familiar with the documentation
describing these libraries; it is available online. The number of libraries is truly amazing.
This is a measure of the popularity of Java and the various types of programs it is used to
create.
We will use a tool called BlueJ to help us integrate all these features the Java language,
JUnit testing, and the Java libraries. Since BlueJ is written in Java, it is a constant
reminder of what you can produce using Java.
For much of this book, we use one example. The example is a North American college,
what some call post-secondary education and others call tertiary education. At this
college, the professors teach students who are enrolled in sections of courses. A course is
a collection of related topics. Students sign up to take many different courses. Since there
are limits on how many students a professor can teach at one time, when there are many
students wanting to take a course there may be a number of different sections of the
course being offered, perhaps all taught by the same professor, perhaps taught by
different professors.
Some courses are best taught using lectures, where the professor teaches and the students
participate by listening and asking questions. Some are best taught using labs, where the
students are doing predefined activities, and the professors provide assistance. Some are
best taught using seminars, where the students study material on their own and present
their discoveries to the rest of the class. Some courses use a mix of two or all of these.
Whether a course uses lectures, labs, or seminars, there are meeting times associated with
each.
The academic year is divided into three portions, called semesters. The semesters run
from September through December, January through April, and May through August.
A section of a course offered in one semester is different from a section of a course
offered in a different semester.
In order to complete a program of study, perhaps resulting in a diploma or a degree,
students may take a course more than once, with marks being awarded for each attempt.
To model this college, we need a way to save large amounts of data.
Some have suggested that an introductory textbook like this should use many small
examples, rather than one large example. But others have suggested that the way to see
the benefits of objects is to see them in a large project. I agree with the latter perspective.
xiii
The exercises at the end of each chapter include other examples, some small and some
large. The lab assignments included in the appendix also include a large project. I hope
you find it interesting. Although it is based on a sports league, it should be
understandable even if you are not interested in sports.
I hope you enjoy the approach I have used here.
Note that I do assume you have used computers before and are familiar with some of the
terminology. I do not assume any programming experience.
To teachers
Like many textbooks, this one arose out of a perceived need. I wanted to teach an
introductory programming course using Java, and I wanted to do it objects-first. I could
not find a textbook that met my needs, so I decided to create my own. The competition
was between additions, and other competitors were not yet available. In the time it has
taken to complete this book, other competitors have appeared. To see this textbook
published, I have used the Creative Commons approach.
When I began writing, Java 5 was the most powerful version of Java available at the time.
Since then, Java 6 has become available, but it does not appear to affect anything here.
I used BlueJ version 2.1.2 as it was the most recent stable version at the time. Since then
other versions have become available. Newer versions offer support for team work. I
have not included a discussion of that aspect in this textbook, but it would make a
valuable addition to the course.
In checking all the code in this textbook, I have used version 2.2 along with the
Checkstyle extension.
BlueJ does not include the most-recent version of JUnit, but the version included serves
to introduce the idea of unit testing.
My reviewers have given me much interesting feedback. One thing which caused many
of them some difficulty is the use of one large project to form the examples in this book.
Of course, using one example in the lab assignments also caused similar concern. In the
preface to students I have explained the project for the benefit of those who are not
familiar with the way a North American college or university works.
Also in the preface to students, I addressed the question of one large example instead of
several small ones. Basically, the problem is that it is very easy to write small problems
using objects, but the objects are not crucial to success. That is, you could write the small
programs without using objects if you wished. But I believe that using objects is an
important way of designing your software, so I want to force people to use objects. Thus I
need a language which forces everything to be an object (Java almost meets that
xiv
requirement.), or I need a project which is so large that you must use objects. I have taken
the second approach, in the example in the textbook, and in the lab assignments.
Notice that after a while I find I have exhausted the North American college example of
ideas. At that point we look at some other examples, starting with mathematics.
My reviewers have also asked why is <that topic> included. Depending on the reviewer,
<that topic> could be persistence, GUIs, applets, patterns, and/or mathematics. I have
included them because I feel they are important topics, which can be appreciated by
students learning to program for the first time.
However, you may wish to omit any or all of those topics. All of those chapters are not
dependent on the rest of the book, except that there is some persistence in the
mathematical chapters but you could omit that too, should you prefer.
Other brief sections which present interesting but non-essential material are shown with a
grey background.
Like this.
I have used this textbook as the basis of a two-semester course, to be followed by a data
structures course. That is, instead of teaching the ACM courses CS1 and CS2, we cover
the content in three semesters. I had a small group of students. A colleague has used the
textbook with a group of 60 students. (He had trouble with it since he is not an objectsfirst person.) We find that the material in this book is adequate for about 80% of a twosemester course. That is, it covers all the material in CS1 plus a little of CS2. For the rest
of the course we use material specific to Okanagan College, related to the environment
and follow-up course. Thus, this textbook is more than adequate for a one-semester
course, and there are chapters, mentioned above, which you could omit without
disturbing the integrity of the course. But should you be using this textbook for a twosemester course, be prepared to supplement it towards the end of the second semester.
The team work features of BlueJ 2.2 would be an ideal supplement.
xv
To everyone
Thank you for purchasing this book. I hope it meets your needs. Remember that Dr
Samuel Johnson said The two most engaging powers of an author are to make new
things familiar, and familiar things new., a quotation which I found at
http://www.chiasmus.com/welcometochiasmus.shtml. I hope I have those powers.
Should you have any comments, positive or negative, and especially if you find any
errors or omissions or unclear sections, please contact me.
Rick Gee
Okanagan College
Kelowna, BC
Canada
rgee at okanagan dot bc dot ca
p.s. Yes, I am Canadian, so the spelling in this book is Canadian, and the examples are, in
many ways, Canadian.
xvi
Acknowledgements
Id like to thank my colleagues Rob McArthur (Okanagan College) and Joseph Fall
(Capilano University) for their help in the writing of the examples in this manual.
I would also like to thank my students, Andrew Faraday, Jonathan Gaudet, Nicholas
Goertz, Maria Nemes, and Brandon Potter for their patience and assistance while we used
this textbook for the first time. Rob McArthur and his students used the textbook in a
revised form.
I would like to thank the reviewers who provided me with interesting and challenging
feedback.
Rick Gee
Kelowna, BC
Summer 2008
Notes
Statements in the Java language are shown using this font. Java snippets,
including variable and class names referred to in the body of the text, are also shown
using this font. Thus, Address is the name of a class we create but address is
the place a person lives. Similarly Person is a class but a person is a member of the
human species, Student is a class but a student is a person, Professor is a class but
a professor is a person, and Section is a class but a section is a specific offering of a
course.
The occasional paragraph is shown with a light-grey background. This is additional
material, which expands on the material nearby. While interesting, the material is not
crucial.
All the examples used assume the Java 2 Platform, Standard Edition, and have been run
through BlueJ 2.2.
The first time a word is defined, it is shown in italics.
xvii
xviii
Chapter 0 Introduction
Learning objectives:
By the end of this chapter, you will be able to:
Describe hardware
Describe software
Name a few programming languages
Say why the author believes Java is the appropriate language to use to introduce
programming
Hardware
Hardware is the parts of a computer which you can touch. Some define hardware as the
parts of a computer you can hit when something goes wrong. The hardware is capable of
responding to instructions on how to do a task. These instructions come via
programming. Depending on the programming, a computer may perform various tasks.
Thus, an MP3 player is not a computer, since it performs only one task, but a computer
can function as an MP3 player. A telephone is not a computer, but a computer can
function as a telephone (with the addition of a microphone and speakers.) An address
book (a personal digital assistant, or PDA) is not a computer, but a computer can function
as an address book.
A computer transforms data into information. Data is the things we know or are given.
Data includes the students who have enrolled in a course and the marks they have earned.
Information is the result of processing, manipulating and organizing data in a way that
adds to the knowledge of the person receiving it.
(http://www.orafaq.com/glossary/faqglosi.htm) For example, the raw data about the
students in a class can be transformed into a class list, or information. The marks students
earn can be transformed into semester averages, information which will in turn become
data for determining which scholarships students win.
It all depends on what programming can be done for the computer.
Hardware the parts of a computer which you can touch. This includes:
Central Processing Unit (CPU) the brains of the computer. This is the part of the
computer which carries out the instructions given by a program. Modern
computers often have two or more CPUs, often on one chip. Heinrich Rudolf
Hertz was a German physicist in the late 1800s. His name has been adopted as
the unit to measure frequency. 1 hertz = 1 oscillation of a wave per second. The
speed of the CPU used to be measured in megahertz (millions of cycles per
second, abbreviated MHz) but is now measured in gigahertz (billions, or
thousands of millions, of cycles per second, abbreviated GHz). In general, the
faster the CPU, the better, although when you have multiple CPUs (or cores) you
can use a slower speed.
Random Access Memory (RAM) used to store data while a program is running.
In general, the more RAM your computer contains, the better. In the old days,
RAM was measured in kilobytes (KB). Then it became cheaper and computers
contained megabytes (MB) or gigabytes (GB) of memory. The reason you want
more memory is twofold. First, you can run larger, and hence more powerful,
programs. Second, you can have more programs in memory simultaneously, with
the CPU giving each a portion of its time.
Read-only Memory (ROM) a portion of memory which uses a battery to retain
its contents when the power to the computer is turned off. ROM is used for things
like configuration information, and a boot program, which is executed when the
power is first turned on. ROM is more expensive than RAM, so you will have less
ROM than RAM, often much less.
Monitor the screen on which output from a program is displayed. This output
may be text or graphics.
Keyboard used for input. There are many different layouts for keyboards
(Where is the backspace key?) depending on the manufacturer of the keyboard
and many different special keys. The keyboard for a Macintosh computer has an
Apple key, for example.
Mouse or trackball used for input, clicking buttons or selecting items from a
menu. We will use the mouse quite extensively to control BlueJ.
Software
Every computer has some programming available for it. This includes an operating
system (Windows XP or Vista, UNIX, Linux, Mac OS, Solaris, etc.). The operating
system allows other programs to run. When you get an operating system, it often comes
with some other programs word processing, calendar, browser, various utility
programs, a few games.
You will be using some parts of the operating system in your labs, to locate files you
have created, to make backup copies of them, and to run programs.
You may also want to create new programs, perhaps one to track the inventory in your
warehouse, or to track the hours worked by employees and pay them. Or perhaps you will
want to create programs to help you write more programs. Perhaps you want to create a
game. For those tasks, you need a programming language and the ability to use it. Or you
may need the money to hire someone who does.
Programming languages
Programming is done using a variety of languages, in a variety of styles. You may have
heard of BASIC, C, Pascal, etc. These are all programming languages which have been
used in the past and continue to be used, to some degree. Today there are many languages
in common use including Java, C++, C#, and Visual Basic.
Why are there so many different programming languages? Because they were designed to
serve special purposes. BASIC (an acronym formed from the phrase Beginners Allpurpose Symbolic Instruction Code) was designed in 1963 as a tool for teaching people to
program. C was developed in 1972 for use with the UNIX operating system. (And yes, ts
predecessor language was called B.) Pascal was developed in 1970 as a language for
teaching people how to program.
When you learn your first programming language, its good to start with an easier one, so
I have chosen Java. Why Java?
Its a language commonly used today, and is being used more and more as time goes by.
It is a powerful language. That is, it allows you to do many different tasks and thus has
applicability in a wide variety of domains (different areas of programming games,
database, and student registration are examples) on a variety of platforms. These
platforms include mainframes, personal computers, and mobile devices like cellphones
(the North American term) or mobile phones (used by the rest of the world).
Java is a language that uses a popular programming paradigm, object-oriented
programming. What is a paradigm? Its a way of thinking about what you are doing. Its
the set of practices that define a scientific discipline during a particular period of time.
(http://en.wikipedia.org/wiki/Paradigm)
Wikipedia is an online encyclopaedia, in which the entries in the encyclopaedia are
written by people like you and me. That means there is a certain risk in using Wikipedia
as a source. How do you know that the author is reliable?
In general, I have found that the articles about computer science are reliable and I will
quote many of them in the material that follows.
Summary
In computing, hardware, software, and programming are combined to create instructions
to control a computer. The instructions are expressed in a programming language, using a
particular programming paradigm. Java supports the object-oriented paradigm.
Define class
Define object
Draw a class diagram
Implement simple classes in Java
Define method, constructor, getters, setter
Create constructors, getters, and setters
Describe some primitive datatypes and some datatypes represented by classes
Document Java code
Describe the purpose of, and implement, a toString method
Introduction
This chapter contains a lot of material, so youll want to take it slowly. First we discuss
what objects and classes are, and how we model them.
Then we discuss how to write a class.
Then we look at BlueJ, the tool we will be use to help us in our modelling and our
coding.
Then we look at JUnit, the tool we will use to test the programming that we create.
Finally, we use what we have learned to create a second class.
Definitions
When you write a program, you provide the instructions to process pieces of data. Data
comes in many different types. Depending on the language you are using, you will have
different datatypes provided to you. You will probably have ways to create new
datatypes. In object-oriented programing, we create new datatypes by creating classes
and using the classes to create objects.
In computing, the word object has a very specific meaning an object is an instance of a
class. Similarly, the word class has a very specific meaning a blueprint for creating
objects.
But those definitions are circular; they dont tell us anything. We need to explore the
concepts of object and class more fully before we can understand the definitions.
So what is an object? Since we are talking about computer programming, an object is a
representation, implemented using a computer language and stored in a computer, of
something in which we are interested. This something may be real, like a person or a
piece of furniture, or imaginary, like a mathematical function.
You, a student, are an object in a system involving college registration. There are many
students at a college; all are objects. All students have things in common, like name,
address, and student number.
A system involving college registration will need other objects. Each course the college
offers is an object. You receive a final mark in each course you take, so there can be
associations between one object and another. When two objects are related in some way,
we say there is an association between the objects. You, a student, register in a section,
and a section is an object. There is an association between a student and a section. One
object may participate in many associations. For example, you are registered in sections,
you earn marks in courses, and you may incur library fines.
But think about this for a moment. There are many student objects enrolled at Okanagan
College. All these objects have some things in common; they each have a name, a student
number, and an address. They each have a associations with sections of a course. Each
section has an association with a course.
Whenever you find things having common features, you create a blueprint for creating
objects, rather than just creating objects.
Thus we have a definition of a class a blueprint for creating objects.
This idea of a blueprint also works in other areas as well. Think of a new building
containing apartments (flats). Many of the apartments have the same layout. They were
all made from the same blueprint. Many of the floors of the building have the same
layout. They are all made from the same blueprint. And look at some of the new
subdivisions being built around our cities. Many of the houses in those subdivisions look
alike; they are made from the same blueprint.
In the building in which I have recently purchased a home, some of the units are identical
and some are mirror-images of each other. But they are built from the same blueprint.
Classes
We have identified student as a class whose objects are in this room. What other
classes can we identify in this room?
Well, depending on the application, we could consider the room itself. It has a room
number and a capacity. A Room class could represent a room. Note that I have shown the
name Room in a different typeface, one which I have reserved for code in the Java
language. This indicates that we may be implementing the Room class in Java.
The word code means the instructions we write in Java. These usually are statements. A
statement in English is a complete thought. A statement in Java is a complete instruction
to the computer.
Looking for other objects in the room, we could consider the desks, the chairs, and the
tables themselves. They may have serial numbers attached. They have a description. A
Furniture class could represent them.
All your textbooks have an ISBN, a title, and an author. A Textbook class could
represent a textbook.
Your professor represents a class. Your professor has a name and an employee number,
the same way you have a name and a student number. A Professor class could
represent a professor.
7
So we are surrounded by objects and can use our language to determine the classes from
which they are instantiated (created as instances.) Once we have identified the classes,
we will go on and identify the associations between them.
So where do we start when we want to model these real-world classes?
Why do we want to model real-world classes in a computer? Perhaps we would like to be
able to determine the effects on our institution if we reduced the class size. How many
more sections of courses would we need to offer? Do we have enough classrooms? A
model would help answer those questions.
Those questions are relatively simple, and we could do the experiment in the real world.
Some experiments are not so simple. To add an environmental note, we have been adding
large amounts of greenhouse gases (carbon dioxide, methane, etc.) to the atmosphere
during the past 250 years. We are now finding that this has an effect. The weather is
becoming more changeable. Most glaciers are melting, whether they are in the
mountains, the Arctic, or the Antarctic. Plants are blooming earlier than previously seen.
The Inuit of the Canadian Arctic are seeing birds (the American Robin, for example)
which they have never seen before; they had to create a new word for the American
Robin! We are making some serious changes to the world, without knowing the
consequences of these changes. Would it not have been better to make a model and do
the experiments there?
When I first began teaching, there was some discussion about nuclear winter. This was
the idea that if there was a nuclear war, the resulting explosions would throw so much
dust into the atmosphere that the amount of sunlight reaching the surface of the Earth
would be reduced significantly. This would lower the temperature, and reduce much of
the plant growth. Again, this is not something we would like to do in the real-world to see
what would happen. Well, we may actually try something like this if a large volcano
erupts.
So how do we make models using the Java language?
Class diagrams
We need to be able to describe these two classes, using some language. In this course we
will use two languages. One, called the Unified Modelling Language (the UML), will be
used for drawing diagrams; after all, a picture is supposed to be worth 1000 words. When
we use words, we will use the Java programming language. (Note that Java is also an
island in Indonesia and a synonym for coffee. There are many coffee jokes associated
with Java, including JavaBeans and the coffee cup logo. There are no Indonesian island
jokes of which I am aware, however.)
In the UML, a class is represented by a rectangle, displaying one, two, or three
subsections. The first subsection always appears, and it contains the name of the class.
The second subsection of the rectangle contains the attributes (the names of the data
items, the pieces of data which an object contains), whose values we discussed earlier.
This subsection is optional in the UML but is obviously necessary when you translate the
diagrams into Java.
The third subsection of the rectangle contains the names of the methods the class
supports. These are the behaviours we discussed earlier. This subsection is optional in the
UML but is obviously necessary when you translate the diagrams into Java.
Thus, lets consider the UML representation of a Student class. It can be represented
simply as follows.
This represents a class. All that we know about the class is its name. We dont know its
attributes. We dont know its methods. But we usually need to know more about the
class.
Consider the attributes of a Student object, an object instantiated from the Student
class. Remember that a Student class is simply a blueprint, describing what a
Student object knows and can do.
We have already mentioned that a student has a name and a student number. Thus a
Student object will have attributes named studentNumber and studentName.
Since names are not unique, everyone needs a number, which will be unique; no other
student will have that number. The Registrars Office is responsible for ensuring that the
student numbers are unique. Part of that responsibility is to ensure that once a student has
graduated, her student number is not reused.
In a previous career, I worked for a company which reused employee numbers. That
caused many problems when you looked at historical data for employees who were no
longer with the company. Their numbers may have been reused two or more times so
information on two or more employees would be combined.
Attribute names do not begin with a capital letter. If an attribute name contains several
words, then the second and subsequent words are capitalised. As English is my native
language, the attribute names consist of English words. There is nothing to stop you from
using words in your native language to create attribute names.
These are two different attributes of every Student object, so they can be shown in the
Student class diagram. Every Student has a name and a number, as each of you do.
Thus, the UML notation of the Student class, showing the attributes is
Note that BlueJ apparently does not allow this form of the class symbol. These class
diagrams are created by a free UML editor called Violet, available at
http://horstmann.com/violet.
Is it a problem that BlueJ only offers simplified UML diagrams? It would be a problem
were we developing a commercial product, but it doesnt seem to be a problem for our
project.
Datatypes
What type of data do we use to store the studentNumber and studentName?
Everything stored in a computer is stored as patterns of 0s and 1s, called bits. When you
collect eight bits and treat them together, as one byte, then a byte can represent 256
different values.
If you have one bit, then you can represent only the values 0 and 1. If you have two bits,
you can represent 00, 01, 10, and 11, four values. If you have three bits, you can
10
represent 000, 001, 010, 011, 100, 101, 110, and 111, eight values. Similarly, four bits
can represent 16 values, five bits can represent 32 bits, six bits can represent 64 values,
seven bits can represent 128 values, and eight bits can represent 256 values. In general,
when you have n bits, you can represent 2n values.
Having 256 possible values allows us to represent all the letters of the English alphabet,
the digits from 0 through 9, some punctuation symbols, plus a few other letters. However,
this does not represent letters in many other alphabets. To do that, we need to group the
bits into a larger unit.
The Unicode standard, described at http://www.unicode.org, uses 16 bits to represent
individual characters. That is apparently enough to represent all Earthly languages.
Klingon, an invented language used in the Star Trek television series and movies, is not
included.
Fortunately, Java has a datatype named String which contains characters, encoded
using the Unicode standards. If we can create the character, this datatype can represent it
faithfully.
What about studentNumber? One of the problems of the English language is its
ambiguity. Just because we use a particular word to describe something, that word is not
necessarily the best for the situation. At first glance you would assume
studentNumber is a number.
Java does support many different types of numbers. As we noted above, a bit is the
smallest unit of memory. A bit has only two values, one or zero, or on and off, depending
on how you look at it. Everything is stored as a collection of bits. Java has a number of
primitive datatypes, which are not objects. These include a byte which contains 8 bits
(256 values, representing integers from -128 to 127), a short which contains 16 bits
(representing integers from -32768 to 32767), an int which contains 32 bits
(representing integers from -2147483648 to 2147483647), and a long which contains 64
bits (representing integers from -263 to 263 1). All of these could be used to store
studentNumber as long as we know they are large enough (contain enough digits);
byte and short are not long enough, since student numbers at Okanagan College
contain nine digits. Thus int and long contain enough digits.
Java also has floats and doubles, to store numbers containing decimal points. At
least we know the studentNumber does not contain decimal points.
This discussion of numeric datatypes is interesting, but is studentNumber really a
number?
A good guideline in deciding whether something called a number is a number or
something else is to ask the question Is it going to be used in mathematical
calculations? If so, you should store it as some type of number. But a
11
No only do we see the name of the class, we see the names of the attributes and their
datatypes.
Well note for the moment that these two diagrams reflect a certain commonality. Well
explore that commonality later in this chapter.
12
If we store the name as one field, we would need to split it up whenever we needed the
parts. That may seem to be a straight-forward task, and its a well-known algorithm,
described below.
An algorithm is a series of instructions to solve a problem, in a finite amount of time,
using a finite amount of space. In this case, a possible algorithm is start at the beginning
of the string and look for the first space. The part before the space will give us the given
name, the part after the space will give us the family name. This algorithm expects the
string containing the name to have the first name at the beginning of the string, the last
name at the end.
Does that algorithm really solve the problem? What about Billy Bob Thornton (whose
given name is Billy Bob)? Our algorithm does not give the correct answer!
Many Asian names dont satisfy the conditions of the algorithm. The family name is
given first instead of last. Thus the algorithm will say that Yang Changji has a first name
of Yang, but that is actually his family name.
A similar algorithm (having similar problems) may be used to find the last, or family,
name. To identify the family name, just start at the end and work forward until we find
the first blank.
Does that algorithm really solve the problem? What about Manfred von Richthofen
(whose family name is von Richthofen). It appears we have a flaw in our logic.
Of course, there is also the problem of hyphenated names. Both first names and last
names may be hyphenated.
To get around the fact that breaking a name into its constituent parts is challenging (if not
impossible), we should store the first and family names separately, using attributes
firstName and familyName. Since we are modifying our design, we should
consider what other name-related fields we should include.
Perhaps a preferred name should be there. Not all Alberts like to be called Albert; they
may prefer Al or Bert or Bertie. Not all people like to be called by their given name.
Cholmondley David Smith may prefer to be called David, or Dave. (Cholmondley is
pronounced Chumly.) I use some software that uses my full name when it says goodbye;
thats too formal for my taste. So, lets have a preferredName attribute.
There may even be times when a persons middle name is required, so lets have a
middleName attribute.
If our program is to be useful in other cultures, we may wish to just use the attributes
name1, name2, and name3 and let the user of the program decide what to enter in each.
13
In addition, there will be times when a persons full name is required, so well store it as
well, but only for use when the full name is necessary.
I will, at least temporarily, use the word student as part of the attribute names for a
Student, and professor as part of the attribute names for a Professor. Thus the
UML diagrams for our Student and Professor classes look like this.
Behaviours
Recall that an object has identity (the name we give the object), state (the values it
contains), and behaviours (the things it can do.) First of all, we note that things is not a
14
very precise word. I would like to define behaviours as the messages to which an object
responds. We will send a remember that your given name attribute value is message to
a Student object and then we can, at some later time, send a please tell me what your
given name value is. message to that object and it will return the value it remembered.
In Java, methods receive the messages and respond appropriately.
This idea of sending messages to an object is a powerful one, and we can extend it to
sending messages to a class, in particular, a message like create an object of your class,
and here are the attribute values you need. Most classes contain a method, called a
constructor, which responds to this message. It will take all the attributes you supply,
save them appropriately, and then return an object for the program to use.
Similarly we will need messages to remember (or set) each of the individual attributes
(one message per attribute.) These messages are called setters. The values we provide to
be remembered are attached to the message through parameters. Another name for these
methods is mutators. They change, or mutate, the value an attribute contains.
We also need messages to retrieve the values of each of the individual attributes (one
message per attribute). These messages are called getters. Another name for these
methods is accessors. They access attributes.
Constructors, getters, and setters are usually not shown in the third subsection of a class
symbol, the area where methods are shown.
Later on, we will see a special type of class, a JavaBean. For these classes, there is a
constructor which takes no parameters, and a setter for every attribute. To create such an
object, first use the constructor to create an object whose attributes have some default
values. Then use the setters to specify the values you want. For now, our constructors will
take parameters.
Once we have the ability to create an object, we should have the ability to display it, to be
sure that it has been created correctly. Every object needs to be displayed, so the class
needs a method, usually called toString. This method converts an object to a humanreadable form suitable for display, perhaps through printing. Well see the code (Code is
computer-speak for anything written in a programming language.) for this method in the
next section of this chapter.
15
Heres part of the code for the Student class. Its followed by an explanation of the
individual statements.
/**
*
A Student
*/
public class Student {
private
private
private
private
private
private
String
String
String
String
String
String
studentNumber;
studentFirstName;
studentMiddleName;
studentLastName;
studentPreferredName;
studentFullName;
/**
*
@param studentNumber
9-digit student
*
number assigned by the college
*
@param studentFirstName
Student First Name
*
@param studentMiddleName
Student Middle Name
*
@param studentLastName
Student Last Name
*
@param studentPreferredName Student preferred
*
name or nickname
*
@param studentFullName
Student legal name
*/
public Student(String studentNumber,
String studentFirstName,
String studentMiddleName,
String studentLastName,
String studentPreferredName,
String studentFullName) {
this.studentNumber = studentNumber;
this.studentFirstName = studentFirstName;
this.studentMiddleName = studentMiddleName;
this.studentLastName = studentLastName;
this.studentPreferredName = studentPreferredName;
this.studentFullName = studentFullName;
} //end constructor
/**
*
@return student number
*/
public String getStudentNumber() {
return studentNumber;
}
/**
16
*
@param student number
*/
public void setStudentNumber(String studentNumber) {
this.studentNumber = studentNumber;
}
} // end class
Lets examine this code in detail, starting with the documentation.
Documentation
The class begins with some documentation, comments you make to read at a later time,
and comments which will be visible to anyone viewing the documentation for your class.
These comments typically explain the purpose of the code and give some information
about its contents.
In a Java class, the documentation section begins with /** and ends with */. If you have
done any programming before, you may be used to /* to begin a multi-line comment,
and */ to end it. While that style still is acceptable, there is a utility program named
javadoc which will process your Java program and identify the comments, as long as
they are preceded by /** and followed by */ Once it has identified the documentation,
it formats it into a readable, useful, form.
Normally the first block of comments in a class will contain information about the class.
It should include such information as the author (using the @author tag) and the date
the class was originally written, along with a revision history (using the @version tag).
Details on writing comments for the javadoc utility program are available at
http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
It is important that you follow these standards for writing comments since javadoc is
accessible via BlueJ and you want your documentation to appear as professional as
possible.
Important points to note are the following.
o Documentation for a class must appear immediately before the class header.
Any import statements, which we will see later, must appear before the class
documentation.
o Documentation for a method must appear immediately before the method
header. The description of the method is the first complete sentence in the
documentation.
o Documentation for the parameters to the method, or the value returned, may
extend over several lines, if necessary.
17
You may be used to writing single-line comments that begin with //. As you can see
above, anything on the same line after // becomes a comment.
Class declaration
The class header says public class Student.
This marks the first line of the class itself, giving the name of the class (Student), and
an indication (the word public) that it can be used by methods outside the class. Of
course it must be used by others; thats the whole point of having classes! Other people
dont need to reinvent your work. Yes, there are private classes and we will use some
ourselves, but they are relatively rare.
The body of the class, including its instance variables and methods, are enclosed in a pair
of braces, sometimes called curly brackets. Whether you place the opening brace on the
same line as the name of the class is the subject of discussion in some circles. It doesnt
really matter.
18
Statistics may be an acceptable class name, since the word statistics may be plural
or singular, and statistic may not be a meaningful word, depending on the situation.
Class names always begin with a capital letter. If the class name contains several words
all are capitalised, for example DepartmentStore.
Instance variables
Looking back at the code, we have six statements allocating memory for the six data
items (instance variables) that each object contains.
private
private
private
private
private
private
String
String
String
String
String
String
studentNumber;
studentFirstName;
studentMiddleName;
studentLastName;
studentPreferredName;
studentFullName;
Each of these lines is a separate Java statement. A Java statement ends with a semi-colon
(;). A Java statement may extend over more than one line, but it must eventually end with
a semi-colon.
Each student has a number and name(s). Note that all these instance variables are
declared private. The only way the outside world may access these variables is via the
getters and setters which we examine below.
The name of an instance variable begins with a lowercase letter. If the name actually
contains several words, the second and subsequent ones are usually capitalised.
studentNumber is a good example of this.
After identifying these instance variables as appropriate for a name, I read (in The Globe
and Mail, a newspaper published in Toronto, Canada, on, 2006-08-16) about the
inhabitants of Norfolk Island. Descendants of the Bounty mutineers, there are very few
family names represented. The phonebook for Norfolk Island includes nicknames for the
inhabitants. Perhaps we should include a nickname attribute!
19
The name you choose to give an object should be meaningful. That is, the name should
convey some idea of the objects purpose or role. If so, then you do not need to provide
documentation of what the object represents. For example, studentNumber really
needs no further documentation (although you may provide it should you wish), while an
instance variable named x does need further documentation.
Constructor(s)
Continuing our examination of the Java code, we see that the instance variables are
followed by a constructor. We know it is a constructor not just because the
documentation says so but because it has the same name as the class.
This constructor is executed whenever we need to create a new Student object. A
constructor uses the values we provide as parameters to initialize the instance variables
for each object we create.
Within a constructor, my programming style is to use the name of the attribute as the
name of the parameter. That leads to statements like
this.studentNumber = studentNumber;
which means, in English, take the value provided as a parameter (studentNumber)
and save it (the equals sign) in another location. The reserved word this means the
current object. Thus, this.studentNumber refers to the studentNumber
attribute within the current object and that is where the value in the parameter is saved.
It is possible to have many constructors in a class, as long as they have different
parameters, different that is in type or in number. You can not have two constructors,
both with two Strings as parameters. But you could have one constructor with two
Strings, and another constructor with a String and an int as parameters. Perhaps
all you know about a Student is the student number and name, but you dont have the
preferredName; the student will provide that later. A second constructor could then
be the following.
public Student(String studentNumber,
String studentFirstName,
String studentMiddleName,
String studentLastName,
String studentFullName) {
this.studentNumber = studentNumber;
this.studentFirstName = studentFirstName;
this.studentMiddleName = studentMiddleName;
this.studentLastName = studentLastName;
this.studentFullName = studentFullName;
this.studentPreferredName = ;
20
} //end constructor
Since we dont know the preferred name, we simply remember an empty String,
signified by the consecutive quotation marks, with no characters between them.
Recall that we said earlier that a method is Javas way of handling messages. If you send
a message, you must identify the receiver, and we will see how to do that later. You must
also identify the message. Part of the message is its name but part is its parameters. If you
ask a friend for dinner, that is a part of the message. The rest of the message involves the
choice of the restaurant and the time at which you would like to meet. The name of the
restaurant and the time are parameters, variable parts of the message.
When we send a message to create an object, we usually provide the values the instance
variables are to assume. These values are the parameters. We have seen several
assignment statements, the ones using the equals sign, which transfer the values of the
parameters into the values of the instance variables, and we have had a simple
explanation of how that statement works. But what does it really mean to say
this.studentFullName = studentFullName;?
Start with the names of the variables. When a computer stores data, it uses its memory.
Each memory location is assigned a unique address. You could write programs that use
those addresses, but it is very difficult to keep the addresses straight in your mind, so
today we use an association between names that we understand and the numbers of
memory locations. The process of translating our program from Java into something the
computer can understand directly (the addresses) is called compiling. The program we
use to do this compiling is called a compiler. The Java compiler is called javac. Of
course, the c part of its name stands for compiling, and the java part stands for,
well, Java.
When we write our program, we use names for the variables we wish to use. The
compiler establishes a correspondence between the names we use and the numeric
addresses the computer knows.
An object requires a block of memory, a collection of memory locations. The block of
locations is referred to using the address of the first location in the block. An object has
an address, and so do each of its instance variables. So to do each of its methods. Thus,
when we refer to the variable this.studentFirstName, we are referring to the
variable studentFirstName within the current object. Javas shorthand for the
current object is this. Thus, this.studentFirstName refers to a specific area of
memory within the memory the computer has allocated to the current object.
studentFirstName (without the this) is another variable. It represents the name
which we provide to the constructor. studentFirstName is a different area of
memory from this.studentFirstName.
21
So now we have two areas of memory that are involved in this assignment statement, a
statement which calculates a value and assigns it to a variable. Now consider the equals
sign.
In mathematics, an equals sign means that the values on the two sides of the equals sign
are identical. In computing, an equals sign is an instruction to take the value on the right
of the equals sign and copy it to the variable on the left hand side. After that copy is
complete, the two areas of memory contain identical values.
In fact this is not strictly true. If we were to look inside a String, we would find that it
contains an address of memory. If you go to that address in memory, you will find the
actual contents of the String. When we say this.studentFullName =
studentFullName;, we are actually saying that the address contained in
this.studentFullName should become the same as the address contained in
studentFullName. We will see this in the exercises to this chapter when we inspect
the objects we create.
22
Getters return a value to wherever the method was called. As such, it must be declared
public (allowing the method to be called). It must provide a datatype describing the
value returned. The body of the getter is very simple. The Java statement to return a value
from a method is simply the word return followed by the value.
It is left as an exercise to the reader to provide the other getters, all correctly documented.
We need one getter for each instance variable.
Finally, we have one setter. We know that a method is a setter because its name begins
with set.
/**
*
@param student number
*/
public void setStudentNumber(String studentNumber) {
this.studentNumber = studentNumber;
}
A setter must be declared public (allowing the method to be called). Setters do not
return a value to wherever the method was called, so it must provide a void datatype.
It is left as an exercise to the reader to provide the other setters, all correctly documented.
We need one setter for each instance variable.
All the methods we write will have a visibility modifier (the ones we have seen are public
and private), the datatype of the value to be returned (or void when nothing is being
23
returned), the name of the method, the parameter list (which may be empty. Well see an
example of this momentarily.), and the body of the method.
toString
Earlier we mentioned the toString method, a method which allows us to display the
contents of an object. Here it is.
/**
* @return
a Student, as a String
*/
public String toString()
{
return Student number: + studentNumber +
Student name: + studentFullName +
( + studentPreferredName + ) ;
}
Note the structure of this method and how it compares to the discussion above. The
visibility is public. The datatype returned is a String. The method name is
toString. The parameter list, in this case, is empty. Those four parts make up the
method header, which is followed by an opening brace. This is followed by the method
body, and then a closing brace.
All methods have this structure: method header, opening brace, method body, closing
brace.
Note its similarity to the structure of a class. First comes the class header, then there is an
opening brace. This is followed by the class body, and then there is a closing brace.
Since we wish to be able to call this method from elsewhere, it needs to be public.
Since it is returning a String, its return type must be String. The name toString
is a name that has been adopted by many classes.
24
In this example, all of the work is done in the statement which begins return. It
describes the value the method will send back to the method which called this one.
return Student number: + studentNumber +
Student name: + studentFullName +
( + studentPreferredName + ) ;
That value is built by concatenating (putting together, one after the other, into a long
String) several shorter Strings. The concatenation symbol is the plus sign.
In this case, we combine seven Strings. Three are the values of instance variables, two
are descriptions of what the instance variable represents, and some are just punctuation.
Student number: is a description. It is an example of a string literal, where we
specify the value, enclosed in double quotation marks, containing whatever text you
would like to display. It will appear as typed, but without the quotation marks. If there is
nothing between the quotation marks, you have an empty string, as we saw earlier. The
plus sign (+) indicates that what you have so far (a description) is to be followed by
another String, this one being the value of the instance variable studentNumber.
One String followed by another one is still a String, and we continue constructing
the final result. The plus sign is known as the concatenation operator.
Next we append a description Student name: and then provide the value of the
instance variable studentFirstName. The String which will be our result is
getting longer, but we append (add to the end). Note that sometimes you prepend, or add
to the beginning of an existing String.
Perhaps it would be nice to show the preferred name since it may not be obvious. We will
do that in parentheses after the full name. Thus, toString continues by adding an
opening parenthesis, the value of the instance variable studentPreferredName, and
a closing parenthesis.
Once the complete String is available, it is returned to the method which asked for it.
Note that this Java statement illustrates the idea that a statement may extend over many
lines in your editor. An exception to that rule is that a string literal may not extend over
more than one line. That is, the opening and closing double quotation marks for a string
literal must be on the same line.
25
In some cases, there is only a return statement in this method. In other cases, the method
may look like this.
// start with an empty string
String result = ;
// build the String by concatenation
return result;
26
// constructor
public College(String name, String phoneNumber,
String homePage) {
this.name = name;
this.phoneNumber = phoneNumber;
this.homePage = homePage;
}
// name and contact information methods
public String getName() {
return name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getHomePage() {
return homePage;
}
public void setName(String name) {
this.name = name;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public void setHomePage(String homePage) {
this.homePage = homePage;
}
public String toString() {
return name + '\n' +
phoneNumber + '\n' +
homePage;
}
}
As mentioned above, the process to create this code is as follows.
o
o
o
27
o
o
o
Summary
Thats it for your introduction to Java. My approach to teaching programming is to
discuss a model of something and then build the model. If we need some special feature
of language to build that model, we will look at that feature when we need it. Thus, we
needed to speak about modelling and Java in this chapter. Wee needed to talk about
constructors, getters, setters, and a toString method.
Now we need to see how to communicate our program to a computer.
28
Exercises
1.
2.
3.
4.
In this chapter, we have mentioned several other classes we could develop. These
included Room, Furniture, and Building. Choose one of those classes and
identify the attributes it could contain. What datatypes are appropriate? Why did
you choose those datatypes?
5.
Explore the online documentation for the URL class. What modifications would we
need to make to our College class to use the URL class?
6.
One simple class to model is a door. A door has width, height, and thickness. It is
either open or closed. Implement a Door class. For a good solution, you will need
to use int (or double) and Boolean variables. As an additional complication,
you may wish to model whether the hinge is on the left or the right.
7.
Consider some other real-world classes which you may have seen. An automobile is
suitable. So is a digital camera, a digital photograph, or an MP3 file. Choose one of
these classes. What is its name? What are its attributes? What are its behaviours?
29
30
Start BlueJ
Use BlueJ to create a class diagram
Use BlueJs editor to create and modify classes
Use BlueJs Object Inspector to examine objects
Define unit tests
Use BlueJ to create and run unit tests
Introduction
So now we have the source code, written in Java, for a Student class. A program is
designed to be executed by a computer, so we must have a way of communicating the
program to the computer. There are many tools which will allow us to communicate with
the computer. We will use one called BlueJ.
BlueJ
BlueJ is an integrated development environment (IDE) which we will be using while
introducing Java. This IDE provides us with many tools:
an editor which understands the syntax (the grammar or the rules) of the Java
language,
a UML diagramming tool,
a tool for creating and testing classes,
a link to a Java compiler, and
a debugger (which we can use to fix hard-to-find errors.)
An integrated development environment provides access to all these tools through one
common interface.
If you are doing your programming at a college or a university, BlueJ will probably have
been installed on the computers you will be using. If you wish to do some work in Java
elsewhere, you will need to install BlueJ and Java on your personal computer. That task
is included as an exercise at the end of this chapter.
Once BlueJ and Java are installed, you can continue to create a new project.
31
Type the name of the class, in this case Student, and click Ok (or press
.) BlueJ
updates its class diagram. Not that Java requires that class names contain no spaces. You
32
could use underscores to separate individual words in a class name, but the standard, as
we have seen, is to capitalize each word of the class name which contains more than one
word.
The class displays diagonal lines as a visual reminder that it has not been compiled. Since
it hasnt been compiled, we cant use any of its features. Right now, thats fine since we
dont know how to compile it. But later on, when you have many classes, and are making
changes to several of them, its good to know which have been compiled and which have
not.
To see the contents of a class, the documentation and Java statements it contains, right
click the class and choose Open Editor (or double-click the class.)
BlueJ provides you with a skeleton of a class, some documentation, the declaration of an
instance variable and a method. Keep what you need, delete the rest. Dont delete the first
few blank lines; well need them later.
Enter the Java code for the Student class which we seen above. After you have entered
the declarations for the instance variables, and the constructor, stop. Click Class, Save (or
press
+S) to save your work.
Look at what you have typed. Are there any obvious errors? If so, fix them. Obvious
errors will make themselves obvious by unexpected indenting, or unexpected colours.
There are a number of advantages to using an IDE. Since the IDE knows the language
you are using it can make intelligent decisions about indenting. It can display different
33
aspects of the language in different colours. If you wish to suppress these helpful features
(why would you want to?), choose Tools from the main menu, then Preferences from the
drop-down menu. On the Editor tab, uncheck the appropriate combo boxes. When you
first run BlueJ, you will find that most of the combo boxes are checked. I leave mine that
way, possibly checking Make backup of source files also.
IDEs can also match parentheses and braces. When you type a closing parenthesis or
brace, the IDE will momentarily highlight the matching opening one. When you type an
opening double quotation mark, the screen will show red characters until you type the
closing double quotation mark.
If you have forgotten your punctuation symbols, an opening parenthesis is (. A closing
parenthesis is ). An opening brace is { and a closing brace is }. The double quotation
mark in programming is and the same mark is used at the beginning and end of all
strings. In typesetting there are two double quotation marks, an opening one and a
closing one .
Have you noticed the row of buttons under the menu in the Editor window? Undo, Cut,
Copy, and Paste have perhaps been useful in entering the code so far. But we now need to
compile the statements we have entered.
Click the Compile button to both save and compile the class. If you have made any
errors, the compiler will find them and BlueJ will highlight (in yellow) the line
containing the first one. If you see the error immediately, fix it and compile again. If you
dont see the error, click the question mark in the lower-right corner of the BlueJ
window, and BlueJ will offer some hints about what the error may be. Sometimes one
error will cause many others, so dont worry about fixing every error at the same time.
Sometimes one error will hide another, so youll see more errors after you fix one than
you had before. But never fear, youll soon reduce the number of errors to zero.
When you have no more errors, continue entering the other getters and setters. Dont
forget getters and setters for each instance variable you created. Remember that the editor
allows you to cut-and-paste, and search-and-replace. Enter the toString method as
well.
The order in which you list the declarations of the instance variables does not matter. Nor
does the order in which you list the methods.
Following the instance variables, I list the constructors. Then I list the getters and setters.
Sometimes I list all the getters and then all the setters, sometimes I do the reverse. And
sometimes I list a getter followed by its setter, or vice versa. It depends on the style you
(and your teacher) prefer.
I usually list the toString method after the getters and setters.
34
When you are finished typing, choose Class, Save from the menu in the window
containing your class. This saves your work but does not compile it.
Later on we will have many classes open at the same time. Then it may be more efficient
to choose Project, Save from the menu in the main BlueJ window, and save all the classes
you have open.
Now all your work has been saved should you have to leave. Assuming you havent left
(or you have returned and opened the project again, using Project, Open Project, or
Project, Open Recent), click the Compile button again.
Note that there are two Compile buttons, one in the window where you have entered the
class, and one in the main BlueJ window. It doesnt matter which one you click.
35
by magic, in the background. Well, its not really magic. Later we will lift the curtain and
see what is in the background.
The noted science fiction author Arthur C. Clarke said that Any sufficiently advanced
technology is indistinguishable from magic.
(http://en.wikipedia.org/wiki/Clarke's_three_laws) I use magic in that sense.
The reference to lifting the curtain comes from the movie The Wizard of Oz.
For now, simply right-click the Student class in the class diagram. A menu appears
showing the public methods of the class, plus Open Editor, Compile, Inspect, Remove,
and, separated by a line, Create Test Class. We will explore those options later. For now,
simply click the name of the constructor It is highlighted because it is the only method
which you can call now. The getters and setters, and toString, can not be used until
we have created an object.
Recall that a constructor allows you to create an instance of the class, an object. By
clicking the name of the constructor, you run the constructor. First, however, you need to
specify the name of the object; student1 is acceptable for now, but you can change it if
you wish. Then you need to specify the parameters the constructor expects. Both are done
through the window shown below.
Remember that all the values being passed to the constructor are Strings, so should be
enclosed in double quotation marks. Remember that you can use an empty string () to
represent a value you do not know.
Note that the documentation you placed at the beginning of the method appears in this
dialogue, to guide you in providing parameter values.
36
Inspecting an object
Recall that earlier in this chapter we talked about the meaning of the equals sign, and how
one string contains the address of another. We can see this, now that we have objects on
the object tray.
Right-click an object. Choose Inspect from the menu which appears. The Object
Inspector window appears. It shows the values you specified for the parameters. Were
you like me and you specified you own name? Or were you more creative?
Highlight any of the six instance variables by clicking them, and click Inspect. For the
next two illustrations, Ill assume we left studentNumber highlighted.
37
Now we are looking under the hood, at how Java represents a String. The line reading
private char[] value shows an arrow. This is a reference to, or the address of,
another area of memory. That area actually contains the contents of the String. If you
click Inspect in this dialog, you will see the image below, showing the characters which
make up the student number.
Now that we are as deep into a String as we can go, the Inspect button is disabled. We
see that the String actually has two instance variables of its own. The first (length)
is the number of characters in the String. I entered six characters. The other is an array
(and we will explore them in more detail later.) which contains the actual characters. I
was lazy and repeated the same digit over and over.
In a similar way, we can see how the studentFullName is stored.
38
In this case, there is too much information to show in a window of the default size, so a
scrollbar allows you to see the rest of the information.
Click Close as many times as necessary to close the Object Inspector.
Breaking code refers to having previously-working code stop working due to some
change you have made. When you change a datatype from one type to another, I
39
guarantee you will break some other code. When you change the order by which you sort
some data, youll probably break code.
After a change, you will perform regression testing. To do this, create and save a series of
unit tests so that we can use them over and over again as necessary. BlueJ provides
access to a tool named JUnit which facilitates this testing. JUnit is a testing framework,
described in more detail at www.junit.org.
What things should you be testing through unit testing?
o Firstly, that each method behaves properly when you provide correct data.
Correct data means data which is reasonable to expect. If the method expects
integer input with values between 5 and 20, 10 and 17 are examples of correct
data.
o Secondly, that each method behaves properly when you provide input that is on
the boundary between correct and incorrect. Using the previous example, data
that is on the boundary between correct and incorrect date include 4, 5, and 6 at
the lower end, and 19, 20, and 21 at the upper end. Note that 4 and 21 are
incorrect data, while 5, 6, 19, and 20 are correct data. If zero is correct data,
make sure you test it.
o Thirdly, that each method behaves correctly when you provide wildly incorrect
data. Using the previous example, what happens when you provide 100 or -43
as input?
40
Then click Ok to dismiss the Preferences dialog and right-click the Student class
again. Choose Create Test Class.
BlueJ names test classes by taking the name of the class being tested and appending the
word Test. The test class for Student is thus named StudentTest.
41
This may not look exactly like what you see. My unit test was drawn too close to the top
of the window. When that happens, click on the Student class and drag it down a little.
StudentTest will follow along. You cant do the same thing by dragging the test
class.
Note that BlueJ continues its practice of colour-coding parts of the diagram. Unit tests are
green. A class is clearly shown as a unit test by the words unit test, enclosed in
guillemets.
These symbols are used in some languages, including French, to mark the beginning and
end of speech, much as English uses the double quotation marks. These symbols have
been adopted in the UML
Open StudentTest in the editor. Document it properly, adding author and version
information.
To do the test, we need some objects. One possibility is to create the objects anew in each
test, or we can create then once and then use them in all the tests. My preference is the
latter.
In the unit test class, before the default constructor and its comments, declare the objects
youll be using. These objects will exist during all of the tests. These declarations are of
42
43
twice, once for a student with a preferred name and once for a student without a
preferred name.
o carries out some processing and produces a result.
o uses an assert statement to check that the result matches the expected result.
Heres a sample test for the getStudentNumber method.
public void testGetStudentNumber() {
assertEquals(123456789, s.getStudentNumber());
In this test, we use the getStudentNumber method on an object whose student
number we know. We use the actual name of the object followed by a period and the
method we wish to use. Since the attributes have private visibility, we can only access
them from inside the object. To retrieve them from outside the object we use a getter. To
modify them from outside the object we use a setter.
Then we use the assertEquals method to check that the student number returned is
the expected value. assertEquals is a method provided as part of JUnit.
Once we are sure that the getStudentNumber method is working correctly, we can
test that the setStudentNumber method also works correctly. The test for the setter
involves the getter as well, so we need to test the getter first.
public void testSetStudentNumber() {
s.setStudentNumber(678678678);
assertEquals(678678678, s.getStudentNumber());
}
Here is the complete unit test for the studentNumber getter and setter.
/**
* The test class StudentTest
*
* @author rick gee
* @version 2006 04 20
*/
public class StudentTest extends junit.framework.TestCase
{
private Student s;
/**
* Default constructor for test class StudentTest
*/
public StudentTest()
{
}
44
/**
* Sets up the test fixture.
*
* Called before every test case method.
*/
protected void setUp()
{
s = new Student(123456789, Richard, Dennis,
Gee, Rick, Richard D. Gee);
}
/**
* Tears down the test fixture.
*
* Called after every test case method.
*/
protected void tearDown()
{
}
public void testGetStudentNumber() {
assertEquals(123456789, s.getStudentNumber());
}
public void testSetStudentNumber() {
s.setStudentNumber(678678678);
assertEquals(678678678, s.getStudentNumber());
}
}
As part of each test, we use the assertEquals method. This method comes in many
forms; the one we use takes two parameters. The first is a String containing the
expected value; the second is a String containing the actual value, returned from a
method. That is, the first parameter is the answer we expect, the second is what the
program gives us. Of course, we expect they should be the same. If they are not the same,
the assertion will fail, and the test will fail. JUnit will indicate that very clearly.
In testGetStudentNumber, we know the student number we used to create the
student, so we test that the value returned by getStudentNumber is the expected
value.
In testSetStudentNumber, we change the student number and then retrieve it. We
know what the expected value is, so we test that the correct value is returned. Technically
this is perhaps better called an integration test since we are combining different
methods and seeing that they work together.
45
When we use JUnit in more advanced situations, well see some other assert methods.
These include assertNull, assertNotNull, assertTrue, and assertSame.
For details on all the assert methods, look at the documentation at
http://junit.sourceforge.net/javadoc/junit/framework/Assert.html In this URL,
capitalization matters!
Notice that when you compile a class, the corresponding unit test class (if it exists) is also
compiled.
46
Below the line, is the number of tests run, and the number of errors detected or tests that
failed.
We wish to test each method in the Student class. There are six getters, one for the
student number and five for the parts of the name, and six setters. Thus we need to create
12 unit tests and ensure each test works successfully. Cut and paste, and be careful as you
make changes!
To test the toString method, we need thirteenth and fourteenth tests, one applied to a
student with a preferred name, one applied to a student without. As noted above, these
could be called testToString1 and testToString2. If you prefer, you could use
the names testToStringWithPreferredName and
testToStringWithoutPreferredName.
Testing toString is a little challenging, since you need to remember all the literals
which are added to the instance variables.
Make it so.
Smoke testing
These simple unit tests are exhaustive. That is, they test everything an object can do.
47
Summary
Thats it for your introduction to BlueJ. As I stated earlier, my approach to teaching
programming is to model something and then build the model. BlueJ and unit testing are
crucial to building the model. An untested model is a useless model. No architect would
build a structure without creating a model first.
Of course the model may show flaws. Thats better than building the structure and then
finding its flaws. A Wikipedia article on the Tacoma Narrows Bridge shows what can
happen when your model has flaws which you do not catch.
http://en.wikipedia.org/wiki/Tacoma_Narrows_Bridge#Film_of_collapse
48
Did you see how strange the output of toString looked when a student did not have a
preferred name? We need to fix that. Thus, the next feature we need is some way of
taking one of two branches, depending on some condition.
49
Exercises
1.
Develop unit tests for all the methods in the Student class. Ensure that your code
passes all the tests. My style is to develop the tests for the getters first, since they do
not depend on the setters. The values of the instance variables are all set by the
constructor.
Test the toString method. It also does not require any setters.
2.
Develop unit test for all the setters in the Student class. Ensure that your code
passes all the tests. Note that the unit tests for the setters will require the getters.
Thus you create the getters first, test them, and fix any errors they contain before
you begin to write the setters.
3.
Create the Professor class. As you do so, develop unit tests for all the methods
in the Professor class.
4.
Download (from bluej.org) and install BlueJ on your laptop or other home
computer. Download (from java.sun.com) and install the Java SE Development Kit.
Download (also from java.sun.com) and install the Java class documentation so that
you do not need to be connected to the Internet while using BlueJ, unless you want
to be, of course. Use Tools, Preferences from the main menu, and then the
Miscellanous tab to tell BlueJ that the documentation is installed locally.
5.
Use BlueJ to create a Student object on the workbench. Inspect it and see how
the attributes are stored. In particular, what is stored when you provide an empty
string for a parameter?
6.
When you write a book or make a presentation, you want everything to look as
good as it can. One way to do this is to associate a Font object to each piece of
text. http://en.wikipedia.org/wiki/Typeface gives an interesting background on type
and its terminology. Read it.
A Font class will need to provide the typeface, the point size, and an indication of
whether the font is to use bold, or italic, or superscript, or subscript. Implement the
Font class.
To do this properly you may need to use int and boolean datatypes in addition
to the String datatype.
7.
50
A bank account should know about its number, owner, balance, and the service
charge per transaction. Test all the methods. The account should contain methods to
make a deposit and to make a withdrawal. It is a simplified bank account since it
does not yet contain a record of all the transactions, so deposits and withdrawals
affect the account balance only. In subsequent chapters, we will implement the
record of transactions.
8.
9.
A die is the singular form of the word dice. Dice are used in many games. Model
a die.
What does a die know? It knows how many faces it has.
Dont jump to the conclusion that a die always has six faces. There are five convex
regular polyhedra (the Platonic solids) which can be use as dice. These regular
polyhedra have all their faces identical; thats the regular part of their name. The
tetrahedron has four triangular faces. The cube has six square faces. The octahedron
has eight triangular faces. The dodecahedron has 12 pentagonal faces. The
icosahedron has 20 triangular faces.
A die also knows how to determine the value that appears when it is thrown. To
implement that we need to know a little about random numbers, something we will
see later.
For now, implement a Die class, having only a constructor and a toString
method. In subsequent chapters, we will use this class to play a board game.
10.
11.
Birders are people who watch birds and record the details of their watching. Every
bird species has a common name (American Robin, as an example) and a scientific
name (Turdus migratorius, for example).
51
Design and implement a Bird class. Include all appropriate unit tests. We will
explore the world of birding in subsequent chapters.
52
The if statement
As we noted at the end of the previous chapter, the output of the toString method may
appear rather strange when you output someone without a preferred name, displaying ()
at the end of the name, instead of (Rick), for example.
How do you suppress the printing of the parentheses when there is no preferred name to
display? There are several ways to do so, all involving testing conditions.
First, lets look at what we mean by conditions.
Boolean algebra
A statement like Today is Tuesday. is either true or it is false. A statement like It is
raining. is either true or it is false. (Yes, there are statements like This statement is
false. which are neither true nor false. We will not deal with them here.) When we are
dealing with conditions, we are dealing with statements or expressions which are either
true or false. True and false are called Boolean values.
Boolean is a tribute to George Boole, a British logician (1815-64) whose life and work
are described at
http://www-history.mcs.st-andrews.ac.uk/Mathematicians/Boole.html.
There is an algebra associated with these Boolean values, describing the rules under
which they may be combined. You have seen one algebra already, when you studied
integers and the addition, subtraction, and multiplication operations. Division is a
problem, since there are many cases where you divide one integer by another but the
result is not an integer.
53
Boolean algebra is based on three operations (and, or, and not) and the values true and
false. The first two operations are binary, the third is unary. That is, you need two
Boolean values when you use an and operation, two with the or, but only one with not.
Two true conditions combined using and make a true, any other combination of two
Boolean conditions combined using and produces a false.
Two true conditions combined using or make a true, as do a true and a false (in either
order) combined with or. Two false conditions combined using or produce a false.
Not true is false, and not false is true.
These statements are usually summarized in the following tables.
Not
True
False
False
True
And
True
False
True
False
True
False
False
False
Or
True
False
True
False
True
True
True
False
What relevance does George Boole have to programming? To have computer programs
do anything interesting, they need to be able to examine the data they are processing and
make decisions about what to do next.
o A program may need to do different processing when a number is positive,
negative, or zero.
o It may need to do different processing if a string is empty or if it contains
characters. Think of a missing preferred name.
o It may need to do different processing when a specified number of pieces of
data have been processed. Think of printing text on a page. You may need to
place a footer on a page once a specific number of lines of data have been
displayed.
o It may need to do different processing depending on a number being even or
odd.
54
They are combined using the and operation. The same way that two Boolean values
anded together produce a true result only if both are themselves true, then several
Boolean values anded together produce a true result only if all are true.
Note that much of North America changed the rules in 2007, so that Daylight Savings
Time now begins three weeks earlier. This is an attempt to save energy on the assumption
that its light outside longer so people dont need to turn on their indoor lights. Early
indications are that people didnt go home but drove around, thus saving some electricity
but using more gasoline, for a net increase in energy usage.
An if statement is a programmers way of writing conditions and executing one of two
different scenarios, depending on the result of the conditions being true or false.
56
Java libraries. You could start there, by scrolling down the list until you see java.lang
and then clicking it. The bottom frame would then show all the topics available from the
java.lang library. Scroll down that list until you find String.
Or you could use the frame in the bottom left corner, and scroll down until you see
String. This is probably the best solution until you know which classes are in which
libraries.
In either case, once you have found the String class, click it. The frame on the right of
the screen now displays the javadoc output for the String class.
Look in the section of the documentation entitled Method Summary. Scroll through it to
find description of all the methods this class supports. In particular, find the length
method.
We use this method to determine how many characters are in the string, by looking at the
value it returns, an int. Further details on the method are in the Method Details. (Why is
that section of the documentation not in alphabetical order? I have no idea.) A value of 0
tells us we are dealing with an empty string, a value greater than 0 tells us there are
characters in the string. When a string contains many characters but only blanks, it will
have a non-zero length. Thus, is different from . The first contains two blanks,
the second contains none.
To determine the length of a string in Java, we use the name of the variable followed by a
period and the name of the method. Thus, studentPreferredName.length() is
the number of characters in studentPreferredName.
To check if the length of a string in Java is zero, we write
if (studentPreferredName.length() == 0)
Recall that the pair of equals signs is shorthand for check the values on each side and
see if they are identical. Since there are only two possibilities (the string is empty, and
thus its length is zero, or it is not empty and its length is greater than zero), the if
statement allows us to distinguish between the two cases.
If the condition is true (meaning there is no preferred name), simply return
Student number: + studentNumber + Student name: +
studentFullName;
If the condition is false (meaning there is a preferred name), return Student
number: + studentNumber + Student name: +
studentFullName + ( + studentPreferredName + )).
57
The statement or statements describing the action(s) to take if the result is true begin(s)
immediately after we test the condition. The statement or statements describing the
action(s) to take if the result is false begins after the word else.
public String toString()
{
if (studentPreferredName.length() == 0)
return Student number: + studentNumber +
Student name: + studentFullName;
else
return Student number: + studentNumber +
Student name: + studentFullName +
( + studentPreferredName + ) ;
}
If you wish to combine more than one simple condition in a larger condition, you may
use the symbols && to represent and, and you may use || for or, or you may use one if
statement within another. Note that not is represented by the symbol !. We will see these
possibilities later on.
The pipe symbols used to make || are typically on your backslash key.
58
else {
result = Student
result = result +
result = result +
result = result +
}
return result;
number: + studentNumber;
Student name: ;
studentFullName + (;
studentPreferredName + ) ;
}
Braces are necessary in this method since several statements are processed when a
decision is made of which path to follow.
Simpler tests
Sometimes you have processing in which you do nothing if the condition is true but do
something if the condition is false, or you do something if the condition is true, but
nothing if it is false. Consider the following alternative version for toString.
To create this version, we first notice that some of the processing in the previous version
is the same whether there is a preferred name or not. We do that processing and then we
use the relational operator greater than to see if there is a preferred name and, if so, do a
little more processing.
public String toString()
{
String result;
result = Student number: + studentNumber;
result = result + Student name: ;
result = result + studentFullName
if (studentPreferredName.length() > 0) {
result = result + (;
result = result + studentPreferredName + ) ;
}
return result;
}
Not surprisingly, there are many other relational operators. These include >= (greater
than or equal), < (less than), <= (less than or equal), and != (not equal).
59
The chapter began with an example that combined four conditions and mentioned that
conditions could be combined with && and ||. But it hasnt shown how. Lets remedy
that deficiency right now.
At Okanagan College, everyone has a number, whether they are students or professors or
any other employee of the college. Anyone can have any number, it appears, except that
students whose permanent residence is outside Canada are given student numbers which
begin with an eight or a nine.
Pause for a moment to create a method, isInternational, which will examine the
student number and decide whether the student is an international student. Use the
charAt method in the String class to extract the first digit of the student number as a
char.
Does your solution look like this?
public boolean isInternational() {
char firstDigit = studentNumber.charAt(0);
if (firstDigit == '8')
return true;
if (firstDigit == '9')
return true;
return false;
}
Or does it look like this?
public boolean isInternational() {
return studentNumber.charAt(0) == '8' ||
studentNumber.charAt(0) == '9';
}
Either solution is correct. The second solution uses the || operator to test if the first
character is an eight or is a nine. Is that solution better or worse than the first solution?
That decision is up to you and your teacher.
How many unit tests did you need to test this method?
You should have used at least three: one for numbers beginning with a nine, one for
numbers beginning with an eight, and one for numbers beginning with any other digit.
Here are two of my tests.
public void testIsInternational1() {
assertFalse(s1.isInternational());
}
60
61
Leap years in the western calendar are an interesting challenge. The simplified rule is that
a year is a leap year if it is evenly divisible by four but if it is divisible by 100, it will only
be a leap year if it is also divisible by 400. Thus 1996, 2000, and 2004 were all leap
years. But 2100, 2200, and 2300 will not be leap years. Create an isLeapYear method
and then come back and look at mine.
public boolean isLeapYear(int year) {
boolean result;
if (year % 4 == 0){
// divisible by four so might be a leap year
result = true;
// check the centuries
if ((year % 100 == 0) && (year % 400 != 0))
// oops, a century but not divisible by
// 400 so not a leap year
result = false;
}
else
// not divisible by four so not a leap year
result = false;
return result;
}
In this method you see that we can nest one if statement within another. That is, we
evaluate a second condition only if a previous condition is true (in this example). Within
a single if statement we can write compound conditions, ones which use && and ||.
This example uses the && operation. Note that we also use the != relational operator.
Summary
This short chapter has introduced one of the fundamental features of programming, using
the data you are processing to control the flow through your program. It has also
introduced Boolean algebra, the Boolean operations and, or, and not, and the relational
operators equals and greater than.
It has also shown you how to use the Java documentation to answer questions about
classes which are part of the Java libraries.
62
Exercises
1.
A simple object to model is a coin. A coin has two sides, typically called heads and
tails.
Assume that a Coin class implements the idea of heads and tails by using a random
number between 0 and 1. For a fair coin, if the number is less than 0.5 consider the
coin to be heads, otherwise consider it to be tails. java.lang.Math contains a
random method which you could use. Create a method, flip, which generates a
random number which two other methods, isHeads and isTails will examine
to decide if the coin is heads or tails. The names of these two methods are based on
practice which says that methods which return a Boolean value should have names
beginning with the word is.
How would you implement your class to allow for biased coins, ones which come
up with either heads or tails more than expected?
Recently I read that when you flip a coin it ends up the same face up as it starts with
51% of the time. This is mentioned at http://en.wikipedia.org/wiki/Coin_flipping.
If you are interested in a tool which can check if your Java code meets coding
standards, you may wish to explore the Java Coding Standard Checker, an opensource project described at http://jcsc.sourceforge.net/. This program comes with a
collection of rules for standards you may accept and it allows you to set your own
rules.
For Suns standards, look at the Code Conventions for the Java Programming
Language document, available at
http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html
2.
Stores have customers. Each customer has a number and a name. Each customer
also has a discount rate. That is, when the customer makes a purchase, he/she will
be granted a discount. This discount may be zero, or it may be a positive percentage
(or a positive amount) based on his/her previous purchases. Of course the discount
might also be based on being a neighbour or friend of the owner, being a student, or
being an employee. How would you model such a discount scheme?
3.
In Canada, each province has its own provincial tax rate, which may be zero.
Anyone who purchases something may need to pay the provincial sales tax or PST.
Some goods are exempt from taxes, but we will ignore that detail for now.
There is also a federal Goods and Services Tax (GST) whose payment is required in
all provinces except Nova Scotia, New Brunswick, Prince Edward Island, and
Newfoundland and Labrador. In those four provinces, there is the Harmonized Sales
Tax. Instead of separate PST and GST, purchases there are subject to an HST.
63
Create a Purchase class. In it, write a method which has parameters amount and
postalCode, and returns the tax (PST plus GST, or HST) necessary for a
purchase. Note that http://www.canadapost.ca/personal/tools/pg/manual/PGaddresse.asp#1380608 contains a map showing all the provinces of Canada plus the first
character of the postal code for that province. The larger provinces may use several
postal codes. To extract the first character from the postal code, use the charAt
method from the String class and provide the value zero as a parameter.
Alternatively, you may wish to use the startsWith method from the String
class.
4.
5.
In an exercise in the previous chapter, you modelled a die. Use the random method
in java.lang.Math to create a roll method.
See exercise 1 of this chapter for further information on the random method.
6.
In an exercise in the previous chapter, you designed a Bird class. In many parts of
the word, birders often use abbreviations for the bird name. Common systems use
four characters (http://elibrary.unm.edu/sora/NABB/v003n01/p0016-p0025.pdf) or
six (http://infohost.nmt.edu/~shipman/z/nom/6home.html).
Different people prefer different abbreviation systems, but one person will always
use the same system. Add the abbreviation as an instance variable of the Bird
class.
Create a method to determine the abbreviation.
In the constructor, the only parameters will be the common name and the scientific
name. Use a separate method to determine the abbreviation, given the common
name. Note that common names change over time, so the setter for the common
name will use this separate method to determine the abbreviation. Since the method
to determine the abbreviation is called only by methods within the Bird class, that
method will be private, not public.
We will explore the world of birding in subsequent chapters.
64
7.
The Canadian postal code is a six-character string. The first, third, and fifth
characters (counting from the left) must be letters. The second, fourth, and sixth
characters (counting from the left) must be digits.
Create a method, isValidPostalCode, which accepts a string as a parameter
and decides if that string represents a valid postal code, returning the appropriate
boolean value.
You will need to use the charAt method from the String class, and the
Character.isLetter and Character.isDigit methods from the
Character class. To check that the character in position loc of a string ess is a
letter, use Character.isletter(ess.charAt(loc)).
Recall that Java numbers its characters starting at zero while English refers to that
character as the first character.
8.
65
66
Chapter 4 Inheritance
Learning objectives:
By the end of this chapter, you will be able to:
Define abstract class and inheritance
Use abstract classes to implement inheritance
Abstract classes
Before we go any further with the Student class, we should think back to a comment
made earlier. We noticed that the Professor and Student classes looked very
similar. After you gain more experience with object-oriented programming (and objectoriented analysis and design) your ears will prick up at a statement that says classes are
similar and intuition will kick in whenever you see such commonality.
Commonality usually implies that there is a better way to deal with two classes than what
you have first decided. In this case, we divided the people in the classroom into two
groups, the students and the professors. But we are all people! So why not create a
People class? That class can contain all the common fields in Professor and
Student.
What common fields are there? Well, right now there are none, but think a little deeper.
Professor has a professorNumber. Student has studentNumber. Are they
different in structure? Not in this example; both are nine-digit numbers, entered as
Strings. Since number is not a very descriptive variable name, I will use
identifier instead.
Professor has professorFirstName. Student has studentFirstName.
Are they really different? No.
Professor and Student have several common fields, common on the basis of
function if not name.
So how can we use this information? We can create a new class, called Person and
derive the Professor and Student classes from it. (The name of a class is usually a
singular noun. Thus we used Person rather than the initial suggestion of People.)
This is shown in the following class diagram.
67
Note the arrow from the Student class to the Person class. This is read as a Student is
a Person or Student is derived from Person. Similarly, there is an arrow from
Professor to Person. One diagramming style, which I have used here, is that the
two arrows share a common path whenever it is reasonable to do so. BlueJ does not do
this.
Person is referred to as a base class for both Student and Professor. Student
and Professor are both derived from Person.
68
If you respond that Yes, there are other types of people we wish to model besides
students and professors. Perhaps there are the administrative deans. Perhaps we wish to
model alumni and the recipients of honorary degrees. then you do not wish to declare
Person as an abstract class.
I can see the arguments behind both answers but, for the model we are creating (and its
my model!), we wont have anyone other than a professor or a student. So I will declare
Person as an abstract class.
public abstract class Person
Notice how the class diagram shows that Person is an abstract class.
Whether or not there is a preferred name, I need to concatenate the identifier and the first,
last, middle, and full names. So I concatenate them, and save the result. The saving is
done in the statement beginning result =. The way to interpret this statement is to say
Calculate the value to the right of the equals sign. Since it is a String, use result to
remember where the value is stored. This is exactly what we have been doing in our
constructors and other toString methods.
Now I need to check whether there is a preferred name. When there is no preferred name,
there is nothing to be done, and that could cause some strange code.
if (preferredName.length()==0)
// do nothing
else
result = result + ( + preferredName + );
There are no executable statement between the if and the else. Most people would
wonder what is missing so we place a comment there to indicate we have not forgotten
something. But the resulting code looks strange to my eyes. So we do not test for
equality, we test for inequality. As we have seen earlier the ! is used to indicate not. Thus
== means equals, and != means not equal. Note that we could have used the symbol
> (or greater than) instead of != since the length can not be negative.
if (preferredName.length() > 0)
result = result + ( + preferredName + );
Testing for inequality or greater than are both acceptable and are preferable, to my eyes,
to testing for equality but having an empty section of code.
When we create the Student and Professor classes, which we will do in a moment,
we will include the following methods in the appropriate classes.
public String toString()
{
return "Student " + super.toString();
}
public String toString()
{
return "Professor " + super.toString();
}
The reserved word super refers to the base or parent class. super.toString() asks
the parent to represent itself as a String, and then, as appropriate, we prepend a word
to that String.
70
Note that we are overriding the toString method in the parent class by creating a
toString method in the derived class.
71
Hint: If you are unsure which classes you have changed and thus should be compiled,
look at the class diagram. You can tell which classes need to be compiled since they
appear striped in the diagram. Simply click the Compile button beside the class diagram
and all will be compiled in the appropriate order. That is, if you have changed both
Student and Person, the parent class, Person, will be compiled first.
Do you need to create a getFirstName method in the Student class? No. That
method, and the other getters and setters should be in the Person class. If we use the
expression s.getFirstName(), where s is a Student, the effect is that the
Student object essentially says I dont recognize that message. Ill ask my parent to
respond to it.
Set up your Student unit tests to ensure that they still work.
/**
* A Student
*
* @author rick gee
* @version september 2007
*/
public class Student extends Person
{
Student(String identifier, String firstName,
String middleName, String lastName,
String fullName, String preferredName) {
super(identifier, firstName, middleName,
lastName, fullName, preferredName);
}
/**
* @return a Student, as a String
*/
public String toString()
{
return "Student " + super.toString();
}
/**
* An international student is one whose identifier
* begins with an eight or a nine
72
Modify the unit tests for Student to take into account the different instance variable
names we are using.
Summary
In this chapter we have had an introduction to inheritance. In particular, we have seen
how to create classes which inherit instance variables and methods from their parents.
We have also gained more experience with unit testing.
Now that we have created well-designed Student and Professor classes, albeit
simplified classes, we can explore them in more detail, identifying missing instance
variables and behaviours. This exploration begins in the following chapter.
73
Exercises
1.
In a previous chapter, I discussed the use of a bank account as a common class for
modellers. Part of its attraction is that there are so many types of bank accounts.
Savings accounts dont allow cheques. High interest accounts allow only a few
withdrawals in a month. Extend your model of the bank account to accommodate
some of these variations. An interesting collection of bank accounts are described at
http://cse.stanford.edu/class/cs108/982handouts/14%20Inheritance%20Examples.pd
f. I dont think a real bank would have account types called Nickel n Dime, and the
Gambler. The programming in this example is in C++ rather than Java.
A second section of the page referred to above contains an interesting perspective
on the role of Instructors at a teaching institution.
2.
In a previous chapter, I discussed birding. Birds and all other living things provide
interesting examples of inheritance. A gull is a type of bird, and there are many
types of Gulls, including Herring, Mew, and California. An albatross is a type of
bird, and there are many types of albatross, including the Black-browed and the
Black-footed. Sparrows come in many varieties, including Harris, Song, and
House. Warblers too come in many varieties, including Yellow, Swainsons, and
Kirtlands. Model these in the simplest way possible.
Note that the biological classification into kingdoms, orders, families, etc. is a
fertile source of modelling exercises.
3.
At Okanagan College there are not just students and professors. There are also
deans, who provide management and direction, and support staff, the people
without whom the institution would not run. Objects of the Dean class need to
know their salary. Support staff are unionized. For historical reasons, there are three
separate locals of the union on campus. Objects of the SupportStaff class need
to know the local to which they belong. Implement Dean and SupportStaff
classes using inheritance from Person.
4.
Sun provides a Java tutorial on its website. One of its inheritance examples is the
bicycle and the different types of bicycle.
http://java.sun.com/docs/books/tutorial/java/concepts/inheritance.html. Model the
inheritance described. How would you change the model to accommodate
unicycles?
5.
Okanagan College has no graduate students, people who have already completed
one degree and are studying for another, higher, one. A GraduateStudent is a
Student. How would you model a graduate student?
74
6.
7.
75
76
Introduction
A college needs to know the address of students, so the Registrars Office can send
written communications, including transcripts. The Library will use that address to send
notices of library fines. Given the problems with violence on campuses, many institutions
also want email and/or cellphone (mobile phone) numbers they can use to contact
students in case of emergencies.
Professors also need to provide an address and contact information, this time to the
Human Resources Office.
In addition, a college also needs to know its own address. It may have a mailing address
and a separate street address, or they may be the same.
An Address class is the focus of this chapter. A phone number is not so interesting. Its
just a String. Email addresses are not so interesting. They too are just Strings.
The interesting discussion is around a persons mailing address. Lets begin there.
Adding an address
Lets now add an address to the Student and Professor classes. Stop! Do not pass
go! What is wrong with the statement?
We do not need to add an address to both classes. We can add an address to the Person
class and both Student and Professor will have access to it.
So what is an Address class? What makes up its state and its behaviour?
77
78
* @author rick
* @version 1 april 2006
*/
public class Address
{
// instance variables
private String number;
private String suffix; // 1702A, for example
private String name;
private String type;
private String direction;
// NW, for example
private String city;
private String province;
private String country;
private String postalCode;
/**
* Constructor for objects of class Address
*/
public Address(String number, String suffix,
String name, String type,
String direction, String city,
String province, String country,
String postalCode)
{
/**
* @param number - the number on the street
* @param suffix - a suffix to the number (1702A)
* @param name - the name of the street
* @param type road, street, crescent, etc.
* @param direction - for a city divided into
* NW, SE.
* @param city - the city, town, or village
* @param province - two-character abbreviation
* @param country the country
* @param postalCode - the postal code.
*/
this.number = number;
this.suffix = suffix;
this.name = name;
this.direction = direction;
this.type = type;
this.city = city;
this.province = province;
this.country = country;
this.postalCode = postalCode;
79
}
/**
* toString - convert the address into
* something suitable for a mailing label
*
* @return - a String containing multiple lines
*/
public String toString() {
String result = new String();
result = number;
if (suffix.length() > 0)
result = result + suffix;
result = result + " " + name;
result = result + " " + type;
if (direction.length() > 0)
result = result + " " + direction;
// end of line 1
result = result + '\n';
result = result + city;
// end of line 2
result
result
result
result
// end
= result +
= result +
= result +
= result +
of line 3
'\n';
province;
" " + country;
" " + postalCode;
return result;
}
}
If you think documentation is not worth writing, press
-J while viewing the Java
code of the Address class. Isnt that a nice piece of documentation that appears? And
you didnt have to do anything special to have it appear. Press
-J again to return to
your source code.
If you prefer not to use the keyboard to see the documentation, simply use the dropdown
box at the top right corner and choose between Source Code and Documentation.
80
Lets look at my code in some detail. The constructor contains nothing new, but there are
some interesting things in toString.
First, each person develops a different programming style. Despite what you see in some
examples, I tend to write many short statements while concatenating Strings. Some
people write fewer and longer statements. For example, instead of
result = result + +name;
result = result + + type;
you could write
result = result + + name + + type;
Some people are even more terse. They will write
result += + name + + type;
x += y; is shorthand for x = x + y; There are other shorthand operations including
-=, *=, and /=.
The effect is the same, concatenating the current value of result, a blank, the value of
name, a blank, and the value of type, and saving the resulting String in the variable
named result.
Why the blanks? So the output is 1702A Fifth Street, not 1702AFifthStreet.
And what is this \n that appears in two places?
A String is delimited by double quotation marks. A String may contain zero or
many characters. If you want a single character, a String may be overkill. (Yes, I have
used a single-character String for the space between fields.) So Java contains a
character datatype, called char. Each letter of the alphabet is a character; each digit of a
number is a character; each punctuation mark is a character. A tab is a character,
represented by \t, and a carriage return and linefeed combination is a character,
represented by \n.
In the days of the typewriter a carriage return moved you from the current position on a
line to the beginning of the line. A line feed moved you down one line. Since the carriage
return didnt move down a line, you could type over what you had typed. This allowed
you to produce some interesting effects. See the Wikipedia article on ASCII art for some
examples. http://en.wikipedia.org/wiki/ASCII_art You may even wish to see the article of
typewriters if you dont know about them. http://en.wikipedia.org/wiki/Typewriter
Some operating systems treat a carriage return and a linefeed as two separate characters,
but that detail is hidden from us when we are using Java.
81
82
class and the other to create the unit tests for the class. Ensure you compile and run the
tests after you create them
Now create the other getters and setters, one pair at a time. Compile the class after you
make each pair. Add, compile, and run unit tests as you do so.
the address
83
In particular, when two Student objects refer to the same Address object, any change
to the Address object will affect both Student objects, since they both contain
references to the same Address object. Consider roommates, one of whom later moves.
Exercise 1 provides directions to see how this can happen. If you are going to do that
exercise, do it now, rather than waiting until you have fixed the problem.
84
86
87
Instead of simply calling the toString method of the instance variable addr, we need
to check whether addr is null.
if (addr != null)
result = result + '\n' + addr.toString();
After making the changes to allow for null address, recompile your classes and test your
work by creating two Student objects, one with an address, and one with a null
address. (Just provide the word null instead of name of the Address object.) Does
your toString method work properly? If not, fix it.
Summary
While an address appeared simple, it turned out to be quite complex. And there are
complexities we have omitted!
Note that everything we have said about a Student object so far applies to a
Professor object. There is no difference in the structure of the two types of object.
There certainly is a difference in the roles they play at the College. Lets look at one more
structure they have in common, and then well start to look at their differences.
88
Exercises
1.
2.
Use the CodePad window to verify that Strings do not need cloning by repeating
the tests described in this chapter. What happens when you do the tests?
Then do another test. Add another statement. s1 = here is a string; Is
s1 equal to s3 or not?
3.
Create a unit test that verifies that Strings do not need cloning.
4.
The Person, Student, and Professor classes we have developed all use
instance variables to represent the different parts of a name. Since we created a
class for Address, perhaps we should do the same for Name. Design and
implement such a class.
5.
Suppose you are dealing with a culture in which not every home has an address. It
may just have a name, or a description. How would you modify the Address class
to support this? Make it so.
89
90
Chapter 6 Dates
Learning objectives:
By the end of this chapter, you will be able to:
Create more complicated classes
Work with dates
Use the online Java documentation to find out about classes.
Introduction
Many things at a college are based on seniority. Perhaps professors who have been there
the longest have first choice on middle-of-the-day teaching times. Thus Professor
objects need to know the date on which they were hired.
Perhaps students who have been there the longest are allowed to register first for a new
semester. You will explore that idea in the exercises at the end of the chapter.
Sections of a course have specific times for meeting. We will explore this in a later
chapter.
All of these involve the use of dates and, perhaps, times. This chapter will explore dates.
Dates
When you look in the Java documentation, you find two Date classes.
91
92
An hour is represented by an integer from 0 to 23. Thus, the hour from midnight
to 1 a.m. is hour 0, and the hour from noon to 1 p.m. is hour 12.
A minute is represented by an integer from 0 to 59 in the usual manner.
A second is represented by an integer from 0 to 61; the values 60 and 61 occur
only for leap seconds and even then only in Java implementations that actually
track leap seconds correctly. Because of the manner in which leap seconds are
currently introduced, it is extremely unlikely that two leap seconds will occur in
the same minute, but this specification follows the date and time conventions for
ISO (International Standards Organization) C.
I (and others) have to wonder why the day of the month starts at 1 but all other fields
(including month) start at 0. Yes, I know January is the first month, but it is an interesting
inconsistency that the month variable starts at 1 while everything else starts at 0.
In English, the names of some of the months represent older calendars. September,
October, November, and December begin with the Latin words for seven, eight, nine, and
ten, despite being the ninth, tenth, eleventh, and twelfth months of the current calendar.
Why might this be? Who had a year that was 10 months long?
The documentation on the first Date class also includes an interesting note. In all cases,
arguments given to methods for these purposes need not fall within the indicated ranges;
for example, a date may be specified as January 32 and is interpreted as meaning
February 1. That makes it easier to decide when 10 days in the future is, if you cross
from one month to another.
How do we use all this information on dates and calendars? Can we use this information?
93
What data type should we use for the year, month, and day instance variables? The
documentation quoted above gives limits on the values, and these values are small
integers.
Primitive types
Java supports many types of integers - byte, short, int, and long. They differ in the
number of digits they can hold but programmers often choose long, the longest of the
four. It wastes a little memory but you wont lose any digits, as long as your number is no
larger than 263-1 and no smaller than -263.
Recall that we talked about bits earlier.
In a computer, a bit is the smallest unit of memory. A bit has only two values, one or
zero, or on and off, depending on how you look at it. Everything is stored as a collection
of bits. When the collection contains two bits, it can represent four values, 00, 01, 10, and
11.
In many languages, characters (letters, punctuation marks, and numbers not used in
calculations) are stored as eight bits (allowing 256 values), also known as a byte. Java
stores its characters using the Unicode standard of 16 bits (allowing for 65536 values).
Representing numbers, a byte contains 8 bits (representing integers from -128 to 127), a
short contains 16 bits (representing integers from -32768 to 32767), an int contains
32 bits (representing integers from -2147483648 to 2147483647), and a long contains
64 bits (representing integers from -263 to 263 1).
94
If you wish to provide a long value as a literal or constant, you must follow it with the
letter l. Due to possible confusion between 1 (one) and l (ell), you should use an
uppercase L instead of a lowercase l. Thus you might see a statement like this.
long numberOfDays = 1000L;
But why do the names of these integer types not begin with a capital letter? The names of
all the other classes we have seen have begun with a capital letter.
The answer is simple. There are some primitive types in Java which are not objects, and
we have just found the names of four more of them. Of course, there is also a wrapper
class called Long which encapsulates a long, and gives it some of the methods of other
classes, including the toString method.
encapsulates means that a Long contains, as its instance variable, a long. As a class,
though, Long supports additional useful methods, like toString.
Digression
When you create a variable of a primitive type, you associate a location in memory with
the name of the variable. When you inspect that location in memory, you will find the
value of the variable.
But when you create an object and inspect the associated memory location, you find the
address of another location in memory. If you go to that address, you will find the object,
including its instance variables.
BlueJ shows this when you inspect an object. Right-click an object and you will see a list
of all the instance variables. For those which are objects, there is another Inspect button
which is enabled. For primitive instance variables, this second Inspect button is disabled.
We discussed some of this, but not in such detail, in a previous section in connection with
cloning Address, Name, and String objects.
96
You may wish to modify the separator. I chose to use a slash (/) but you may prefer to use
a hyphen (-). Note that I have used the international ordering (year, month, day) in this
method. You may wish to create two extra methods, one to return the date in the
American format (month, day, year) and one to return the date in the British format (day,
month, year). Note that these two methods can not be named toString since there
would be no way to distinguish between the three methods. They would all have the same
name and the same parameters; their signatures (the name and the type and number of
parameters) would be identical, a bad thing which would prevent your program from
compiling.
Note that you could use the int datatype for year, month, and day if you wish. In that
case, you will use Integer in place of Long in toString.
97
In the toString method, do you want the first nine months of the year to appear as
single digits (as happens with the code above) or as two digits? Do you want the first nine
days of the month to appear as single digits or as two digits? If the answer is two digits,
modify toString as shown below.
public String toString()
{
String result;
result = (new Long(year)).toString();
if (month < 10)
result = result + /0;
else
result = result + /;
result = result + (new Long(month)).toString();
if (day < 10)
result = result + /0;
else
result = result + /;
result = result + (new Long(day)).toString();
return result;
}
Make the MyDate class cloneable, as we did for the Address class. This is because we
need to keep dates with no possibility of changing them accidentally. Can you imagine an
example where such changes would be very bad?
Seniority matters, so changing dates there could be a problem. In a different application,
keeping track of when parcels arrived could be important.
98
In BlueJ, you can create an Address object and then a Student object. Then rightclick the Student object, and choose Inherited from Person, and then
getBirthDate.
Unit testing is easier. My getBirthDate method is shown below.
/**
@return birthDate as a MyDate
*/
public MyDate getBirthDate() {
return birthDate;
}
Now modify Professor to contain a dateHired. This is an instance variable which
Student does not have, and thus it must be an instance variable in the Professor
class.
This is the first time we have had different instance variables in Student and
Professor. Both these classes are derived from Person, and thus contain all the
Person instance variables, but now we see that derived classes may also have their own
instance variables.
We will also see that derived classes may also have their own methods, perhaps with the
same names in the different classes, but different behaviours. As an example,
getPhoneNumber may return the home number for a student but an office number for
a professor.
How do you test that birth dates and hired date are handled correctly?
Create two MyDate objects, one for birth date and one for date hired. Create a Student
object, using the birth date object you just created. Create a second Student object,
specifying null for the birth date. Does toString display the birthdates properly?
Create a Professor object. Execute its toString method or its unit tests. While the
constructor will accept a null date, it makes no sense to have a null date here. We will
need to insert additional code at a later time to prevent this from happening.
99
My diagram now has uses arrows from Person, Student, and Professor classes
to MyDate. The Student class does not directly use the MyDate class, so I deleted
that uses arrow.
My diagram has uses arrows from the Person, Student, and Professor classes
to the Address class. But Student and Professor do not directly use the
Address class, so I deleted those uses arrows as well.
You may find that you can make the class diagram more readable by moving the classes
around with your mouse. Here is my current diagram (omitting the unit tests). You have
created all your unit tests, havent you?
The diagonal shading indicates that I need to compile all my classes. Ive made some
change to them, so I cant run them until I recompile them.
Retirement of a professor
Recall that I mentioned earlier that a professor at Okanagan College is expected to retire
on the June 30 following his/her 65th birthday. Lets create an instance variable for the
100
retirement date, and a getter method. We dont have a setter method, since we can
calculate the retirement date once we are given the birth date.
When the month of birth is June or earlier, the professor retires on June 30 of the year in
which he/she turns 65. When the month of birth is July through December, the professor
retires on June 30 of the following year.
Whenever we set the birth date, we calculate the retirement date; that is the only way the
retirement date is calculated it can not be set through a public setter. Thus, we can
create the following private method.
private void setRetirementDate(MyDate birthDate) {
long retirementMonth = 6;
long retirementDay = 30;
long retirementYear = birthDate.getYear() + 65;
if (birthDate.getMonth() >= 7)
retirementYear++;
dateRetirement = new MyDate (retirementYear,
retirementMonth, retirementDay);
}
Notice some things about this method:
It is private so that only another method within the Professor class may
call it.
We use the getters from the MyDate class to extract the year, month, and day.
We use the ++ operation to increase the year when the birthday is in the second
half of the year. ++ is shorthand for increment by 1.
We use the constructor for MyDate to create the retirement date.
But which method calls this one?
The constructor for Professor will certainly need to call it.
So too will the setBirthDate method. But setBirthDate is inherited from the
Person class. Student objects dont have a retirement date; Professor objects do.
The best way to handle this quandary is to create another setBirthDate method in the
Professor class. It will do everything the Person setBirthDate method does,
and will also calculate the retirement date. Technically, the setBirthDate method in
Professor overrides the setBirthDate method in Person.
public void setBirthDate(MyDate birthDate) {
super.setBirthDate(birthDate);
setRetirementDate(birthDate);
}
101
102
long retirementMonth = 6;
long retirementDay = 30;
long retirementYear = birthDate.getYear() + 65;
if ((birthDate.getMonth() >= 7) ||
((birthDate.getMonth() == 6) &&
(birthDate.getDay() == 30)))
retirementYear ++;
retirementDate = new MyDate(retirementYear,
retirementMonth, retirementDay);
}
Note the parentheses. Every if statement begins with if ( some condition).
Here, the condition consists of two parts (joined by the symbol for or, ||), and one of the
parts consists of two parts (joined by the symbol for and, &&.) Either the month is greater
than or equal to seven (a condition enclosed in parentheses) or the birthday is June 30. To
test if the birthday is June 30, we need to test if the month is June (a condition enclosed in
parentheses) and if the day is 30 (also enclosed in parentheses.) To ensure there is no
ambiguity, these last two conditions are combined using the && operator and then the
result is enclosed in parentheses. We finish by using the || operator to decide if either of
the two conditions, the month is greater than or equal to seven or the birthday is June 30,
is true.
Logical operators have a priority. In particular, && is done before ||. Thus many of the
parentheses in the method above are superfluous. But I have put them there to clarify how
the tests are being done.
parenthesis when you type the closing parenthesis. I find it helps to type the
opening parenthesis, then the closing parenthesis, and then go back and fill in
what should be between the parentheses.
Mismatched braces. Missing braces is a more common problem than missing
parentheses. For every opening brace there must be a corresponding closing
brace. BlueJ helps by indicating the matching opening brace when you type the
closing brace. I find it helps to type the opening brace, then the closing brace, and
then go back and fill in what should be between the braces.
Missing or superfluous semi-colons. A semi-colon marks the end of a statement,
so dont forget it, and dont add unnecessary semi-colons. They dont hurt; they
just mark you as inexperienced or careless.
Missing or superfluous commas. Commas are only used to separate items in a list.
In my programming style commas appear only when you are defining or using a
method (or when you want them to appear in the output of a toString method.)
== or =. A single equals sign is used when you are assigning a value to a variable.
A double equals sign is used when you are checking that two variables (usually
primitive data types) have the same value. The double equals is also used when
you are checking whether two objects both refer to the same area of memory.
Summary
In this chapter we have created two more classes, and explored how we derive classes
from a base class, overriding methods as required, accessing variables and methods in the
parent class, and adding variables and methods to the derived class.
Doing this well will be crucial to your success as a programmer.
But we have reached a plateau in what we can do with single-valued variables. We need
to consider collections, the topic of the next chapter.
104
Exercises
1.
The Java libraries contain a DateFormat class. Use it to display a date with twodigit months and days. That is, use it to eliminate the comparisons in the
toString method of MyDate.
2.
3.
Modify the MyDate class to display the name of the day of the week?
4.
In a previous chapter, we talked about birders and the sightings they make. Part of a
sighting is the date on which it occurred. Using either MyDate or
GregorianCalendar, create a Sighting class which contains the species of
bird, the date and time of the sighting, and a note about the sighting. This note may
include location, weather, bird behaviour, number of males and females, and the
number of young. Are you thinking that the note would best be represented by
several separate instance variables? Good. If you use MyDate, you will need to add
the time of day to the class, or you will need to derive a MyDateAndTime class
from MyDate.
5.
105
106
Life is complicated
You may have noticed that you are taking a number of courses. You may have noticed
that some of your courses have one section but others have multiple sections. You may
have noticed that a professor generally teaches a number of courses. You may have
noticed that your studies extend over more than one semester. You may have noticed that
a department at your college or university offers many courses. You may have noticed
that a department at your college or university consists of many professors.
All of these remind us that the world contains objects in multiples, not as singles.
Up to now, we have been able to create two professors, by giving each Professor
object a separate name. How do we accommodate varying numbers of professors in a
department? That is, we have two situations. First, different departments have different
numbers of professors. Second, the same department may have a different number of
professors at different times as professors retire or resign, and new professors are hired.
Similarly, how do we accommodate students taking varying numbers of courses? Some
students may choose to take only three courses at one time. Others may take four, five, or
more.
We need to look at data structures, commonly called collections. A collection of X is an
object, containing within itself a number of different objects of type X.
Thus you can talk (in other examples) about a collection of automobiles, a collection of
houses, a collection of vacation destinations, and a collection of occupations. We can
even have a collection of collections. The college model we are examining contains many
collections.
107
108
Next we have an interesting collection, the collection of sections. One possibility is that a
course contains a collection of sections. While that may be reasonable, it will lead to
problems later when we attempt to model the associations between students and sections.
There are several such associations; one which represents the students in a section, one
which represents the sections in which a student is currently enrolled, and one which
represents the sections in which a student was enrolled in the past.
Rather than have the course contain collection of sections, a better possibility is to have
the college contain a collection of sections. When we implement this collection, well see
that it is actually a collection of Section objects. This is the only collection containing
Section objects. All other collections of sections will contain only section
identifiers.
Each section contains a collection of students. When we implement this collection, well
see that it is best to implement a collection of student identifiers, rather than a collection
of Student objects.
Each student contains two collections of sections, one consisting of sections in which the
student is currently enrolled, and one consisting of sections in which the student was
enrolled in the past. For past sections, we remember the marks the student earned at that
time. When we implement these collections, well see that it is best to implement a
collection of section identifiers, rather than a collection of Section objects.
Each section contains a collection of its meeting times. When we implement this
collection, well see that it is actually a collection of Meeting objects. This will be the
only collection containing Meeting objects. All other collections of meetings will
contain only meeting identifiers.
Why will some collections contain objects and some the identifiers of objects?
The first reason is that we wish to have only one copy of an object. That is, there will be
only one Professor object representing each professor at the college. There will be
only one Student object representing each student at the college. By having only one
object corresponding to each item in the real world, we will eliminate duplication of data.
We will also ensure that when an object is updated, the update needs to happen in only
one place.
These ideas are behind the practice of database normalization. Any course in database
management will explain this in more detail.
Now that we have an idea of what collections our model needs, lets see what Java
provides to implement the collections.
109
Is the size of the collection fixed, or may it vary? The number of months in the
Gregorian year is fixed at 12, but the number of courses a student takes may vary
from student to student, and from semester to semester.
Does the collection allow duplicate values or does it not? A rack of Scrabble
tiles can contain duplicate tiles but a hand of playing cards drawn from a 52-card
deck cannot.
To check that an item is in the collection, how many other items do we need to
check? Is the collection designed to allow speedy searching? This matters when
we are searching through large collections.
Are the items in the collection sorted in some order, or are they unsorted? If they
are in one order, can they be placed in a different order?
Does an item in the collection consist of one value or is the item a pair, consisting
of a key and a value? If you are creating a list of the words used in a document,
the collection need only contain the words. But when you are creating a
dictionary, the collection needs to contain both the word and a definition. Many
words have multiple definitions, so each item of the collection may itself contain
a collection. Think also of the index of this book. There are very few words in the
index that appear on only one page. One such word, chosen for no particular
reason, is elegant.
By asking questions such as those above, we can determine the type of collection we
should use in a particular circumstance.
Sets and lists are collections of single values. The difference between them is that a set
does not allow duplicates, but a list does. Consider the students in a section of a course or
the sections of a course a professor teaches. These would be represented by sets, since
sections are unique and students are unique. The uniqueness is enforced by having unique
identifiers. Consider the coins in your pocket, or the list of items you need to pick up on
the way home. These are lists as you may have two coins of the same denomination or
two loaves of bread on your shopping list.
A map uses a key/value metaphor to store information. Think of a table with two columns
and many rows. The first column represents the key; the second column represents the
value associated with the key. For example, the key might be a student identifier and the
value might be a student object. Think again of the list mentioned above, the things you
must pick up on the way home. The key may be loaf of bread and the value may be
two.
A multimap is a map in which the values are themselves collections. For example, the
key might be a course identifier and the value might be the professors teaching the
110
course. A multimap is also called a dictionary, since a word (the key) may have many
meanings (the values).
The Java Collections Framework provides collections via interfaces. These interfaces
describe the characteristics of the collection without providing knowledge of the
implementation details. The Java Collections Framework also provides classes which
implement the interfaces in varying ways.
One collection type, and hence interface, is Set. Implementations of this interface
include HashSet and TreeSet. HashSet is designed to allow speedy retrieval and
insertion. TreeSet provides a sorting mechanism. But both allow an element to occur
only once in the collection.
Another collection type (and interface) is List. Implementations of this interface
include ArrayList and Vector.
A third collection type (and interface) is Map. An implementation of this interface is
HashMap.
We will look at some of the aspects of collections in this and the following chapters.
More details on the Java Collections Framework are available in the Java tutorial, at
http://java.sun.com/docs/books/tutorial/collections/index.html
We will begin our discussion of collections by considering the simplest collection, a set, a
collection of data items with no duplicates.
A set
In mathematical terms, a set is an unordered collection of items, with no duplicates. {1, 2,
3} is a set, the same set as {3, 1, 2}. {1, 2, 4, 5, 6, 4} is not a set because of the
duplication of the number 4.
There is a collection called a multiset (or a bag) which allows multiple occurrences of an
item, but remains unordered. There is no Java class for a multiset available as part of the
Java Collections Framework, at least not today (2008-06-19).
As noted above, we can use sets in our model of the college. After all, a person will not
be registered twice in the same section of a course, nor will a professor teach the same
section twice.
At my college a student may be registered in one lecture section, one lab section, and one
seminar section of the same course in a semester. This is very common and our model
supports it since each section will have its own identifier.
111
At my college a student may be registered in two lecture sections of the same course at
the same time only when the course is a Special Topics course and the two sections focus
on different topics. Even in this special case, a student is not registered twice in the same
section.
Does the order in which we store the courses in which a student is registered matter?
Does the order in which we store the professors in a department matter? In general, the
order doesnt matter. When it does, we will sort the data structure before processing it.
What matters is that there are no duplicates in the collection.
A collection of professors
Lets consider how to implement a collection of professors, all of whom are employees of
the college. Recall that this collection will be an instance variable of the College class.
The collection of professors will be represented as a set, since there are no duplicates
(Every professor has a unique employee number or identifier. That ensures there are no
duplicates.). We will assume no ordering on the elements. More correctly, there are many
possible orderings of professors, but none stands out as the predominant one. Professors
might be ordered by hiring date. They might be ordered by employee number. They
might be ordered by name. They might be ordered by name within department. Given
these possibilities, we will not impose an order until the time we need to process the
collection. Then, should it be necessary, we will impose an order.
To implement this collection, we need to import a Java library into the College class.
Use this import statement
import java.util.Set;
Place this statement at line 1 of the class. BlueJ will use javadoc to create the
documentation for a class, but one of javadocs restrictions is that the comments
describing the class must appear immediately before the class header. Thus, any import
statements must appear even earlier. BlueJ politely leaves a blank line at the beginning of
the file when it creates the skeleton of a class for us.
If you look at the documentation describing java.util.Set, youll find, as expected,
that it is an interface rather than a class. An interface describes the capabilities of a data
structure, but it does not describe how these capabilities are implemented. The
documentation lists All Known Implementing Classes. These are the classes which
implement the capabilities of the Set interface. Of the several choices presented, well
choose the HashSet. Why we choose HashSet is discussed below.
112
HashSet
A HashSet is a very efficient way of implementing a set. That is, when we need to
retrieve an element from the set, that retrieval is very quick. When we need to place an
element in the set, that operation too is very quick.
The word Hash refers to the way in which the set keeps track of its members, but one of
the beauties of object-oriented programming is that we dont need to know the details of
how a HashSet works; we just need to know how to use it. That is, we need to know
how to import it into our project and what methods it supports. All of this is described in
the online documentation.
The food called hash is a mix of a variety of different ingredients, often chopped up.
Some people say it looks like pet food.
The idea behind the computer technique called hashing is similar. Take the value of the
Object and mix it up some way, producing a single number, the hashcode. That number
is an indication of the contents of the Object. Two Objects with identical values
should give the same hashcode.
Java is actually a very simple language. Most of its complexity and power comes from its
many libraries. When you wish to use a class from one of the libraries (or packages), you
must import that library or package, or the portion of it which you need.
The Java documentation tells us that HashSet is in the java.util.HashSet package,
so we need to import that package as well.
Now College contains two import statements.
import java.util.Set;
import java.util.HashSet;
Generic collections
Java collections are generic. That is, the collection, and its capabilities, is independent of
the type of data it contains.
While we could create a collection of Object, and thus place any type of object in it,
that is not a good idea since we might have to check the type of each object as we
retrieved it. It is much better to identify the type of element in the collection as precisely
as we can when we first declare the collection. By writing HashSet<Professor>
when we declare a variable we are saying we will be using a HashSet all of whose
items are Professor objects.
113
We are also asking the Java compiler and interpreter to let us know should we ever
attempt to place some other type of object in the collection. The compiler will display an
error message when we attempt to compile a program including statements to add an
object of an incorrect type. At least it will as long as we have gone through the BlueJ
menu choices Tools, Preferences, Miscellaneous, and checked Show compiler warnings
when unsafe collections are used.
Note that this will also cause the compiler to tell us whenever we declare a variable as
just a HashSet without specifying the type of its elements.
We need to name the collection of professors at the college. Lets use professors. Its
declaration is
private Set<Professor> professors;
By declaring professors as a Set, we are describing its general structure and
capabilities. We have not yet indicated the specific implementation of Set we would like
to use. We do that in the constructor.
114
Programming style
When declaring variables which are collections, declare the type as an interface, and then
use a specific implementation when the variable is instantiated.
I will often name a collection after the class of object it contains, changing the first letter
to lowercase and adding an s to the end of the class. Thus I would name a collection of
Student objects as students. I would name a collection of Course objects as
courses. I would name a collection of Meeting objects as meetings.
HashSet<Professor>
Note the details in the documentation that says that any Object placed in a HashSet
must implement the equals and hashCode methods.
hashCode is used to determine the location in the set which the Object will occupy,
and equals is used to detect collisions, when two Objects have the same hashcode.
I have said that a set is unordered. That is correct, but the HashSet implementation
needs to determine where in the set to place a particular object. It uses the hashcode of
the object to do so.
Note that the order of elements using the hashcode may have no connection to the order
you might expect. To see this, suppose the hashcode is the number formed from digits
two, four, and seven of the professor identifier. If you have professors whose identifiers
are 193456789, 487654271, and 777234567, their hashcodes are 947, 862, and 725. They
will be placed in the set in the order of the hashcodes, so 777234567 comes first, and
193456789 comes last, exactly the reverse of the order you might have expected. A
different way to calculate the hashcode would result in a different order.
115
116
method returns the value false. If it is a Person object, then the objects are equal
when the given names, other names, and family names are identical.
Alternatively, as noted above, we could check that the identifiers are equal. This is
probably safer since schools have unique identifiers for Persons, both students and
professors.
What happens when a professor takes some courses, thus becoming a student?
At my college, there is no problem since the identifiers are the same structure, as we saw
earlier. Two recent graduates from a degree program were working at the college while
completing the degree. Their classification at the college was support staff, a type of
person we are not modelling here. But we could.
Thus a better equals method is:
/*
* test for Person equality
@return true if both objects are Person objects and the
identifiers are the same
*/
public boolean equals(Object o) {
boolean result = false;
if (o instanceof Person) {
Person p = (Person) o;
result = this.identifier.equals(p.getIdentifier());
}
return result;
}
Recall our discussion of George Boole earlier. boolean variables have the value true
or false.
117
Every Object has a hashcode. Since the identifier is an Object, we will simply use its
hashcode as the hashcode representing the Person object rather than inventing our own
hashing algorithm.
/*
* @return hash code for the Person object
*/
public int hashCode() {
return this.identifier.hashCode();
}
118
119
Adding a professor
Java libraries have generally been well-designed (but see the earlier discussion about
dates.) We will find, by looking further at the online documentation, that collections
support an add method which will, obviously, add an object to the collection. For a Set,
this addition will take place only when the element is not already present, since sets do
not allow duplicates.
/**
* add a Professor as a new employee
* @param the Professor object to be added
*/
public void addProfessor(Professor p) {
professors.add(p);
}
Create a unit test which uses this method to add some professors to the collection and
then use professorList to display the list of professors.
You may do this within your unit tests in several ways. One, admittedly clunky solution,
is
assertTrue(c.professorList(), false);
where c is a variable of type College.
This is a form of the assert statement we havent seen before. In it we provide a
message (the first parameter) which will appear should the assertion (the second
parameter) fail, which it always does in this case.
When JUnit tells us the test has failed, by placing an X beside the name of the test (and
displaying the dreaded red line), click the name of the test and see its more-detailed
explanation. The explanation is the list of professors working at the college.
But there are other solutions. One is to replace the call to assertTrue with
System.out.println(c.professorList());
As we have seen in many places so far, there is often more than one way to produce a
result. Which is better (or best) is sometimes a judgement call, a matter of personal
preference.
System is a package which the Java libraries provide us. It is always imported. Within
that package is an object named out. This object exists to display messages on the
standard output device, the screen or monitor you are using. The out object supports a
method called println which displays the value we pass to it, in this case the
representation of a professor.
120
Cloning a professor
Note that we have not cloned the Professor object when we added it to the collection.
We cloned addresses. Why should we not clone professors?
Actually, we should clone the object, but we dont yet have a clone method. Lets
remedy that right now.
/**
* clone
* @return a copy of the Professor
*/
public Professor clone() {
return new Professor(identifier, firstName,
121
Removing a professor
By looking at the online documentation we see that many collections support a remove
method to remove an object from the collection. This method assumes that we know
(have a reference to) the professor object we wish to remove. But, since we cloned the
object, we do not know the object. However, we do know its identifier.
Thus, to remove a professor, we need to look through all the elements in the collection
until we find the correct one, the one with the identifier we are seeking, and then we
remove it.
If we were developing a more sophisticated model, we would probably not actually
remove the professor. We would probably mark her as inactive. There are at least two
types of inactive; she could have resigned and gone elsewhere, or she could have retired.
If we wish to do this, removeProfessor would not actually remove the professor. It
would set a flag (an instance variable with a limited set of special values) to show that the
professor is inactive.
The for-each statement is very handy when we wish to just display the elements in a
collection. But we wish to delete an element from the collection and the for-each
statement does not allow that. We need an iterator.
122
o next returns the next unvisited item in the collection, should there be one.
Before using the next method, you should check the result of the hasNext
method to be sure there is an unvisited item.
Depending on how the collection is implemented, an iterator may be implemented in
many different ways. But we wont worry about its details! It is sufficient to know that
we import the iterator class from java.util.Iterator, associate it with a collection,
and then use the hasNext and next methods.
For more details than we have seen here, see the Java documentation on Iterator.
We have already created a HashSet instance variable professors. To allow us to
create its iterator, we place another import statement in the College class
import java.util.Iterator;
In any method in Professor which requires an iterator, place a statement to create but
not instantiate an instance variable which is an iterator over Professor objects.
Iterator<Professor> it;
Instantiate the iterator (attach it to the correct collection and ensure it is initialised) when
we need it, to make sure it iterates over all members of the HashSet<Professor>,
ignoring any previous setting it may have had.
it = professors.iterator();
When you are using an iterator, you often need a while loop as well.
A while statement (or while loop) uses a condition to determine whether a statement
(or a block of statements, enclosed in braces) should be executed and then whether the
statement (or statements) should be repeated.
While the condition remains true, the loop continues processing data. This means that
something inside the loop must change the value of the condition. Otherwise you have
created what is called an infinite loop. We know no way to get out of an infinite loop yet.
By the way, Apple Computer has its headquarters located at 1 Infinite Loop, Cupertino,
California.
Here is a version of professorList which uses an iterator and a while loop.
public String professorList() {
Iterator<Professor> it;
String result = "Professors";
it = professors.iterator();
123
while (it.hasNext()) {
result = result + '\n' + it.next().toString();
}
return result;
}
Recall our discussions of George Boole earlier. boolean variables have the value true
or false. The hasNext method returns a boolean value. When it returns true there
are more elements in the HashSet beyond those, if any, we have processed. If it is
false, there are no more elements to process.
In English, this while loop does the following.
o Step 1 check whether there are more elements in the HashSet.
o Step 2 - If so, process the element which is available, and repeat Step 1.
o Step 3 - If not, the loop is completed, so resume execution of the method with
the statement following the closing brace at the end of the while.
In a similar manner we use an iterator and a while loop to create a
removeProfessor method.
/**
* remove a Professor from the collection
* @param the identifier of the Professor object to
* be removed from the collection
*/
public void removeProfessor(String target) {
it = professors.iterator();
while (it.hasNext()) {
Professor p = it.next();
if (target.equals(p.getIdentifier())){
professors.remove(p);
break;
}
}
}
The iterator provides the elements in the collection one element at a time. We compare
the identifier (the employee number) of the element to the target, the identifier of the
object we wish to delete. Once we have found the appropriate Professor object, we
use the collections remove method to remove the object, and then we use a break
statement to terminate the loop immediately. Since there is only one professor with the
specified identifier, we need look no further.
The break statement is equivalent to terminate the loop immediately!
124
Some would say that a break statement is a harsh way to terminate a loop. Is this a
better way?
public void removeProfessor(String target) {
Iterator<Professor> it = professors.iterator();
boolean notFinished = it.hasNext();
while (notFinished) {
Professor p = it.next();
if (target.equals(p.getIdentifier())){
professors.remove(p);
notFinished = false;
}
else
notFinished = it.hasNext();
}
}
Does this method work when you attempt to remove a Professor object which does
not exist?
125
}
public String toString() {
String result = "Name: " + departmentName +
" " + "Chair: " + chairPerson;
return result;
}
public void addMember(String identifier) {
members.add(identifier);
}
public void removeMember(String identifier) {
Iterator<String> it = members.iterator();
while (it.hasNext()) {
String s = it.next();
if (identifier.equals(s)) {
members.remove(s);
break;
}
}
}
}
126
127
This technique is one which has been used in many circumstances. The word pattern is
used to describe such generally-useful techniques. Patterns will be covered in more detail
later in the book. This particular pattern is called Singleton.
When you explore the Singleton pattern in other references you may find that the method
name getInstance is used in place of the getCollege name we have used. Either
name is acceptable, but getInstance is perhaps more common when using the
Singleton pattern.
128
The unit test involves creating a college, creating some professors who work at the
college, creating a department, placing the professors in the department, and then listing
the members of the department.
public void testDepartmentList() {
// sample dates
MyDate date1 = new MyDate(1999, 10, 12);
MyDate date2 = new MyDate(2004, 12, 8);
// sample professors
Professor p1 = new Professor("111", "F1", "M1",
"L1", "p1", "F1 M1 L1", null,
date1, date2);
Professor p2 = new Professor("222", "F2", "M2",
"L2", "p2", "F2 M2 L2", null,
date1, date2);
Professor p3 = new Professor("333", "F3", "M3",
"L3", "p3", "F3 M3 L3", null,
date1, date2);
// professors work at the college
c.addProfessor(p1);
c.addProfessor(p2);
c.addProfessor(p3);
// professors are in the department
d1.addMember("111");
d1.addMember("222");
d1.addMember("333");
System.out.println(d1.departmentList());
}
The department list appears in the Terminal window since this test uses
System.out.println instead of assert.
Collection of departments
Now that we have a functioning Department class, we can modify College to
contain a collection of Department objects.
Make it so.
129
Collection of students
As mentioned earlier, a college contains a collection of students too. Everything we have
said about the collection of professors also applies to the collection of students.
We will leave it as an exercise foryou to implement the collection of students, and the
addStudent, removeStudent, and studentList methods. Is there a clone
method for students? There should be one.
Summary
Things in the world do not come in singles, they come in collections. We have seen one
type of collection, the set, and how to use it. We noted that there are other kinds of
collection, including lists and maps, which we will see in subsequent chapters.
Lets see how to use these other types of collections.
130
Exercises
1.
2.
3.
Modify the college model so that a professor is not actually removed from
professors.
To do this, create an instance variable which contains the professors status.
Possible values are active, resigned, and retired. Only active professors should
appear in the output of professorList.
4.
5.
131
6.
7.
8.
132
Introduction
In this chapter, we will examine more of the collections a college contains, and then
focus on tools and techniques we need to be able to process collections. This includes the
production of complicated reports, including formatting numbers.
A collection of courses
Not only does a college contain a collection of students, a collection of employees, and a
collection of departments, it also contains a collection of courses. What is a course? What
data do we need to remember about courses?
133
134
Using the idea of formatting numbers, you look in the Java documentation, and find the
Format class.
This is an abstract base class for formatting locale-sensitive information such as dates,
messages, and numbers. That sounds like what we want to do, but we cant instantiate
objects of an abstract class.
In the see also: section of the documentation, youll see NumberFormat which is
also an abstract base class.
The see also: section of NumberFormat includes a link to DecimalFormat, a
concrete subclass of NumberFormat that formats decimal numbers. Exploring this
class eventually leads you to a chapter in the Java tutorial, entitled Formatting.
http://java.sun.com/docs/books/tutorial/i18n/format/index.html
After reading through the tutorial, you realise that this class will solve our problem, and
that formatting consists of two steps.
First, you create a formatting object using a specific pattern which shows how the
output is to be formatted.
Second, you provide a number to that formatting object, which returns it,
formatted according to the pattern you specified.
In our case, specifying a pattern of #.# will provide the output in the correct format. It
will display the digit before the decimal point and will display the decimal point and digit
following it only when that digit is not zero.
Thus, we add the statement
import java.text.DecimalFormat;
to Course. Notice that we are using a different Java package for this class. Previously
we have imported classes only from java.util.
Modify toString so it appears as follows. Comments have been omitted in the interest
of saving trees.
public String toString() {
DecimalFormat myFormatter = new DecimalFormat(#.#);
String result;
double totalHours;
totalHours = lectureHours + laboratoryHours +
seminarHours;
result = subjectAbbreviation + + courseNumber;
result = result + - + credits + -
+ myFormatter.format(hours);
result = result + '\n' + title
135
+
+
+
+
'\n' + description
(
myFormatter.format(lectureHours)
',' +
myFormatter.format(laboratoryHours)
+ ',' + myFormatter.format(seminarHours)
+ ')';
return result;
}
Create the unit tests for the getters and setters; then create the getters and setters
themselves.
What happens if you create a class with 10 or more hours of lectures per week?
Set? List?
Rather than using a Set as we did earlier, lets look at another type of data structure, a
List. Note that there is nothing wrong with a set. A set would be an acceptable
136
137
}
This method adds the section after already existing elements. If you wish to add it
elsewhere, at the beginning for example, use an alternative form of the add method,
which has two parameters, the position (or index) at which the object is to be added and
the object itself. If that position is zero, the object is added at the beginning of the
collection rather than at the end.
Do we need to clone the Section when we add it? Yes, we should. We want only one
copy of the Section available so that when we change a meeting time of the
Section, we need only change it in one place.
You said the Section doesnt contain a meeting time? Oops, we made an error in our
original analysis of the problem and designed a class which omits an important feature.
Correcting this omission is another example of refactoring. We will correct this omission
in the next chapter, where we create a Meeting class.
The code in the last few pages looks good, but we dont have a Section class yet, so
none of the code will even compile.
What is a section?
What information do we really need to model a Section?
We have used unique identifiers for professors (employee numbers) and for students
(student numbers). The combination of subject abbreviation and course number ensures
uniqueness for courses so we dont need a unique identifier. For sections, one possible
unique identifier is subject abbreviation, course number, plus something to indicate when
the section is offered. That sounds complicated. Lets create a unique identifier (a
number) instead.
We still need the course with which a section is associated. We will ensure this by having
a Section object contain the subjectAbbreviation and the courseNumber.
Thus we have the beginning of a Section class.
private int identifier;
private String subjectAbbreviation;
private int courseNumber;
For our reports, we need to know whether we are dealing with a lecture section, a lab
section, or a seminar section. This gives us another instance variable.
private String sectionNumber;
138
int
int
int
int
startYear;
startMonth;
endYear;
endMonth;
139
One reason is that the values these instance variables will contain are small. Another is
that the Field Summary portion of the documentation describing Calendar says the
values for the months and days of the week are ints and we are using its constants. We
need to modify the constructor to accept the values of those instance variables and we
need setters and getters as well as creating and/or modifying the appropriate unit tests (an
exercise left to the reader).
Warning just because unit tests are left to the reader doesnt mean they are
unimportant. The author refuses to evaluate any classes which do not come with a
complete set of unit tests.
We need to modify the toString method so it shows the start and end dates. We need
to deal with two subtleties in toString.
First, the months need to be increased by one before printing. We can do that.
Second, recall the problems we had displaying birth dates, with single integers less than
10 displaying with no leading zero. As a solution, we used an if statement to display a
zero if necessary. But now we know how to use the DecimalFormat class to print that
leading zero, if necessary.
DecimalFormat actually lets us solve both problems simultaneously. These
statements form the body of the toString method.
DecimalFormat myFormatter = new DecimalFormat(00);
String result;
// details of the section
result = identifier + + departmentAbbreviation
+ courseNumber + + sectionNumber;
+ ( + startYear + '-'
+ myFormatter.format(startMonth +1)
+ to + endYear + '-'
+ myFormatter.format(endMonth + 1)
+ ')';
The pattern we have used causes two digits to be displayed, with the first one being zero
if necessary.
What would the output be if we used
result = result + ( + startYear + '-' + startMonth +1
+ to + endYear + '-' + endMonth + 1
+ ')';
Can you explain why the output is not what you expected?
140
141
In all comparisons, some values are more important than others, so we make sure we
compare them first. For example, when sorting a group of people by name, family name
is more important than given name which is more important than middle name. When
sorting by date, the year is probably more important than the month which is probably
more important than the day.
Of course, when you are producing a list of people in order by their birthday, you may
want only the month and the day; the year may not matter.
Lets begin by seeing how to compare Professor objects.
Comparable
Since we have used the word compare several times, you may have gone to the online
documentation and looked up compare. The closest match is Comparable.
The Comparable interface is interesting, but that is not a suitable solution to our
problem, since Comparable only allows one way of sorting, specified in a
compareTo method. This one way of sorting is sometimes called the natural ordering
for the data.
The natural order for numbers is numeric. The natural order for Strings is alphabetic,
using the Unicode coding scheme. But what is the natural order for professors - by name,
by identifier, by salary, or perhaps by hiring date?
What entry comes immediately after Comparable in the online listing of classes and
interfaces? The Comparator interface!
Comparators
The Comparator interface allows us to have many compare methods associated with
one class.
We begin by having the Person class (after all, a professor is a person) create a
Comparator object.
All Comparator objects contain a methodnamed compare. All compare methods
behave in the same way: they accept two parameters and return a negative integer when
the first is less than the second, zero when their values are the same, and a positive
integer when the first is the larger.
142
/**
143
* alphabetic comparison
* @return negative, 0, positive if Person p1 is <, =,
*
or > Person p2
* based on the family name, given name, and other name
*/
public static final Comparator<Person>
ALPHABETIC_ORDER = new Comparator<Person>() {
public int compare(Person p1, Person p2) {
int result = 0;
// compare family names
result = p1.getLastName().
compareTo(p2.getLastName());
if (result == 0){
result = p1.getFirstName().
compareTo(p2.getFirstName());
if (result == 0)
result = p1.getMiddleName().
compareTo(p2.getMiddleName());
}
return result;
}
};
Note that this method assumes both objects being compared are of type Person. It will
not work if this assumption is not valid. Any statement about what must be true before a
method is invoked is known as a precondition, and should be documented in the method.
Any statement about what must be true after a method completes its processing is known
as a postcondition. It too should be documented.
Particularly note the semi-colon after the final brace. That semi-colon must be there as
this statement, loengthy though it is, is just declaring a variable, and every declaration
statement must end with a semi-colon.
This Comparator is declared to be static. This means there is only one method with
this name (ALPHABETIC_ORDER) within the Person class. There may be many
Person objects, but they all use the same method when comparing two Persons
alphabetically. The name of the comparator is capitalised, following the convention that
constants (declared using the reserved word final) are capitalised.
Compile the Person, Student, and Professor classes.
144
145
146
To use a TreeSet requires the following two changes in the College class. First, we
add an import statement.
import java.util.TreeSet;
Then we create a new method, alphaProfessorList.
/**
* produce a professor list, in alphabetical order
*/
public String alphaProfessorList()
{
// create the TreeSet
Set<Professor> alphaProfessors =
new TreeSet<Professor>(Person.ALPHABETIC_ORDER);
alphaProfessors.addAll(professors);
// traverse the Set (in alphabetical order)
// and compute a String
String result = "";
String prefix = "";
for (Professor p: alphaProfessors) {
result = result + prefix + p.toString();
prefix = "\n";
}
return result;
}
The statement that creates the TreeSet indicates what type of objects will be in the set
and what comparator is used to compare two such objects.
The addAll method transfers all the elements of the (unordered) HashSet into the
(ordered) TreeSet. Once that is complete, we simply need to visit the elements of the
TreeSet one after the other and we will visit them in alphabetical order.
How a TreeSet represents its elements in order is of no concern to us right now. If you
want more details, consider taking a data structures course, the normal follow-up to an
introductory programming course.
147
String result = ;
String prefix =
for (Professor p:
alphaProfessors) {
result = result + prefix +
p.toString();
prefix = \n;
}
We could use an iterator to process all objects in the collection but this for statement
provides a shorthand way of referring to individual elements in the collection.
BlueJ revisited
This may be an appropriate point to mention the Test Fixture to Object Bench menu
option.
When you right-click a unit test on your class diagram, one of the options on the menu
that appears is Test Fixture to Object Bench. This creates all the objects you need to run
your tests, and places them on the Object Bench, which we used in the beginning of this
course but havent used lately.
We can run individual methods of these objects, independent of the unit tests. This can be
useful when we are trying to debug (fix the errors in) an object or class.
Debug is a term that dates back to the beginning of computers. At that time, the memory
was switches which actually moved. The story is that one of these switches was behaving
erratically. When someone went inside the computer (Yes, they were very large!) to
check out why, they found a moth (colloquially called a bug) had become caught in the
switch. Hence, the switch was de-bugged.
Wikipedia points out the previous history of the term and notes there is some question
about this story. http://en.wikipedia.org/wiki/Computer_bug In any case, I prefer not to
use the word debug as it has somewhat humorous connotations. You are finding and
removing the errors in your program, you are not debugging it.
148
149
Set<Professor> ordered =
new TreeSet<Professor>(theComparator);
ordered.addAll(professors);
// traverse the Set
// and compute a String
String result = ;
Boolean needsReturn = false;
for (Professor p: ordered) {
if (needsReturn)
result = result + \n;
result = result + p.toString() + '\n';
needsReturn = true;
}
return result;
}
Dont forget
import java.util.Comparator;
The coding in the method above is slightly different from that in
alphaProfessorList. Once again we see that there are many ways to accomplish
the same task.
A unit test for the alphabetic Comparator could look like this.
public void testAlphaProfessorList1() {
College c = College.getCollege();
// create some professors
c.addProfessor (professor1);
c.addProfessor(professor4);
c.addProfessor (professor3);
c.addProfessor (professor2);
System.out.println(s.professorList(Person.ALPHABETIC_ORDER)
);
}
Remember to use View, Show Terminal so there is a place on the screen to display the
class list.
The unit test for the numeric Comparator would be the same, replacing
ALPHABETIC_ORDER with NUMERIC_ORDER.
150
151
153
154
return (p1.getIdentifier().
compareTo(p2.getIdentifier()));
}
};
Modify alphaClassList so its name is simply classList, and so that it accepts a
comparator as a parameter. Create unit tests for both comparators.
Summary
Now that a section knows the semester in which it is taught, we can focus on the daily
meetings of the class, the details of which include a room number, the start time, and the
end time. We will do that in the following chapter.
To produce a schedule will introduce us to the wonders and challenges of string
manipulation.
155
Exercises
1.
A student enrols in many sections in a semester. Using the techniques from this
chapter, model that.
That is, a Student object needs a collection of the section identifiers of the
current sections. Each Student object should be able to produce a
currentEnrollment report listing all these sections.
2.
A professor teaches many sections in a semester. Using the techniques from this
chapter, model that.
That is, a Professor object needs a collection of the section identifiers of the
current sections. Each Professor object should be able to produce a
currentTeachingLoad report listing all these sections.
3.
A professor who as been at the college for more than one semester has a history of
the courses which he/she has taught. Model that.
That is, a Professor object needs a collection of the subject abbreviations and
course numbers of previously-taught courses. Each Professor object should be
able to produce a previouslyTaught report listing all these courses.
4.
5.
6.
7.
156
Introduction
In the previous chapter, we mentioned that we wish to model when a Section is
offered and we mentioned there are two aspects to when; the semester (which we have
modelled), and the time and day within the semester. We begin this chapter by modelling
the time and day within the semester.
157
Instance variables
Since a section meets many times a week, we should create another class, this one called
Meeting, and a Section object should contain a collection of Meeting objects. An
object has identity, state, and behaviour, as we have already seen many times.
Identity is the name of the object, and we create the name through a declaration
statement, so there is no problem there.
What is the state of a Meeting object? That is, what are its instance variables and how
do they receive their values?
private
private
private
private
int dayOfWeek;
String roomNumber;
int startTime;
int endTime;
Behaviours
And what are the behaviours of a Meeting object? Aside from the obvious getters and
setters, it may be useful to have a method that computes the duration of a meeting in
minutes.
A meeting may begin at 1000 and end at 1050, a duration of 50 minutes. But it may begin
at 0830 and end at 0920, also a duration of 50 minutes. In the first case, you can subtract
the start time from the end time and calculate the length immediately, but you can not do
this in the second case.
How do you calculate the length of a meeting?
Time arithmetic
There are many ways to calculate the duration of a meeting, but all require you to have
both the minutes and hours of both the beginning and ending times available. How do
you separate a number like 0830 into its hours (08) and its minutes (30)? Probably the
easiest way is as follows.
int startHours = startTime / 100;
int startMinutes = startTime % 100;
In those statements we are declaring two variables and we are giving them values at the
same time.
158
The first statement uses division (division is represented by the /, or slash) to isolate the
hours. When you divide an integer by an integer, the result is an integer; any remainder in
the division is dropped and no rounding takes place. For example, 830 / 100 is 8,
1020/100 is 10, and 955 / 100 is 9.
To determine the remainder discarded in the integer division, use the modulus operator
(represented by %). 830 % 100 is 30. That is, the remainder when you divided 830 by
100 is 30. 1020 % 100 is 20. The remainder when you divide 1020 by 100 is 20. 955 %
100 is 55. The remainder when you divide 955 by 100 is 55.
Similarly, we can calculate when the meeting ends.
int endHours = endTime / 100;
int endMinutes = endTime % 100;
Once you have the hour and minute when the meeting begins and ends, it is a simple
matter to calculate its duration, in minutes.
return (endHours - startHours) * 60
+ (endMinutes - startMinutes);
This is one of the more complicated arithmetic calculations we have seen. It involves the
idea that some calculations have priority over others. That is, some calculations are done
before others. Parentheses indicate that the calculations within the parentheses should be
done before any other calculations. In our case, there are two parenthesised calculations
which are done (the two subtractions) and the results saved in temporary storage.
To decide what is done with the those results, you need to know that multiplication
(indicated by an asterisk, *) has a higher priority than addition. Thus the first saved result
is multiplied by 60 and the result of that calculation is added to the second saved result.
For example, when a meeting runs from 0830 to 0950, the calculation computes (9 8) *
60 + (50 30) or 1 * 60 + 20, or 60 + 20, or 80 minutes. If a meeting runs from 1130 to
1220, the calculation computes (12 11) * 60 + (20 30), or 1 * 60 + ( 10), or 60 + (10), or 50 minutes.
Should a calculation use more than one multiplication operation, they would be evaluated
from left to right. So too would division and so too would a mix of multiplication and
division operations.
More-complicated calculations could use more than one addition operation; they would
be evaluated left to right as well, but only after the multiplication and division operations
had been completed. Subtraction and addition have the same priority so are done left to
right.
Note that these calculations assume there are no errors (intentional or otherwise) in the
times provided. There is an expression Garbage in, garbage out. For example, due to
159
sloppy input handling, a meeting may have a start time of 3456 and an end time of 6578.
It may have a start time of 1200 and an end time of 1030. We will eventually modify the
class to prevent bad data being provided, but we cant do that right now; we dont know
how to throw exceptions.
}
return result;
}
Note that we need to use an equals method with the roomNumber, since it is an
Object, in this case a String; we use == with the day of week and the times, since
they are primitive datatypes.
If you dont like using both equals and ==, you can convert an int to an Integer,
and then use its equals method. But that makes code that is overly complicated and
hard to read.
We combine four boolean values, using the and operation (&&). The only time this
combination will evaluate to true is when all four boolean values are true; that is,
when the day, room number, start time, and end time are all the same.
On to the comparator.
What does it mean that one meeting is less than another? Here is my understanding.
When the first is on an earlier day than the second, the first is less.
If both are on the same day, the one with the earlier start time is less.
If both are on the same day and have the same start time, then the meeting which
ends first is less.
Based on that understanding, here is my Comparator<Meeting>.
/**
* which meeting is less?
* @return -1, 0, 1 if Meeting m1 is <, =, or > Meeting m2
* based on the day of week, start time, and end time
*/
public static final Comparator<Meeting>
TIME_ORDER = new Comparator<Meeting>() {
public int compare(Meeting m1, Meeting m2) {
int result = 0;
int d1 = m1.getDayOfWeek();
int d2 = m2.getDayOfWeek();
// compare day of week
if (d1 < d2) // m1 is earlier in the week
result = -1;
else
if (d1 > d2) // m2 is later in the week
result = +1;
else {
// both on same day of the week
int s1 = m1.getStartTime();
161
int s2 = m2.getStartTime();
if (s1 < s2)
result = -1;
else
if (s1 > s2)
result = +1;
else {
// same day, same start time
int e1 = m1.getEndTime();
int e2 = m2.getEndTime();
if (e1 < e2)
result = -1;
else
if (e1 > e2)
result = +1;
else
result = 0;
} // compare end times
} // compare start time
return result;
}; // end TIME_ORDER
};
This comparator is longer than the others we have seen. The reason is that all the values
we are examining are primitive datatypes. Thus we can not use the compareTo method
we have used with Objects. We must use the less than (<) and greater than (>)
operators instead.
In the old days, programmers were considered better when they could write shorter
methods to accomplish a task. Many languages included special features to allow this.
Java still includes one, the use of the conditional operator, the question mark (?). I prefer
not to use it, however. I feel that clarity is reduced by the use of that operator.
If you really want to use the conditional operator, explore its use yourself.
162
You might be inclined to add the Meeting objects to the constructor. But sections are
often planned and created, without knowing the actual times the section will meet. Thus,
I have not modified my constructor other than to create an empty collection of meetings.
private Set<Meeting> meetings;
this.meetings =
new TreeSet<Meeting>(Meeting.TIME_ORDER);
To add a meeting to the collection, I have created a new method addMeeting.
/**
* add a meeting time
*/
public void addMeeting(Meeting m) {
meetings.add(m);
}
How do you remove a meeting from the collection?
/**
* remove a Meeting object from the section
* @param the meeting to be removed
*/
public void removeMeeting(Meeting m) {
if (meetings.contains(m))
meetings.remove(m);
}
How do you change the time of a meeting? Changing the time of a meeting may seem
difficult, but it is actually easy. One solution is to simply remove the old meeting and
replace it with a new.
/**
* change the time of a meeting
* @param m1 the meeting to be changed
* @param m2 the replacement meeting
*/
public void changeMeeting(Meeting m1, Meeting m2) {
meetings.remove(m1);
meetings.add(m2);
}
It could be even easier by using the setters of the Meeting class.
164
Arrays
So what type of collection should we use to store five Strings? The collections we
have seen all can vary in size. Using any of them is, to some degree, a matter of overkill.
There is a tried-and-true collection type we can use, since we know that the size of the
collection is small; we can use an array.
165
We have seen the idea of an array mentioned when we first saw the ArrayList class.
An array is a collection of data items, in which individual elements are referred to by
means of an index. That is, when you have an array containing five elements, the first one
has index zero, the second has index one, and the fifth has index four. This is the same
zero-based numbering we found in the Calendar class, and it permeates computing.
Why? Remember that the smallest non-negative integer is zero. Back in the old days,
when memory really mattered and machines were slow, it made sense to start numbering
at zero.
So perhaps we should create an array of String. But wait; didnt we say in an earlier
chapter that a String is immutable? Yes, we did. That means that once it is given a
value, it cant be given a new value.
You may be wondering a little about right now. Havent we created a string by
concatenating a number of values together? Havent we done that in a series of
statements? Consider, for example a slightly-modified version of the toString method
in the Course class.
public String toString() {
DecimalFormat myFormatter = new DecimalFormat(#.#);
String result;
double totalHours;
totalHours = lectureHours + laboratoryHours +
seminarHours;
result = departmentAbbreviation + + courseNumber;
result = result + - + credits + -
result = result + myFormatter.format(hours);
result = result + '\n' + title + '\n' + description;
result = result + (
result = result + myFormatter.format(lectureHours)
result = result + ',' +
myFormatter.format(laboratoryHours)
result = result + ',' + myFormatter.format(seminarHours)
result = result + ')';
return result;
}
Every time we add one string to the end of another we appear to be changing the value of
result.
True, but we did by being wasteful of memory. When we perform those statements in the
method above, we use the value of result to create another variable. We them have
result refer to that new variable. The original value is left in memory, but nothing is
referring to it. At some later time, a process called garbage collection will be used to
166
identify the memory which is not referred to, and return it to the pool of available
memory.
Note that all the changes we made involved adding characters to the end of an existing
value. While summarizing meeting times, Im thinking of a process in which we
construct a String as we read meeting details, perhaps modifying the String
(inserting new characters, or changing existing ones) when we read a different meeting
detail. But a String wont allow this insertion. We need something like a String but
which one allows us to modify it. How about a StringBuffer?
StringBuffer
The online documentation states that a StringBuffer is a mutable sequence of
characters. A StringBuffer is like a String, but can be modified. At any point in
time it contains some particular sequence of characters, but the length and content of the
sequence can be changed through certain method calls. Thats what we want.
In fact, when you read the documentation on StringBuffer youll find that the most
recent versions of Java have included a new class, StringBuilder, which might be
even better. Well use StringBuffers here and leave StringBuilders as an
exercise.
I am thinking we should create five StringBuffers, each of which will contain seven
characters representing the days of the week, a space (to make the result more readable),
the four digit start time, a space (to make the result more readable), the four-digit end
time, a space (to increase readability), and the room number for the meeting, as shown in
the example above.
Thus each StringBuffer needs to contain 18 characters (seven plus one plus four
plus one plus four plus one) plus the length of a room number (in the case of my college,
four characters), a total of 22 characters. The length of a room number may vary for a
different college. For that matter, other values we have considered (the subject
abbreviation and the course number) may be different for other colleges too.
Now we need an array which we can use to access the individual StringBuffers.
// assumption - no more than five meetings a week
StringBuffer temp[] = new StringBuffer[5];
Then we need to initialize each element of the array to a string of 22 blanks.
int row = 0;
for (row = 0; row < 5; row++)
temp[row] = new StringBuffer(
);
167
There are 22 spaces between the quotation marks in the last statement.
168
169
Manipulating StringBuffers
Placing data in StringBuffers
The last snippet of code said we need to do something as we process each meeting.
What is the something we need to do?
For the first Meeting object, we need to place its details into the first StringBuffer;
we need to store the day of the week, the start and end times, and the room number.
170
For each subsequent Meeting object, we need to compare its details to the details
already in the array of StringBuffers; when there is a match of room and times, we
need to modify the matching StringBuffer to indicate a meeting on a different day.
When there is no match to any of the existing StringBuffers, we need to begin
building a new StringBuffer in the appropriate format.
How do we know we are dealing with the first Meeting object? Alternatively, how do
we know we are not dealing with the first Meeting object?
In answering those two questions, you begin to review the datatypes you know to find
one that takes on only two values. Right, boolean variables!
boolean first = true;
for (Meeting mt: meetings) {
if (first) {
// do something
first = false;
}
else
// do something different
}
From the description of the output wed like to create (seven characters for the days of
the week, a space, the four-digit start time, a space, the four-digit end time, a space, and
the room number for the meeting), you can replace the comment // do something
with the following.
temp[0].replace(8, 11, mt.getStartTime());
temp[0].replace(13, 16, mt.getEndTime());
temp[0].replace(18, 21, mt.getRoomNumber());
StringBuffers are objects, so the syntax we need is the name of the object, followed
by a period and the name of the method we want to invoke, followed by the parameters of
that method. In these cases, we are replacing four blanks in the StringBuffer with
the values returned by the Meeting getters. We indicate which characters to replace by
giving the index of the first one to be replaced, and the index of the last one to be
replaced.
But the numbers 11, 13, 16, 18, and 21 are calculated on the basis of the lengths of the
start and end times of a meeting, and the length of the room number. The number eight is
calculated as one more than the number of days in the week. Remember that the first
position in the StringBuffer is position zero. Thus the abbreviations for the days of the
week will be in positions zero through six. Position seven will be occupied by a space.
Thus the first digit of the start time will be position eight.
We should calculate those numbers so they reflect differences between colleges.
171
172
173
venerdFriday
sabatoSaturday
domenicaSunday
What are appropriate abbreviations for Tuesday and Wednesday?
A lot of code
Now we have an idea of what we need to do to deal with all the meeting objects. Take a
deep breath as there is a lot of code in the following method.
/**
* produce a list of meeting times
*/
public String meetingsList() {
int maxMeetings = College.getMaxMeetingsPerWeek();
// assumption - no more than maxMeetings meetings
// per week
StringBuffer temp[] = new StringBuffer[maxMeetings];
// create an array of Strings of the correct length,
// containing blanks
// some of the blanks will be replaced later
int lengthNeeded = 7 + 1 +
College.getMeetingStartTimeLength() +
174
1 + College.getMeetingEndTimeLength() + 1 +
College.getRoomNumberLength();
for (int row = 0; row < maxMeetings; row++){
temp[row] = new StringBuffer(lengthNeeded);
for (int j = 0; j < lengthNeeded; j++) {
temp[row].insert(j, " ");
}
}
// remember if we are processing first Meeting
boolean first = true;
// ensure times are four-digits
DecimalFormat myFormatter = new DecimalFormat("0000");
// abbreviations for days of the week
String dayAbbreviations = "SMTWRFA";
// number of rows of output we generate
int rowsGenerated = 0;
// p1a is the location of the first digit of start time
// p1b is the location of the last digit of start time
// p2a ditto for end time
// p2b ditto for end time
// p3a ditto for room number
// p3b ditto for romm number
int p1a = 8; // days in the week plus 1
int p1b = p1a + College.getMeetingStartTimeLength()
- 1;
int p2a = p1b + 2;
int p2b = p2a + College.getMeetingEndTimeLength() - 1 ;
int p3a = p2b + 2;
int p3b = p3a + College.getRoomNumberLength() - 1;
// examine all Meeting objects
for (Meeting mt: meetings) {
// extract fields necessary for comparison
// done mainly for efficiency, so we dont need
// to extract the same field several times
String st = myFormatter.format(mt.getStartTime());
String et = myFormatter.format(mt.getEndTime());
String rt = new String(mt.getRoomNumber());
int dt1 = mt.getDayOfWeek();
char dt = dayAbbreviations.charAt(dt1);
// the first Meeting object is a special case
if (first) {
// simply place the Meeting details in the
// appropriate positions of the first element
// of temp. Note that we use the replace
175
176
//
//
//
if
Programming Style
Lets step back a minute and look at the style in which this method has been written.
177
Does it contain too many statements? Some people will suggest that a method should fit
on a page, or on a screen; this one takes several printed pages.
Others suggest that a method should be cohesive. A method is cohesive when it carries
out one task. Thus cohesion is at least partly in the eye of the beholder. I would argue that
this method is cohesive, since it summarises a collection of Meeting objects into a
String. Yes, there are several subtasks involved, but they are all essential to the main
task; the subtasks probably would not exist independently of the main task. Since the
method is cohesive, I feel there is no need to decompose it into shorter methods. To
check that a method is cohesive give it a name that describes what the method does. If the
name requires the word and or the word or, then the method is probably not
cohesive.
Are there too many comments? Some would argue yes, but I feel it is generally
impossible to have too many comments, providing they are meaningful. An example of a
useless comment is
i = 3;
When you are using i to represent the month of March, then the comment should say
that. Other simple statements, for example found = true; should be commented as it
is perhaps unclear what the effect of the statement is.
Notice that declaration statements are scattered throughout the method. Some would
argue that all declarations should be at the beginning of the method. Sometimes I agree,
when it is a short method, but for longer methods, I prefer a just-in-time style where I
declare variables when I need them.
Are the variable names meaningful? Guilty! Some of the names are very short (et, st,
rt, dt1, dt) and could be better. However, my justification is that the names are used
only within this method (and within a small portion of this method) and they actually
make sense; the two-letters names are temporary (hence the t) values for end, start,
room, and day of the week. dt1 does not quite follow that pattern.
178
179
180
Summary
That was quite a chapter. The coding was long and complicated, but it does show what
you can do with a little time and care.
The next chapter continues processing collections.
If you prefer to skip that chapter, continue to chapter 11 and then chapter 12. Now that
the student has data which must persist over time (the marks he/she has earned in the
past), and the professor has data which must persist over time (the sections and courses
taught in the past), perhaps we should consider how to implement persistence.
That is, how do we save data so that it will be there when we run our program again?
Persistence is the topic of chapter 12.
181
Exercises
1.
2.
3.
4.
5.
6.
7.
8.
Explore the use of the modulus operator when either one or both of its arguments
are negative.
9.
10.
182
Learning objectives
By the end of this chapter you will be able to
Introduction
We have seen many collections so far. Usually, we have added elements to them, and
then converted them to a String for display.
In one case we have done more. We processed all the meeting objects associated with a
section and merged them into something suitable for a timetable entry.
Now, Id like to return to another collection, involving Mark objects.
Grade Average
Recall that each course has a credit value associated with it. We can calculate how many
credits a student has earned in total by summing data in all Mark objects (eliminating
duplicates) but we dont know the total by semester. Lets see how to calculate those
numbers.
When do we want to know the number of credits completed in a semester? The obvious
time is when we are producing a transcript, a record of a students progress in a semester.
At the same time, we will be calculating an average mark. How do you calculate the
average mark?
183
graduating average involves all courses taken towards graduation, taking the higher mark
in case a student repeated a course, and eliminating failures.
Weighted average
In either case, we calculate a weighted average. To do this, create a weighted sum of the
percentages (the percentage earned in a course is multiplied by the number of credits the
course is assigned) and divide it by the sum of the credits. The result of the division is the
grade average.
The priority of operations in a programming language is the same as the priorities in
mathematics.
For example, assume that a student takes three courses at one time. In the first course,
worth two credits, the student earns a mark of 65%. In the second course, worth three
credits, the student earns a mark of 74%. In the third course, worth one credit, the student
earns a mark of 80%.
The weighted average is calculated as
2 * 65 + 3 * 74 + 1 * 80
2 + 3 +1
To evaluate this expression, you must first evaluate the numerator, then the denominator,
and then divide the numerator by the denominator.
For the numerator, calculate 2 * 65 and remember the result (130). Calculate 3 * 74 and
remember the result (222). Calculate 1 * 80 and remember the result (80). Add together
130 and 222 and remember the result (352). Add 352 and 80 and remember the result
(432).
For the denominator, add two and three and remember the result (five). Add five and one
and remember the result (six).
For the average, divide the numerator (432) by the denominator (six) and get a result of
72.
/**
184
185
186
Integer division
With the exception of the semesterAverage, all these variables are ints. When you
divide any type of integer variable by another integer variable, your result is an integer.
4739/60 is 78, rather than the 78.98333 my calculator shows. When calculating an
average mark, most students like to see at least one or two decimal places. To ensure that
an integer divided by an integer gives a result with decimal places, we cast (or convert)
one of the integers to a double datatype and then do the division. When you have an
expression involving a variety of datatypes, the Java compiler then takes over and
converts them all to the highest datatype. In this case, it converts the other integer to a
double and divides a double by a double, giving a double. You see this in the
first statement.
semesterAverage = (double) semesterNumerator
/ semesterDenominator;
When you test this method, you will find that unfortunately the semester and overall
averages appear with a varying number of decimal places. A DecimalFormat object
using the pattern ##0.00 will resolve that. Read the pattern as Display the digit in the
hundreds column when it is not zero, otherwise leave a blank. Display the digit in the tens
column when it is not zero or when it is zero and the digit in the hundreds column
printed, otherwise leave blank. Always display the digit in the units column. Always
display the decimal point and two digits after it, rounding as appropriate.
As I write this (Spring 2006), the track and field world is discussing how a time of 9.766
seconds was mistakenly rounded to 9.76 instead of 9.77.
187
The rule usually used is When the first digit to be dropped is more than five, drop it and
increase the previous digit by one. When the first digit to be dropped is less than 5, drop
it and leave the previous digit unchanged. When the first digit to be dropped is exactly
five, change the previous digit to be even.
Thus 9.766 rounds to 9.77 (first part of the rule), 9.773 rounds to 9.77 (second part of the
rule), and 9.775 rounds to 9.78 (third part of the rule). In many cases, the third part of the
rule is ignored or forgotten. Wikipedia gives a good discussion of rounding at
http://en.wikipedia.org/wiki/Rounding
The declarations
Of course, all of this requires the declarations of the variables.
String result ="";
int previousYear = 0;
String previousMonth = "";
int currentYear = 0;
String currentMonth = "";
int totalNumerator = 0;
188
int totalDenominator = 0;
double totalAverage;
int totalCredits = 0;
int semesterNumerator = 0;
int semesterDenominator = 0;
double semesterAverage;
int semesterCredits = 0;
189
when the resulting String should have the semester summary appearing last, and
false when the resulting String should have the semester summary appearing first.
A second way is to have two different methods, one for summary-first and one for
summary-last.
You would be right in thinking these methods would repeat much of the code in the
transcript method, but in some other order. That would not be sensible, since any
errors in one method could be repeated in the other. Instead, we will write a method
which takes the String which the transcript method calculates and manipulates it
so the details about the averages appear in the correct places.
190
The second retrieves the characters beginning at position 20 and extending to the end of
the string. That is, lastWord will be the string Cholmondley.
Of course, you can use a similar statement to determine the middle word.
String middleWord = name.substring(10, 19);
How did we know which positions to use in the above statements? We counted
characters. But there are alternative. We could use the split method, which returns an
array of String. Or we could use the indexOf method to help us find the blanks.
Details of split
The split method takes a string and returns an array of strings, breaking the original string
at places which match the value of a regular expression which you specify. A regular
expression is a pattern, possibly including wildcards, of letters, numbers, and/or
punctuation.
The following statements will break the variable name into its pieces and display them.
Pattern p = Pattern.compile("\\s");
String[] names = p.split(name);
System.out.print("broken into strings, " + name +
" consists of [");
for (int i = 0; i < names.length; i++)
System.out.print(" " + names[i]);
System.out.println("]");
Recall that previously we have used \n and \t to represent a new line and a tab,
respectively. The regular expression we want to use consists of two characters, \s, which
refers to any whitespace character, a blank, a tab, a newline, etc. What do we use to
represent a backslash?
Right, \\. To get both the backslash and the s, we use \\s.
More details on regular expressions and patterns are in the Java documentation describing
the Pattern class.
191
Reversing a String
As an exercise in using various String methods, write a method which will accept a string
and then generate the string in reverse. That is, for an input of here is a string, the
output will be gnirts a si ereh.
One possibility is to reverse a string using an iterative algorithm. That is, we could look
at each character and process it.
192
Palindromes
While we are playing with strings, how about exploring palindromes? The simplest
definition of a palindrome is a sequence of characters that (not counting spaces,
punctuation, and case) reads the same forwards and backwards. Some English words
193
(civic, tot, level, a) are palindromes. Wikipedia has a discussion of palindromes in other
languages and music, as well. http://en.wikipedia.org/wiki/Palindrome
Can we write a method that tells us whether or not a string is a palindrome?
Certainly. It uses one of the reverse methods we just created.
public static boolean isPalindrome(String s) {
String target = "";
// keep only those characters which are letters
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (Character.isLetter(ch))
target = target + ch;
}
return target.equalsIgnoreCase(rReverse(target));
}
As you can see, most of the method involves taking out the characters that are not letters.
We use a method from the Character class, isLetter, to identify the characters
which are letters.
Determining that the string is a palindrome takes place in the return statement.
Try this method with some of the well-known palindromes. Madam, Im Adam. A
man, a plan, a canal: Panama! Others are given in the Wikipedia article cited earlier.
Now that we have seen some of the String methods, we can go back to the original
problem of producing a transcript.
194
pointing to a different area of memory. The way the assignment statement above is
implemented might be as follows.
Copy the contents of the memory to which result refers to another area of
memory (call it area 1) and append total credits earned = to the contents of area
1. Change result so its reference points to area 1.
Copy the contents of area 1 to another area of memory (call it area 2). Convert
totalCredits to a String and append that to the contents of area 2. Change
result so its reference points to area 2.
Copy the contents of result to another area of memory (call it area 3) and append
average =. Change result so its reference points to area 3.
Copy the contents of result to another area of memory (call it area 4). Convert
totalAverage to a String and append that to area 4. Change result so its
reference points to area 4.
But what about the areas of memory I have called area 1, area 2, and area 3? They now
have no references referring to them, so they are eligible for garbage collection, a process
by which unused (that is, unreferenced) areas of memory are made available for reuse.
So now you see how we have been able to construct Strings from other Strings,
even though Strings are immutable.
How do we use this to manipulate the String that transcript has produced? First we
note that there are some special substrings we need to identify and move. These begin
with credits earned and end with \n. (Be careful that you do not find the substring
beginning total credits earned.) Only these substrings need to be moved. All others
remain in the same order.
Try to write the method transcriptSummaryFirst without reading on.
Here is my transcriptSummaryFirst method.
/**
* create a transcript String in which the summary
* comes before the details of the semester
* @return transcript with summary first
*/
public String transcriptSummaryFirst() {
String result = "";
String temp = "";
int startPhrase; // where the phrase starts
int endPhrase;
// where the phrase ends
String semesterDetails = "";
String summaryLine = "";
// get transcript with summary last
temp = transcript();
195
196
At my college, when a student takes a course twice (or more), its mark counts towards the
semester average for each semester in which it is taken. But only the highest mark should
be included in the overall graduating average.
This will involve a rethinking of how the transcript method calculates the overall average.
Actually, the problem is even more complicated. We have special topics courses which
may be taken more than once for credit, provided the topics are different. Implementing
this would require us to rethink our model quite seriously, so we will ignore this problem.
This emphasises the importance of getting your model correct before you start
implementing it.
Rather than saving the credits and percentages we need to calculate the overall average,
well create a collection that contains only the marks well need. Then, once we have
examined all the marks, well calculate the average.
We need a collection in which we can store objects containing a subject abbreviation,
course number, course credits, and mark earned. But we already have a class like that,
Mark, except that Mark contains the year and month of the semester in which the mark
was earned. Since the year and month dont matter, can we use the Mark class?
Yes, we can. We can extend it to create a new class, HighestMark that does not
concern itself with the year and semester.
class HighestMark extends Mark {
public HighestMark(String departmentAbbreviation,
int courseNumber, int credits,
int markEarned) {
super(departmentAbbreviation, courseNumber,
credits, 1999, "SOMETIME", markEarned);
}
}
Since we dont care about the year and semester, we just provide some values; the exact
values we provide dont matter!
That is all there is to the class. All the methods which we created for Mark will be
available to HighestMark objects as well. That is, the child class HighestMark is
derived from its parent class Mark, so its methods are inherited from its parent.
So what type of collection should we use as the basis of the collection of HighestMark
objects? The order in which they are stored does not matter, but we would like to be able
to check whether there already is a mark stored for the course. Many datatypes allow this,
but we havent used an ArrayList for a while, so lets use one now.
197
198
And what if we wish the summary lines of the transcript printed before the details?
Nothing we have done has changed that method. But to be safe, run a unit test on it again.
This is a practice called regression testing, making sure that a change you made has not
broken anything else.
Summary
This chapter has focussed on manipulating strings. This is an important skill, as much
processing involves string manipulation.
Now that we have experienced a serious amount of sophisticated Java code, it is time to
go back and cover some of the things that have been glossed-over. In particular, we need
to talk about exceptions.
Then we will look at putting a graphical front-end on our program, so that we are not so
dependent on BlueJ.
199
Exercises
1.
How would you change the model to handle special topics courses?
2.
You can use the ideas from creating a transcript to create a class list. Of course class
lists may be in order by the name of the student or by student number.
Make it so.
3.
In previous chapters we have looked at the data a birder collects. Create a method to
list all the birds seen.
Make sure that the list tells how many birds have been seen each month, and how
many each year.
In addition, provide the number of species seen each month, and each year.
4.
200
Chapter 11 - Exceptions
Learning objectives
By the end of this chapter you will be able to:
Define exception
Define catch and try
Identify when exceptions are thrown and describe how to catch them
Create, throw, and catch your own exceptions
Use exceptions to create programs which gracefully handle erroneous input and
mistakes in computation
Definition
We have mentioned exceptions at at least three points so far. The first was in the clone
method where we had to handle a CloneNotSupportedException. The second
was when we discussed the need for a Professor object to have a hiring date. The
third was when we discussed preventing bad data getting into Meeting objects.
There many other exceptions and we shall see some of them in the following chapters.
These include ClassNotFoundException, IOException, and
FileNotFoundException. Before we see these exceptions, we should first answer
the question What is an exception?
There are many sources that will answer the question, but one of my favourites is Suns
Java tutorial, available at http://java.sun.com/docs/books/tutorial/. One of the trails
through the tutorial takes you through essential classes and features. Exceptions are
essential, and are described at
http://java.sun.com/docs/books/tutorial/essential/exceptions/definition.html
This section of the tutorial contains a number of quotations from the tutorial, as viewed
on 2006-05-19. For example, we have this introduction:
What Is an Exception?
The term exception is shorthand for the phrase "exceptional event."
201
Examples
When we attempted to clone an object, a CloneNotSupportedException may
occur. Normally, you would want to be able to clone any object. However, there are some
cases where you want to ensure there is one and only one copy of an object.
An example would be in the College system we have been modelling, where we wish a
college-wide policy to describe how percentages translate to letter grades. Everybody
follows the same policy, so there should be only one copy. This is actually an example of
the Singleton pattern, which we will discuss in a later chapter.
If you want to prevent cloning an object, you could use this clone method.
public SomeDataType clone()
throws CloneNotSupportedException{
throw new CloneNotSupportedException
(Cloning is not supported);
}
202
Division by zero is another place that you may want to throw an exception. Fortunately,
we have not seen one of those yet. We might have seen one if we tried to calculate a
students grade average for a semester in which the student was not enrolled in any
courses.
When we developed the Meeting class, we wanted a way to identify incorrect or
inappropriate times for the Meeting. 2507 is an incorrect time. 0400 is a correct time,
but it may be inappropriate. Perhaps the class should throw a
DataFormatException.
When we were developing the Professor class, we wanted a way to ensure that a
hiring date was specified. Perhaps we need to create our own type of exception, perhaps
naming it MissingHiringDateException.
203
204
205
/**
* Default constructor for test class MeetingTest
*/
public MeetingTest()
{
}
/**
* Sets up the test fixture.
*
* Called before every test case method.
*/
protected void setUp()
{
}
/**
* Tears down the test fixture.
*
* Called after every test case method.
*/
protected void tearDown()
{
}
public void testMeetingSmallDay()
throws DataFormatException {
Meeting m = new Meeting(-1, "L322", 1000, 1030);
assertNotNull("was created", m);
}
public void testMeetingLargeDay()
throws DataFormatException {
Meeting m = new Meeting(7, "L322", 1000, 1030);
assertNotNull("was created", m);
}
}
We can also use a DataFormatException to indicate that the start or end times are
incorrect or inappropriate, but it may be better to create our own, more meaningfullynamed exceptions. Note that the DataFormatException we have created can tell us
about two problems that the input is too small, or that it is too large. But the same
exception in thrown is both cases. By providing our own messages (the parameter passed
to the constructor), we can include the offending data in the message should we wish (and
we should wish, since it is a good idea to provide as much information as possible about
errors which occur.)
206
But it may be better, since our programs will be simpler, to create our own exceptions.
class BadHourException extends Exception {
public BadHourException(String msg){
super(msg);
}
}
class BadMinuteException extends Exception {
public BadMinuteException(String msg){
super(msg);
}
}
We identify the need to throw these exceptions by modifying the Meeting constructor a
little more. Since we need the same sort of processing for both the startTime and the
endTime, we create a small helper method to do the processing. This helper method is a
private method, callable only within the Meeting class. (When you use javadoc to
generate the documentation for a class, private methods are not exposed.)
/**
* check if a time value is acceptable
* @return true if okay
* @return throw an exception if not
*/
private boolean isValidTime(int t)
throws BadHourException, BadMinuteException{
int hours = t / 100;
int minutes = t % 100;
if ((hours < 0) || (hours > 23))
throw new BadHourException("For time " + t +
" hours should be between 00 and 23 inclusive");
if ((minutes < 0) || (minutes > 59))
throw new BadMinuteException("For time " + t +
" minutes should be between 00 and 59, +
" inclusive.");
return true;
}
When an exception is thrown, the processing in the current block of code ceases (unless
there is a finally clause, described below) and the method, its calling method, or a
method before it in the calling stack, is expected to handle the exception.
We invoke the isValidTime method as follows.
if (isValidTime(startTime))
207
this.startTime = startTime;
if (isValidTime(endTime))
this.endTime = endTime;
Since isValidTime may throw exceptions and the constructor doesnt handle them,
the constructor must indicate that the exceptions may be thrown.
public Meeting(int dayOfWeek, String roomNumber,
int startTime, int endTime)
throws DataFormatException,
BadHourException, BadMinuteException
Write unit tests to attempt to create Meeting objects with bad times. Youll need three
tests; hours too small, hours too large, and minutes too large.
Why dont you need a test for minutes too small?
The unit tests look like this.
public void testMeetingLargeHour()
throws BadHourException, BadMinuteException,
DataFormatException {
Meeting m = new Meeting(3, "L322", 2500, 1030);
assertNotNull("was created", m);
}
How would you handle a time like 10:30 p.m.? That is, what happens if someone wanted
to be able to provide military time as well as civilian time? Can you create a method to
handle such times?
Now we can create and throw exceptions. But how do we handle the exception
eventually. That is what catch clauses do.
Catching exceptions
In baseball, basketball, rugby, water polo, and lacrosse, when you throw the ball,
someone else catches it (unless you have scored a goal). Thats the theory behind
throwing and catching exceptions.
When the problem is something we can handle ourselves immediately, we place the code
which could cause an exception inside a try block. In a general way, we show this as
try {
Some statements which can throw exceptions
208
}
catch (one type of exception) {
}
catch (another type of exception){
}
CloneNotSupportedException
You have seen a catch clause in the clone method. It is repeated below.
/**
* clone
* @return a copy of the Address
*/
public final Object clone() {
try {
return super.clone();
}
catch (CloneNotSupportedException e) {
return null; // can not happen
}
}
In this case, the CloneNotSupportedException may be thrown by
super.clone(). Thus some piece of code must handle it. In this case, we handle it
ourselves. Since we know it can not happen, the processing we do is minimal.
To repeat, in this situation the code in the catch block will never be executed.
209
//
//
//
//
//
//
}
Rather than handling the exception yourself here, you need to pass it back to the method
which called this one.
Summary
Exceptions are the powerful technique Java uses to handle errors. We will see many more
exceptions in the chapters that follow.
The following two chapters involve writing data to external devices. This process uses
exceptions extensively.
210
Exercises
1.
Modify the constructor for Person so that it determines that both a first and last
name are provided and throws exceptions if either are missing. In Indonesia, many
people go by just one name. How could you determine that you are in a country
where people use only one name?
There are many stories about programs that have difficulty with names. One story is
about a payroll system in which last names with only one character were used for
testing purposes. That worked well until a person with the last name A joined the
organization.
A second story was about the person who didnt have a long first and middle name.
His name was something like R B Jones. The payroll system threw exceptions for
one-character names. So the data entry people entered the name as R(only) B(only)
Jones. The people who wrote the system stripped out unusual characters from the
input. Thus R B Jones became Ronly Bonly Jones.
2.
Modify the constructor for Person so that it throws one or more exceptions if
there is a birth date provided but it is an inappropriate date.
3.
Explore the Exception class to see what types of exceptions are available to you.
211
212
Define persistence
Implement persistence through object serialization
Why persistence?
Unit testing provides a way to create and destroy objects, but only for the duration of the
test. Yes, BlueJ allows us to place the objects it creates on the Object Bench, but they
exist only until you recompile the object or exit BlueJ.
Suppose we wish to create several objects and have them available at a later time,
perhaps the next day, after you have ended BlueJ, turned the computer off, and then
restarted BlueJ. We need a way to create objects and then save and restore them so we
can use them again. In short, we need to investigate ways to ensure the persistence of
objects.
Persistence has many meanings, ranging through doggedness and a tendency to
continue. In computing, persistence is closer to the second definition; it is the ability to
save an object and retrieve it at a later time.
The random access memory (RAM) in a computer is volatile; when it loses power, it
loses its contents. Thus RAM is not suitable for persisting data; you would need to ensure
that the supply of electricity in uninterrupted. The ability to save data has been required
since the earliest days of computing. Thus we have seen the use of non-volatile memory
(punched cards, magnetic tapes, floppy disks, hard drives, and now CDs and DVDs.)
There are programming languages (COBOL and RPG are examples) designed to simplify
the processing of such data.
213
Java is a more generalized language, supporting many types of processing of data. It too
requires the processing of data saved by other programs.
Since an object has identity, state, and behaviour, what does saving an object mean?
Saving an object involves converting the objects state (the values of its instance
variables) to a stream (a continuous sequence) of bytes and writing those bytes onto a
medium which will not lose its contents when the power is removed.
When an object is restored, it will be given a name and will have behaviour (as specified
in the class definition), and its state will be reconstituted from what was previously saved.
The terms used in Java are serialization and deserialization. Java allows us to serialize an
object, thus granting it persistence. Serializing an object involves writing it to an external
storage device. Deserializing an object involves reading it back from that external storage
device.
Notice that there are other ways of saving data. Traditional file processing is possible, but
we will not discuss that here. The use of databases is possible as well, but that is much
more complicated and we will not discuss it here.
External devices
Before we can look at serialization, we must consider the external devices we will use
and their characteristics.
The external devices are mostly disk drives and the files they contain. These files must be
opened, written to (or read from), and then closed. Files have names.
The external devices could also be other computers, local or somewhere else on the
Internet. You can use serialization to send objects from one computer to another, in real
time. We will not explore that option here; it is a topic for more-advanced study.
Streams
Writing to and reading from external devices is done through streams. You can think of
streams as sequences of characters, perhaps including special characters.
So we need streams to which we can write and from which we can read and we need a
way to associate a stream with an external device.
214
215
Here is the unit test method to check that we can serialize these two Address objects. It
will attempt to write out two objects and then check that two objects were really written.
The deserialization test, below, will check that the correct data was written.
public void testSerialization(){
216
int i = 0;
try {
FileOutputStream out = new
FileOutputStream("address.dat");
ObjectOutputStream oos = new
ObjectOutputStream(out);
// write one object and remember that
oos.writeObject(a);
i++;
// write a second object and remember that
oos.writeObject(a1);
i++;
// output may be held in memory until a buffer
// is full. We have no more output so ensure
// the buffer is written to a disk somewhere
oos.flush();
// we dont need the stream any more
oos.close();
}
catch (FileNotFoundException e) {
System.err.println("problem with address.dat " +
e.toString());
}
catch(IOException e) {
System.err.println("problem with ObjectOutput " +
e.toString());
}
// check that we were able to write two objects
assertEquals(i, 2);
}
Note that in order to use serialization we must also use exceptions, which we saw in the
previous chapter.
For this unit test to compile, we need to include the statements
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
But it is easier, when you need to import many classes from one package, to just use
import java.io.*;
217
The asterisk is a wildcard. A wildcard means any value that matches this pattern, where
any value can replace the asterisk. In this case, java.io.* means every class in the
java.io package, but not any packages within java.io.
The unit test uses the writeObject method to actually write an Address object. The
writeObject method is a method in the ObjectOutputStream class.
Since an Address is made of serializable objects and Address implements
Serializable, an Address knows how to serialize itself.
Heres a suggestion. Before you run any methods which use exceptions, make sure that
you open the BlueJ Terminal window, so that the messages displayed in the catch
clauses have a place to display, should there be an error of some kind. To do this, choose
View, Show Terminal, or press
+ T.
Here is a unit test to check that we can deserialize Address objects.
public void testDeserialization() {
try {
FileInputStream in =
new FileInputStream("address.dat");
// we use the same file name as in writeObject
ObjectInputStream ois = new ObjectInputStream(in);
a1 = (Address)ois.readObject();
a = (Address)ois.readObject();
ois.close();
}
catch (FileNotFoundException e) {
System.err.println("Can not find address.dat " +
e.toString());
}
catch (IOException e) {
System.err.println(
"file problem with ObjectInput " +
e.toString());
}
catch (ClassNotFoundException e) {
// will not happen but must be caught
}
assertEquals(a1.getCity(), "Kelowna");
assertEquals(a.getCity(), "Calgary");
}
The readObject method is a method in ObjectInputStream. It reads an object
from the stream (throwing an exception, an error, when the stream can not be opened),
218
and we must then indicate the type of object, by casting to an Address. Note that any
method which casts an Object to some other datatype must handle a possible
ClassNotFoundException. Usually this will not happen, but the casting process
requires us to handle that possibility.
Both of these tests succeed. But note there is something unusual going on here.
We have talked about saving and then restoring an object. If you look closely at the code,
youll see that we serialized the two Address objects in one order (a and then a1), but
restored them in the reverse order (a1 and then a.) We can do this because both are
Address objects. If we were to serialize a collection, then we would have many objects
in the stream; when we recreated the collection, their order would be restored.
But serialization is not designed to work quite like this, with one method serializing and
another immediately deserializing. The deserialization takes place at a later time, after all
the existing objects become null, or have ceased to exist.
219
220
We can encrypt variables using the java.security package, a topic which we will not
discuss here.
We can handle collections on our own too. Like encryption, to use that technique we will
need to write some extra methods. In particular, we will need to provide replacements for
writeObject and readObject.
221
oos.writeObject(students.size());
for (Student s:students) {
oos.writeObject(s);
}
where oos has been opened earlier. This assumes a Student object is serializable. You
can guess how to do that. To see it in detail, read ahead to the next part of this chapter.
Similarly, we create our own readObject method.
private void readObject(ObjectInputStream ois)
throws IOException {
try {
// read instance variables here
}
catch (ClassNotFoundException e) {
System.err.println("Unable to deserialize " +
e.toString());
}
} // end readObject
To use this method, we could use, for example,
Set<Student> students = new HashSet<Student>();
int n = (Integer) oos.readObject();
for (int i = 0; i < n; i++) {
students.add((Student) oos.readObject());
}
Any special handling required by transient or encrypted variables will need to be
included in the method as well.
As we have seen in the examples above, both the readObject and the writeObject
methods will be called by other methods, which look after opening and closing the
streams, as well as extracting the objects and saving them with unique identities, perhaps
as members of a collection.
Lets see how this applies to the classes we have developed, in particular, to the ones
which contain collections.
222
223
Now we can create the writeObject method itself, in the Section class. Much of it
is taken up with processing the collections which are part of the object.
private void writeObject(ObjectOutputStream oos)
throws IOException {
// write the single-valued instance variables
oos.writeObject(identifier);
oos.writeObject(departmentAbbreviation);
oos.writeObject(courseNumber);
oos.writeObject(sectionNumber);
oos.writeObject(startYear);
oos.writeObject(startMonth);
oos.writeObject(endYear);
oos.writeObject(endMonth);
// write the collections
oos.writeObject(getStudentCount());
if (getStudentCount() > 0)
for (String s:students)
oos.writeObject(s);
oos.writeObject(howManyMeetings());
if (howManyMeetings() > 0)
for (Meeting m:meetings)
oos.writeObject(m);
};
Note that we have two writeObject methods. One appears in
testObjectSerialization as oos.writeObject(s2);
The other appears as a private method in the Section class, as
private void writeObject(ObjectOutputStream oos) throws
IOException
The first is automatically translated into a call to the second. Run the unit test and
confirm that you can serialize a Section.
Now you can serialize a section, but how do you deserialize it?
224
225
226
Autoboxing carries out the first statement for us and thus makes it easier to convert
primitive datatypes to the corresponding object Autounboxing carries out the second
statement for us and thus makes it easier to extract primitive datatypes from objects.
Programming style
Should you have one large data file which contains many objects of different types and
you dont know the order of the types, you may extract objects one at a time from the file
and then use instanceof to determine the datatype of the object before you cast it
appropriately. This gives rise to very complicated programs, and it is not advisable. In
fact, many programming standards forbid this use of instanceof. We will follow
those standards.
Instead, it is better to have an idea ahead of time of the structure of the file; in particular,
it is important to know that the file contains only one class of object. We will do so.
227
Summary
In this chapter we have seen one way to save the state of our objects. The problem with
the particular technique is that it produces files which are only readable by another Java
program.
In many cases it might be better if the file could be read by a program written in a
language other than Java. Why would we want to do this? Perhaps so that we could create
a webpage from the data we read. Perhaps so that we could create printed material from
the data we read.
Well explore a technique which allows this in the next chapter.
228
Exercises
1.
2.
3.
4.
5.
6.
7.
8.
Explore the java.security package to see how you can encrypt data when you
serialize it.
229
230
231
Most modern browsers will display SVG graphics. The advantage of them is that they are
very small. For more details on SVG files, search the web for terms like SVG and create.
new BufferedOutputStream(
new FileOutputStream("address.xml")));
// the variable a has been defined in the
// setUp method
a.writeObject(e);
e.close();
}
catch (FileNotFoundException fe) {
System.err.println(fe.toString());
}
}
Note the line that reads
Thread.currentThread().setContextClassLoader(getClass().get
ClassLoader());
Threads are a mechanism by which a program can be doing several things at once,
perhaps sharing a processor, or perhaps using separate processors. We will explore
threads in a later chapter. For now, we simply use that statement whenever we wish to
use an XMLEncoder or an XMLDecoder.
Without that line, the XMLEncoder is unaware of the class Address. Your method
will generate a ClassNotFoundException error. To avoid this error, include this
statement in any method which creates either an XMLEncoder or XMLDecoder object.
This hint was provided through an answer to a question at
http://dev.eclipse.org/mhonarc/lists/gef-dev/msg00469.html.
Once the XMLEncoder is aware of the class it is processing, it can prepare output for all
instance variables which have setter methods. Note that when our objects contain
collections, there are no setters for the collections, so the collections are not output. We
need to do that ourselves and we will see how in a few moments.
Suppose we create an address object with the statement
Address a1 = new Address("1234", "X", "Fifth", "Avenue",
"SE", "Calgary", "AB", "Canada", "T0L0M0");
Then the file created when we pass a1 to the XMLEncoder is shown below The heading
lines may change as different versions of XML and Java appear.
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.5.0_01" class="java.beans.XMLDecoder">
<object class="Address">
<void property="city">
<string>Calgary</string>
233
</void>
<void property="country">
<string>CA</string>
</void>
<void property="direction">
<string>NE</string>
</void>
<void property="name">
<string>4th</string>
</void>
<void property="number">
<string>1234</string>
</void>
<void property="postalCode">
<string>T0L0M0</string>
</void>
<void property="province">
<string>AB</string>
</void>
<void property="suffix">
<string>A</string>
</void>
<void property="type">
<string>ST</string>
</void>
</object>
</java>
You can see how the file encodes the class name and instance variables. The <object>
tag indicates the class of the object. The property tags indicate, in alphabetical order,
the instance variables and <string> indicates the datatype of the instance variables.
Other datatypes will appear when necessary. Each tag has its corresponding closing tag,
beginning with </.
234
235
} // end readAddressXML
If we used a statement like this.number = a.number; we would have two
variables both referencing the same area of memory. By writing this.number =
new String(a.number); we have two variables, referencing different areas of
memory, with the different areas containing the same data. We could use a clone method
to copy the variables if we prefer.
We can test this decoding with simple unit tests. Create two Address objects in the
setUp method, containing different values. Then write one out, and retrieve its value
into the other.
public void testXMLSerialization() {
Thread.currentThread().setContextClassLoader(getClass().get
ClassLoader());
try {
XMLEncoder e = new XMLEncoder(
new BufferedOutputStream(
new FileOutputStream("address.xml")));
a.writeObject(e);
e.close();
}
catch (FileNotFoundException fe) {
System.err.println(fe.toString());
}
}
public void testXMLDeserialization() {
Thread.currentThread().setContextClassLoader(getClass().get
ClassLoader());
try {
XMLDecoder e = new XMLDecoder(
new BufferedInputStream(
new FileInputStream("address.xml")));
a1.readObject(e);
e.close();
}
catch (FileNotFoundException fe) {
System.err.println("unable to create file");
}
System.out.println(a1.toString());
assertEquals("addresses match", a.toString(),
a1.toString());
}
236
Object newInstance,
Encoder out) {
// single-valued instance variables
Section s = (Section) oldInstance;
out.writeStatement(new Statement(oldInstance,
"setIdentifier",
new Object[]{s.getIdentifier()}));
out.writeStatement(new Statement(oldInstance,
"setDepartmentAbbreviation",
new Object[]{s.getDepartmentAbbreviation()}));
out.writeStatement(new Statement(oldInstance,
"setCourseNumber",
new Object[]{s.getCourseNumber()}));
out.writeStatement(new Statement(oldInstance,
"setSectionNumber",
new Object[]{s.getSectionNumber()}));
out.writeStatement(new Statement(oldInstance,
"setStartYear",
new Object[]{s.getStartYear()}));
out.writeStatement(new Statement(oldInstance,
"setStartMonth",
new Object[]{s.getStartMonth()}));
out.writeStatement(new Statement(oldInstance,
"setEndYear",
new Object[]{s.getEndYear()}));
out.writeStatement(new Statement(oldInstance,
"setEndMonth",
new Object[]{s.getEndMonth()}));
// collections
for (Student st:s.getStudents()) {
out.writeStatement(new Statement(oldInstance,
"addStudent",
new Object[]{st}));
}
for (Meeting mt:s.getMeetings()){
out.writeStatement(new Statement(oldInstance,
"addMeeting",
new Object[]{mt}));
}
}
}
238
Recall that the students and meetings collections both have private visibility. To
access their elements, we need two more methods in the Section class,
getStudents and getMeetings.
public Set<String> getStudents() {
return students;
}
public Set<Meeting> getMeetings() {
return meetings;
}
Finally we create a DefaultPersistenceDelegate object and attach it to the
XMLEncoder we are using.
/**
* save a Section as XML
* @return nothing
*/
public void writeObject(XMLEncoder e){
// need a DefaultPersistenceDelegate because of
// students and meetings
e.setPersistenceDelegate(Section.class,
new SectionPersistenceDelegate());
e.writeObject(this);
} // end writeObject XML
Thats it.
Except of course there are some import statements we need.
import java.beans.DefaultPersistenceDelegate;
import java.beans.Statement;
import java.beans.Encoder;
Yes, there is a unit test which defines the file to which the XMLEncoder is connected.
public void testXMLSerialization() {
Thread.currentThread().setContextClassLoader(getClass().get
ClassLoader());
try {
XMLEncoder e = new XMLEncoder(
new BufferedOutputStream(
239
new FileOutputStream("student.xml")));
// create a Section object
// and save it
s.writeObject(e);
e.flush();
e.close();
}
catch (FileNotFoundException fe) {
System.err.println(fe.toString());
}
}
Now we can write a Section object to an XML file, sometimes called an archive.
If you wish to see the XML file produced by the writeObject method, simply
navigate to the folder or directory in which it was placed (the default is the one in which
your project is located) and open it with a text editor. In a Microsoft Windows
environment, right-click the file and choose Open to open the file in the XML Editor
which Microsoft supplies, really Internet Explorer in disguise.
The file we have just created contains many lines, but is not that complicated. Here is
sample output, describing a section containing four students, meeting twice a week.
240
- <void property="endMonth">
<int>11</int>
</void>
- <void method="addStudent">
<string>3</string>
</void>
- <void method="addStudent">
<string>2</string>
</void>
- <void method="addStudent">
<string>1</string>
</void>
- <void method="addStudent">
<string>4</string>
</void>
- <void method="addMeeting">
- <object class="Meeting">
- <void property="dayOfWeek">
<int>1</int>
</void>
- <void property="endTime">
<int>1250</int>
</void>
- <void property="roomNumber">
<string>L322</string>
</void>
- <void property="startTime">
<int>1200</int>
</void>
</object>
</void>
- <void method="addMeeting">
- <object class="Meeting">
- <void property="dayOfWeek">
<int>3</int>
</void>
- <void property="endTime">
<int>1250</int>
</void>
- <void property="roomNumber">
<string>L322</string>
</void>
- <void property="startTime">
<int>1100</int>
</void>
</object>
</void>
</object>
</java>
241
You can simplify the display somewhat (when you are using the XML Editor) by clicking
the minus sign beside each line that begins <object class =
This gives the following file.
<?xml version=1.0 encoding=UTF-8 ?>
- <java version=1.5.0_01 class=java.beans.XMLEncoder>
+ <object class=Section>
</java>
The plus and minus signs are from the editor. They tell us that the file contains an object
of type Section and nothing else, unlike when we were using object serialization. To
see more details about the instance variables of an object, click the plus sign. To suppress
the details, click the minus sign which replaces the plus sign.
242
}
The statement
Section s = (Section) d.readObject();
implements the instructions we placed in the archive to create a temporary object of the
appropriate type and we copy that temporary object into our object.
While copying elements from the temporary copy into our object, we ensure the object
contains its own elements, not just references to the elements of the temporary copy.
Thus we need to use new String for Strings, and the clone method for the
Meeting objects. If you have not implemented the clone method for Meeting, now
would be a good time to do so.
The other instance variables are numbers, primitive datatypes. We simply use an
assignment statement for them.
The code above shows how to deal with collections which are lists or sets. What about
maps? One possible solution is to save each key-value pair, and then reconstruct the map.
The Map.Entry interface provides an easy way to retrieve the key-value pairs.
Programming Style
I have adopted the practice of having readObject (having an XMLDecoder as a
parameter) and writeObject (having an XLEncoder object as a parameter) methods
in all my classes. The corresponding unit tests are called testXMLSerialization
and testXMLDeserialization.
Similarly, I have readObject (having an ObjectInputStream as a parameter) and
writeObject (having an ObjectOutputStream as a parameter) methods in all my
classes. The corresponding unit tests are called testObjectSerialization and
testObjectDeserialization.
As these methods have different signatures, Java does not get confused. I find it useful to
use the similar names for similar methods.
243
Summary
This chapter covered what is perhaps the most-advanced topic in this book. But it is a
topic which will become more and more important as the role of XML increases.
Now that we know how to create classes, and how to persist them, it is time to look at
how we allow the users to provide the data to create classes. In particular, we need to see
how to build data entry screens.
244
Exercises
1.
Implement the clone method in the Student and Meeting class. You will
need it for the Student class when you use XML to persist the College class.
2.
Check your knowledge of XML serialization by using it with some of the other
classes we have in our college model.
Remember that we have about 10 different classes from which you can choose. Of
course, if this were a real model, you would need to implement XML serialization
for all of the classes.
3.
4.
5.
6.
7.
Explore the topic of using databases to persist Java objects. A good place to begin
is http://java.sun.com/javase/technologies/database/
245
246
Introduction
You may have noticed that the way we have been running our program is not the way
you normally run a program. While BlueJ uses windows and buttons, the code we have
been creating has no windows, no buttons, and no labels. We need to develop a way in
which the program can present a prettier face to the world.
We need to explore the world of the graphical user interface. An alternative name which
is becoming common is user experience.
247
GUI
A graphical user interface (usually abbreviated GUI and pronounced gooey) is one way
by which we can enter data that exists in the real world into the program we have created,
which models that real world.
The major parts of a GUI are:
a frame which contains all the pieces of the interface,
panels, which divide the interface into logical sections,
labels, which identify data you should provide,
textboxes, which provide a space where you provide data,
buttons, which provide a way to enter commands using a mouse or other pointing
device,
radio buttons, which allow you to make a single choice from a collection,
checkboxes, which allow you to make zero or more choices from a collection, and
a layout manager, which helps you position all the parts of the interface.
These parts are standard (although the terminology may change somewhat) regardless of
the language you are using to create the GUI.
GUIs are built using a toolkit of predefined widgets. Widget is a generic term, with
several meanings. When I looked up the word in Wikipedia (on 2006-05-23), I found two
appropriate meanings. A placeholder name for any unspecified device or good and a
component of a graphical user interface that the user interacts with. The two names are
actually related; rather than list all the components of the interface, you might simply call
them widgets.
The toolkit you use to build a GUI provides the widgets, but it also provides a look and
feel. That is, it provides a specific style of widget. Windows programs have a different
look and feel from those designed to run on a Macintosh. Even within Windows, some
progress bars have square ends while others have rounded ends, and different versions of
Windows have a different look and feel.
The toolkit we will use is called Swing, and is available in the javax.swing package.
To quote from the documentation, this package Provides a set of "lightweight" (all-Java
language) components that, to the maximum degree possible, work the same on all
platforms. An earlier toolkit contained widgets which used code that was dependent on
the processor you were using.
The widgets mentioned above are called JFrame, JLabel, JTextField, JButton,
JCheckBox, and JRadioButton.
In addition to these widgets we will use JApplet, JMenuBar, JMenu, JMenuItem,
and JPanel.
248
The widgets in Swing all have names beginning with the letter J.
Most Western cultures read from left to right, and top to bottom. That means that
the important information someone must enter should be at the top, and any
buttons should be at the bottom. It also means that those rules dont apply when
your market is a culture that is not Western. It also means that a product designed
for international use should be customizable.
A cluttered screen is not good; it confuses people. Your mother was right,
neatness counts.
Balance is good. The left and right sides of a screen should be balanced with
approximately the same number of visible widgets and the same amount of
whitespace (the background) on each. Its okay when the top of the screen
contains more than the bottom.
Menus, which we will explore in the next chapter, should use standard terms, and
they should be in standard places. Commonly, File is the first item on a menu,
Help is the last.
Java is designed to run on many different operating systems, so the GUI for your
program should respect that. Dont present an interface suitable for a Macintosh
unless the computer actually is a Macintosh.
Colours are nice, but garish is not nice. Many people, particularly men, are
colour-blind, so pay attention to the colours you use. When you are colour-blind,
you may not see colours the same way others see them. A common problem is an
inability to distinguish between red and green. The Wikipedia article on color
blindness goes into a great deal of detail on this subject.
249
Layout managers
Some of the rules above affect the positioning of widgets within your frame. A layout
manager helps us to follow these rules. What is a layout manager?
To once again quote the Java tutorial, a layout manager [d]efines the interface for
classes that know how to lay out Containers. A container is something (like a JPanel
or a JFrame) into which you can place widgets, including other containers.
There are many different layout managers, some very simple and some very complicated.
FlowLayout is one of the simpler layout managers. It places its widgets in left-to-right
(or right-to-left, should you choose) order. This can be very useful when you are placing
a series of buttons side-by-side. It can be useful if you are placing checkboxes beside
each other. While this layout manager is often used for widgets in a single line, it will
also serve for multi-line layouts. If there are too many widgets on one line, the next
widget will simply continue onto the next one. Of course, if you resize the container,
widgets may move from one line to another. This could be confusing for many of the
people using your GUI. Thus we will use FlowLayout only in special situations.
BorderLayout breaks its container into five regions, North, South, East, West, and
Center, and allows you to place widgets in each. This can be useful; place the buttons on
your screen in the South region, for example.
GridLayout might be useful should you be creating a Minesweeper-clone, a
calculator, a telephone, or a chess game. We wont be using it here.
GridBagLayout is useful when you are creating a more complicated screen, but it is
very complicated to use. Ideally, you will find a tool which lets you draw the screen and
then creates the appropriate Java code for you. This is considered the most complicated
layout manager.
I prefer a layout manager which lets you place widgets in relation to one another, or at
fixed percentages from the left and top of the surrounding container. Java provides a
couple of choices.
GroupLayout is described in the Java tutorial as a layout manager that was
developed for use by GUI builder tools, but it can also be used manually.
Yes, there are builder tools which will assist us in laying out a GUI. However, we wont
use them here. The experience of laying out a GUI yourself is something everyone should
do at least once, if only to understand the underlying principles.
Java also provides the SpringLayout class, but the documentation for this class
clearly advises against writing code using SpringLayout. Due to the painstaking
250
nature of writing layout code by hand, it is strongly recommended that you use the
SpringLayout layout manager combined with a builder tool to lay out your GUI.
One of the strengths of Java is that you can add new features to it, simply by providing a
new library which implements that feature. Thats why there are so many layout
managers; everyone has his or her own idea of the way it should be done. One layout
manager, which matches the way I think but is not supplied by Sun, is
RelativeLayout, described several years ago at
http://www.onjava.com/pub/a/onjava/2002/09/18/relativelayout.html?page=1 A more
detailed explanation about how this layout manager works is at
http://www.onjava.com/pub/a/onjava/2002/11/27/layout2.html
Like SpringLayout, GroupLayout, and GridBagLayout, using this layout
manager requires painstaking attention to detail.
There are several things wrong with this illustration. The first is that there is probably too
much space between a label and its textfield. Can you spot the other errors? There at least
two.
Import statements
To use the RelativeLayout layout manager, here is the import statement.
import com.brunchboy.util.swing.relativelayout.*;
This provides the layout manager and its associated components.
To use Swing components, here are the additional import statements we need.
import javax.swing.*;
import java.awt.event.*;
252
The first provides the widgets we will be using. These are better widgets than those in
the Abstract Window Toolkit, or AWT.
The second provides the AWT events to which we can respond. More details on events
will be given later.
253
254
myDateInput.getContentPane().add(dayText, "dayText");
myDateLayout.addConstraint("dayText",
AttributeType.LEFT, yearTextLeft);
myDateLayout.addConstraint("dayText",
AttributeType.TOP, dayLabelTop);
// done
return myDateInput;
}
255
The JFrame
Lets begin with the creation of the JFrame.
final JFrame myDateInput = new JFrame("Enter MyDate");
You have seen frames whenever you have used a computer with a windowing system.
The Java tutorial says A frame, implemented as an instance of the JFrame class, is a
window that typically has decorations such as a border, a title, and buttons for closing
and iconifying the window. Applications with a GUI typically use at least one frame.
The JFrame we create has the title Enter MyDate.
myDateInput.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
When you click the X button in the top right, the JFrame will hide itself. Thus it will be
available quickly should we need it again.
The labels
Continuing through the method, we begin to create and place widgets, starting with a
label. For each piece of data we wish to enter, there should be a label.
JLabel yearLabel = new JLabel("Year - four digits");
myDateInput.getContentPane().add(yearLabel, "year");
The way in which we create and place widgets is the same.
256
o
o
o
o
create a widget,
add it to a container (usually a JFrame or a JPanel),
create the constraints related to the position of the widget, and
add those constraints to the containers layout manager.
In the code above, we create a JLabel containing the words Year four digits and
then add it to the content pane of the JFrame. As noted above, we could have added it to
the JFrame directly.
Note the way in which we add a widget to the content pane. We provide the name of the
widget being added, and then we specify a String which gives the widget a logical
name.
This logical name is used to associate the constraints with the correct widget. As youll
see below, when we add a constraint to the layout manager, we dont specify the name of
the widget; we specify its logical name instead.
It happens that the characters of the logical name are similar to the characters of the name
of the widget because we are using the English language. But the logical names could be
any appropriate name, provided the names are unique. No two widgets in a frame can
have the same name. You may wish to use these logical names when you specify the
ActionCommand for widgets. ActionCommands are discussed below.
If you had only three widgets I suppose you could name them Huey, Dewey, and Louie,
after Donald Ducks nephews. (Perhaps its time for a joke about getting your ducks in a
row.) If you had many widgets, I suppose you could name them after the letters in the
Greek alphabet (alpha, beta, gamma, delta, and so on.) But neither naming strategy is a
good one.
constant. From this name we realise we are accessing a static member of the class.) We
use this first JLabel as the anchor and then position other widgets relative to it.
The first constraint positions the left edge of the JLabel 20 pixels from the left edge of
the JFrame. The second constraint positions the JLabel 30 pixels down from the top.
The upper left corner of the JFrame is the origin (or 0, 0) in this measurement system.
Any positive number represents measurement to the right or down, depending on the
direction of motion. Similarly any negative number represents measurement to the left or
up. Also, note that we are positioning the widget in an absolute position; it may be better
to position it, say, 10% of the way down the JFrame. Well look at how to accomplish
that later.
We could create constraints describing the height and width of the JLabel but there is
no need to do so. JLabels have a default height, and their width expands to display the
contents.
myDateLayout.addConstraint("year",
AttributeType.LEFT, yearLabelLeft);
myDateLayout.addConstraint("year",
AttributeType.TOP, yearLabelTop);
We have created the two constraints so we add them to the layout manager. When the
JFrame is made visible, the layout manager will process all the constraints and position
the widgets properly.
Now we have our first widget anchored relative t the frame. We can place the other
widgets relative to the anchor.
258
We need to create a constraint describing its top edge, saying, in this case, that it is 50
pixels down from the top of the year JLabel.
We do not need to create a constraint describing its left edge as we have decided that the
left edges of the JLabels will line up and we already have a constraint for the left edge
of a JLabel.
The layout manager will tell us, via IllegalStateExceptions, whenever we
provide too little information about the positioning of a widget. This problem usually
comes when we are sloppy in our cutting and pasting, and dont change all the logical
names we need to change, thus leaving one of the constraints attached to the wrong
widget.
The code to create the third JLabel is almost identical to the code for the second, so is
omitted here.
Textfields
Once we have the JLabels in position, we deal with the JTextFields, in which you
will enter data.
final JTextField yearText = new JTextField(4);
We create the JTextField, giving it enough space to accommodate four characters.
This width of four characters is based on the average width of the characters in the font
you are using; after all, a W is wider than an I. Thus the actual width (in pixels) may
vary depending on the typeface and point size you are using; a font is the combination of
typeface and point size. In the same way, the width of a label is determined by the
typeface and point size.
myDateInput.getContentPane().add(yearText, "yearText");
We add the JTextField to the content pane
AttributeConstraint yearTextLeft = new
AttributeConstraint(DependencyManager.ROOT_NAME,
AttributeType.RIGHT, -150);
We create a constraint to describe the left edge of the JTextField. We define the left
edge as 150 pixels to the left of the right edge of the JFrame. Since we are measuring
distances increasing to the right and decreasing to the left, we provide a negative number.
myDateLayout.addConstraint("yearText",
AttributeType.LEFT, yearTextLeft);
259
myDateLayout.addConstraint("yearText",
AttributeType.TOP, yearLabelTop);
And then we use an existing constraint to ensure the top of the JTextField is even
with the top of its corresponding JLabel.
Other textfields
Similarly, we place the other two JTextFields on the content pane. Note that the
statements to create their constraints are simpler as we are reusing existing constraints,
yearTextLeft for the left edge and the appropriate JLabel top constraint.
Once we have created the JFrame, we return it to the calling method.
260
Make it visible
box.setVisible(true);
And then we make it visible. There may be times when you wish to make it invisible, as
something else becomes visible. Simply change the parameter to false.
Keep it visible
while (box.isVisible()) {}
The while loop simply ensures the JFrame remains visible until we click the X in the
top right corner to dismiss the frame.
Exit
System.exit(0);
And when we click the X, the frame disappears and the program exits gracefully.
yearText.setToolTipText
("Please enter the year - four digits");
How do we use hot-keys to move to a specific JTextField?
Sometimes, you have many widgets on the screen and you wish to move to a specific
one, not by tabbing through the others, not by taking your hands off the keyboard to click
the one you want. You just want to press a combination of keys which will take you to
the correct widget.
Hot-keys allow you to do this.
We first specify the mnemonic, the letter in the JLabel caption which is to be
underlined (if your interface underlines mnemonics) and which will function as a hotkey. Here we assign Y as the mnemonic representing year. The documentation of the
KeyEvent class lists all the mnemonics you may use.
yearLabel.setDisplayedMnemonic(KeyEvent.VK_Y);
Then specify the JTextField with which the JLabel is associated. When you press
and the mnemonic, the cursor will move to the JTextField.
yearLabel.setLabelFor(yearText);
How do I start the frame full-screen? Minimized?
In general, I feel it is not a good idea to start a frame full-size. Its rude, intruding on,
indeed obscuring, whatever a person is doing. Should you wish to ignore this advice (and
many programs do), import the java.awt.Frame class and then use this statement.
box.setExtendedState(Frame.MAXIMIZED_BOTH);
If you wish the frame to start minimized, import the java.awt.Frame class if you
have not already done so, and then use the statement
box.setExtendedState(Frame.ICONIFIED);
But these are minor questions. There are two biggies what about buttons (did you notice
they were missing from the frame we built?), and what about editing the data provided?
Buttons
263
The JFrame we created is quite unusual. It has JLabels and JTextFields, but it has
no JButtons. We would expect at least an OK button and a Cancel button.
Cancel button
Lets create the Cancel button first. Note the idiom we have seen throughout this section.
o create a widget,
o add it to a container,
o create constraints for the position of the widget, and
o add the constraints to the layout manager of the container.
264
Create a mnemonic
Buttons have mnemonics too, in this case the letter C.
cancelButton.setMnemonic(KeyEvent.VK_C);
Button action
What should happen when we click the Cancel button (or press
)? The same actions
as when we click the X button to exit the JFrame. How we make this happen is shown
below.
The OK Button
We use similar statements to create an OK button. Place it to the left of the Cancel
button.
265
ActionCommand
To handle these ActionEvents, we need to associate an ActionCommand with each
widget that can create an ActionEvent. We will use a widgets logical name (required
by RelativeLayout) as the ActionCommand.
okayButton.setActionCommand("okay");
cancelButton.setActionCommand("cancel");
If we have one ActionListener that responds to many events, it may be looking at
the text in these ActionCommands to determine the source of the ActionEvent.
Creating an ActionListener
We need to create an ActionListener, an object which processes ActionEvents.
There are a number of ways in which to do this; here is the simplest.
ActionListener actionListener = new ActionListener () {
public void actionPerformed(ActionEvent e) {
if ("cancel".equals(e.getActionCommand())) {
System.err.println("cancel");
266
} else {
System.err.println("okay");
}
}
};
This ActionListener simply scans through the ActionCommands associated with
the widgets, seeking the widget which caused the event. In a few moments we will see
how to create individual ActionListeners for each widget.
Have you noticed that we have used the logical name for each widget several times? We
used it when we added the widget to the container. We used it when we were adding the
constraints to the layout manager. We used it again when we wrote the
ActionListener. Perhaps it would be a good idea if we used a constant for the
logical name. That way we would need to change it only once, if we needed to change it
at all.
Programming Style
Should you wish, you could create a separate ActionListener for each JButton,
rather than using the same one and having to check for the specific ActionCommand.
This is probably a better idea, as it allows several programmers to develop code in
parallel.
Well come back to this idea at a later time.
267
A BlueJ Terminal window appears with the word okay or cancel, depending on the
JButton you clicked. If you continue clicking the JButtons, the words will continue
to appear.
So far, clicking a JButton simply displays a message. For a nicer message, replace the
System.err.println statement with
JOptionPane.showMessageDialog(myDateInput,
"Clicked okay.", "Okay",
JOptionPane.INFORMATION_MESSAGE);
This produces an informational message, shown below.
o
o
o
o
Note the different colours or shading in the titles of the two frames. The Okay dialog is
modal; you must click the OK button within it before you can continue and do anything
else with this application.
268
Editing data
There are two times as you are entering data when it may be appropriate to test it is
reasonable. One is as it is being entered, the other is when an Okay button is clicked.
Why do we need both times? Consider the MyDateEntry frame.
We can check that the JTextFields contain only numbers.
We can not check that the month JTextField contains only the numbers from 1
through 12 until the JTextField is complete.
We can not check that the day JTextField is appropriate to the month (30 days hath
September ) until all three JTextFields (year, month and day) have been provided,
and we do not know the order in which the data will be provided.
Modern authors suggest that you not intercept keystrokes as they are typed, but you
verify the contents of a JTextField as you attempt to leave it. This is done with an
InputVerifier.
269
270
271
if (screenDay < 1) {
// regardless of the month, 0 or a negative
// number is a bad day
JOptionPane.showMessageDialog(myDateInput,
"Day number is too small.", "Bad day",
JOptionPane.ERROR_MESSAGE);
return false;
}
// if the year, month, and day have been
// specified check maximum value for day
int screenYear = 0;
int screenMonth = 0;
try {
// do we have a year?
screenYear =
(new Integer(yearText.getText())).
intValue();
// do we have a month?
screenMonth =
(new Integer(monthText.getText())).
intValue();
}
catch (Exception e) {
// some part of the date is missing.
// do no more day editing
// screenYear or screenMonth is zero, so
// the following if statement will not be
// executed
}
if ((screenYear > 0) && (screenMonth > 0)) {
int maxDays = 31;
if (screenMonth == 2)
if (isLeapYear(screenYear))
// isLeapYear follows this method
maxDays = 29;
else
maxDays = 28;
if ((screenMonth == 9) ||
(screenMonth == 4) ||
(screenMonth == 6) ||
(screenMonth == 11))
maxDays = 30;
// now we know the maximum number of
// days in the month
if (screenDay > maxDays) {
// too many days
JOptionPane.showMessageDialog(
272
myDateInput,
"Too many days in the month.",
"Bad day",
JOptionPane.ERROR_MESSAGE);
return false;
} // end too many
} // end year and month provided
} // end try
catch (Exception e) {
JOptionPane.showMessageDialog(myDateInput,
"Day number contains non-numeric characters.",
"Bad day", JOptionPane.ERROR_MESSAGE);
return false;
} // end non-numeric
return true;
} // end verify
}; // end verifierDay
The logic verifying the day is similar to that verifying the year and month (Is there
anything provided? If so, is it numeric? If so, is it appropriate?) but the logic for checking
the upper limit is a little more complicated since it depends on the year and the month.
First, this method checks that a year and month have been provided.
If so, it determines the correct number of days in the month and compares the value
provided to that number.
Note the isLeapYear method.
/**
* calculate if a year is a leap year
* @return true if so, false if not
*/
public static boolean isLeapYear(int year) {
return (new GregorianCalendar()).isLeapYear(year);
}
Rather than writing yet another leap year routine, embodying the logic necessary to
decide when a year is a leap year (see, for example,
http://en.wikipedia.org/wiki/Leap_year), I have chosen to use the
GregorianCalendar class and its isLeapYear method. Dont forget the import
statement.
import java.util.GregorianCalendar;
Of course, we attach this InputIdentifier to its widget.
273
dayText.setInputVerifier(verifierDay);
Summary
In this chapter, we have focussed on the basics of creating data entry screens, and have
seen how to create one simple data entry screen.
In the next chapter, we will see how to create more complicated data entry screens.
Section has some interesting aspects, so well examine its data entry screen next.
274
Exercises
1.
2.
Create a data entry screen for the Student class, collecting personal information,
but not registration information.
3.
Create a data entry screen for the Professor class, collecting personal
information but nothing on the sections and courses the professor teaches.
4.
Create a data entry screen for the Course class, but not collecting any information
about the sections of the courses which are offered.
5.
Create a data entry screen for the Meeting class, but not associating it with a
section just yet.
6.
Create a data entry screen for the Mark class, but not associating it with a student
and course just yet.
7.
8.
Create a data entry screen for the College class. It will allow you to enter its
address and website, but nothing about employees, students, or courses.
9.
Explore the subject of look and feel. What options do you have with the Swing
toolkit? What are the differences between the options?
10.
What are some of the other layout managers you can use? When would you use
each? Could you use any of them to create the data entry screens we have seen in
this chapter?
11.
Use the GridBagLayout manager to recreate one of the data entry screens we
have created in this chapter. Which layout manager do you prefer?
12.
13.
14.
275
15.
276
Introduction
The data entry screen we created in the previous chapter is relatively simple. It contains
some labels, some textfields, and some buttons. But more complicated data entry screens
are very common. They include panels to separate portions of the frame. They include
checkboxes and radio buttons. They include lists of items.
Lets see how we create these data entry screens.
277
Lets examine parts of the method necessary to build this screen, a few statements at a
time.
279
verticalBox.add(top);
verticalBox.add(middle);
verticalBox.add(bottom);
Notice that we have created the three JPanels and placed them within the
VerticalBox, but we have not yet specified what the JPanels contain. Well do that
in a moment, starting with the top JPanel.
280
281
Labels
Note that there is a row of labels. For each label, we follow our usual practice.
282
First create a widget and add it to its container. Then create the constraints (a minimum
of two, when using RelativeLayout) for a widget, and add the constraints to the
containers layout manager.
In this case, the top edge of all the labels is the same, so we create a constraint for that,
and then use it for each label.
AxisConstraint labelTop = new AxisConstraint(
DependencyManager.ROOT_NAME,
AttributeAxis.VERTICAL, 0.10);
JLabel dayLabel = new JLabel("Day of the week");
middle.add(dayLabel, "day");
middleLayout.addConstraint("day",
AttributeType.TOP, labelTop);
AxisConstraint dayLeft = new AxisConstraint(
DependencyManager.ROOT_NAME,
AttributeAxis.HORIZONTAL, 0.10);
middleLayout.addConstraint("day",
AttributeType.LEFT, dayLeft);
283
The meetings
The labels are followed by five similar rows for the different meetings of the section.
This limit of five rows is because we made a design decision that a Section would
meet no more than five times a week.
284
For each meeting of the section (for each of the five rows), we need seven
JRadioButtons (corresponding to the days of the week), and three JTextFields
(the start and end times of the meeting, and the room in which it will take place.) Well
see how to do the checkboxes in a moment.
So we have five rows of widgets, each row of which appears identical. Intuition (another
name for learning from past mistakes) tells me that a good way to do this is to use a data
structure of some kind, perhaps an array or a Vector or a List, with each element of
the collection corresponding to one row on the data entry screen. Continuing with
intuition, we realise there are seven radio buttons. They could make a collection too.
Since the data structure or collection is quite simple and there is no real processing of the
elements in the collection, we can use an array. To do that, we should examine arrays
more fully before we continue.
Arrays
We have used collections before. A Student object contains a collection of Mark
objects. A Course object contains a number of Section objects. A Section object
contains a number of student identifiers.
Generally we needed to go through all the elements in the collection, starting at the first
and ending at the last. But what if we need to access the elements of the collection
randomly? That is, we want the fourth element, then the eighth, then the first.
It would be very inefficient (slow) to have to process all preceding elements in the
collection to retrieves a specific one. Fortunately, there is a very traditional data structure
which supports random access. This is the array.
An array has two parts. The first is the collection of data items. The second is the
subscript or index, a number used to indicate which element of the collection we wish.
Suppose we have a collection of Student objects, which we wish to store as an array
because we need to access the Student objects randomly. Consider the statement
Student[] theStudents = new Student[50];
This array, whose name is theStudents, contains space for 50 Student objects. The
objects are numbered zero through 49. The index always starts at zero. In memory, these
50 objects are stored next to each other.
When we wish to retrieve the last Student object we retrieve theStudents[49]. If
we wish to retrieve the first Student object, we retrieve theStudents[0].
285
When we wish to retrieve all the Student objects, we use a for statement.
for (int i = 0; i < 50; i++) {
// do something with theStudents[i]
}
But why stop at an array of Student objects? We can have an array of anything,
primitive datatypes like int or char (Did you realise that you can think of a String
as an array of char? You probably did since we inspected a String and saw its array
of char.), or an array of arrays.
An array of arrays? Yes, each element of one array is itself a second array. You have
probably seen this structure often.
The board on which you play chess or checkers is a two-dimensional array. The rows are
elements of an array; but each row contains an array, each element of which is a column.
Each sheet of a workbook (a spreadsheet) is a two-dimensional array, whose elements are
much more complicated than those of a chess board.
That is, these are arrays in which you can think of each array element consisting of an
array.
Were we to create a variable to represent a chessboard, we might use something like this.
int [][] chessboard = new int [8][8];
The model of a chessboard and its pieces could represent an empty cell with a zero, a cell
containing a white piece with a negative number (different numbers correspond to
different pieces), and a cell containing a black piece with a positive number.
If you stretch your mind you can imagine ragged arrays, in which each row may not have
the same number of cells. Imagine this histogram turned on its side.
286
So, now that we know a little more about arrays, lets continue building the data entry
screen.
ButtonGroup
Note that radio buttons occur in groups. When you select one button in a group, all the
others are deselected. Thats the traditional way you choose which radio station to listen
to on your vehicle radio. When you have more than one group of radio buttons on a
screen, selecting and deselecting in one group does not influence your choices in the
other groups. We accomplish this by placing the buttons for each of the five possible
meetings in different ButtonGroups.
ButtonGroup aGroup = null;
We will see how to add JButtons to ButtonGroups in a moment.
Note that there are some applications in which choosing a button in one group may affect
the contents of another group. For example, a washing machine I recently purchased lets
you choose the type of material you are washing. Depending on your choice, you may not
be able to use hot water. The hot water choice in the temperature group is disabled.
287
An array of textfields
Similarly, we create arrays of textfields for the start and end times, and the room
numbers.
final JTextField[] startText =
new JTextField[MAX_MEETINGS];
final JTextField[] endText =
new JTextField[MAX_MEETINGS];
final JTextField[] roomText =
new JTextField[MAX_MEETINGS];
Checkboxes
In thinking about this screen, I realised it would be difficult to tell which of the five
meeting possibilities are actually being used. So I decided to place a column of
checkboxes down the right side of the screen. Whenever the data entry person chooses a
day, or a time, or a room, the appropriate checkbox will be checked by the method.
(When the data entry person unchecks a checkbox, the appropriate time and room will be
erased from the screen.) When the data entry person clicks the OK button, we will
process only those rows which have the checkbox checked. This requires another array.
final JCheckBox[] hasMeeting =
new JCheckBox[MAX_MEETINGS];
And it requires another constraint, specifying the horizontal position of the checkboxes.
AxisConstraint checkLeft = new AxisConstraint(
DependencyManager.ROOT_NAME,
AttributeAxis.HORIZONTAL, 0.95);
288
289
290
InputVerifiers
But wait a moment! We realize that we will need InputVerifiers for the textfields,
methods which check that the data we enter is reasonable.
// InputVerifier for the times
InputVerifier verifierTime = new InputVerifier() {
public boolean verify(JComponent input) {
int screenTime;
final JTextComponent source =
(JTextComponent) input;
String text = source.getText();
if (text.length() == 0) {
// no time specified
JOptionPane.showMessageDialog(
sectionInput, "Need to supply some time",
"Missing data",
JOptionPane.ERROR_MESSAGE);
return false;
}; // length = 0
try {
// is there a numeric time
screenTime =
(new Integer(text).intValue());
// and is it reasonable?
int screenHour = screenTime/100;
int screenMinutes = screenTime % 100;
if ((screenTime < 0000) ||
(screenTime > 2359) ||
(screenMinutes > 59)) {
JOptionPane.showMessageDialog
(sectionInput,
291
292
endText[i].setInputVerifier(verifierTime);
roomText[i].setInputVerifier(verifierRoom);
}
293
intValue();
startText[row].setText("");
endText[row].setText("");
roomText[row].setText("");
}
catch (Exception x) {
}
}
}
};
ActionEvents
Once we have created the radio button ActionListeners, we need to define the
messages to which the ActionListeners will respond. These messages are simple
just numbers. Since the radio buttons are represented as a two-dimensional array, we
need two for loops, one nested within the other.
// set up ActionEvents relating to JCheckBoxes
for (int i = 0; i < MAX_MEETINGS; i++){
String eventMessage = (new Integer(i)).toString();
hasMeeting[i].setActionCommand(eventMessage);
hasMeeting[i].addActionListener(
actionListenerCheck);
for (int j = 0; j < DAYS_IN_WEEK; j++) {
aRadioButton[i][j].setActionCommand
(eventMessage);
aRadioButton[i][j].addActionListener
(actionListenerRadioButton);
}
}
294
Button ActionListeners
Of course, buttons need ActionListeners, attached to the appropriate buttons.
ActionListener actionListenerOkay =
new ActionListener () {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(sectionInput,
"Clicked okay.", "Okay",
JOptionPane.INFORMATION_MESSAGE);
}
};
ActionListener actionListenerCancel =
new ActionListener () {
public void actionPerformed(ActionEvent e) {
sectionInput.setVisible(false);
}
295
};
okayButton.addActionListener(actionListenerOkay);
cancelButton.addActionListener(
actionListenerCancel);
Done!
And then the screen is complete.
// done
return sectionInput;
}
Create a method to display it. Yes, this is exactly the same technique we used to display
the MyDate data entry screen.
/**
* show the Frame
*/
public static void showDataEntryFrame() {
// Show the data entry frame
JFrame box = buildDataEntryFrame();
// Specify the initial location (first two
// parameters and size (second two parameters)
// of the frame
box.setBounds(100, 100, 500, 300);
box.pack();
// and show it on the screen.
box.setVisible(true);
while (box.isVisible()) {}
System.exit(0);
}
Test it.
Modify it as you think appropriate. Some companies have design standards to which you
must adhere, but for now, you may customise the data entry screen to suit your
preference.
296
297
Attempt to leave the data entry screen without entering any data. A person might do this
when there were many data entry screens and the person accidentally chose the wrong
one. If you choose the X in the top-right corner, you are able to leave the data entry
screen with no problem. But if you choose the cancel button, an error message appears.
Why does this happen? More importantly, how can you correct it? Hint, consider this
statement.
cancelButton.setVerifyInputWhenFocusTarget(false);
How is that for the name of a method? What does that method do?
Heres another problem: assume you have entered 2009/02/30 as the date on the
Meeting data entry screen you created in the previous chapter. You meant to enter
2009/03/30. As you leave the day JTextField you will receive an error message. You
acknowledge it, and click in the month JTextField. But the day error message
reappears. The only way to leave the day JTextField is to provide a valid day within
February 2009. Then you may change the month to March, and return to the day field to
enter the number 30. Why does this happen? How can you avoid it?
Similarly, suppose your Section data entry screen checks that the end time is after the
start time, but you entered the start time incorrectly. How can you fix it? You may be
forced to change a time which is correct and then change it back. For example, a start
time of 1130 with an end time of 1120, when you meant to enter 1030 and 1120.
298
Exercises
1.
Combining the code for placement into a group with the placement on the screen
may make for shorter code, but does it make for better code?
2.
3.
Use the GridBagLayout manager to recreate one of the data entry screens we
have created in this chapter. Which layout manager do you prefer?
4.
5.
In previous chapters we discussed modelling a bank account. Assume that this has
grown into a system which has many accounts. Develop a screen which allows you
to display details on a specific account, whose number you must provide.
6.
7.
299
300
Introduction
Having data entry screens is fine, but how do you access them?
In modern computer applications, you often have a menu system that allows you to
access the various parts of your system. In this chapter, we will focus on creating a menu
system. In particular, BlueJ has menus, and BlueJ is a Java application. How did the
developers make those menus?
301
302
303
JMenuItem menuItem;
And then we create the choices.
// create JMenuItems for fileMenu
menuItem = new JMenuItem("New", KeyEvent.VK_N);
menuItem.getAccessibleContext().
setAccessibleDescription
("Create a new file");
fileMenu.add(menuItem);
// repeat for other JMenuItems (see below)
// repeat for other JMenus (see below)
// done
return bar;
}
As we did with the data entry screens, the method above creates the menu bar but does
not display it. We need another method which will create the frame into which we can
place the menu.
/**
* Create the GUI and show it.
*/
public static void createAndShowGUI() {
// provide a frame in which to display the menu
JFrame frame = new JFrame("COSC 111/121 Example);
frame.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
// create the menu system
CollegeMenu demo = new CollegeMenu();
frame.setJMenuBar(demo.createMenuBar());
//Display the window.
frame.setSize(450, 260);
frame.setVisible(true);
}
}
The code as shown will work, but it is incomplete.
To test the menu system, compile the CollegeMenu class and then execute its
createAndShowGUI method.
304
Accessibility
We have used some new methods here, relating to accessibility.
Accessibility refers to making computers easier to use by those with different types of
sensory challenges. In this case, we are providing access for those with serious vision
problems, who may use a screen reader to identify the mouse location and the text on
which it is located. An alternative to using the
getAccessibleContext().setAccessibleDescription method would be
to use tooltips for those who dont use a screen reader. Tooltips appear when you hover
your mouse over a choice but do not click a button on the mouse; we saw them in an
earlier chapter. An example is
menuItem.setToolTipText("Create a new file");
To accommodate all users, you should use both AccessibleDescriptions and
ToolTips.
305
Performing actions
Some of the choices on a menu simply lead to other choices. Others, however, lead to
data entry screens, or query screens (but we dont have any of those yet). How do we
invoke methods to do these actions?
We use ActionListeners again. We have seen where the frame implements the
ActionListener and we have seen where each widget implements the
ActionListener.
Herer the CollegeMenu class itself will implement the ActionListener interface.
public class CollegeMenu implements ActionListener
Each menuItem variable must register itself with the listener.
menuItem.addActionListener(this);
The word this refers to the current object, the menu itself, which is an
ActionListener, since it implements the ActionListener interface.
We use the setActionCommand method to identify each menu choice. For example,
menuItem.setActionCommand("exit");
The listener uses the ActionPerformed method to identify which menu selection is
made. This is a simple version, listening only for the Exit and About menuItems.
public void actionPerformed(ActionEvent e) {
JMenuItem source = (JMenuItem)(e.getSource());
if (source.getText().equals("Exit")) {
System.exit(0);
}
if (source.getText().equals("about")) {
// display message box
JOptionPane.showMessageDialog(null,
"CollegeMenu - demo program for COSC 111/121",
"About", JOptionPane.INFORMATION_MESSAGE);
}
// do something for each other possibility
return;
};
306
The getText method allows us to identify which menu item has been chosen, by
returning the word which it displays. Should you be planning to internationalise this
system, so that the menu selections will appear in other languages, be prepared to change
this method. It will either have to recognise the other languages.
Would it be better after all to have separate listeners for each menu item? What changes
would you need to make to your menu to make this possible?
Help
Implementing a help system is a complicated task, since it should be able to answer any
questions that the user has about your program. Rather than going into great detail about
implementing a help system, Ill refer you to the website for JavaHelp. This is described
as an open source software, a full-featured, platform-independent, extensible help
system that enables you to incorporate online help in applets, components, applications,
operating systems, and devices.
For more details on JavaHelp, see
http://java.sun.com/javase/technologies/desktop/javahelp/
Summary
There is still a lot of work to do to make this system a complete, working system, but
there are other ideas we should pursue. You have seen the basics of how to design a
program, entering data, storing data, and manipulating data. You have seen a large
amount of Java.
However, there are additional aspects of programming, and of Java, which do not fit well
into this example of a college. Thus, well set the college, its students, and its professors
aside as we consider other ideas and examples.
307
Exercises
1
2.
3.
308
Chapter 17 - Applets
Learning objectives
By the end of this chapter you will be able to:
Define applet
Use the predefined methods which applets provide
Create applets to carry out a variety of tasks
Introduction
Up to now, we have been building parts of an application as a stand-alone program which
runs on its own. The GUI is part of the application and so are all the classes.
But Java can also be used to create applets. These are small programs, used as part of a
web page. They are described in most books on Java, and online, for example at
http://java.sun.com/applets. This site includes many sample applets.
Lets see how we can create an applet.
Creating an applet
Open BlueJ and create a new project. Then create a new class in that project, but make
sure it is an applet, not a class. Traditionally, the first program students write is named
HelloWorld, so we will use that name for the first applet you write.
309
Well, it looks like a class on its class diagram, but it is clearly marked as an applet.
Running an applet
I know we have added no code to the applet, but BlueJ has created a complete, workable,
although minimal, applet for us. To see this, right-click the applet and choose Compile
the Applet, and then right-click the applet again and choose Run Applet. The window
below appears.
310
The appropriate radio button has been chosen, and the height and width are fine. Click
Ok, and the applet opens, using an application called AppletViewer.
Much of what you see comes from AppletViewer; in particular, the menu allowing you to
control the applet comes from AppletViewer. The output of the applet is two lines of text.
Terminate AppletViewer.
311
In my case, the name in the title bar was truncated. The three dots at the end of the name
(an ellipsis) indicate this truncation. Due to the font chosen and the width of the frame,
there was not enough room to display the full name. Run the applet again, but this time
make its width larger. I set the width to 600 and the complete title appeared.
Run the applet a third time, but this time choose Run applet in web browser. Then click
Ok. The applet runs, but this time in your default web browser. In my case, it started on a
new tab in Firefox, as Firefox was already running. If the browser was not running, it
would have started.
How does the applet do its work? Lets open it in the editor and see.
313
/**
* Returns information about this applet.
* An applet should override this method to
* return a String containing information about
* the author, version, and copyright of the JApplet.
*
* @return a String representation of
* information about this JApplet
*/
public String getAppletInfo()
{
// provide information about the applet
return "Title:
\nAuthor:
\n +
A simple applet example description. ";
}
/**
* Returns parameter information about this JApplet.
* Returns information about the parameters than
* are understood by this JApplet. An applet should
* override this method to return an array of Strings
* describing these parameters.
* Each element of the array should be a set of
314
init
The documentation BlueJ creates states it all.
/**
* Called by the browser or applet viewer to inform
* this JApplet that it has been loaded into the system.
* It is always called before the first time that the
315
start
The documentation BlueJ creates gives the purpose of the start method.
/**
* Called by the browser or applet viewer to inform
* this JApplet that it should start its execution.
* It is called after the init method and each time
* the JApplet is revisited in a Web page.
*/
Applets can not open files on the machine on which they are running without asking your
permission, but they can contact other websites to download images, files, or other data.
Should your applet do that, the code to do it is here.
Of course, this method may do nothing.
stop
The documentation BlueJ creates gives the purpose of the stop method.
/**
* Called by the browser or applet viewer to inform
* this JApplet that it should stop its execution.
* It is called when the Web page that contains this
* JApplet has been replaced by another page, and also
* just before the JApplet is to be destroyed.
*/
Perhaps the applet is playing some music and it should stop. Perhaps it is running an
animation and it should stop.
This method may do nothing.
316
paint
This is the method which does all the heavy lifting in an applet. That is, this is the method
in which you will write most of your Java statements. The documentation BlueJ creates
does not let you know this.
An applet is a graphics program. As such, it needs an area on which to draw. This area,
called a graphics context (an object of type Graphics) is provided as a parameter and
then you can issue whatever commands you need to produce the graphics output you
wish.
As you see from the sample BlueJ provides, these commands allow you to specify
colours, to draw filled rectangles (and other shapes as well), and to write text (beginning
at a specified position).
g.setColor(Color.white);
g.fillRect(0, 0, 200, 100);
This creates a white rectangle. Its upper corner is in the upper left corner of the window
(and has co-ordinates 0, 0). The rectangle has a width of 200 pixels and a height of 100
pixels. It is hard to see the rectangle, since it appears on a white background. To see the
rectangle, change it to a different colour, perhaps Color.yellow.
g.setColor(Color.black);
g.drawString("Sample Applet", 20, 20);
This changes the colour to black, and displays some text (in black). The lower left corner
of the leftmost character is 20 pixels from the left edge and 20 down from the top.
g.setColor(Color.blue);
g.drawString("created by BlueJ", 20, 40);
This changes the colour to blue and displays some more text.
To see the other methods you can use to draw, look at the online documentation of the
Graphics class. You will have the opportunity to use some of the other methods in the
exercises for this chapter.
destroy
The documentation BlueJ creates gives the purpose of the destroy method.
/**
317
getAppletInfo
The BlueJ documentation gives the purpose of the getAppletInfo method. You can
see that it is similar to the toString methods we have written in all of the classes we
have developed earlier.
/**
* Returns information about this applet.
* An applet should override this method to return a
* String containing information about the author,
* version, and copyright of the JApplet.
*
* @return a String representation of information
* about this JApplet
*/
It is only politeness that you should provide meaningful information from this method.
The default value of null is not informative.
getParameterInfo
When you run an applet, you can provide it with input. This information is provided
through parameters. You can see this in the window which appears when we choose to
run the applet.
318
This method returns a two-dimensional array (the documentation does not make this
completely clear), the elements of which are Strings.
Interpret this two-dimensional array as a one-dimensional array, each of whose elements
is an array whose three elements are the name of the parameter (element 0), the type of
the parameter (element 1), and a description of the parameter (element 2).
319
Notice the way you can declare an array and give its elements at the same time. Thus
{"firstParameter", "1-10",
"description of first parameter"}
is an array, containing three elements, all Strings, and
String paramInfo[][] = {
{"firstParameter", "1-10",
"description of first parameter"},
{"status", "boolean",
"description of second parameter"},
{"images", "url",
"description of third parameter"}
}
declares an array of three elements, each of whose elements is itself an array of three
elements. You can refer to individual elements of paramInfo by giving the individual
row and column subscripts. Thus paramInfo[1][2] is description of second
parameter. paramInfo[2][1] is url. Trying to access paramInfo[3][3] will cause
an exception ( an ArrayIndexOutOfBoundsException) to be thrown. Remember
that both row and column subscripts start at 0.
When you use AppletViewer to view the applet, you can specify those parameters. When
you run the applet from a web browser, you can specify those parameters.
But you cant specify the parameters unless you know what they are called. This method
will tell you about the parameters.
The applets we create in this chapter do not use parameters, but you will have the
opportunity to do so in the exercises. To complete those exercises, you will need to
explore the getParameter method of the Applet class.
Hello world
Since Kernighan and Ritchie published their book on C, in which the first sample
program was to display the words Hello World, beginning programmers have learned
how to do this as well.
You are definitely not a beginning programmer if you have covered the previous
chapters, but writing a Hello World program is something all programmers do.
320
To follow in the steps of those who have gone before, modify the skeleton applet to
display the message Hello World, in green text on a purple background, in 32 point text.
Oh, purple is not one of the constants in the Color class!
All colours can be constructed from a mixture of red, green, and blue. Google the words
RGB purple and you will find many sites which tell you that purple is something like
160-32-240. The colours (the firstnumber refers to the amount of red in the colour, the
second to the amount of green, and the third to the amount of blue) are measured on a
scale from 0 (none) to 255 (all). Thus 0-0-0 is black, and 255-255-255 is white. 160-32240 is interpreted by our brain as purple.
Color purple = new Color(160, 32, 240);
g.setColor(purple);
Once we have the colour we want, we create a rectangle in which we can display the text.
g.fillRect(0, 0, 200, 100);
Before we display the text, we need to create the correct font, based on the default font.
g.setFont(g.getFont().deriveFont(Font.PLAIN,32));
Then we set the color of the text, this time using a predefined value.
g.setColor(Color.green);
And we display the text.
g.drawString("Hello World", 20, 50);
Ugly, isnt it? That is, green on purple is ugly, not the Java code to create green text on a
purple background.
A problem
When I resize the applet in AppletViewer, the contents disappear. When I minimize
AppletViewer and then restore it, the contents remain.
This behaviour does not happen when I run the applet in my web browser.
Why does it happen in AppletViewer and how can you fix it?
321
Squares
You can create an abstract painting by placing a series of filled rectangles on the graphics
context.
Make it so.
Canadian flag
Or you can create your national flag. Being Canadian, Ill create a Canadian flag.
The bars and background are easy to draw. The maple leaf is the challenge. All you need
to do is identify the points on the maple leaf where the line surrounding it changes
direction and then provide that information to the drawPolygon method.
http://www.pch.gc.ca/progs/cpsc-ccsp/etiquette/images/fig3.gif provides an appropriate
image. I used a drawing program to identify the approximately 30 points I needed to
define the vertices of the polygon adequately.
As an aside, what does the world think of Canada? While looking for this image in
Microsoft Office Word 2003s library, I found images of maple syrup, blueberry pie,
hockey, skating, tuques, and Nanaimo bars, along with the maps, flags, and flowers of the
provinces, and a loon!
322
Compile the applet; Ive chosen the Canadian Flag applet. When you run the applet, dont
tell BlueJ to run it in AppletViewer or the web browser; tell BlueJ to create webpage
only; in my case, since the applet is called CanadianFlag.java, the webpage is called
CanadianFlag.html and is located in the same folder as the applet.
If you wished to create the webpage with another name, you could just rename the
automatically-created page. Or you could right-click the applet in the class diagram;
choose Run Applet, Generate web page only, and click Ok. A Save As window
appears, titled Select HTML page destination. Double-click the Applet folder name,
and then provide a name for the HTML page. I chose CanadianFlagTest and the
extension .html was appended automatically.
Here are the contents of CanadianFlag.html (or CanadianFlagTest.html).
<html>
<!-- This file automatically generated by BlueJ Java Development -->
<!-- Environment. It is regenerated automatically each time the -->
<!-- applet is run. Any manual changes made to file will be lost -->
<!-- when the applet is next run inside BlueJ. Save into a
-->
<!-- directory outside of the package directory if you want to -->
<!-- preserve this file. -->
<head>
<title>CanadianFlag Applet</title>
</head>
<body>
<h1>CanadianFlag Applet</h1>
<hr>
<applet code="CanadianFlag.class"
width=500
height=500
codebase="F:\DATA\COSC111\java\Fall2006\Applet"
archive="file:/C:/BlueJ/lib/bluejcore.jar,file:/C:/BlueJ/lib/junit.jar,file:/F:/DATA/COSC1
11/java/Fall2006/RelativeLayout/RelativeLayout.jar,file:/F:/DATA/COSC111/java/Fall2
006/Applet/"
alt="Your browser understands the <APPLET> tag but isn't running the
applet, for some reason."
>
Your browser is ignoring the <APPLET> tag!
</applet>
<hr>
</body>
</html>
But much of this is unnecessary. The simpler version of the HTML file is:
323
<html>
<head>
<title>CanadianFlag Applet</title>
</head>
<body>
<h1>CanadianFlag Applet</h1>
<hr>
<applet code="CanadianFlag.class"
width=500
height=500 >
</applet>
</body>
</html>
324
Should you wish to add some text to the webpage (not shown here), surround each
paragraph you wish to add with <p> and </p> tags. For example <p>This is the national
flag of the great country of Canada. Its national holiday is July 1, celebrating the day
Canada became a nation in 1867.</p>
Should you wish to add comments within your webpage (see the second line of the
automatically-generated webpage), comments begin with <!-- and end with -->, and may
extend over any lines if you wish. These comments will not appear when you display the
webpage.
Summary
Applets are graphics programs designed to run in a browser. Should you develop
websites, you find you use them extensively. If not, you may not use them at all.
I have included them here as many students enjoy creating visual artefacts, and because
they can be fun.
In the next chapter, we will step back from Java for a moment, and look at some of the
underpinnings of good analysis and design. We will look at patterns, solutions which
have worked for others and which you can adopt to make your own programs better.
325
Exercises
1.
If you dont specify the size, your applet should choose some reasonable value.
To accomplish all this, begin by declaring instance variables for your applet. Create
a loadParameters method and call it from the init method. Lets start with
height and width
The declarations are
private int width;
private int height;
and the loadParameters method is
private void loadParameters() {
String parameter = getParameter("width");
if (parameter != null)
326
Create a simple applet to draw some geometric shapes. You may draw circles and
ovals using the drawOval and fillOval methods. (An oval is a circle drawn
within a non-square rectangle.) You may draw squares and rectangles with the
drawRect and fillRect methods.
Other polygons are drawn using the drawPolygon and fillPolygon methods.
You will need to explore the Graphics classto use these methods.
3.
Create an applet which generates a circle in the centre of its graphics context. To
determine the centre, use something like the following statements.
Rectangle r = g.getClipBounds();
int x = (r.x + r.width)/2;
int y = (r.y + r.height)/2;
The Rectangle that ClipBounds returns has its top left corner, represented by
x and y, as 0, 0. The Rectangles width and height are the values you (or
BlueJ) specify when you run the applet. Assume for a moment that you use the
height and width that BlueJ suggests.
327
When you draw a circle, recall that you are specifying the corners of the box
surrounding it. Thus when you use the statements
int width = r.width / 4;
int height = r.height / 4;
to determine the size of the box and then use the statement
g.drawOval(x y, width, height);
to draw the circle, you will not draw a circle in the centre of the graphics context.
You will draw a circle in an invisible box, whose top left corner is at the centre of
the graphics context. To actually draw a circle whose centre is in the centre of the
graphics context, you need to use this statement.
g.drawOval(x - width / 2, y height / 2,
width, height);
If you specify that the applet runs in a non-square frame, the calculation of the
centre of the frame is the same. But the statement above will draw an oval, not a
circle. What value should you use as the diameter of the circle if we really want to
draw a circle? I would suggest the minimum of the height and width you have
calculated.
int diameter = Math.min(height, width);
int radius = diameter / 2;
g.drawOval(x - radius, y - radius,
diameter, diameter);
Make the circle move from the centre along a path whose angle you specify, in
degrees, through a parameter. This involves a little trigonometry.
When you move in a direction, any direction, there are at least two ways to do so.
You could go directly to your destination, or you could do it in two steps; move a
certain distance in one direction and move a certain distance in anther direction.
Think of a right-angled triangle. You could follow a route along the hypotenuse (the
long side), or you could follow a route along the other two sides. Both routes get
you to the same destination. They just take different amounts of time.
We can calculate the distances in the x and y directions by using the cosine and sine
functions, applied to the angle we specified. Thinking again of the right-angled
triangle, we cant determine the lengths of the sides unless we also know the length
of the hypotenuse. Consider the statements below. We have used a
loadParameters method to provide a value for angle.
// distance to move radially
328
329
These statements determine the colours to use, and draw the first circle. Then they
redraw it using the background colour (this makes it disappear), calculate the new
location of the circle and draw it there, repeating as many times as necessary.
When you run the applet, you may not see the circle move, unless you have a very
slow computer. You may see it only at its last position. How do you slow this action
down?
One solution is to use the Timer class defined in javax.swing.Timer.
Explore that class.
The circle should appear to vanish as it reaches the edge of the frame. But that
happens automatically!
4.
Create an applet which generates a small circle in the graphics context. Its centre is
specified via parameters. Make the circle move along a path whose angle you
specify through a parameter. The circle should bounce off the edge of the frame and
continue on its path.
This exercise will use many of the ideas from the previous exercise, plus you will
need to detect collisions between the circle (think of it as a ball) and the edge of the
frame (think of it as a wall.)
If the edge of the frame is smooth (and it is) the angle at which the circle
approaches the edge of the frame is the supplement of the angle at which it bounces
off. (Two angles are supplements if they total 180 degrees or radians.) Assume
that you move the ball by deltaX and deltaY, as in the previous exercise. Then,
hitting a vertical wall means changing the sign of deltaX while leaving deltaY
unchanged. Hitting a horizontal wall means changing the sign of deltaY while
leaving deltaX unchanged.
5.
6.
Create an applet which creates the flag of your country. Some flags are harder than
others due to curved shapes. If you look at the World Flag Database, at
www.flags.net, youll quickly find some of these difficult-to-draw flags. For
example, Afghanistan and Albania are probably impossible to create using Java.
Some international organizations, for example the African Union, are also difficult
to create. But the Olympic Movement flag would be easy to create.
330
Introduction
In his book on Design Patterns, Steven John Metsker states that A pattern is a way of
doing something, or a way of pursuing an intent. This idea applies to cooking, making
fireworks, developing software, and to any other craft. In any craft that is mature or that
is starting to mature, you can find common, effective methods for achieving aims and
solving problems in various contexts. The community of people who practice a craft
usually invent jargon that helps them talk about their craft. This jargon often refers to
patterns, or standardized ways of achieving certain aims. Writers document these
patterns, helping to standardize the jargon. Writers also ensure that the accumulated
wisdom of a craft is available to future generations of practitioners.
Among the crafts in which patterns are used are sewing, knitting, and woodworking. The
name may be pattern, or recipe, or blueprint.
The idea of patterns in computer science comes from the world of architecture,
popularised by Christopher Alexander. An architectural example is the pattern called
Capturing Light. Many people like to have a quiet place to have their morning coffee.
Often they prefer to sit in the sunlight, so the quiet place should be on the east side of the
building, since the sun rises in the east. You can see this in the house described at
http://www.architectureweek.com/2003/0423/patterns.html and, you can see nine
additional architectural patterns at http://www.architectureweek.com/topics/patterns.html.
They have names like Inhabiting the Site, Sheltering Roof, and Parts in Proportion.
The word pattern became known in computing with the publication of Design Patterns:
Elements of Reusable Object-Oriented Software in 1995. The authors are Erich Gamma,
Richard Helm, Ralph Johnson and the late John Vlissides; collectively they are known as
the Gang of Four or simply GoF.) They define a pattern as having four essential
elements. The pattern name is a handle we can use to describe a design problem, its
solutions, and consequences in a word or two. The problem describes when to apply
the pattern. The solution describes the elements that make up the design, their
331
332
Interface pattern
Here is a pattern that underlies much of the philosophy behind Java. This pattern is not
directly named in the GoF book since the authors were not using Java examples; they
used Smalltalk and C++ and those languages dont support interfaces as Java does.
We have used many Java interfaces in our coding, including Serializable and
Comparable. The basic idea behind this pattern is to describe some additional
capabilities a class may have, capabilities which many classes may be expected to have,
and then leave the classes to provide the details of how those capabilities are
implemented.
For the Serializable interface, we saw that the capabilities it specifies are the ability
to write objects of a class into a format that may be read back easily. The details of how
that is accomplished depend on the specific class.
333
Creator pattern
This pattern is a simple one. It helps you answer the question Which class should create
new objects? As such it is a pattern that you use while designing your system. It is not a
pattern you use in solving problems that arise while implementing the system.
For example, in our college model, we have students and courses. Which class should
create a new Student object? Which should create a new Course object?
Since students are registered at the college, College should create new Student
objects.
Courses are taught by professors. Does that mean Professor objects should create
Course objects? No, a course is taught by a professor, but the course continues to be
available after the professor has left the college. Thus, College should create Course
objects.
The Creator pattern was publicised by Larman as part of his General Responsibility
Assignment Software Patterns, GRASP.
334
High Cohesion
This is another of Larmans patterns, and gives a name to a practice used for a long time.
Many would note that this is a design principle and not a pattern. A design principle is
less important than a pattern.
The problem it is solving is how to write high quality code, code which can be
understood by a single programmer.
Something which gets in the way of understanding is complicated code, code which
attempts to do too much.
This pattern (or design principle) states that a class should do a limited number of things
and it should do them well.
Low Coupling
This too is another of Larmans patterns, and gives a name to a practice used for a long
time. Many would note that this is a design principle and not a pattern.
The problem it is solving is how to minimise the connections between classes, so that a
change in one class has minimal effects on another.
This pattern (or design principle) states there should be no more connection between
classes than is necessary, and that those connections be as simple as possible.
This is related to the GoF Mediator pattern.
335
Delegation
This is another pattern which is clearly reflected in the philosophy of Java; objects appear
to possess behaviours but delegate those behaviours to other objects.
We saw this most clearly while using the XMLEncoder; we replaced the
DefaultPersistenceDelegate with one which knew about the collections our
object contained.
More details on this pattern are at
http://en.wikipedia.org/wiki/Delegation_%28programming%29.
Factory
Like delegation, this is a pattern which is clearly reflected in the philosophy of Java. Not
all classes have the constructors you would expect. In some cases they have methods
which act as constructors. More correctly, the methods are factories, which build objects.
We have seen this with the BigInteger.valueOf method, and the various
BorderFactory.create methods.
More details on this pattern are at http://en.wikipedia.org/wiki/Factory_pattern.
336
Dynamic programming, which we will use with some of the mathematical functions in
the next chapter, is another use of the lazy initialization pattern.
More details on this pattern are at http://en.wikipedia.org/wiki/Lazy_initialization_pattern
And when the aria onwhat you want no longer . The
Singleton pattern
The GoF book defines the problem this pattern solves as Ensure a class only has one
instance, and provide a global point of access to it.
We used this pattern when we created the College class, a class which ensures there is
only one set of rules which applies to all students. For our college, the marks for a student
in a course are stored as a percentage. But sometimes, you want to display them as a
letter grade. The college needs to have one set of rules for translating percentages to letter
grades, independent of the student, the course, or the department.
We would need a class which creates a method to translate these percentages into letter
grades. But we wish only one such method. Thus the constructor would need to see it
there already was such a method created.
Similarly, were we to examine bank accounts as an example of objects, we would find
that the Singleton pattern helps us keep the interest rates paid in only one place. There
would be only one rule to determine the interest paid on a bank account.
As an aside, many older programming languages used global variables, variables which
were known and changeable in all parts of a system. This allowed for all sorts of
problems when values were accidentally changed. The Singleton pattern allows the
retrieval of global values, but it can limit the changing of those values. As such, it is a
more powerful technique than using global variables.
It also allows changing values on the fly, by reading something in from a resource
bundle, without recompiling the system.
More details on this pattern are at http://en.wikipedia.org/wiki/Singleton_pattern
Model-view-controller (MVC)
This is quite an old pattern, dating back to the days when Smalltalk, one of the first
object-oriented languages, was new. Of course, the term pattern was not used then.
MVC is the pattern we have tried to use in creating the GUI for the College.
337
The model represents the data and how it is stored. Note that we have looked at several
candidates for the model, including serialization and XML. We have also mentioned the
use of databases.
The view represents the interface we have created to manipulate and present the data.
The controllers are the glue that connects the model and the view. Controllers respond to
user events and modify the model. By using controllers, the interface is isolated from the
details of the model implementation. If we write the controllers correctly, they too are
ignorant of the model implementation. For example, the model could have a
writeData method which the controller could invoke. That method would know
whether XML or a database is being used. This is also an example of the application of
the Low Coupling pattern.
A famous example of MVC divides the screen into four quadrants. In the first you enter
data into the model. In the second, that data is displayed as a histogram. In the third, it is
displayed as a pie chart. In the fourth it is displayed as a table of data. I wish I could find
it!
More details on this pattern are at http://en.wikipedia.org/wiki/Model-view-controller
Summary
Patterns are a very important part of design. This chapter has provided a very brief
introduction to patterns. We are not able to go into them further as this book, while it
touches on design, is mainly dealing with implementation.
If you wish to look into patterns in more detail (and you may do so in further courses),
good places to start online are http://en.wikipedia.org/wiki/Design_Patterns and
http://c2.com/cgi/wiki?DesignPatternsBook, and the exercises on the next page.
338
Exercises
1.
Look in the GoF book and explore the patterns they discuss. Their catalog contains
23 patterns with which you should become familiar. These include Abstract
Factory, Adapter, Bridge, Builder, Chain of Responsibility, Command, Composite,
Decorator, Faade, Factory Method, Flyweight, Interpreter, Iterator, Mediator,
Memento, Observer, Prototype, Proxy, Singleton, State, Strategy, Template
Method, and Visitor. You will become a better designer and programmer when you
gain familiarity with these patterns. Dont attempt to become familiar with all the
patterns at once.
Note that the GoF book is available on CD as well as in printed format. For learning
the patterns, the CD is perhaps better.
2.
Look in Metskers book and see how the GoF pattern may be implemented in Java.
I suggest you use Metskers book in combination with the GoF book. One provides
the theoretical underpinnings for the pattern, and the other provides a Java
implementation.
3.
Look in Larmans book and explore the patterns he discusses. These include
Information Expert, Creator, Controller, Low Coupling, High Cohesion,
Polymorphism, Pure Fabrication, Indirection, and Protected Variations
4.
339
340
Introduction
The classes we have created in earlier chapters were all related to computer
representations of things in real life. Sometimes a class can represent mathematical
objects instead.
Id like to look at some mathematical examples now, one of which is available as part of
a Java package, and two of which we can create ourselves.
BigInteger
What is the largest integer?
Since the time of the Greeks it has been known there is no largest integer. (If there were,
what would that integer plus one be?) Unfortunately computers dont work well with
large numbers. We have already seen the limits Java imposes on the size of integers. For
numbers, a byte contains 8 bits (representing integers from -128 to 127), a short
contain 16 bits (representing integers from -32768 to 32767), an int contains 32 bits
(representing integers from -2147483648 to 2147483647), and a long contains 64 bits
(representing integers from -263 to 263 1).
Suppose you need numbers larger than that. The national debt for a large country may
exceed even the limit for a long. Certainly the gross national product for many
companies will exceed even the limit for a long.
Public key encryption using the RSA algorithm involves factoring large numbers. In
early 2007, researchers reporting factoring a 307-digit number into its two prime-factors,
one an 80-digit number, the other a 227-digit number. It took several supercomputers
about 11 months to accomplish this feat.
341
So how do we represent even larger numbers? The BigInteger class comes to the
rescue. The online Java documentation says BigInteger provides analogues to all of
Java's primitive integer operators, and all relevant methods from java.lang.Math.
Additionally, BigInteger provides operations for modular arithmetic, GCD
calculation, primality testing, prime generation, bit manipulation, and a few other
miscellaneous operations.
To understand that statement, we should look at java.lang.Math first.
342
BigInteger, continued
The GCD calculation referred to above is the greatest common divisor calculation, a nice
recursive method which we examine in more detail in the next chapter.
When you look through the methods available in the BigInteger class you see all the
variety.
When I checked (2007-11-29, via http://www.brillig.com/debt_clock/), the USA national
debt it was $9,132,167,824,005.26. You would need a BigDecimal to represent this
exactly, but we can round it to the BigInteger value of $9,132,167,824,005. To test
BigInteger, I created an applet. In it I placed the following statements.
import java.math.BigInteger
private BigInteger usDebt =
newBigInteger("9132167824005");
g.setColor(Color.red);
g.drawString("US debt " + usDebt.toString(), 20, 40);
343
valueOf is a static method within the BigInteger class, the same way abs is a
static method within the Math class. In both cases, that means we use the name of the
class, followed by a period and the method. This is an example of the Factory pattern.
The BigDecimal class, on the other hand, has the constructors you would expect. For
example, it has a constructor which accepts a double as its argument.
Rational numbers
There are two types of numbers which java.lang.Math does not support. First well
look at the rational numbers. Then well look at complex numbers.
What is a rational number?
It is any number which can be expressed as the ratio of two integers. is a rational
number, as is 36/48. In fact, those two rational numbers are the same. The numerator (36)
may be written as 12 * 3. The denominator (48) may be represented as 12 * 4. After you
remove the common factor (12) from the numerator and denominator, you are left with
.
Note that there are some numbers which are not rational numbers. These are called
irrational numbers. Perhaps the simplest irrational number is the square root of 2.
Some rules we will adopt for our rational numbers.
Common factors will be removed from the numerator and the denominator.
A zero denominator is not allowed.
A negative rational number will have its numerator negative and denominator
positive.
A rational number with both numerator and denominator negative will be
replaced by one with both denominator and numerator positive.
Zero is represented as 0/1.
So lets create a rational number class. Yes, I know that you can find many examples of a
RationalNumber class on the Internet. This is a standard programming example in
many introductory programming courses.
Following my usual practice, we begin with a constructor and the toString method.
/**
* class RationalNumber.
*
* @author rick gee
* @version may 2006
344
*/
public class RationalNumber
{
// instance variables
private int n; // the numerator
private int d; // the denominator
/**
* Constructor for objects of class RationalNumber
*/
public RationalNumber(int n, int d)
throws NumberFormatException
{
int num;
int denom;
if (d == 0)
throw new NumberFormatException(
"RationalNumber: denominator may not be zero");
num = n;
denom = d;
if (denom < 0) {
// handles both -5/-4 and 5/-7
num = - num;
denom = - denom;
}
if (num == 0)
denom = 1;
else {
int divisor;
divisor = gcd(Math.abs(num), Math.abs(denom));
num = num / divisor;
denom = denom / divisor;
}
// initialise instance variables
this.n = num;
this.d = denom;
}
/**
* @return a String representation of a rational number
*/
345
/**
* add(r)
* add two rational numbers
* @return this + r
*/
public RationalNumber add(RationalNumber r) {
RationalNumber result = new RationalNumber(
this.n * r.getDenominator() +
this.d * r.getNumerator(),
346
this.d * r.getDenominator());
return result;
}
Notice that we need two getter methods, one for the numerator and one for the
denominator.
Notice also that by using the constructor, this method does not need to concern itself with
removing common factors at the end, nor does it need to concern itself with negative
signs.
347
Complex numbers
Another type of numbers that java.lang.Math does not handle is complex numbers.
Recall that a complex number has two parts, the real part and the imaginary part. For
example, 6 + 4i is a complex number, as are -5 + 5i, 0 + 0i, and 2 7i. Complex numbers
are usually displayed as a String containing the real part, the sign of the imaginary
part, the absolute value of the imaginary part, and the letter i. The letter i represents the
square root of -1, a number which does not exist, hence is called an imaginary number.
But in some mathematics an imaginary number can be very useful.
Complex numbers sometimes are represented as (6, 4), (-5, 5), (0, 0), and (2, -7). Thus we
have two choices for the implementation of the toString method.
348
Implementing ComplexNumber
Make it so. Note that while my first examples show integer values for both the real and
imaginary parts, they should actually be doubles.
Summary
Java is useful for many types of applications. One of its strengths is the ease with which
you may add libraries, thus extending the areas in which it is applicable.
We have seen this by creating RationalNumber and ComplexNumber classes.
349
Exercises
1.
Complete the RationalNumber class. Change the datatype for its instance
variable to long. Describe the effects. Change the datatype to BigInteger.
Describe the effects.
2.
3.
Complex numbers may be represented by x and y as we have done, but they may
also be represented as an angle and a radius. The angle may be calculated as the
inverse tangent of y over x, and the radius is the square root of x squared plus y
squared. Supplement the ComplexNumber class to include accessors for the angle
and the radius.
You can not write a ComplexNumber class that has two constructors, one which
accepts x and y and one which accepts the angle and the radius. Why not?
4.
Just as complex numbers are the extension of real numbers from 1-space into 2space, quaternions are their extension into 4-space. Learn something about
quaternions and implement a Quaternion class.
5.
Just as complex numbers are the extension of real numbers from 1-space into 2space and quaternions are their extension into 4-space, octonions are their extension
into 8-space. Learn something about octonions and implement an Octonion class.
350
Define algorithm
Write methods to implement mathematical algorithms
Define iteration.
Define recursion
Write iterative methods
Write recursive methods
Write methods that use formulas.
Use dynamic programming
Introduction
Sometimes a class can be a collection of useful constants or methods. Imagine a class that
contained the various constants used in physics and chemistry; the speed of light,
Plancks constant, Avogadros number, etc.
Imagine a class that contained information on all the chemical elements.
But Id like to look at a collection of simple algorithms and the methods which
implement those algorithms. Lets create a class called Algorithms. (Yes, I know that
once upon a time I said the name of a class should be a singular noun.)
What is an algorithm?
An algorithm is a set of rules that you can follow to solve a problem. An algorithm will
solve the problem in a finite number of steps and it will solve the problem in a finite
amount of time. Frequently there are several algorithms which solve the same problem.
Analysis of Algorithms is the study of algorithms (understanding which are better and
when) and the development of new algorithms.
/**
* compute the greatest common divisor of the two
* positive parameters. Uses Euclid's algorithm.
*/
private int gcdIterative (int num1, int num2)
{
while (num1 != num2) {
if (num1 > num2)
num1 = num1 - num2;
else
num2 = num2 - num1;
}
return num1;
}
This algorithm is the Euclidean (or Euclids) algorithm. Euclid was a Greek
mathematician and the algotithm dates back to around 300 BCE and is described in
http://en.wikipedia.org/wiki/Euclidean_algorithm
This method implements an iterative solution to the problem. An iterative solution is one
in which you perform one step after another, under the control of a loop of some kind, in
this case a while loop.
One or more stopping case(s) for which we know the answer, and
A statement that calculates an answer by calling the method with a smaller
argument.
352
The modulus operator (%) calculates the remainder when you divide the first integer
argument by the second. For example, 11 % 2 is 1, since 2 goes into 11 five times, with a
remainder of 1.
We start with num1 = 1071 and num2 = 1029. The numbers are not the same,
num1 is the larger, and so num1 becomes the difference, 42, while num2 remains
1029.
num1 and num2 are not the same, but now num2 is the larger, so it becomes 1029
42 or 987 while num1 remains 42.
num1 and num2 are not the same, and num2 is the larger, so it becomes 987 42
or 945 while num1 remains 42.
This continues, removing 42 from num2 each time, until it becomes 63.
num1 and num2 are not the same, and num2 is larger, so num2 becomes 63 42
or 21 while num1 remains 42.
Now num1 is larger than num2, so becomes 42 21 or 21.
Now both num1 and num2 are 21 so the algorithm terminates and the greatest
common divisor is 21. 1071 = 3 * 17 * 21 and 1029 = 7 * 7 * 21.
Both forms of the algorithm produce the same answer (Thats good!) but the second
requires more memory. The first may require more time. Once you have the algorithm
explained in a recursive form, it seems like magic the way the method works.
353
Declaring a method static says that this method is a method belonging to the class. It
is not a method which belongs to instances of the class. It is a singleton. In combination
with declaring the method public, other classes may call the method using the
Algorithms.gcdRecursive(int, int).
Note that the iterative method was a private method. If we are going to use it in
outside our class, it will need to become a public static method also.
The worst performance for the gcd function occurs when the numbers are consecutive
Fibonacci numbers, which are described below.
Fibonacci numbers
If you read or saw The da Vinci Code, you have seen Fibonacci numbers. (They were the
first line of numbers written on the floor of the Louvre, functioning as the answer to the
secret question if you forget your account number.)
This series of numbers occurs in many places in the world, both natural and
mathematical. Wikipedia has some interesting examples at
http://en.wikipedia.org/wiki/Fibonacci_number, dating back to 200 BCE. The series
derives its name from Leonardo of Pisa. His father was nicknamed Bonaccio (good
natured or simple). Leonardo was posthumously given the nickname Fibonacci
(derived from the Latin words filius Bonacci, meaning son of Bonaccio).
The values in the series are 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, etc., where the first number
(F0) is zero, the second (F1) is one, and every subsequent Fibonacci number is the sum of
the previous two. That is, in functional notation:
F(0) = 0;
F(1) = 1;
F(n) = F(n-1) + F(n-2) when n > 1
Sometimes, the series starts at 1, 1, 2. This is because Fibonacci was dealing with
questions about the breeding behaviour of pairs of rabbits and it is difficult to breed more
rabbits if you have no pairs. Of course, if you have one pair, you will soon have many.
Fibonacci wanted to know how many pairs you would have.
int f1 = 1;
int t;
for (int i = 0; i < n; i++) {
t = f0 + f1;
f0= f1;
f1 = t;
}
return f0;
}
We know that there is a limit to the size of ints so there is a limit to which Fibonacci
numbers we can calculate. iFib(46) looks okay, but iFib(47) is negative. How can
two positive numbers added together become negative?
It has to do with the way numbers are stored as bits. One of the bits is the sign bit. When
the calculations in the other bits result into a carry into the sign bit, it may change from a
positive number to a negative one. Thats what happens here. The result is obviously
wrong.
355
If we are going to use these Fibonacci methods for large values of n, we will need to go
to BigIntegers, and we will have to do something about the speed of the recursive
method. Well see how to do that later.
The Wikipedia article mentioned at the beginning of this section also has some
interesting generalizations of the Fibonacci series. One that I find particularly interesting
was explored by Divakar Viswanath. The random Fibonacci sequences are defined by:
T(0) = 1;
T(1) = 1;
T(n) = T(n-1) T(n-2)
The plus or minus signs are equally likely and are chosen independently, perhaps using a
coin-toss. His article (available at http://www.ams.org/mcom/2000-69-231/S0025-571899-01145-X/home.html) points out that it is not clear what will happen to the numbers in
this series, but he proves that they have an interesting behaviour.
Note that the Fibonacci numbers we discussed have an interesting, but different,
behaviour themselves. When you calculate F(n+1) / F(n) for a number of values of n, you
will find that this ratio comes closer and closer to the golden ratio (, pronounce feye
or fee) or approximately 1.618034.
In addition, it is possible to calculate F(n) via a formula. F(n) is the largest integer less
than (n/5) + 0.5.
356
/**
* iFactorial
* @return n factorial
*/
public static int iFactorial (int n) {
if (n == 0)
return 1;
int result = 1;
for (int i = 1; i <= n; i++)
result = result * i;
return result;
}
The second definition leads to this implementation of the recursive factorial function.
/**
* rFactorial
* @return n factorial
*/
public static int rFactorial(int n) {
if (n == 0)
return 1;
return n * rFactorial(n-1);
}
Both of these calculate 16! successfully and then overflow when you attempt to calculate
17!.
A Formula!
In Java, this method is very simple to write. It is neither iterative nor recursive. It is
simply a formula, but a formula that uses one of the factorial methods.
/**
* the number of ways to choose n items from m
357
* @return m choose n
*/
public static int choose(int m, int n) {
return iFactorial(m) /
(iFactorial(n) * iFactorial(m - n));
}
Unfortunately it does not give correct answers in many cases, due to the overflow
problems we have noticed earlier. How could we modify it to work properly?
I suppose we could use BigInteger, or we could use a more intelligent algorithm.
358
A(0, n) = n +1
A(m, 0) = A (m - 1, 1)
A(m, n) = A(m - 1, A(m, n - 1))
359
The table gives use closed forms for A(1, n), A(2, n), and A(3, n) as well.
A(1, n) = n + 2
A(2, n) = 2 * n + 3
A(3, n) = 2(n+3) 3
This last equation would be implemented as Math.pow(2.0, (double(n + 3))
3.0
Note that the Math.pow method expects both the base (in this case 2.0) and the
exponent (in this case, n + 3) to be doubles. 2.0 is a double; n + 3 is not, so we must
cast it to a double. We could have the same effect by using 3.0 instead of 3.
For other values of m and n, there are no simple formulas, although there are a few values
calculated which we could use. That idea of calculating and saving values is the idea
behind dynamic programming.
Dynamic programming
Dynamic programming, a technique which remembers the results of previous
calculations, is described at http://en.wikipedia.org/wiki/Dynamic_programming, a
description which even mentions the Fibonacci numbers we have examined already.
Why does it mention them? Obviously because to calculate Fib(100) you need all the
Fibonacci numbers less than Fib(100). So if you follow up the calculation of Fib(100)
with a request for Fib(50), it would be much more efficient to simply look up the value in
a table rather than to recalculate it.
Dynamic programming involves calculating and keeping the table. This statement
conceals several questions. What type of data structure do we use for the table? When do
we create it? Do we want to save it and if so, how do we save it?
360
361
362
fibData.trimToSize();
try {
FileOutputStream fos;
ObjectOutputStream oos;
fos = new FileOutputStream("fib.obj");
oos = new ObjectOutputStream(fos);
for (int i = 0; i <fibData.size(); i++) {
oos.writeObject(new Integer(i));
oos.writeObject(fibData.get(i));
}
oos.flush();
oos.close();
} // end try
catch (IOException e) {
// can't write. maybe in an applet
} // end catch
}
}
363
while (more) {
try{
Integer i = (Integer) ois.readObject();
Integer fibI =
(Integer) ois.readObject();
fibData.add(i,fibI);
}
// could be IOException or
// NumberFormatException
catch (Exception e) {
more = false;
}
}
ois.close();
} // end try
catch (IOException e) {
} // end catch
}
// if there was a problem with loading from the file,
// fibData may still be null
if (fibData == null) {
fibData = new ArrayList<Integer>(2);
fibData.add(0, new Integer(0));
fibData.add(1, new Integer(1));
}
// is the answer in the arrayList?
// if not, calculate it and put it there
try {
Integer x = fibData.get(n);
return x.intValue();
}
catch (Exception e) {
result = rFib(n-1) + rFib(n-2);
fibData.add(n, new Integer(result));
return result;
}
}
But the Ackermann method implements a function of two variables, so we cant use an
ArrayList. Instead, we need a HashMap.
Recall that a HashMap provides a key (the two parameters to the function) and a value to
which the key maps (the value of the function.) The datatype Point, although designed
for graphics, will serve as our key. (Note that we could have used a HashMap with the
Fibonacci function; the key would be one integer and the value would be the other.)
import java.util.HashMap;
import java.awt.Point;
We declare the variable
private static HashMap<Point, Integer> ackData;
In the constructor, we add the line
ackData = null;
Whenever we calculate, a result, we add it to the table of remembered data.
if (m == 0) {
result = n + 1;
ackData.put(new Point(m, n), new Integer(result));
}
else {
if (n == 0) {
result = A(m-1, 1);
ackData.put(new Point(m, n),
new Integer(result));
}
else {
result = A(m-1, A(m, n-1));
ackData.put(new Point(m, n),
new Integer(result));
}
}
365
366
catch (Exception e) {
more = false;
}
}
ois.close();
} // end try
catch (IOException e) {
} // end catch
}
Summary
We have seen how to write several mathematical functions, using iteration and recursion,
and using (for the Fibonacci and Ackermann functions) dynamic programming
techniques. The other methods in this class dont need, but could use, dynamic
programming.
The main problem some of these methods have is that the results become large very
quickly. We can address that with the BigInteger class, should we wish. Of course, it
is always possible to create a number so large that the memory of the computer is not
large enough to hold it. Thats the fun of working with numbers; there is always one
bigger.
367
Exercises
1.
2.
Write a class that contains some of the chemical elements and their properties.
3.
Modify your Fibonacci function to use at least longs if not BigIntegers. Use
your Fibonacci functions to explore the ratio F(n+1) / F(n) as n increases.
4.
Explore Pascals triangle and the functions associated with it. One such function is
n n 1 n 1
the choose function, but a second formula is =
+
. Use these
k k 1 k
formulas to allow you to calculate the expansion of (x + y) raised to the power n.
5.
formula arise? Imagine m + n - 1 items lined up. Remove n 1 of them. The first
child gets all the items (if any) up to the first item removed. The second child gets
all the items (if any) up to the second item removed. And so on, until the last child
gets all the items (if any) after the last item removed.
6.
368
Introduction
We have looked at many aspects of creating solutions using Java, and all the code we
have seen works. But it is not all well-written Java. In particular, there are some issues
relating to threads which we should discuss.
Threads - definition
Wikiped (http://en.wikipedia.org/wiki/Thread_%28computer_science%29) defines a
thread as a way for a program to split itself into two or more simultaneously (or pseudosimultaneously) running tasks.
Have you ever been working in your word processor and made a spelling or grammar
mistake? A word appears underlined (in my case in red) to indicate a spelling mistake. A
phrase appears underlined (in my case in green) to indicate a grammar concern.
A spell-check thread is running at the same time as a grammar-check thread (or maybe
one thread is doing both checks. I dont know for sure.) and both are running at the same
time as the thread that allows me to type the document. Simultaneously, other processes
are running on my computer, but these processes are part of another program, which itself
may have threads (checking for appointments and incoming mail, for example.)
The Java interpreter uses a garbage collection thread to identify objects for which there
are no references. It then reuses the memory for new variables.
Youll see how threads and processes interact in an operating systems course. For now,
we need to consider how threads work in Java.
369
while (box.isVisible()) {}
System.exit(0);
} // end run
}; // end Runnable
EventQueue.invokeLater(runner);
} // end showDataEntryFrame
Note that we have made only a few changes.
First, as indicated by the shading, we have added a few statements. These create a
Runnable object and then place that object on the event queue.
370
Then we have deleted a couple of statements. The deleted statements made sure the data
entry form remained on the screen. Now that the data entry form is on the event queue,
the queue itself will keep the screen displayed until it is ended.
Inner classes
This topic is often considered to be an advanced topic, but it is one we have been using
occasionally from near the beginning of this book.
An inner class is defined in Wikipedia (http://en.wikipedia.org/wiki/Inner_class) as a
class defined entirely within another class.
There are several types of inner classes; the one we have used the most is the anonymous
class.
The Comparators we have used while creating collections (and specifying the order of
elements within the classes.) are anonymous classes.
The InputVerifiers we used for the MyDate and Section data input screens are
anonymous classes.
The first event handler we created in the GUI should have been a series of event handlers,
implemented as anonymous classes. Lets see how to do that. Note that we have seen this
trick, when we created the Section data entry screen.
Recall that we created a CollegeMenu class which implemented the
ActionListener interface. As a consequence, the class is required to implement an
ActionPerformed method.
For each item on the menu, we needed to indicate that the CollegeMenu class was
responsible for handling events associated with that menu item.
menuItem.addActionListener(this);
The actionPerformed method then is a series of if statements, identifying the
source of the item, and doing an appropriate action.
public void actionPerformed(ActionEvent e) {
JMenuItem source = (JMenuItem)(e.getSource());
if (source.getText().equals("Exit")) {
System.exit(0);
}
if (source.getText().equals("About")) {
371
372
But this is not something Id like to see. After all, what if there were several ways to
cause the same event to happen? Youd need to repeat this code several times; it would
be better to simply assign the same ActionListener.
Refactoring
Throughout this document, we have seen good code, but not always great code. We have
seen code that a programmer would write if he or she was unaware of some of the latest
advances in software engineering.
In particular, one thing that we have done is to create Comparators within the class
that needs them. We have been fortunate that they were only used in that one class.
However, as the College application expands, we may find that we need those
Comparators in other classes as well. Thus, we should move the Comparators to
another class, declare them static, and then use them as necessary.
Similarly we have the, possibly better, example of InputVerifiers. We have
developed them for year, month, day, and time. We started to develop one for rooms. We
will surely use these in other places. So we should move them to another class, declare
them static, and use them as necessary.
This idea of improving the structure of your design and its resulting program is the
foundation of refactoring.
Wikipedia (http://en.wikipedia.org/wiki/Refactoring) gives this definition. Refactoring
is the process of rewriting a computer program or other material to improve its structure
or readability, while explicitly preserving its meaning or behavior.
That definition would even include using constants like MAX_MEETINGS in place of
the number 5.
Can you find other places in the code we have seen which would benefit from some
refactoring?
373
You create a main method in your class. That is, you create a method whose name is
main, and whose signature is the heading for this section.
Suppose you are testing. Depending on the testing you wish to do, there may be a main
method for each class you wish to test. Recall that we created unit tests for Student and
Professor. In each, we tested the constructors, the toString method, and the
getters and setters. If we didnt have BlueJ we would have needed a main method. In it
we would have constructed one or more objects and then invoked the methods we wished
to test.
Suppose you wish to create a GUI for an application. You will create a class which
generates and displays the GUI. That class will contain a main method.
Without an integrated development environment like BlueJ, you may be running from the
command line. Youll need to use the command javac SomeClass.java to compile
the class. To execute the main method of SomeClass, youll need to use the command
java SomeClass and the Java interpreter will look for a method named main.
Lets examine the parts of the header for this main method.
public static void main(String[] args)
public tells us that this method is available outside the class. It has to be so that others
can invoke it.
static tells us that there is only one copy of this method in this class.
void tells us that when the method completes its processing, it just completes; it doesnt
return any special value.
main tells us the name of the method. The Java interpreter expects to find a main
method. Yes, BlueJ created this for us in the background.
String[] args tells us that we can provide arguments to the main method, similar
to the way we provide arguments to applets.
If you are using the command line, the arguments are placed on the command line after
java SomeClass.
The arguments are provided as elements of the array of String called args , which is
accessible within the main method The first one is placed in args[0], the second in
args[1].
374
Summary
This chapter completes our discussion of the Java language. Now you have a good basis
for going forward and using Java, and for learning more about Java, since this is only an
introduction to Java.
375
Exercises
1.
Modify the data entry screen for the Section class so that it is thread-safe.
2.
Modify the data entry screen for the Section class so that it uses inner classes.
3.
Modify the data entry screen for the MyDate class so that it uses inner classes.
4.
376
In Conclusion
Now we have seen how the basic (and some not-so-basic) techniques of Java allow us to
implement our ideas. Go forth and expand your knowledge.
Here are some possible areas you may wish to explore to build upon the knowledge you
have gained here.
Professor class office number, phone number, degrees (where, subject, when).
Include subjects taught and when as a separate class.
Databases. How to normalize them and how to use them with Java programs.
A related example of inheritance. Sports players and their teams. See the appendix
for the labs I used while teaching a course using this textbook for the first time.
Customisation How do you adapt menus and data entry screens and reports to
other languages? How do you allow people to change the colours of your
interface? How do you allow them to change the way courses and rooms are
numbered?
377
378
References
Printed Reference materials
Fisher, Timothy Java phrasebook, Sams Publishing, 2007
Gamma, Erich, Richard Helm, Ralph Johnson, John Vlissides, Design Patterns: Elements
of Reusable Object-Oriented Software, Addison-Wesley, 1995
Goodliffe, Pete, Code Craft, No Starch Press, 2007
Hu, Chenglie. Just Say A Class Defines a Data Type, Communications of the ACM,
volume 51, number 3, 19-21
Kernighan, Brian, and Dennis Ritchie, The C Programming Language 2nd ed. PrenticeHall 1988
Larman, Craig, Applying UML and Patterns: An Introduction to Object-Oriented
Analysis and Design and Iterative Development, 3rd ed. Prentice Hall Ptr 2004
Marrero, Will, and Amber Settle, Testing First: Emphasizing Testing in Early
Programming Classes, ACM SIGCSE Bulletin, volume 37, number 3, 4-8
Metsker, Steven John, Design Patterns Java Workbook, Addison-Wesley, 2002
Oram, Andy, and Greg Wilson, Beautiful Code: Leading Programmers Explain How
They Think, OReilly, 2007
Paterson, James H., John Haddow, Michael Nairn, A Design Patterns Extension for the
BlueJ IDE, ACM SIGCSE Bulletin, volume 38 number 3, 280-4
Pecinovsk, Rudolf, Jarmila Pavlkov, Lubo Pavliek, Lets Modify the Objects-First
Approach into Design-Patterns-First, ACM SIGCSE Bulletin, volume 38 number 3, 18892
Zukowski, John The Definitive Guide to Java Swing, 3rd ed. APress 2005
379
Online references
BlueJ the interactive Java environment. http://www.bluej.org
Elliott, James. RelativeLayout, a Constraint-Based Layout Manager.
http://www.onjava.com/pub/a/onjava/2002/09/18/relativelayout.html (viewed 2006-0520)
Elliott, James. Inside RelativeLayout.
http://www.onjava.com/pub/a/onjava/2002/11/27/layout2.html (viewed 2006-05-20)
Ganesh, S.G. Uncommon Java Bugs, http://java.sys-con.com/read/564291.htm (viewed
2008-05-27)
Horstmann, Cay. http://www.horstmann.com/violet Violet is intended for students,
teachers, and authors who need to produce simple UML diagrams quickly.
Java online documentation. http://java.sun.com/j2se/1.5.0/docs/api/index.html
Java tutorial. http://java.sun.com/docs/books/tutorial/index.html
JUnit.org. http://www.junit.org
Kolawa, Adam, Gina Assaf, and Roberto Scaramuzzi. Rule 5: Make your clone() method
final http://www.ftponline.com/javapro/2004_04/online/rule5_04_07_04/
Liu, J.F. http://dev.eclipse.org/mhonarc/lists/gef-dev/msg00469.html
OHanley, John, http://www.javapractices.com
Paul, Thomas. Collections in Java, Part 2 The Set Interface
http://www.javaranch.com/newsletter/July2002/setinterface.html
Unicode.org http://www.unicode.org
Wikipedia. http://en.wikipedia.org
380
381
382
The icon and the BlueJ website, at www.bluej.org, show a bird called the Blue Jay,
Cyanocitta cristata, generally found in the eastern part of North America. On occasion, it
wanders as far west as British Columbia. In western Canada we have the Stellers Jay,
Cyanocitta steller. You can see that the two species are related from the common family
name Cyanocitta. The Stellers Jay is the official bird of British Columbia.
Why is the Blue Jay on the icon? I guess its a play on words. BlueJ is a programming
environment for programming in the language Java and J is an abbreviation for Java and,
Blue is
Anyways, click the icon. You will see something like the screen below.
BlueJ appears as a typical Windows application. The title bar contains the name of the
program plus buttons to minimise, maximise, and close the program.
A menu appears below the title bar.
Below the menu, on the left, are a number of disabled buttons. Once we begin to work
with a project, a collection of classes, these buttons will become enabled.
383
Below the menu, on the right is the class diagram. In this area, BlueJ displays a diagram
representing the classes we use in our project.
Below the column of buttons and the class diagram is another window or two.
Below that is a message area, currently saying Package saved.
You will get to know all of these intimately by the end of the course. For now, lets look
at the menu.
The first option is Project. We will be creating projects, collections of classes. In some
cases, a project will represent your work for a one-week period; in some cases a project
will represent your work over several weeks.
The Project menu allows you to create a new, empty project (New Project), to open an
existing project (Open Project), to open a project on which you have worked recently
(Open Recent Project), as well as to close and save projects. You can also print a
project, including the class diagram and/or the source code. The Project menu also
allows you to quit BlueJ (Quit).
The second option is Edit. This option allows you to create and remove classes, as well
as create new arrows on the class diagrams.
The third option is Tools. There are several categories on this menu. To run and test the
Java classes we write, we need to compile them, translating them into the bytecode the
Java interpreter processes. If a project has several classes, we may need to compile only
one or we may need to compile many. Perhaps we need to compile them all. There are
options for all these.
Documentation is very important when writing code and Java supports a very nice type of
documentation. This documentation is created from simple statements we insert in the
code; the Documentation option analyzes the documentation we have created and
formats it nicely.
Testing is also very important when writing code. The Testing option allows us to run
some or all of the tests we create.
Preferences allows us to set many preferences in the appearance and working of BlueJ.
For example, I have old eyes so I have modified the font size to 20. The View option
allows us to customise the appearance still further. The choices on this menu are things
which you may wish to change frequently. The Preferences menu includes things you
change less-frequently.
Now we consider the Help menu, beginning with its About BlueJ option.
384
The next Help menu option is Check Version. Use this to decide if there is a newer
version of BlueJ than the one you are using. At Okanagan College, you do not have the
authority to install software, so the information from this choice is for information only.
At home, you can keep it up to date if you wish. It may be better to keep the same version
at home and school.
The next Help menu option is Installed Extensions. For more about extensions, check
http://www.bluej.org/extensions/extensions.html.
The next Help menu option is Copyright.
385
The next Help menu option is BlueJ Web Site. This provides a direct link to
www.bluej.org, displaying the site in your default browser.
The next Help menu option is BlueJ Tutorial. This is a very complete tutorial on how
to use BlueJ. The main task of this lab assignment is to complete the tutorial.
The final Help menu option is Java Class Libraries. This is a very important link. It
leads us to http://java.sun.com/j2se/1.5.0/docs/api/index.html, a site which describes
everything you would like to know about the Java language. We will be visiting this site
regularly. Note that you can download the documentation to your own computer and have
this menu option link to your local copy. This could be handy should you be using BlueJ
while you are not connected to the Internet.
Now that you have quickly seen the menu, its time to use BlueJ.
Rather than create my own tutorial, I will direct you to the BlueJ Tutorial option on the
Help menu. Read and follow the steps in that tutorial. Yes, the tutorial is for BlueJ 2.0.X
and we are using 2.1.X but I havent found any differences. Complete the first three
sections of the tutorial.
Hand-in:
A note to me stating that you have completed the first three sections of the
tutorial. Note that I will not be repeating material from the tutorial in subsequent
labs, so you should complete the rest of the tutorial when it is appropriate.
Make sure that the note is word-processed, neatly-formatted, perfectly-speled, and free of
grammar mistakes.
Due date:
Two weeks
386
387
Scoring and penalty statistics are the second kind of player statistics. All players on a
lacrosse team, including goalies, can receive penalties, and score and assist on goals. As
for the goalies, the numbers shown on the websites represent the sum of separate
statistics.
Create and test a class called GoalieStatistic. A GoalieStatistic object contains details for
a single game. These details should include at least the date of the game, the number of
minutes played, the number of shots faced, and the number of goals allowed.
Create and test a class called PlayerStatistic. There are two different ways to do this. The
find is to record each event (goal, assist, penalty) separately. For example, goal at 12:45
of first period, assist at 1:53 of second period, two-minute penalty at 11:23 of second
period, five-minute penalty at 7:25 of third period. The second is to record the number of
events in each game. For example, one goal, one assist, seven minutes of penalties.
While the second is easier, the first is more flexible. In August 2006, Kerry Susheski
scored nine goals in a playoff game. One of the observations that came up from this was
the length of time between the first and the last goals. Thus, we can see that the WLA is
using the first way of recording these statistics.
You may find you have a better design, if you derive Goal, Assist, and Penalty classes
from PlayerStatistic.
You will need collections of GoalieStatistic objects and PlayerStatistic objects. Which is
the class which contains these collections?
Following the model in the textbook, you might suggest a League object. But we dont
have a league object yet. We will create one in the next assignment.
The alternative is for a Player object to contain a collection of PlayerStatistic objects, and
for a Goalie object to contain a collection of GoalieStatistic objects. This design may
possibly cause difficulties later, so we will not pursue it further except that we will need a
Goalie object. Since a goalie is a player, this is best accomplished by deriving a Goalie
class from the Player class.
Create and test a class called Goalie.
Hand-in:
Provide me with the Player, Goalie, GoalieStatistic, Goal, Assist, Penalty, and
PlayerStatistic classes. Also provide any other classes you have developed to support
these classes.
Due date:
TBA
388
389
Recall that the League also contains collections of PlayerStatistic objects and
GoalieStatistic objects. What are appropriate structures for these collections?
Each team needs to know its schedule against all the other teams in its league. I created a
ScheduleEntry class, and then stored a teams schedule in an
ArrayList<ScheduleEntry>. Consideration: Is it better for the League to know
the schedule for all games in the season, or is it better for the League to construct a
schedule by asking all the Teams for their own schedule? Patterns say that the League
should own the schedule, not the teams.
Ensure your Team and League classes support all the above information.
If you would like a challenge:
Each league has its own rules about the number of active (that is, not including those on
the injured-players list) goalies a team has. Each league has its own rules about the
number of active players (including goalies) a team has. Implement these rules as part of
League. You will need static variables. You may wish to use exceptions to detect a
violation of the rules.
We can use unit tests to populate teams (including their schedules, active players, and
injured players) and leagues. If this were a real application, we would require some form
of persistence to remember data from one run of the program to another. Implement
persistence. Include persistence for the goals, assists, and penalties.
Hand-in:
Provide me with the Team and League classes and their unit tests.
Due date:
TBA
390
391
those teams about its own scoring. The league can then combine those answers and
produce its own answers. Note that several players may be tied, on a team or in a league.
Make it so.
That is, modify Player to include a method to calculate the number of points (two for a
goal, one for an assist) each player earns, modify Team to determine the players(s) with
the highest points total, and modify League to determine the player(s) with the highest
points total.
Which team wins the league championship? This is harder since the rules in the leagues
are slightly different.
In the WLA and MSL (both of which have only a few teams) the first- and fourth-place
teams play a best-of-seven series, the second- and third-place teams play a best-of-seven
series, and the winners of those two series play a best-of-seven series for the
championship. (See http://www.theboxrocks.com/l_play_offs.asp for the summer 2006
results in the WLA.)
In the NLL, there are two divisions, each approximately the size of the WLA or MSL. In
each, the first- and fourth-place teams play a single-elimination game and the second- and
third-place teams play a single-elimination game, with the winners playing a singleelimination game for the division championship. The division champions then play a
single-elimination game for the league championship. (See
http://www.nll.com/playoffs.shtml for the winter 2006 results.)
But we dont know the placing of the teams since we have not recorded the winners and
losers of the games played. Modify and test the Team class so that the results of games
are known. Then use WLA/MSL rules to determine which teams are in first-, second-,
third-, and fourth-places, assuming two points for a win, one for a tie, and zero for a loss.
If two teams are tied, consider only the games they have played against each other, again
with two points for a win, one for a tie, and zero for a loss. If the teams are still tied, use a
random number to simulate a coin toss.
Add playoff data to the League class so that the League can identify the champion teams
and produce a report showing the results of all playoff games.
Hand-in:
Provide me with the revised Team and League classes.
Due date:
TBA
392
Hand-in:
Provide me with all the code necessary to create and test your data entry screen(s).
Due date:
TBA
393
) or the
Italian (
) flags. The difference doesnt show up in black and white, but the bar on
the left of the French flag is blue, while the bar on the left of the Italian flag is green. The
centre bar on both is white and the bar on the right is red.
) and
).
), Macedonia (
), Australia (
).
394
This illustrates the important topic of reuse. Pay particular attention to the stars on the last
two flags.
Illustrations of these and other flags are at many places on the Internet, including
www.flags.net (loading quickly since there is a page for each letter of the alphabet) and
https://www.cia.gov/cia/publications/factbook/docs/flagsoftheworld.html (loading more
slowly since all flags appear on one page).
Hand-in:
Your HelloWorld and Flag applets and the web pages to run them.
Due date:
TBA
395
Hand-in:
The RationalNumber and ComplexNumber classes, plus software to test them.
Due date:
TBA
396
397
calculates these numbers using the formulas numbered (2) and (3). Note that (2n)!
increases very rapidly, so you may wish to rethink the data types in the factorial methods.
Hand-in:
Due date:
TBA
398
399
400
Index
401
402
403
404
405
int, 11, 20, 29, 50, 57, 61, 62, 65, 94, 97,
118, 133, 138, 139, 141, 144, 146,
149, 154, 158, 159, 161, 162, 167,
168, 169, 172, 174, 175, 176, 177,
179, 185, 188, 189, 191, 192, 193,
194, 195, 197, 198, 204, 205, 207,
208, 217, 222, 223, 225, 226, 240,
241, 270, 271, 272, 273, 286, 288,
289, 291, 292, 293, 294, 312, 326,
327, 328, 329, 341, 343, 345, 346,
352, 354, 355, 357, 358, 359, 361, 363
Int, 149, 154, 161, 162, 286
Integer, 65, 97, 161, 187, 222, 225, 226,
270, 271, 272, 290, 291, 293, 294,
327, 361, 362, 363, 364, 365, 366, 389
interface, 111, 112, 115, 137, 142, 146,
216, 219, 243, 247, 248, 249, 250,
256, 263, 282, 306, 333, 334, 338,
371, 377, 380
Interface, 333, 334, 380
internationalisation, 182
interpret, 15, 70, 256
intValue, 226, 270, 271, 272, 291, 293,
294, 329, 361, 364
invokeLater, 370
IOException, 201, 204, 217, 218, 221,
222, 223, 224, 225, 235, 363, 364,
366, 367
isLetter, 65, 194
isSelected, 293
iterative, 192, 351, 352, 353, 354, 356,
357
iterator, 107, 122, 123, 124, 125, 126,
147, 148, 154, 182
Iterator, 123
JApplet, 248, 312, 313, 314, 315, 316,
318, 319
jar, 251, 323
Java, xii, xiii, xiv, xvii, 1, 3, 4, 5, 7, 8, 9,
11, 15, 17, 18, 19, 20, 21, 23, 25, 26,
28, 31, 33, 35, 38, 50, 53, 56, 57, 62,
63, 65, 68, 71, 74, 77, 78, 80, 81, 84,
87, 91, 92, 93, 94, 95, 97, 105, 109,
110, 111, 112, 113, 114, 119, 120,
123, 135, 162, 163, 167, 182, 186,
187, 191, 199, 201, 203, 204, 214,
406
407
Math.abs, 345
Math.pow, 360
mathematics, xv, 22, 342, 348
MB, 2
Meeting, 109, 115, 138, 157, 158, 160,
161, 163, 164, 165, 170, 171, 175,
176, 178, 201, 203, 204, 205, 206,
207, 208, 219, 223, 224, 226, 229,
237, 238, 239, 241, 242, 243, 245,
274, 275, 298
megahertz, 2
menu, 2, 32, 34, 35, 36, 37, 40, 46, 50,
56, 71, 82, 114, 121, 148, 249, 298,
301, 302, 303, 304, 305, 306, 307,
308, 311, 336, 371, 372, 376, 383,
384, 385, 386
menubar, 303
metaphor, 110
method, 5, 15, 17, 18, 21, 23, 24, 25, 26,
28, 33, 34, 35, 36, 37, 40, 43, 44, 45,
46, 47, 50, 51, 53, 55, 56, 57, 58, 60,
61, 62, 63, 64, 65, 69, 71, 72, 73, 78,
82, 83, 84, 85, 86, 87, 88, 89, 93, 94,
95, 96, 97, 98, 99, 101, 102, 103, 104,
105, 116, 117, 118, 119, 120, 121,
122, 123, 124, 125, 127, 128, 130,
134, 137, 138, 140, 141, 142, 143,
144, 146, 147, 149, 151, 152, 153,
154, 158, 160, 161, 162, 164, 165,
166, 167, 171, 173, 174, 176, 177,
178, 180, 182, 184, 185, 186, 187,
189, 190, 191, 192, 193, 194, 195,
196, 197, 198, 199, 201, 202, 203,
206, 207, 209, 210, 216, 218, 219,
220, 221, 222, 223, 224, 225, 226,
232, 233, 234, 235, 236, 237, 240,
241, 242, 243, 245, 253, 255, 256,
260, 261, 262, 267, 271, 272, 273,
279, 282, 288, 296, 297, 298, 302,
303, 304, 305, 306, 307, 312, 313,
314, 316, 317, 318, 319, 320, 322,
326, 327, 328, 329, 334, 336, 337,
338, 342, 343, 344, 346, 347, 348,
351, 352, 353, 354, 355, 356, 357,
358, 360, 361, 362, 363, 364, 365,
408
precondition, 144
print, 140, 191, 382, 384
Print, 305
println, 82, 120, 129, 150, 191, 217, 218,
222, 223, 225, 233, 235, 236, 240,
266, 267, 268, 269, 375
private, 16, 18, 19, 23, 26, 38, 43, 44,
71, 78, 79, 96, 101, 114, 125, 127,
137, 138, 139, 151, 158, 164, 168,
169, 207, 221, 222, 224, 225, 239,
302, 303, 312, 326, 343, 345, 346,
352, 354, 362, 365
professor, xiii, xvii, 7, 8, 12, 14, 22, 69,
91, 99, 100, 101, 102, 107, 109, 110,
111, 112, 115, 116, 117, 118, 120,
121, 122, 124, 125, 128, 131, 141,
142, 147, 148, 149, 156, 181, 275,
302, 334
Professor, xvii, 7, 8, 12, 14, 48, 67, 68,
71, 77, 86, 88, 98, 99, 100, 101, 102,
107, 144, 181, 203, 227, 274, 275,
302, 374, 377
protected, 45, 71, 83, 206, 237
public, 16, 17, 18, 20, 23, 24, 26, 27, 35,
36, 43, 44, 45, 56, 58, 59, 60, 61, 62,
69, 70, 71, 72, 73, 79, 80, 85, 86, 96,
98, 99, 101, 102, 116, 117, 118, 119,
120, 121, 123, 124, 125, 126, 127,
128, 129, 135, 137, 141, 144, 145,
146, 147, 149, 150, 151, 152, 153,
154, 160, 161, 164, 166, 168, 169,
174, 179, 185, 193, 194, 195, 197,
202, 204, 205, 206, 207, 208, 209,
216, 218, 223, 225, 232, 235, 236,
237, 239, 242, 253, 255, 266, 270,
271, 273, 279, 291, 292, 293, 295,
296, 303, 304, 306, 312, 313, 314,
315, 345, 346, 352, 354, 355, 357,
358, 359, 361, 362, 363, 369, 370,
371, 372, 373, 374
public static void main, 369, 373, 374
purple, 321
question mark, 34, 162
queue, 313, 370, 371, 382
RAM, 2, 213
rational number, 341, 344, 345, 346, 347
409
410
411
412
413