Javanotes5 1 50
Javanotes5 1 50
David J. Eck
Hobart and William Smith Colleges
ii
Preface xiii
iii
iv CONTENTS
3 Control 61
3.1 Blocks, Loops, and Branches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.1.1 Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.1.2 The Basic While Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.1.3 The Basic If Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
3.2 Algorithm Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.2.1 Pseudocode and Stepwise Refinement . . . . . . . . . . . . . . . . . . . . 66
3.2.2 The 3N+1 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3.2.3 Coding, Testing, Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.3 while and do..while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.3.1 The while Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
3.3.2 The do..while Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
3.3.3 break and continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.4 The for Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.4.1 For Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.4.2 Example: Counting Divisors . . . . . . . . . . . . . . . . . . . . . . . . . . 83
3.4.3 Nested for Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
3.4.4 Enums and for-each Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.5 The if Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
3.5.1 The Dangling else Problem . . . . . . . . . . . . . . . . . . . . . . . . . . 89
3.5.2 The if...else if Construction . . . . . . . . . . . . . . . . . . . . . . . . . . 89
3.5.3 If Statement Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.5.4 The Empty Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
3.6 The switch Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
3.6.1 The Basic switch Statement . . . . . . . . . . . . . . . . . . . . . . . . . . 96
3.6.2 Menus and switch Statements . . . . . . . . . . . . . . . . . . . . . . . . . 97
3.6.3 Enums in switch Statements . . . . . . . . . . . . . . . . . . . . . . . . . 98
3.6.4 Definite Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
3.7 Exceptions and try..catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
3.7.1 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
3.7.2 try..catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
3.7.3 Exceptions in TextIO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3.8 GUI Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Exercises for Chapter 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Quiz on Chapter 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
4 Subroutines 117
4.1 Black Boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
4.2 Static Subroutines and Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.2.1 Subroutine Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.2.2 Calling Subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
CONTENTS v
7 Arrays 313
7.1 Creating and Using Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
7.1.1 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
7.1.2 Using Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
7.1.3 Array Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
7.2 Programming With Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
7.2.1 Arrays and for Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
7.2.2 Arrays and for-each Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
7.2.3 Array Types in Subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . 321
7.2.4 Random Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
7.2.5 Arrays of Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
7.2.6 Variable Arity Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
7.3 Dynamic Arrays and ArrayLists . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
7.3.1 Partially Full Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
7.3.2 Dynamic Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
7.3.3 ArrrayLists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
7.3.4 Parameterized Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
7.3.5 Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
7.4 Searching and Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
7.4.1 Searching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
7.4.2 Association Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
7.4.3 Insertion Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
7.4.4 Selection Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
7.4.5 Unsorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
viii CONTENTS
xiii
xiv Preface
of programming to cover more advanced topics. Chapter 8 is mostly about writing robust and
correct programs, but it also has a section on parallel processing and threads. Chapters 9 and
10 cover recursion and data structures, including the Java Collection Framework. Chapter 11 is
about files and networking. Finally, Chapter 12 returns to the topic of graphical user interface
programming to cover some of Java’s more advanced capabilities.
∗ ∗ ∗
Major changes have been made in the fifth edition. Perhaps the most significant change is
the use of parameterized types in the chapter on generic programming. Parameterized types—
Java’s version of templates—were the most eagerly anticipated new feature in Java 5.0.
Other new features in Java 5.0 are also covered. Enumerated types are introduced, although
they are not covered in their full complexity. The “for-each” loop is covered and is used
extensively. Formatted output is also used extensively, and the Scanner class is covered (though
not until Chapter 11). Static import is covered briefly, as are variable arity methods.
The non-standard TextIO class that I use for input in the first half of the book has been
rewritten to support formatted output. I have also added some file I/O capabilities to this class
to make it possible to cover some examples that use files early in the book.
Javadoc comments are covered for the first time in this edition. Almost all code examples
have been revised to use Javadoc-style comments.
The coverage of graphical user interface programming has been reorganized, much of it has
been rewritten, and new material has been added. In previous editions, I emphasized applets.
Stand-alone GUI applications were covered at the end, almost as an afterthought. In the fifth
edition, the emphasis on applets is gone, and almost all examples are presented as stand-alone
applications. However, applet versions of each example are still presented on the web pages of
the on-line version of the book. The chapter on advanced GUI programming has been moved
to the end, and a significant amount of new material has been added, including coverage of
some of the features of Graphics2D.
Aside from the changes in content, the appearance of the book has been improved, especially
the appearance of the PDF version. For the first time, the quality of the PDF approaches that
of conventional textbooks.
∗ ∗ ∗
The latest complete edition of Introduction to Programming using Java is always available
on line at http://math.hws.edu/javanotes/. The first version of the book was written in 1996,
and there have been several editions since then. All editions are archived at the following Web
addresses:
• First edition: http://math.hws.edu/eck/cs124/javanotes1/ (Covers Java 1.0.)
• Second edition: http://math.hws.edu/eck/cs124/javanotes2/ (Covers Java 1.1.)
• Third edition: http://math.hws.edu/eck/cs124/javanotes3/ (Covers Java 1.1.)
• Fourth edition: http://math.hws.edu/eck/cs124/javanotes4/ (Covers Java 1.4.)
• Fifth edition: http://math.hws.edu/eck/cs124/javanotes5/ (Covers Java 5.0.)
Introduction to Programming using Java is free, but it is not in the public domain. As
of Version 5.0, it is published under the terms of the Creative Commons Attribution-Share
Alike 2.5 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-
sa/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco,
California, 94105, USA. This license allows redistribution and modification under certain terms.
For example, you can:
Preface xv
• Post an unmodified copy of the on-line version on your own Web site (including the parts
that list the author and state the license under which it is distributed!).
• Give away or sell printed, unmodified copies of this book, as long as they meet the re-
quirements of the license.
• Make modified copies of the complete book or parts of it and post them on the web or
otherwise distribute them, provided that attribution to the author is given, the modifica-
tions are clearly noted, and the modified copies are distributed under the same license as
the original. This includes translations to other languages.
While it is not actually required by the license, I do appreciate hearing from people who
are using or distributing my work.
∗ ∗ ∗
A technical note on production: The on-line and PDF versions of this book are created
from a single source, which is written largely in XML. To produce the PDF version, the XML
is processed into a form that can be used by the TeX typesetting program. In addition to XML
files, the source includes DTDs, XSLT transformations, Java source code files, image files, a
TeX macro file, and a couple of scripts that are used in processing. I have not made the source
materials available for download, since they are not in a clean enough form to be publishable,
and because it would require a fair amount of expertise to make any use of them. However,
they are not meant to be secret, and I am willing to make them available on request.
∗ ∗ ∗
Professor David J. Eck
Department of Mathematics and Computer Science
Hobart and William Smith Colleges
Geneva, New York 14456, USA
Email: [email protected]
WWW: http://math.hws.edu/eck/
xvi Preface
Chapter 1
When you begin a journey, it’s a good idea to have a mental map of the terrain you’ll
be passing through. The same is true for an intellectual journey, such as learning to write
computer programs. In this case, you’ll need to know the basics of what computers are and
how they work. You’ll want to have some idea of what a computer program is and how one is
created. Since you will be writing programs in the Java programming language, you’ll want to
know something about that language in particular and about the modern, networked computing
environment for which Java is designed.
As you read this chapter, don’t worry if you can’t understand everything in detail. (In fact,
it would be impossible for you to learn all the details from the brief expositions in this chapter.)
Concentrate on learning enough about the big ideas to orient yourself, in preparation for the
rest of the book. Most of what is covered in this chapter will be covered in much greater detail
later in the book.
1
2 CHAPTER 1. THE MENTAL LANDSCAPE
location. The CPU can also store information in memory by specifying the information to be
stored and the address of the location where it is to be stored.
On the level of machine language, the operation of the CPU is fairly straightforward (al-
though it is very complicated in detail). The CPU executes a program that is stored as a
sequence of machine language instructions in main memory. It does this by repeatedly reading,
or fetching , an instruction from memory and then carrying out, or executing , that instruc-
tion. This process—fetch an instruction, execute it, fetch another instruction, execute it, and so
on forever—is called the fetch-and-execute cycle. With one exception, which will be covered
in the next section, this is all that the CPU ever does.
The details of the fetch-and-execute cycle are not terribly important, but there are a few
basic things you should know. The CPU contains a few internal registers, which are small
memory units capable of holding a single number or machine language instruction. The CPU
uses one of these registers—the program counter , or PC—to keep track of where it is in the
program it is executing. The PC stores the address of the next instruction that the CPU should
execute. At the beginning of each fetch-and-execute cycle, the CPU checks the PC to see which
instruction it should fetch. During the course of the fetch-and-execute cycle, the number in the
PC is updated to indicate the instruction that is to be executed in the next cycle. (Usually,
but not always, this is just the instruction that sequentially follows the current instruction in
the program.)
∗ ∗ ∗
Memory
00101110 (Location 0)
11010011 (Location 1)
Data to memory 01010011 (Location 2)
00010000 (Location 3)
CPU 10111111
Data from memory 10100110
11101001
Program 00000111
counter: 10100110
Address for
1011100001 reading/writing 00010001
data 00111110 (Location 10)
∗ ∗ ∗
A computer system consisting of many devices is typically organized by connecting those
devices to one or more busses. A bus is a set of wires that carry various sorts of information
between the devices connected to those wires. The wires carry data, addresses, and control
signals. An address directs the data to a particular device and perhaps to a particular register
or location within that device. Control signals can be used, for example, by one device to alert
another that data is available for it on the data bus. A fairly simple computer system might
be organized like this:
Now, devices such as keyboard, mouse, and network interface can produce input that needs
to be processed by the CPU. How does the CPU know that the data is there? One simple idea,
which turns out to be not very satisfactory, is for the CPU to keep checking for incoming data
over and over. Whenever it finds data, it processes it. This method is called polling , since
the CPU polls the input devices continually to see whether they have any input data to report.
Unfortunately, although polling is very simple, it is also very inefficient. The CPU can waste
an awful lot of time just waiting for input.
To avoid this inefficiency, interrupts are often used instead of polling. An interrupt is
a signal sent by another device to the CPU. The CPU responds to an interrupt signal by
putting aside whatever it is doing in order to respond to the interrupt. Once it has handled
the interrupt, it returns to what it was doing before the interrupt occurred. For example, when
you press a key on your computer keyboard, a keyboard interrupt is sent to the CPU. The
CPU responds to this signal by interrupting what it is doing, reading the key that you pressed,
processing it, and then returning to the task it was performing before you pressed the key.
Again, you should understand that this is a purely mechanical process: A device signals an
interrupt simply by turning on a wire. The CPU is built so that when that wire is turned on,
the CPU saves enough information about what it is currently doing so that it can return to
the same state later. This information consists of the contents of important internal registers
such as the program counter. Then the CPU jumps to some predetermined memory location
and begins executing the instructions stored there. Those instructions make up an interrupt
handler that does the processing necessary to respond to the interrupt. (This interrupt handler
is part of the device driver software for the device that signalled the interrupt.) At the end of
1.2. ASYNCHRONOUS EVENTS 5
the interrupt handler is an instruction that tells the CPU to jump back to what it was doing;
it does that by restoring its previously saved state.
Interrupts allow the CPU to deal with asynchronous events. In the regular fetch-and-
execute cycle, things happen in a predetermined order; everything that happens is “synchro-
nized” with everything else. Interrupts make it possible for the CPU to deal efficiently with
events that happen “asynchronously,” that is, at unpredictable times.
As another example of how interrupts are used, consider what happens when the CPU needs
to access data that is stored on the hard disk. The CPU can access data directly only if it is
in main memory. Data on the disk has to be copied into memory before it can be accessed.
Unfortunately, on the scale of speed at which the CPU operates, the disk drive is extremely
slow. When the CPU needs data from the disk, it sends a signal to the disk drive telling it
to locate the data and get it ready. (This signal is sent synchronously, under the control of a
regular program.) Then, instead of just waiting the long and unpredictalble amount of time
that the disk drive will take to do this, the CPU goes on with some other task. When the disk
drive has the data ready, it sends an interrupt signal to the CPU. The interrupt handler can
then read the requested data.
∗ ∗ ∗
Now, you might have noticed that all this only makes sense if the CPU actually has several
tasks to perform. If it has nothing better to do, it might as well spend its time polling for input
or waiting for disk drive operations to complete. All modern computers use multitasking to
perform several tasks at once. Some computers can be used by several people at once. Since the
CPU is so fast, it can quickly switch its attention from one user to another, devoting a fraction
of a second to each user in turn. This application of multitasking is called timesharing . But a
modern personal computer with just a single user also uses multitasking. For example, the user
might be typing a paper while a clock is continuously displaying the time and a file is being
downloaded over the network.
Each of the individual tasks that the CPU is working on is called a thread . (Or a process;
there are technical differences between threads and processes, but they are not important here.)
At any given time, only one thread can actually be executed by a CPU. The CPU will continue
running the same thread until one of several things happens:
• The thread might voluntarily yield control, to give other threads a chance to run.
• The thread might have to wait for some asynchronous event to occur. For example, the
thread might request some data from the disk drive, or it might wait for the user to press
a key. While it is waiting, the thread is said to be blocked , and other threads have a
chance to run. When the event occurs, an interrupt will “wake up” the thread so that it
can continue running.
• The thread might use up its allotted slice of time and be suspended to allow other threads
to run. Not all computers can “forcibly” suspend a thread in this way; those that can
are said to use preemptive multitasking . To do preemptive multitasking, a computer
needs a special timer device that generates an interrupt at regular intervals, such as 100
times per second. When a timer interrupt occurs, the CPU has a chance to switch from
one thread to another, whether the thread that is currently running likes it or not.
Ordinary users, and indeed ordinary programmers, have no need to deal with interrupts and
interrupt handlers. They can concentrate on the different tasks or threads that they want the
computer to perform; the details of how the computer manages to get all those tasks done are
not important to them. In fact, most users, and many programmers, can ignore threads and
6 CHAPTER 1. THE MENTAL LANDSCAPE
J a v a I n t e r p r e t e r
f
o r M a c O S
J a v a
J a v a J a v a I n t e r p r e t e r
o m p i l e r B y t e c o d e
f
P r o g r a m C o r W i n d o w s
P r o g r a m
J a v a I n t e r p r e t e r
f
o r L i n u x
Why, you might wonder, use the intermediate Java bytecode at all? Why not just distribute
the original Java program and let each person compile it into the machine language of whatever
computer they want to run it on? There are many reasons. First of all, a compiler has to
understand Java, a complex high-level language. The compiler is itself a complex program. A
Java bytecode interpreter, on the other hand, is a fairly small, simple program. This makes it
easy to write a bytecode interpreter for a new type of computer; once that is done, that computer
can run any compiled Java program. It would be much harder to write a Java compiler for the
same computer.
Furthermore, many Java programs are meant to be downloaded over a network. This leads
to obvious security concerns: you don’t want to download and run a program that will damage
your computer or your files. The bytecode interpreter acts as a buffer between you and the
program you download. You are really running the interpreter, which runs the downloaded
program indirectly. The interpreter can protect you from potentially dangerous actions on the
part of that program.
I should note that there is no necessary connection between Java and Java bytecode. A pro-
gram written in Java could certainly be compiled into the machine language of a real computer.
And programs written in other languages could be compiled into Java bytecode. However, it is
the combination of Java and Java bytecode that is platform-independent, secure, and network-
compatible while allowing you to program in a modern high-level object-oriented language.
8 CHAPTER 1. THE MENTAL LANDSCAPE
∗ ∗ ∗
I should also note that the really hard part of platform-independence is providing a “Graph-
ical User Interface”—with windows, buttons, etc.—that will work on all the platforms that
support Java. You’ll see more about this problem in Section 1.6.
interest should be computed by multiplying the principal by 0.04. A program needs some
way of expressing this type of decision. In Java, it could be expressed using the following “if
statement”:
if (principal > 10000)
interest = principal * 0.05;
else
interest = principal * 0.04;
(Don’t worry about the details for now. Just remember that the computer can test a condition
and decide what to do next on the basis of that test.)
Loops are used when the same task has to be performed more than once. For example,
if you want to print out a mailing label for each name on a mailing list, you might say, “Get
the first name and address and print the label; get the second name and address and print
the label; get the third name and address and print the label—” But this quickly becomes
ridiculous—and might not work at all if you don’t know in advance how many names there are.
What you would like to say is something like “While there are more names to process, get the
next name and address, and print the label.” A loop can be used in a program to express such
repetition.
∗ ∗ ∗
Large programs are so complex that it would be almost impossible to write them if there
were not some way to break them up into manageable “chunks.” Subroutines provide one way to
do this. A subroutine consists of the instructions for performing some task, grouped together
as a unit and given a name. That name can then be used as a substitute for the whole set of
instructions. For example, suppose that one of the tasks that your program needs to perform
is to draw a house on the screen. You can take the necessary instructions, make them into
a subroutine, and give that subroutine some appropriate name—say, “drawHouse()”. Then
anyplace in your program where you need to draw a house, you can do so with the single
command:
drawHouse();
This will have the same effect as repeating all the house-drawing instructions in each place.
The advantage here is not just that you save typing. Organizing your program into sub-
routines also helps you organize your thinking and your program design effort. While writing
the house-drawing subroutine, you can concentrate on the problem of drawing a house without
worrying for the moment about the rest of the program. And once the subroutine is written,
you can forget about the details of drawing houses—that problem is solved, since you have a
subroutine to do it for you. A subroutine becomes just like a built-in part of the language which
you can use without thinking about the details of what goes on “inside” the subroutine.
∗ ∗ ∗
Variables, types, loops, branches, and subroutines are the basis of what might be called
“traditional programming.” However, as programs become larger, additional structure is needed
to help deal with their complexity. One of the most effective tools that has been found is object-
oriented programming, which is discussed in the next section.
the construction of correct, working, well-written programs. The software engineer tends to
use accepted and proven methods for analyzing the problem to be solved and for designing a
program to solve that problem.
During the 1970s and into the 80s, the primary software engineering methodology was
structured programming . The structured programming approach to program design was
based on the following advice: To solve a large problem, break the problem into several pieces
and work on each piece separately; to solve each piece, treat it as a new problem which can itself
be broken down into smaller problems; eventually, you will work your way down to problems
that can be solved directly, without further decomposition. This approach is called top-down
programming .
There is nothing wrong with top-down programming. It is a valuable and often-used ap-
proach to problem-solving. However, it is incomplete. For one thing, it deals almost entirely
with producing the instructions necessary to solve a problem. But as time went on, people
realized that the design of the data structures for a program was as least as important as the
design of subroutines and control structures. Top-down programming doesn’t give adequate
consideration to the data that the program manipulates.
Another problem with strict top-down programming is that it makes it difficult to reuse
work done for other projects. By starting with a particular problem and subdividing it into
convenient pieces, top-down programming tends to produce a design that is unique to that
problem. It is unlikely that you will be able to take a large chunk of programming from another
program and fit it into your project, at least not without extensive modification. Producing
high-quality programs is difficult and expensive, so programmers and the people who employ
them are always eager to reuse past work.
∗ ∗ ∗
So, in practice, top-down design is often combined with bottom-up design. In bottom-up
design, the approach is to start “at the bottom,” with problems that you already know how to
solve (and for which you might already have a reusable software component at hand). From
there, you can work upwards towards a solution to the overall problem.
The reusable components should be as “modular” as possible. A module is a component of a
larger system that interacts with the rest of the system in a simple, well-defined, straightforward
manner. The idea is that a module can be “plugged into” a system. The details of what goes on
inside the module are not important to the system as a whole, as long as the module fulfills its
assigned role correctly. This is called information hiding , and it is one of the most important
principles of software engineering.
One common format for software modules is to contain some data, along with some sub-
routines for manipulating that data. For example, a mailing-list module might contain a list of
names and addresses along with a subroutine for adding a new name, a subroutine for printing
mailing labels, and so forth. In such modules, the data itself is often hidden inside the module;
a program that uses the module can then manipulate the data only indirectly, by calling the
subroutines provided by the module. This protects the data, since it can only be manipulated
in known, well-defined ways. And it makes it easier for programs to use the module, since they
don’t have to worry about the details of how the data is represented. Information about the
representation of the data is hidden.
Modules that could support this kind of information-hiding became common in program-
ming languages in the early 1980s. Since then, a more advanced form of the same idea has
more or less taken over software engineering. This latest approach is called object-oriented
programming , often abbreviated as OOP.
1.5. OBJECT-ORIENTED PROGRAMMING 11
The central concept of object-oriented programming is the object, which is a kind of module
containing data and subroutines. The point-of-view in OOP is that an object is a kind of self-
sufficient entity that has an internal state (the data it contains) and that can respond to
messages (calls to its subroutines). A mailing list object, for example, has a state consisting
of a list of names and addresses. If you send it a message telling it to add a name, it will
respond by modifying its state to reflect the change. If you send it a message telling it to print
itself, it will respond by printing out its list of names and addresses.
The OOP approach to software engineering is to start by identifying the objects involved in
a problem and the messages that those objects should respond to. The program that results is
a collection of objects, each with its own data and its own set of responsibilities. The objects
interact by sending messages to each other. There is not much “top-down” in such a program,
and people used to more traditional programs can have a hard time getting used to OOP.
However, people who use OOP would claim that object-oriented programs tend to be better
models of the way the world itself works, and that they are therefore easier to write, easier to
understand, and more likely to be correct.
∗ ∗ ∗
You should think of objects as “knowing” how to respond to certain messages. Different
objects might respond to the same message in different ways. For example, a “print” message
would produce very different results, depending on the object it is sent to. This property of
objects—that different objects can respond to the same message in different ways—is called
polymorphism .
It is common for objects to bear a kind of “family resemblance” to one another. Objects
that contain the same type of data and that respond to the same messages in the same way
belong to the same class. (In actual programming, the class is primary; that is, a class is
created and then one or more objects are created using that class as a template.) But objects
can be similar without being in exactly the same class.
For example, consider a drawing program that lets the user draw lines, rectangles, ovals,
polygons, and curves on the screen. In the program, each visible object on the screen could be
represented by a software object in the program. There would be five classes of objects in the
program, one for each type of visible object that can be drawn. All the lines would belong to
one class, all the rectangles to another class, and so on. These classes are obviously related;
all of them represent “drawable objects.” They would, for example, all presumably be able to
respond to a “draw yourself” message. Another level of grouping, based on the data needed
to represent each type of object, is less obvious, but would be very useful in a program: We
can group polygons and curves together as “multipoint objects,” while lines, rectangles, and
ovals are “two-point objects.” (A line is determined by its endpoints, a rectangle by two of its
corners, and an oval by two corners of the rectangle that contains it.) We could diagram these
relationships as follows:
12 CHAPTER 1. THE MENTAL LANDSCAPE
D r a w a b l e O b j e c t
M u l t i p o i n t O b j e c t T w o P o i n t O b j e c t
P o l y g o n C u r v e L e c t a n g l e O v a l
i n e R
and Linux. Java programs, which are supposed to run on many different platforms without
modification to the program, can use all the standard GUI components. They might vary a
little in appearance from platform to platform, but their functionality should be identical on
any computer on which the program runs.
Shown below is an image of a very simple Java program—actually an “applet”, since it is
meant to appear on a Web page—that shows a few standard GUI interface components. There
are four components that the user can interact with: a button, a checkbox, a text field, and a
pop-up menu. These components are labeled. There are a few other components in the applet.
The labels themselves are components (even though you can’t interact with them). The right
half of the applet is a text area component, which can display multiple lines of text, and a
scrollbar component appears alongside the text area when the number of lines of text becomes
larger than will fit in the text area. And in fact, in Java terminology, the whole applet is itself
considered to be a “component.”
Now, Java actually has two complete sets of GUI components. One of these, the AWT or
Abstract Windowing Toolkit, was available in the original version of Java. The other, which
is known as Swing , is included in Java version 1.2 or later, and is used in preference to the
AWT in most modern Java programs. The applet that is shown above uses components that
are part of Swing. If your Web browser uses an old version of Java, you might get an error
when the browser tries to load the applet. Remember that most of the applets in this textbook
require Java 5.0 (or higher).
When a user interacts with the GUI components in this applet, an “event” is generated.
For example, clicking a push button generates an event, and pressing return while typing in a
text field generates an event. Each time an event is generated, a message is sent to the applet
telling it that the event has occurred, and the applet responds according to its program. In
fact, the program consists mainly of “event handlers” that tell the applet how to respond to
various types of events. In this example, the applet has been programmed to respond to each
event by displaying a message in the text area.
The use of the term “message” here is deliberate. Messages, as you saw in the previous sec-
tion, are sent to objects. In fact, Java GUI components are implemented as objects. Java
includes many predefined classes that represent various types of GUI components. Some of
these classes are subclasses of others. Here is a diagram showing some of Swing’s GUI classes
and their relationships:
14 CHAPTER 1. THE MENTAL LANDSCAPE
J C o m p o n e n t
J L a b e l J A b s t r a c t B u t t o n J C o m b o B o x J S c r o l l b a r J T e x t C o m p o n e n t
J B u t t o n J T o g g l e B u t t o n J T e x t F i e l d J T e x t A r e a
J C h e c k B o x J R a d i o B u t t o n
Don’t worry about the details for now, but try to get some feel about how object-oriented
programming and inheritance are used here. Note that all the GUI classes are subclasses,
directly or indirectly, of a class called JComponent, which represents general properties that are
shared by all Swing components. Two of the direct subclasses of JComponent themselves have
subclasses. The classes JTextArea and JTextField, which have certain behaviors in common,
are grouped together as subclasses of JTextComponent. Similarly JButton and JToggleButton
are subclasses of JAbstractButton, which represents properties common to both buttons and
checkboxes. (JComboBox, by the way, is the Swing class that represents pop-up menus.)
Just from this brief discussion, perhaps you can see how GUI programming can make effec-
tive use of object-oriented design. In fact, GUI’s, with their “visible objects,” are probably a
major factor contributing to the popularity of OOP.
Programming with GUI components and events is one of the most interesting aspects of
Java. However, we will spend several chapters on the basics before returning to this topic in
Chapter 6.
use TCP/IP to send specific types of information such as web pages, electronic mail, and data
files.
All communication over the Internet is in the form of packets. A packet consists of some
data being sent from one computer to another, along with addressing information that indicates
where on the Internet that data is supposed to go. Think of a packet as an envelope with an
address on the outside and a message on the inside. (The message is the data.) The packet
also includes a “return address,” that is, the address of the sender. A packet can hold only
a limited amount of data; longer messages must be divided among several packets, which are
then sent individually over the net and reassembled at their destination.
Every computer on the Internet has an IP address, a number that identifies it uniquely
among all the computers on the net. The IP address is used for addressing packets. A computer
can only send data to another computer on the Internet if it knows that computer’s IP address.
Since people prefer to use names rather than numbers, most computers are also identified by
names, called domain names. For example, the main computer of the Mathematics Depart-
ment at Hobart and William Smith Colleges has the domain name math.hws.edu. (Domain
names are just for convenience; your computer still needs to know IP addresses before it can
communicate. There are computers on the Internet whose job it is to translate domain names
to IP addresses. When you use a domain name, your computer sends a message to a domain
name server to find out the corresponding IP address. Then, your computer uses the IP address,
rather than the domain name, to communicate with the other computer.)
The Internet provides a number of services to the computers connected to it (and, of course,
to the users of those computers). These services use TCP/IP to send various types of data over
the net. Among the most popular services are instant messaging, file sharing, electronic mail,
and the World-Wide Web. Each service has its own protocols, which are used to control
transmission of data over the network. Each service also has some sort of user interface, which
allows the user to view, send, and receive data through the service.
For example, the email service uses a protocol known as SMTP (Simple Mail Transfer
Protocol) to transfer email messages from one computer to another. Other protocols, such as
POP and IMAP, are used to fetch messages from an email account so that the recipient can
read them. A person who uses email, however, doesn’t need to understand or even know about
these protocols. Instead, they are used behind the scenes by the programs that the person uses
to send and receive email messages. These programs provide an easy-to-use user interface to
the underlying network protocols.
The World-Wide Web is perhaps the most exciting of network services. The World-Wide
Web allows you to request pages of information that are stored on computers all over the
Internet. A Web page can contain links to other pages on the same computer from which it
was obtained or to other computers anywhere in the world. A computer that stores such pages
of information is called a web server . The user interface to the Web is the type of program
known as a web browser . Common web browsers include Internet Explorer and Firefox. You
use a Web browser to request a page of information. The browser will send a request for that
page to the computer on which the page is stored, and when a response is received from that
computer, the web browser displays it to you in a neatly formatted form. A web browser is just
a user interface to the Web. Behind the scenes, the web browser uses a protocol called HTTP
(HyperText Transfer Protocol) to send each page request and to receive the response from the
web server.
∗ ∗ ∗
Now just what, you might be thinking, does all this have to do with Java? In fact, Java
16 CHAPTER 1. THE MENTAL LANDSCAPE
is intimately associated with the Internet and the World-Wide Web. As you have seen in the
previous section, special Java programs called applets are meant to be transmitted over the
Internet and displayed on Web pages. A Web server transmits a Java applet just as it would
transmit any other type of information. A Web browser that understands Java—that is, that
includes an interpreter for the Java virtual machine—can then run the applet right on the Web
page. Since applets are programs, they can do almost anything, including complex interaction
with the user. With Java, a Web page becomes more than just a passive display of information.
It becomes anything that programmers can imagine and implement.
But applets are only one aspect of Java’s relationship with the Internet, and not the major
one. In fact, as both Java and the Internet have matured, applets have become less important.
At the same time, however, Java has increasingly been used to write complex, stand-alone
applications that do not depend on a web browser. Many of these programs are network-
related. For example many of the largest and most complex web sites use web server software
that is written in Java. Java includes excellent support for network protocols, and its platform
independence makes it possible to write network programs that work on many different types
of computer.
Its association with the Internet is not Java’s only advantage. But many good programming
languages have been invented only to be soon forgotten. Java has had the good luck to ride on
the coattails of the Internet’s immense and increasing popularity.
Quiz 17
Quiz on Chapter 1
1. One of the components of a computer is its CPU. What is a CPU and what role does it
play in a computer?
5. If you have the source code for a Java program, and you want to run that program, you
will need both a compiler and an interpreter. What does the Java compiler do, and what
does the Java interpreter do?
6. What is a subroutine?
8. What is a variable? (There are four different ideas associated with variables in Java. Try
to mention all four aspects in your answer. Hint: One of the aspects is the variable’s
name.)
10. What is the “Internet”? Give some examples of how it is used. (What kind of services
does it provide?)
18 CHAPTER 1. THE MENTAL LANDSCAPE
Chapter 2
On a basic level (the level of machine language), a computer can perform only very simple
operations. A computer performs complex tasks by stringing together large numbers of such
operations. Such tasks must be “scripted” in complete and perfect detail by programs. Creating
complex programs will never be really easy, but the difficulty can be handled to some extent by
giving the program a clear overall structure. The design of the overall structure of a program
is what I call “programming in the large.”
Programming in the small, which is sometimes called coding , would then refer to filling in
the details of that design. The details are the explicit, step-by-step instructions for performing
fairly small-scale tasks. When you do coding, you are working fairly “close to the machine,”
with some of the same concepts that you might use in machine language: memory locations,
arithmetic operations, loops and branches. In a high-level language such as Java, you get to
work with these concepts on a level several steps above machine language. However, you still
have to worry about getting all the details exactly right.
This chapter and the next examine the facilities for programming in the small in the Java
programming language. Don’t be misled by the term “programming in the small” into thinking
that this material is easy or unimportant. This material is an essential foundation for all types
of programming. If you don’t understand it, you can’t write programs, no matter how good
you get at designing their large-scale structure.
19
20 CHAPTER 2. NAMES AND THINGS
of the programming language that you are using. However, syntax is only part of the story. It’s
not enough to write a program that will run—you want a program that will run and produce
the correct result! That is, the meaning of the program has to be right. The meaning of a
program is referred to as its semantics. A semantically correct program is one that does what
you want it to.
Furthermore, a program can be syntactically and semantically correct but still be a pretty
bad program. Using the language correctly is not the same as using it well. For example, a
good program has “style.” It is written in a way that will make it easy for people to read and
to understand. It follows conventions that will be familiar to other programmers. And it has
an overall design that will make sense to human readers. The computer is completely oblivious
to such things, but to a human reader, they are paramount. These aspects of programming are
sometimes referred to as pragmatics.
When I introduce a new language feature, I will explain the syntax, the semantics, and
some of the pragmatics of that feature. You should memorize the syntax; that’s the easy part.
Then you should get a feeling for the semantics by following the examples given, making sure
that you understand how they work, and maybe writing short programs of your own to test
your understanding. And you should try to appreciate and absorb the pragmatics—this means
learning how to use the language feature well, with style that will earn you the admiration of
other programmers.
Of course, even when you’ve become familiar with all the individual features of the language,
that doesn’t make you a programmer. You still have to learn how to construct complex programs
to solve particular problems. For that, you’ll need both experience and taste. You’ll find hints
about software development throughout this textbook.
∗ ∗ ∗
We begin our exploration of Java with the problem that has become traditional for such
beginnings: to write a program that displays the message “Hello World!”. This might seem like
a trivial problem, but getting a computer to do this is really a big first step in learning a new
programming language (especially if it’s your first programming language). It means that you
understand the basic process of:
1. getting the program text into the computer,
2. compiling the program, and
3. running the compiled program.
The first time through, each of these steps will probably take you a few tries to get right. I
won’t go into the details here of how you do each of these steps; it depends on the particular
computer and Java programming environment that you are using. See Section 2.6 for informa-
tion about creating and running Java programs in specific programming environments. But in
general, you will type the program using some sort of text editor and save the program in a
file. Then, you will use some command to try to compile the file. You’ll either get a message
that the program contains syntax errors, or you’ll get a compiled version of the program. In
the case of Java, the program is compiled into Java bytecode, not into machine language. Fi-
nally, you can run the compiled program by giving some appropriate command. For Java, you
will actually use an interpreter to execute the Java bytecode. Your programming environment
might automate some of the steps for you, but you can be sure that the same three steps are
being done in the background.
Here is a Java program to display the message “Hello World!”. Don’t expect to understand
what’s going on here just yet—some of it you won’t really understand until a few chapters from
2.1. THE BASIC JAVA APPLICATION 21
now:
// A program to display the message
// "Hello World!" on standard output
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
} // end of class HelloWorld
When you tell the Java interpreter to run the program, the interpreter calls the main()
subroutine, and the statements that it contains are executed. These statements make up the
script that tells the computer exactly what to do when the program is executed. The main()
routine can call subroutines that are defined in the same class or even in other classes, but it is
the main() routine that determines how and in what order the other subroutines are used.
22 CHAPTER 2. NAMES AND THINGS
The word “public” in the first line of main() means that this routine can be called from out-
side the program. This is essential because the main() routine is called by the Java interpreter,
which is something external to the program itself. The remainder of the first line of the routine
is harder to explain at the moment; for now, just think of it as part of the required syntax.
The definition of the subroutine—that is, the instructions that say what it does—consists of
the sequence of “statements” enclosed between braces, { and }. Here, I’ve used hstatementsi as
a placeholder for the actual statements that make up the program. Throughout this textbook,
I will always use a similar format: anything that you see in hthis style of texti (italic in angle
brackets) is a placeholder that describes something you need to type when you write an actual
program.
As noted above, a subroutine can’t exist by itself. It has to be part of a “class”. A program
is defined by a public class that takes the form:
public class hprogram-name i {
hoptional-variable-declarations-and-subroutines i
public static void main(String[] args) {
hstatements i
}
hoptional-variable-declarations-and-subroutines i
}
The name on the first line is the name of the program, as well as the name of the class. If the
name of the class is HelloWorld, then the class must be saved in a file called HelloWorld.java.
When this file is compiled, another file named HelloWorld.class will be produced. This class
file, HelloWorld.class, contains the Java bytecode that is executed by a Java interpreter.
HelloWorld.java is called the source code for the program. To execute the program, you
only need the compiled class file, not the source code.
The layout of the program on the page, such as the use of blank lines and indentation, is
not part of the syntax or semantics of the language. The computer doesn’t care about layout—
you could run the entire program together on one line as far as it is concerned. However,
layout is important to human readers, and there are certain style guidelines for layout that are
followed by most programmers. These style guidelines are part of the pragmatics of the Java
programming language.
Also note that according to the above syntax specification, a program can contain other
subroutines besides main(), as well as things called “variable declarations.” You’ll learn more
about these later, but not until Chapter 4.
No spaces are allowed in identifiers; HelloWorld is a legal identifier, but “Hello World” is
not. Upper case and lower case letters are considered to be different, so that HelloWorld,
helloworld, HELLOWORLD, and hElloWorLD are all distinct names. Certain names are reserved
for special uses in Java, and cannot be used by the programmer for other purposes. These
reserved words include: class, public, static, if, else, while, and several dozen other
words.
Java is actually pretty liberal about what counts as a letter or a digit. Java uses the
Unicode character set, which includes thousands of characters from many different languages
and different alphabets, and many of these characters count as letters or digits. However, I will
be sticking to what can be typed on a regular English keyboard.
The pragmatics of naming includes style guidelines about how to choose names for things.
For example, it is customary for names of classes to begin with upper case letters, while names
of variables and of subroutines begin with lower case letters; you can avoid a lot of confusion
by following the same convention in your own programs. Most Java programmers do not use
underscores in names, although some do use them at the beginning of the names of certain kinds
of variables. When a name is made up of several words, such as HelloWorld or interestRate,
it is customary to capitalize each word, except possibly the first; this is sometimes referred
to as camel case, since the upper case letters in the middle of a name are supposed to look
something like the humps on a camel’s back.
Finally, I’ll note that things are often referred to by compound names which consist
of several ordinary names separated by periods. (Compound names are also called qualified
names.) You’ve already seen an example: System.out.println. The idea here is that things
in Java can contain other things. A compound name is a kind of path to an item through one
or more levels of containment. The name System.out.println indicates that something called
“System” contains something called “out” which in turn contains something called “println”.
Non-compound names are called simple identifiers. I’ll use the term identifier to refer to
any name—simple or compound—that can be used to refer to something in Java. (Note that
the reserved words are not identifiers, since they can’t be used as names for things.)
2.2.1 Variables
Programs manipulate data that are stored in memory. In machine language, data can only
be referred to by giving the numerical address of the location in memory where it is stored.
In a high-level language such as Java, names are used instead of numbers to refer to data. It
is the job of the computer to keep track of where in memory the data is actually stored; the
programmer only has to remember the name. A name used in this way—to refer to data stored
in memory—is called a variable.
Variables are actually rather subtle. Properly speaking, a variable is not a name for the
data itself but for a location in memory that can hold data. You should think of a variable as
a container or box where you can store data that you will need to use later. The variable refers
directly to the box and only indirectly to the data in the box. Since the data in the box can
change, a variable can refer to different data values at different times during the execution of
the program, but it always refers to the same box. Confusion can arise, especially for beginning
programmers, because when a variable is used in a program in certain ways, it refers to the
container, but when it is used in other ways, it refers to the data in the container. You’ll see
examples of both cases below.
(In this way, a variable is something like the title, “The President of the United States.”
This title can refer to different people at different times, but it always refers to the same office.
24 CHAPTER 2. NAMES AND THINGS
If I say “the President went fishing,” I mean that George W. Bush went fishing. But if I say
“Hillary Clinton wants to be President” I mean that she wants to fill the office, not that she
wants to be George Bush.)
In Java, the only way to get data into a variable—that is, into the box that the variable
names—is with an assignment statement . An assignment statement takes the form:
hvariable i = hexpression i;
where hexpressioni represents anything that refers to or computes a data value. When the
computer comes to an assignment statement in the course of executing a program, it evaluates
the expression and puts the resulting data value into the variable. For example, consider the
simple assignment statement
rate = 0.07;
The hvariablei in this assignment statement is rate, and the hexpressioni is the number 0.07.
The computer executes this assignment statement by putting the number 0.07 in the variable
rate, replacing whatever was there before. Now, consider the following more complicated
assignment statement, which might come later in the same program:
interest = rate * principal;
Here, the value of the expression “rate * principal” is being assigned to the variable
interest. In the expression, the * is a “multiplication operator” that tells the computer
to multiply rate times principal. The names rate and principal are themselves variables,
and it is really the values stored in those variables that are to be multiplied. We see that when
a variable is used in an expression, it is the value stored in the variable that matters; in this
case, the variable seems to refer to the data in the box, rather than to the box itself. When
the computer executes this assignment statement, it takes the value of rate, multiplies it by
the value of principal, and stores the answer in the box referred to by interest. When a
variable is used on the left-hand side of an assignment statement, it refers to the box that is
named by the variable.
(Note, by the way, that an assignment statement is a command that is executed by the
computer at a certain time. It is not a statement of fact. For example, suppose a program
includes the statement “rate = 0.07;”. If the statement “interest = rate * principal;”
is executed later in the program, can we say that the principal is multiplied by 0.07? No!
The value of rate might have been changed in the meantime by another statement. The
meaning of an assignment statement is completely different from the meaning of an equation
in mathematics, even though both use the symbol “=”.)
Any data value stored in the computer’s memory must be represented as a binary number,
that is as a string of zeros and ones. A single zero or one is called a bit. A string of eight
bits is called a byte. Memory is usually measured in terms of bytes. Not surprisingly, the byte
data type refers to a single byte of memory. A variable of type byte holds a string of eight
bits, which can represent any of the integers between -128 and 127, inclusive. (There are 256
integers in that range; eight bits can represent 256—two raised to the power eight—different
values.) As for the other integer types,
• short corresponds to two bytes (16 bits). Variables of type short have values in the range
-32768 to 32767.
• int corresponds to four bytes (32 bits). Variables of type int have values in the range
-2147483648 to 2147483647.
• long corresponds to eight bytes (64 bits). Variables of type long have values in the range
-9223372036854775808 to 9223372036854775807.
You don’t have to remember these numbers, but they do give you some idea of the size of
integers that you can work with. Usually, you should just stick to the int data type, which is
good enough for most purposes.
The float data type is represented in four bytes of memory, using a standard method for
encoding real numbers. The maximum value for a float is about 10 raised to the power 38.
A float can have about 7 significant digits. (So that 32.3989231134 and 32.3989234399 would
both have to be rounded off to about 32.398923 in order to be stored in a variable of type
float.) A double takes up 8 bytes, can range up to about 10 to the power 308, and has about
15 significant digits. Ordinarily, you should stick to the double type for real values.
A variable of type char occupies two bytes in memory. The value of a char variable is a
single character such as A, *, x, or a space character. The value can also be a special character
such a tab or a carriage return or one of the many Unicode characters that come from different
languages. When a character is typed into a program, it must be surrounded by single quotes;
for example: ’A’, ’*’, or ’x’. Without the quotes, A would be an identifier and * would be a
multiplication operator. The quotes are not part of the value and are not stored in the variable;
they are just a convention for naming a particular character constant in a program.
A name for a constant value is called a literal . A literal is what you have to type in a
program to represent a value. ’A’ and ’*’ are literals of type char, representing the character
values A and *. Certain special characters have special literals that use a backslash, \, as an
“escape character”. In particular, a tab is represented as ’\t’, a carriage return as ’\r’, a
linefeed as ’\n’, the single quote character as ’\’’, and the backslash itself as ’\\’. Note that
even though you type two characters between the quotes in ’\t’, the value represented by this
literal is a single tab character.
Numeric literals are a little more complicated than you might expect. Of course, there
are the obvious literals such as 317 and 17.42. But there are other possibilities for expressing
numbers in a Java program. First of all, real numbers can be represented in an exponential
form such as 1.3e12 or 12.3737e-108. The “e12” and “e-108” represent powers of 10, so that
1.3e12 means 1.3 times 1012 and 12.3737e-108 means 12.3737 times 10−108 . This format can
be used to express very large and very small numbers. Any numerical literal that contains a
decimal point or exponential is a literal of type double. To make a literal of type float, you
have to append an “F” or “f” to the end of the number. For example, “1.2F” stands for 1.2
considered as a value of type float. (Occasionally, you need to know this because the rules of
Java say that you can’t assign a value of type double to a variable of type float, so you might be
26 CHAPTER 2. NAMES AND THINGS
confronted with a ridiculous-seeming error message if you try to do something like “x = 1.2;”
when x is a variable of type float. You have to say “x = 1.2F;". This is one reason why I
advise sticking to type double for real numbers.)
Even for integer literals, there are some complications. Ordinary integers such as 177777
and -32 are literals of type byte, short, or int, depending on their size. You can make a literal
of type long by adding “L” as a suffix. For example: 17L or 728476874368L. As another
complication, Java allows octal (base-8) and hexadecimal (base-16) literals. I don’t want to
cover base-8 and base-16 in detail, but in case you run into them in other people’s programs,
it’s worth knowing a few things: Octal numbers use only the digits 0 through 7. In Java, a
numeric literal that begins with a 0 is interpreted as an octal number; for example, the literal
045 represents the number 37, not the number 45. Hexadecimal numbers use 16 digits, the
usual digits 0 through 9 and the letters A, B, C, D, E, and F. Upper case and lower case letters
can be used interchangeably in this context. The letters represent the numbers 10 through 15.
In Java, a hexadecimal literal begins with 0x or 0X, as in 0x45 or 0xFF7A.
Hexadecimal numbers are also used in character literals to represent arbitrary Unicode
characters. A Unicode literal consists of \u followed by four hexadecimal digits. For example,
the character literal ’\u00E9’ represents the Unicode character that is an “e” with an acute
accent.
For the type boolean, there are precisely two literals: true and false. These literals are
typed just as I’ve written them here, without quotes, but they represent values, not variables.
Boolean values occur most often as the values of conditional expressions. For example,
is a boolean-valued expression that evaluates to true if the value of the variable rate is greater
than 0.05, and to false if the value of rate is not greater than 0.05. As you’ll see in Chapter 3,
boolean-valued expressions are used extensively in control structures. Of course, boolean values
can also be assigned to variables of type boolean.
Java has other types in addition to the primitive types, but all the other types represent
objects rather than “primitive” data values. For the most part, we are not concerned with
objects for the time being. However, there is one predefined object type that is very important:
the type String. A String is a sequence of characters. You’ve already seen a string literal:
"Hello World!". The double quotes are part of the literal; they have to be typed in the
program. However, they are not part of the actual string value, which consists of just the
characters between the quotes. Within a string, special characters can be represented using
the backslash notation. Within this context, the double quote is itself a special character. For
example, to represent the string value
with a linefeed at the end, you would have to type the string literal:
You can also use \t, \r, \\, and unicode sequences such as \u00E9 to represent other special
characters in string literals. Because strings are objects, their behavior in programs is peculiar
in some respects (to someone who is not used to objects). I’ll have more to say about them in
the next section.
2.2. VARIABLES AND TYPES 27
/* Do the computations. */
principal = 17000;
rate = 0.07;
interest = principal * rate; // Compute the interest.
} // end of main()
This program uses several subroutine call statements to display information to the user of the
program. Two different subroutines are used: System.out.print and System.out.println.
The difference between these is that System.out.println adds a linefeed after the end of the
information that it displays, while System.out.print does not. Thus, the value of interest,
which is displayed by the subroutine call “System.out.println(interest);”, follows on the
same line after the string displayed by the previous System.out.print statement. Note that
the value to be displayed by System.out.print or System.out.println is provided in paren-
theses after the subroutine name. This value is called a parameter to the subroutine. A
parameter provides a subroutine with information it needs to perform its task. In a subroutine
call statement, any parameters are listed in parentheses after the subroutine name. Not all
subroutines have parameters. If there are no parameters in a subroutine call statement, the
subroutine name must be followed by an empty pair of parentheses.
All the sample programs for this textbook are available in separate source code files in the
on-line version of this text at http://math.hws.edu/javanotes/source. They are also included
in the downloadable archives of the web site. The source code for the Interest program, for
example, can be found in the file Interest.java.
tine that sends information to that particular destination. Other objects of type PrintStream
might send information to other destinations such as files or across a network to other com-
puters. This is object-oriented programming: Many different things which have something in
common—they can all be used as destinations for information—can all be used in the same
way—through a print subroutine. The PrintStream class expresses the commonalities among
all these objects.)
Since class names and variable names are used in similar ways, it might be hard to tell
which is which. Remember that all the built-in, predefined names in Java follow the rule that
class names begin with an upper case letter while variable names begin with a lower case letter.
While this is not a formal syntax rule, I recommend that you follow it in your own programming.
Subroutine names should also begin with lower case letters. There is no possibility of confusing
a variable with a subroutine, since a subroutine name in a program is always followed by a left
parenthesis.
(As one final general note, you should be aware that subroutines in Java are often referred
to as methods. Generally, the term “method” means a subroutine that is contained in a class
or in an object. Since this is true of every subroutine in Java, every subroutine in Java is
a method. The same is not true for other programming languages. Nevertheless, the term
“method” is mostly used in the context of object-oriented programming, and until we start
doing real object-oriented programming in Chapter 5, I will prefer to use the more general
term, “subroutine.”)
∗ ∗ ∗
Classes can contain static member subroutines, as well as static member variables. For
example, the System class contains a subroutine named exit. In a program, of course, this
subroutine must be referred to as System.exit. Calling this subroutine will terminate the
program. You could use it if you had some reason to terminate the program before the end
of the main routine. For historical reasons, this subroutine takes an integer as a parameter,
so the subroutine call statement might look like “System.exit(0);” or “System.exit(1);”.
(The parameter tells the computer why the program was terminated. A parameter value of 0
indicates that the program ended normally. Any other value indicates that the program was
terminated because an error was detected. But in practice, the value of the parameter is usually
ignored.)
Every subroutine performs some specific task. For some subroutines, that task is to compute
or retrieve some data value. Subroutines of this type are called functions. We say that a
function returns a value. The returned value must then be used somehow in the program.
You are familiar with the mathematical function that computes the square root of a num-
ber. Java has a corresponding function called Math.sqrt. This function is a static member
subroutine of the class named Math. If x is any numerical value, then Math.sqrt(x) computes
and returns the square root of that value. Since Math.sqrt(x) represents a value, it doesn’t
make sense to put it on a line by itself in a subroutine call statement such as
Math.sqrt(x); // This doesn’t make sense!
What, after all, would the computer do with the value computed by the function in this case?
You have to tell the computer to do something with the value. You might tell the computer to
display it:
System.out.print( Math.sqrt(x) ); // Display the square root of x.
or you might use an assignment statement to tell the computer to store that value in a variable:
2.3. OBJECTS AND SUBROUTINES 31
lengthOfSide = Math.sqrt(x);
The function call Math.sqrt(x) represents a value of type double, and it can be used anyplace
where a numeric literal of type double could be used.
The Math class contains many static member functions. Here is a list of some of the more
important of them:
• Math.abs(x), which computes the absolute value of x.
• The usual trigonometric functions, Math.sin(x), Math.cos(x), and Math.tan(x). (For
all the trigonometric functions, angles are measured in radians, not degrees.)
• The inverse trigonometric functions arcsin, arccos, and arctan, which are written as:
Math.asin(x), Math.acos(x), and Math.atan(x). The return value is expressed in radi-
ans, not degrees.
• The exponential function Math.exp(x) for computing the number e raised to the power
x, and the natural logarithm function Math.log(x) for computing the logarithm of x in
the base e.
• Math.pow(x,y) for computing x raised to the power y.
• Math.floor(x), which rounds x down to the nearest integer value that is less than or
equal to x. Even though the return value is mathematically an integer, it is returned
as a value of type double, rather than of type int as you might expect. For example,
Math.floor(3.76) is 3.0. The function Math.round(x) returns the integer that is closest
to x.
• Math.random(), which returns a randomly chosen double in the range 0.0 <=
Math.random() < 1.0. (The computer actually calculates so-called “pseudorandom”
numbers, which are not truly random but are random enough for most purposes.)
For these functions, the type of the parameter—the x or y inside the parentheses—can be
any value of any numeric type. For most of the functions, the value returned by the function
is of type double no matter what the type of the parameter. However, for Math.abs(x), the
value returned will be the same type as x; if x is of type int, then so is Math.abs(x). So, for
example, while Math.sqrt(9) is the double value 3.0, Math.abs(9) is the int value 9.
Note that Math.random() does not have any parameter. You still need the parentheses, even
though there’s nothing between them. The parentheses let the computer know that this is a sub-
routine rather than a variable. Another example of a subroutine that has no parameters is the
function System.currentTimeMillis(), from the System class. When this function is executed,
it retrieves the current time, expressed as the number of milliseconds that have passed since a
standardized base time (the start of the year 1970 in Greenwich Mean Time, if you care). One
millisecond is one-thousandth of a second. The return value of System.currentTimeMillis()
is of type long. This function can be used to measure the time that it takes the computer to
perform a task. Just record the time at which the task is begun and the time at which it is
finished and take the difference.
Here is a sample program that performs a few mathematical tasks and reports the time
that it takes for the program to run. On some computers, the time reported might be zero,
because it is too small to measure in milliseconds. Even if it’s not zero, you can be sure that
most of the time reported by the computer was spent doing output or working on tasks other
than the program, since the calculations performed in this program occupy only a tiny fraction
of a second of a computer’s time.
32 CHAPTER 2. NAMES AND THINGS
/**
* This program performs some mathematical computations and displays
* the results. It then reports the number of seconds that the
* computer spent on this task.
*/
startTime = System.currentTimeMillis();
endTime = System.currentTimeMillis();
time = (endTime - startTime) / 1000.0;
} // end main()
Then advice.length() is a function call that returns the number of characters in the string
“Seize the day!”. In this case, the return value would be 14. In general, for any string variable
str, the value of str.length() is an int equal to the number of characters in the string that is
the value of str. Note that this function has no parameter; the particular string whose length
is being computed is the value of str. The length subroutine is defined by the class String,
and it can be used with any value of type String. It can even be used with String literals, which
are, after all, just constant values of type String. For example, you could have a program count
the characters in “Hello World” for you by saying
System.out.print("The number of characters in ");
System.out.println("the string \"Hello World\" is ");
System.out.println( "Hello World".length() );
The String class defines a lot of functions. Here are some that you might find useful. Assume
that s1 and s2 refer to values of type String :
• s1.equals(s2) is a function that returns a boolean value. It returns true if s1 consists
of exactly the same sequence of characters as s2, and returns false otherwise.
• s1.equalsIgnoreCase(s2) is another boolean-valued function that checks whether s1
is the same string as s2, but this function considers upper and lower case letters
to be equivalent. Thus, if s1 is “cat”, then s1.equals("Cat") is false, while
s1.equalsIgnoreCase("Cat") is true.
• s1.length(), as mentioned above, is an integer-valued function that gives the number of
characters in s1.
• s1.charAt(N), where N is an integer, returns a value of type char. It returns the N-
th character in the string. Positions are numbered starting with 0, so s1.charAt(0) is
actually the first character, s1.charAt(1) is the second, and so on. The final position is
s1.length() - 1. For example, the value of "cat".charAt(1) is ’a’. An error occurs if
the value of the parameter is less than zero or greater than s1.length() - 1.
• s1.substring(N,M), where N and M are integers, returns a value of type String. The
returned value consists of the characters in s1 in positions N, N+1,. . . , M-1. Note that the
character in position M is not included. The returned value is called a substring of s1.
• s1.indexOf(s2) returns an integer. If s2 occurs as a substring of s1, then the returned
value is the starting position of that substring. Otherwise, the returned value is -1. You
can also use s1.indexOf(ch) to search for a particular character, ch, in s1. To find the
first occurrence of x at or after position N, you can use s1.indexOf(x,N).
• s1.compareTo(s2) is an integer-valued function that compares the two strings. If the
strings are equal, the value returned is zero. If s1 is less than s2, the value returned is
a number less than zero, and if s1 is greater than s2, the value returned is some number
greater than zero. (If both of the strings consist entirely of lower case letters, then “less
than” and “greater than” refer to alphabetical order. Otherwise, the ordering is more
complicated.)
• s1.toUpperCase() is a String -valued function that returns a new string that is equal to s1,
except that any lower case letters in s1 have been converted to upper case. For example,
"Cat".toUpperCase() is the string "CAT". There is also a function s1.toLowerCase().
• s1.trim() is a String -valued function that returns a new string that is equal to s1 except
that any non-printing characters such as spaces and tabs have been trimmed from the
34 CHAPTER 2. NAMES AND THINGS
beginning and from the end of the string. Thus, if s1 has the value "fred ", then
s1.trim() is the string "fred".
For the functions s1.toUpperCase(), s1.toLowerCase(), and s1.trim(), note that the
value of s1 is not modified. Instead a new string is created and returned as the value of
the function. The returned value could be used, for example, in an assignment statement
such as “smallLetters = s1.toLowerCase();”. To change the value of s1, you could use an
assignment “s1 = s1.toLowerCase();”.
∗ ∗ ∗
Here is another extremely useful fact about strings: You can use the plus operator, +, to
concatenate two strings. The concatenation of two strings is a new string consisting of all the
characters of the first string followed by all the characters of the second string. For example,
"Hello" + "World" evaluates to "HelloWorld". (Gotta watch those spaces, of course—if you
want a space in the concatenated string, it has to be somewhere in the input data, as in
"Hello " + "World".)
Let’s suppose that name is a variable of type String and that it already refers to the name
of the person using the program. Then, the program could greet the user by executing the
statement:
System.out.println("Hello, " + name + ". Pleased to meet you!");
Even more surprising is that you can actually concatenate values of any type onto a String
using the + operator. The value is converted to a string, just as it would be if you printed it to
the standard output, and then it is concatenated onto the string. For example, the expression
"Number" + 42 evaluates to the string "Number42". And the statements
System.out.print("After ");
System.out.print(years);
System.out.print(" years, the value is ");
System.out.print(principal);
can be replaced by the single statement:
System.out.print("After " + years +
" years, the value is " + principal);
Obviously, this is very convenient. It would have shortened some of the examples presented
earlier in this chapter.