Wednesday, July 23, 2008

Exception handling:dealing with errors

Exception handling:dealing with errors
Ever since the beginning of programming languages, error handling has been one of the
most difficult issues. Because it’s so hard to design a good error-handling scheme, many
languages simply ignore the issue, passing the problem on to library designers who come up
with halfway measures that can work in many situations but can easily be circumvented,
generally by just ignoring them. A major problem with most error-handling schemes is that
6 Note that this is true only for objects that are created on the heap, with new. However, the problem
described, and indeed any general programming problem, requires objects to be created on the heap.
7 According to a technical reader for this book, one existing real-time Java implementation
(www.newmonics.com) has guarantees on garbage collector performance.
56 Thinking in Java www.BruceEckel.com
they rely on programmer vigilance in following an agreed-upon convention that is not
enforced by the language. If the programmer is not vigilant, which is often if they are in a
hurry, these schemes can easily be forgotten.
Exception handling wires error handling directly into the programming language and
sometimes even the operating system. An exception is an object that is “thrown” from the
site of the error and can be “caught” by an appropriate exception handler designed to handle
that particular type of error. It’s as if exception handling is a different, parallel path of
execution that can be taken when things go wrong. And because it uses a separate execution
path, it doesn’t need to interfere with your normally-executing code. This makes that code
simpler to write since you aren’t constantly forced to check for errors. In addition, a thrown
exception is unlike an error value that’s returned from a function or a flag that’s set by a
function in order to indicate an error condition, These can be ignored. An exception cannot
be ignored so it’s guaranteed to be dealt with at some point. Finally, exceptions provide a
way to reliably recover from a bad situation. Instead of just exiting you are often able to set
things right and restore the execution of a program, which produces much more robust
programs.
Java’s exception handling stands out among programming languages, because in Java,
exception-handling was wired in from the beginning and you’re forced to use it. If you don’t
write your code to properly handle exceptions, you’ll get a compile-time error message. This
guaranteed consistency makes error-handling much easier.
It’s worth noting that exception handling isn’t an object-oriented feature, although in objectoriented
languages the exception is normally represented with an object. Exception handling
existed before object-oriented languages.

Garbage collectors vs. efficiency and flexibility

Garbage collectors vs. efficiency and flexibility
If all this is such a good idea, why didn’t they do the same thing in C++? Well of course
there’s a price you pay for all this programming convenience, and that price is run-time
overhead. As mentioned before, in C++ you can create objects on the stack, and in this case
they’re automatically cleaned up (but you don’t have the flexibility of creating as many as
you want at run-time). Creating objects on the stack is the most efficient way to allocate
storage for objects and to free that storage. Creating objects on the heap can be much more
expensive. Always inheriting from a base class and making all function calls polymorphic
also exacts a small toll. But the garbage collector is a particular problem because you never
quite know when it’s going to start up or how long it will take. This means that there’s an
inconsistency in the rate of execution of a Java program, so you can’t use it in certain
situations, such as when the rate of execution of a program is uniformly critical. (These are
generally called real time programs, although not all real-time programming problems are
this stringent.)7
The designers of the C++ language, trying to woo C programmers (and most successfully, at
that), did not want to add any features to the language that would impact the speed or the
use of C++ in any situation where C might be used. This goal was realized, but at the price
of greater complexity when programming in C++. Java is simpler than C++, but the
tradeoff is in efficiency and sometimes applicability. For a significant portion of
programming problems, however, Java is often the superior choice.

housekeeping dilemma:who should clean up?

housekeeping dilemma:who should clean up?
Each object requires resources in order to exist, most notably memory. When an object is no
longer needed it must be cleaned up so that these resources are released for reuse. In simple
programming situations the question of how an object is cleaned up doesn’t seem too
challenging: you create the object, use it for as long as it’s needed, and then it should be
destroyed. It’s not too hard, however, to encounter situations in which the situation is more
complex.
Suppose, for example, you are designing a system to manage air traffic for an airport. (The
same model might also work for managing crates in a warehouse, or a video rental system,
or a kennel for boarding pets.) At first it seems simple: make a collection to hold airplanes,
then create a new airplane and place it in the collection for each airplane that enters the airtraffic-
control zone. For cleanup, simply delete the appropriate airplane object when a plane
leaves the zone.
But perhaps you have some other system to record data about the planes; perhaps data that
doesn’t require such immediate attention as the main controller function. Maybe it’s a
record of the flight plans of all the small planes that leave the airport. So you have a second
collection of small planes, and whenever you create a plane object you also put it in this
collection if it’s a small plane. Then some background process performs operations on the
objects in this collection during idle moments.
Now the problem is more difficult: how can you possibly know when to destroy the objects?
When you’re done with the object, some other part of the system might not be. This same
problem can arise in a number of other situations, and in programming systems (such as
Chapter 1: Introduction to Objects 55
C++) in which you must explicitly delete an object when you’re done with it this can
become quite complex.6
With Java, the garbage collector is designed to take care of the problem of releasing the
memory (although this doesn’t include other aspects of cleaning up an object). The garbage
collector “knows” when an object is no longer in use, and it then automatically releases the
memory for that object. This, combined with the fact that all objects are inherited from the
single root class Object and that you can create objects only one way, on the heap, makes
the process of programming in Java much simpler than programming in C++. You have far
fewer decisions to make and hurdles to overcome.

Collection libraries and support for easy collection use

Collection libraries and support for easy collection use
Because a collection is a tool that you’ll use frequently, it makes sense to have a library of
collections that are built in a reusable fashion, so you can take one off the shelf and plug it
into your program. Java provides such a library, although it is fairly limited in Java 1.0 and
1.1 (the Java 1.2 collections library, however, satisfies most needs).
Downcasting vs. templates/generics
To make these collections reusable, they contain the one universal type in Java that was
previously mentioned: Object. The singly-rooted hierarchy means that everything is an
Object, so a collection that holds Objects can hold anything. This makes it easy to reuse.
To use such a collection, you simply add object handles to it, and later ask for them back.
But, since the collection holds only Objects, when you add your object handle into the
collection it is upcast to Object, thus losing its identity. When you fetch it back, you get an
Object handle, and not a handle to the type that you put in. So how do you turn it back into
something that has the useful interface of the object that you put into the collection?
Here, the cast is used again, but this time you’re not casting up the inheritance hierarchy to
a more general type, you cast down the hierarchy to a more specific type. This manner of
casting is called downcasting. With upcasting, you know, for example, that a Circle is a type
of Shape so it’s safe to upcast, but you don’t know that an Object is necessarily a Circle or
a Shape so it’s hardly safe to downcast unless you know that’s what you’re dealing with.
54 Thinking in Java www.BruceEckel.com
It’s not completely dangerous, however, because if you downcast to the wrong thing you’ll
get a run-time error called an exception, which will be described shortly. When you fetch
object handles from a collection, though, you must have some way to remember exactly
what they are so you can perform a proper downcast.
Downcasting and the run-time checks require extra time for the running program, and extra
effort from the programmer. Wouldn’t it make sense to somehow create the collection so
that it knows the types that it holds, eliminating the need for the downcast and possible
mistake? The solution is parameterized types, which are classes that the compiler can
automatically customize to work with particular types. For example, with a parameterized
collection, the compiler could customize that collection so that it would accept only Shapes
and fetch only Shapes.
Parameterized types are an important part of C++, partly because C++ has no singlyrooted
hierarchy. In C++, the keyword that implements parameterized types is template.
Java currently has no parameterized types since it is possible for it to get by – however
awkwardly – using the singly-rooted hierarchy. At one point the word generic (the keyword
used by Ada for its templates) was on a list of keywords that were “reserved for future
implementation.” Some of these seemed to have mysteriously slipped into a kind of
“keyword Bermuda Triangle” and it’s difficult to know what might eventually happen.

The singly-rooted hierarchy

The singly-rooted hierarchy
One of the issues in OOP that has become especially prominent since the introduction of
C++ is whether all classes should ultimately be inherited from a single base class. In Java (as
with virtually all other OOP languages) the answer is “yes” and the name of this ultimate
base class is simply Object. It turns out that the benefits of the singly-rooted hierarchy are
many.
All objects in a singly-rooted hierarchy have an interface in common, so they are all
ultimately the same type. The alternative (provided by C++) is that you don’t know that
everything is the same fundamental type. From a backwards-compatibility standpoint this
fits the model of C better and can be thought of as less restrictive, but when you want to do
full-on object-oriented programming you must then build your own hierarchy to provide
the same convenience that’s built into other OOP languages. And in any new class library
you acquire, some other incompatible interface will be used. It requires effort (and possibly
multiple inheritance) to work the new interface into your design. Is the extra “flexibility” of
Chapter 1: Introduction to Objects 53
C++ worth it? If you need it – if you have a large investment in C – it’s quite valuable. If
you’re starting from scratch, other alternatives such as Java can often be more productive.
All objects in a singly-rooted hierarchy (such as Java provides) can be guaranteed to have
certain functionality. You know you can perform certain basic operations on every object in
your system. A singly-rooted hierarchy, along with creating all objects on the heap, greatly
simplifies argument passing (one of the more complex topics in C++).
A singly-rooted hierarchy makes it much easier to implement a garbage collector. The
necessary support can be installed in the base class, and the garbage collector can thus send
the appropriate messages to every object in the system. Without a singly-rooted hierarchy
and a system to manipulate an object via a handle, it is difficult to implement a garbage
collector.
Since run-time type information is guaranteed to be in all objects, you’ll never end up with
an object whose type you cannot determine. This is especially important with system level
operations, such as exception handling, and to allow greater flexibility in programming.
You may wonder why, if it’s so beneficial, a singly-rooted hierarchy isn’t it in C++. It’s the
old bugaboo of efficiency and control. A singly-rooted hierarchy puts constraints on your
program designs, and in particular it was perceived to put constraints on the use of existing
C code. These constraints cause problems only in certain situations, but for maximum
flexibility there is no requirement for a singly-rooted hierarchy in C++. In Java, which
started from scratch and has no backward-compatibility issues with any existing language,
it was a logical choice to use the singly-rooted hierarchy in common with most other objectoriented
programming languages.

Collections and iterators

Collections and iterators
If you don’t know how many objects you’re going to need to solve a particular problem, or
how long they will last, you also don’t know how to store those objects. How can you know
how much space to create for those objects? You can’t, since that information isn’t known
until run time.
The solution to most problems in object-oriented design seems flippant: you create another
type of object. The new type of object that solves this particular problem holds handles to
other objects. Of course, you can do the same thing with an array, which is available in most
languages. But there’s more. This new object, generally called a collection (also called a
container, but the AWT uses that term in a different sense so this book will use “collection”),
will expand itself whenever necessary to accommodate everything you place inside it. So you
don’t need to know how many objects you’re going to hold in a collection. Just create a
collection object and let it take care of the details.
Fortunately, a good OOP language comes with a set of collections as part of the package. In
C++, it’s the Standard Template Library (STL). Object Pascal has collections in its Visual
Component Library (VCL). Smalltalk has a very complete set of collections. Java also has
collections in its standard library. In some libraries, a generic collection is considered good
enough for all needs, and in others (C++ in particular) the library has different types of
collections for different needs: a vector for consistent access to all elements, and a linked list
for consistent insertion at all elements, for example, so you can choose the particular type
that fits your needs. These may include sets, queues, hash tables, trees, stacks, etc.
All collections have some way to put things in and get things out. The way that you place
something into a collection is fairly obvious. There’s a function called “push” or “add” or a
similar name. Fetching things out of a collection is not always as apparent; if it’s an arraylike
entity such as a vector, you might be able to use an indexing operator or function. But in
many situations this doesn’t make sense. Also, a single-selection function is restrictive. What
if you want to manipulate or compare a set of elements in the collection instead of just one?
The solution is an iterator, which is an object whose job is to select the elements within a
collection and present them to the user of the iterator. As a class, it also provides a level of
52 Thinking in Java www.BruceEckel.com
abstraction. This abstraction can be used to separate the details of the collection from the
code that’s accessing that collection. The collection, via the iterator, is abstracted to be simply
a sequence. The iterator allows you to traverse that sequence without worrying about the
underlying structure – that is, whether it’s a vector, a linked list, a stack or something else.
This gives you the flexibility to easily change the underlying data structure without
disturbing the code in your program. Java began (in version 1.0 and 1.1) with a standard
iterator, called Enumeration, for all of its collection classes. Java 1.2 has added a much
more complete collection library which contains an iterator called Iterator that does more
than the older Enumeration.
From the design standpoint, all you really want is a sequence that can be manipulated to
solve your problem. If a single type of sequence satisfied all of your needs, there’d be no
reason to have different kinds. There are two reasons that you need a choice of collections.
First, collections provide different types of interfaces and external behavior. A stack has a
different interface and behavior than that of a queue, which is different than that of a set or
a list. One of these might provide a more flexible solution to your problem than the other.
Second, different collections have different efficiencies for certain operations. The best
example is a vector and a list. Both are simple sequences that can have identical interfaces
and external behaviors. But certain operations can have radically different costs. Randomly
accessing elements in a vector is a constant-time operation; it takes the same amount of time
regardless of the element you select. However, in a linked list it is expensive to move through
the list to randomly select an element, and it takes longer to find an element if it is further
down the list. On the other hand, if you want to insert an element in the middle of a
sequence, it’s much cheaper in a list than in a vector. These and other operations have
different efficiencies depending upon the underlying structure of the sequence. In the design
phase, you might start with a list and, when tuning for performance, change to a vector.
Because of the abstraction via iterators, you can change from one to the other with minimal
impact on your code.
In the end, remember that a collection is only a storage cabinet to put objects in. If that
cabinet solves all of your needs, it doesn’t really matter how it is implemented (a basic
concept with most types of objects). If you’re working in a programming environment that
has built-in overhead due to other factors (running under Windows, for example, or the cost
of a garbage collector), then the cost difference between a vector and a linked list might not
matter. You might need only one type of sequence. You can even imagine the “perfect”
collection abstraction, which can automatically change its underlying implementation
according to the way it is used.

Object landscapes and lifetimes

Object landscapes and lifetimes
Technically, OOP is just about abstract data typing, inheritance and polymorphism, but other
issues can be at least as important. The remainder of this section will cover these issues.
One of the most important factors is the way objects are created and destroyed. Where is the
data for an object and how is the lifetime of the object controlled? There are different
philosophies at work here. C++ takes the approach that control of efficiency is the most
important issue, so it gives the programmer a choice. For maximum run-time speed, the
storage and lifetime can be determined while the program is being written, by placing the
objects on the stack (these are sometimes called automatic or scoped variables) or in the static
storage area. This places a priority on the speed of storage allocation and release, and control
of these can be very valuable in some situations. However, you sacrifice flexibility because
you must know the exact quantity, lifetime and type of objects while you’re writing the
program. If you are trying to solve a more general problem such as computer-aided design,
warehouse management or air-traffic control, this is too restrictive.
The second approach is to create objects dynamically in a pool of memory called the heap. In
this approach you don’t know until run time how many objects you need, what their
lifetime is or what their exact type is. Those are determined at the spur of the moment while
the program is running. If you need a new object, you simply make it on the heap at the
point that you need it. Because the storage is managed dynamically, at run time, the amount
of time required to allocate storage on the heap is significantly longer than the time to create
storage on the stack. (Creating storage on the stack is often a single assembly instruction to
move the stack pointer down, and another to move it back up.) The dynamic approach
makes the generally logical assumption that objects tend to be complicated, so the extra
overhead of finding storage and releasing that storage will not have an important impact on
Chapter 1: Introduction to Objects 51
the creation of an object. In addition, the greater flexibility is essential to solve the general
programming problem.
C++ allows you to determine whether the objects are created while you write the program
or at run time to allow the control of efficiency. You might think that since it’s more flexible,
you’d always want to create objects on the heap rather than the stack. There’s another issue,
however, and that’s the lifetime of an object. If you create an object on the stack or in static
storage, the compiler determines how long the object lasts and can automatically destroy it.
However, if you create it on the heap the compiler has no knowledge of its lifetime. A
programmer has two options for destroying objects: you can determine programmatically
when to destroy the object, or the environment can provide a feature called a garbage
collector that automatically discovers when an object is no longer in use and destroys it. Of
course, a garbage collector is much more convenient, but it requires that all applications
must be able to tolerate the existence of the garbage collector and the other overhead for
garbage collection. This does not meet the design requirements of the C++ language and so
it was not included, but Java does have a garbage collector (as does Smalltalk; Delphi does
not but one could be added. Third-party garbage collectors exist for C++).
The rest of this section looks at additional factors concerning object lifetimes and landscapes.

Abstract base classes and interfaces

Abstract base classes and interfaces
Often in a design, you want the base class to present only an interface for its derived classes.
That is, you don’t want anyone to actually create an object of the base class, only to upcast
to it so that its interface can be used. This is accomplished by making that class abstract
using the abstract keyword. If anyone tries to make an object of an abstract class, the
compiler prevents them. This is a tool to enforce a particular design.
You can also use the abstract keyword to describe a method that hasn’t been implemented
yet – as a stub indicating “here is an interface function for all types inherited from this class,
but at this point I don’t have any implementation for it.” An abstract method may be
created only inside an abstract class. When the class is inherited, that method must be
implemented, or the inherited class becomes abstract as well. Creating an abstract method
allows you to put a method in an interface without being forced to provide a possibly
meaningless body of code for that method.
The interface keyword takes the concept of an abstract class one step further by preventing
any function definitions at all. The interface is a very useful and commonly-used tool, as it
provides the perfect separation of interface and implementation. In addition, you can
combine many interfaces together, if you wish. (You cannot inherit from more than one
regular class or abstract class.)

Dynamic binding

Dynamic binding
What’s amazing about the code in doStuff( ) is that somehow the right thing happens.
Calling draw( ) for Circle causes different code to be executed than when calling draw( ) for
a Square or a Line, but when the draw( ) message is sent to an anonymous Shape, the
correct behavior occurs based on the actual type that the Shape handle happens to be
connected to. This is amazing because when the Java compiler is compiling the code for
doStuff( ), it cannot know exactly what types it is dealing with. So ordinarily, you’d expect
it to end up calling the version of erase( ) for Shape, and draw( ) for Shape and not for the
specific Circle, Square, or Line. And yet the right thing happens. Here’s how it works.
When you send a message to an object even though you don’t know what specific type it is,
and the right thing happens, that’s called polymorphism. The process used by object-oriented
programming languages to implement polymorphism is called dynamic binding. The
compiler and run-time system handle the details; all you need to know is that it happens
and more importantly how to design with it.
Some languages require you to use a special keyword to enable dynamic binding. In C++
this keyword is virtual. In Java, you never need to remember to add a keyword because
functions are automatically dynamically bound. So you can always expect that when you
send a message to an object, the object will do the right thing, even when upcasting is
involved.
50 Thinking in Java www.BruceEckel.com

Interchangeable objects

Interchangeable objects
with polymorphism
Inheritance usually ends up creating a family of classes, all based on the same uniform
interface. We express this with an inverted tree diagram:5
One of the most important things you do with such a family of classes is to treat an object
of a derived class as an object of the base class. This is important because it means you can
write a single piece of code that ignores the specific details of type and talks just to the base
class. That code is then decoupled from type-specific information, and thus is simpler to
write and easier to understand. And, if a new type – a Triangle, for example – is added
through inheritance, the code you write will work just as well for the new type of Shape as
it did on the existing types. Thus the program is extensible.
Consider the above example. If you write a function in Java:
void doStuff(Shape s) {
s.erase();
// ...
s.draw();
}
This function speaks to any Shape, so it is independent of the specific type of object it’s
drawing and erasing. If in some other program we use the doStuff( ) function:
Circle c = new Circle();
Triangle t = new Triangle();
Line l = new Line();
doStuff(c);
doStuff(t);
doStuff(l);
5 This uses the Unified Notation, which will primarily be used in this book.
Shape
draw()
erase()
Circle
draw()
erase(
Square
draw()
erase()
Line
draw()
erase()
Chapter 1: Introduction to Objects 49
The calls to doStuff( ) automatically work right, regardless of the exact type of the object.
This is actually a pretty amazing trick. Consider the line:
doStuff(c);
What’s happening here is that a Circle handle is being passed into a function that’s
expecting a Shape handle. Since a Circle is a Shape it can be treated as one by doStuff( ).
That is, any message that doStuff( ) can send to a Shape, a Circle can accept. So it is a
completely safe and logical thing to do.
We call this process of treating a derived type as though it were its base type upcasting. The
name cast is used in the sense of casting into a mold and the up comes from the way the
inheritance diagram is typically arranged, with the base type at the top and the derived
classes fanning out downward. Thus, casting to a base type is moving up the inheritance
diagram: upcasting.
An object-oriented program contains some upcasting somewhere, because that’s how you
decouple yourself from knowing about the exact type you’re working with. Look at the code
in doStuff( ):
s.erase();
// ...
s.draw();
Notice that it doesn’t say “If you’re a Circle, do this, if you’re a Square, do that, etc.” If you
write that kind of code, which checks for all the possible types a Shape can actually be, it’s
messy and you need to change it every time you add a new kind of Shape. Here, you just
say “You’re a shape, I know you can erase( ) yourself, do it and take care of the details
correctly.”

Is-a vs. is-like-a relationships

Is-a vs. is-like-a relationships
There’s a certain debate that can occur about inheritance: Should inheritance override only
base-class functions? This means that the derived type is exactly the same type as the base
class since it has exactly the same interface. As a result, you can exactly substitute an object
of the derived class for an object of the base class. This can be thought of as pure substitution.
In a sense, this is the ideal way to treat inheritance. We often refer to the relationship
between the base class and derived classes in this case as an is-a relationship, because you
can say “a circle is a shape.” A test for inheritance is whether you can state the is-a
relationship about the classes and have it make sense.
There are times when you must add new interface elements to a derived type, thus extending
the interface and creating a new type. The new type can still be substituted for the base type,
but the substitution isn’t perfect in a sense because your new functions are not accessible
from the base type. This can be described as an is-like-a relationship; the new type has the
interface of the old type but it also contains other functions, so you can’t really say it’s
exactly the same. For example, consider an air conditioner. Suppose your house is wired with
all the controls for cooling; that is, it has an interface that allows you to control cooling.
Imagine that the air conditioner breaks down and you replace it with a heat pump, which
can both heat and cool. The heat pump is-like-an air conditioner, but it can do more. Because
your house is wired only to control cooling, it is restricted to communication with the
cooling part of the new object. The interface of the new object has been extended, and the
existing system doesn’t know about anything except the original interface.
When you see the substitution principle it’s easy to feel like that’s the only way to do things,
and in fact it is nice if your design works out that way. But you’ll find that there are times
when it’s equally clear that you must add new functions to the interface of a derived class.
With inspection both cases should be reasonably obvious.

Overriding base-class functionality

Overriding base-class functionality
Although the extends keyword implies that you are going to add new functions to the
interface, that’s not necessarily true. The second way to differentiate your new class is to
change the behavior of an existing base-class function. This is referred to as overriding that
function.
To override a function, you simply create a new definition for the function in the derived
class. You’re saying “I’m using the same interface function here, but I want it to do
something different for my new type.”

Inheritance:reusing the interface

Inheritance:reusing the interface
By itself, the concept of an object is a convenient tool. It allows you to package data and
functionality together by concept, so you can represent an appropriate problem-space idea
rather than being forced to use the idioms of the underlying machine. These concepts are
expressed in the primary idea of the programming language as a data type (using the class
keyword).
It seems a pity, however, to go to all the trouble to create a data type and then be forced to
create a brand new one that might have similar functionality. It’s nicer if we can take the
existing data type, clone it and make additions and modifications to the clone. This is
effectively what you get with inheritance, with the exception that if the original class (called
the base or super or parent class) is changed, the modified “clone” (called the derived or
inherited or sub or child class) also reflects the appropriate changes. Inheritance is
implemented in Java with the extends keyword. You make a new class and you say that it
extends an existing class.
When you inherit you create a new type, and the new type contains not only all the
members of the existing type (although the private ones are hidden away and inaccessible),
but more importantly it duplicates the interface of the base class. That is, all the messages
you can send to objects of the base class you can also send to objects of the derived class.
Since we know the type of a class by the messages we can send to it, this means that the
derived class is the same type as the base class. This type equivalence via inheritance is one of
the fundamental gateways in understanding the meaning of object-oriented programming.
Since both the base class and derived class have the same interface, there must be some
implementation to go along with that interface. That is, there must be a method to execute
Chapter 1: Introduction to Objects 47
when an object receives a particular message. If you simply inherit a class and don’t do
anything else, the methods from the base-class interface come right along into the derived
class. That means objects of the derived class have not only the same type, they also have the
same behavior, which doesn’t seem particularly interesting.
You have two ways to differentiate your new derived class from the original base class it
inherits from. The first is quite straightforward: you simply add brand new functions to the
derived class. These new functions are not part of the base class interface. This means that
the base class simply didn’t do as much as you wanted it to, so you add more functions. This
simple and primitive use for inheritance is, at times, the perfect solution to your problem.
However, you should look closely for the possibility that your base class might need these
additional functions.

Reusing the implementation

Reusing the implementation
Once a class has been created and tested, it should (ideally) represent a useful unit of code. It
turns out that this reusability is not nearly so easy to achieve as many would hope; it takes
experience and insight to achieve a good design. But once you have such a design, it begs to
46 Thinking in Java www.BruceEckel.com
be reused. Code reuse is arguably the greatest leverage that object-oriented programming
languages provide.
The simplest way to reuse a class is to just use an object of that class directly, but you can
also place an object of that class inside a new class. We call this “creating a member object.”
Your new class can be made up of any number and type of other objects, whatever is
necessary to achieve the functionality desired in your new class. This concept is called
composition, since you are composing a new class from existing classes. Sometimes
composition is referred to as a “has-a” relationship, as in “a car has a trunk.”
Composition comes with a great deal of flexibility. The member objects of your new class are
usually private, making them inaccessible to client programmers using the class. This allows
you to change those members without disturbing existing client code. You can also change
the member objects at run time, which provides great flexibility. Inheritance, which is
described next, does not have this flexibility since the compiler must place restrictions on
classes created with inheritance.
Because inheritance is so important in object-oriented programming it is often highly
emphasized, and the new programmer can get the idea that inheritance should be used
everywhere. This can result in awkward and overcomplicated designs. Instead, you should
first look to composition when creating new classes, since it is simpler and more flexible. If
you take this approach, your designs will stay cleaner. It will be reasonably obvious when
you need inheritance.

The hidden implementation

The hidden implementation
It is helpful to break up the playing field into class creators (those who create new data
types) and client programmers4 (the class consumers who use the data types in their
applications). The goal of the client programmer is to collect a toolbox full of classes to use
for rapid application development. The goal of the class creator is to build a class that
exposes only what’s necessary to the client programmer and keeps everything else hidden.
Why? If it’s hidden, the client programmer can’t use it, which means that the class creator
can change the hidden portion at will without worrying about the impact to anyone else.
4 I’m indebted to my friend Scott Meyers for this term.
Light
on( )
off( )
brighten( )
dim( )
Type Name
Interface
Chapter 1: Introduction to Objects 45
The interface establishes what requests you can make for a particular object. However, there
must be code somewhere to satisfy that request. This, along with the hidden data, comprises
the implementation. From a procedural programming standpoint, it’s not that complicated. A
type has a function associated with each possible request, and when you make a particular
request to an object, that function is called. This process is often summarized by saying that
you “send a message” (make a request) to an object, and the object figures out what to do
with that message (it executes code).
In any relationship it’s important to have boundaries that are respected by all parties
involved. When you create a library, you establish a relationship with the client
programmer, who is another programmer, but one who is putting together an application or
using your library to build a bigger library.
If all the members of a class are available to everyone, then the client programmer can do
anything with that class and there’s no way to force any particular behaviors. Even though
you might really prefer that the client programmer not directly manipulate some of the
members of your class, without access control there’s no way to prevent it. Everything’s
naked to the world.
There are two reasons for controlling access to members. The first is to keep client
programmers’ hands off portions they shouldn’t touch – parts that are necessary for the
internal machinations of the data type but not part of the interface that users need to solve
their particular problems. This is actually a service to users because they can easily see
what’s important to them and what they can ignore.
The second reason for access control is to allow the library designer to change the internal
workings of the structure without worrying about how it will affect the client programmer.
For example, you might implement a particular class in a simple fashion to ease
development, and then later decide you need to rewrite it to make it run faster. If the
interface and implementation are clearly separated and protected, you can accomplish this
and require only a relink by the user.
Java uses three explicit keywords and one implied keyword to set the boundaries in a class:
public, private, protected and the implied “friendly,” which is what you get if you don’t
specify one of the other keywords. Their use and meaning are remarkably straightforward.
These access specifiers determine who can use the definition that follows. public means the
following definition is available to everyone. The private keyword, on the other hand,
means that no one can access that definition except you, the creator of the type, inside
function members of that type. private is a brick wall between you and the client
programmer. If someone tries to access a private member, they’ll get a compile-time error.
“Friendly” has to do with something called a “package,” which is Java’s way of making
libraries. If something is “friendly” it’s available only within the package. (Thus this access
level is sometimes referred to as “package access.”) protected acts just like private, with the
exception that an inheriting class has access to protected members, but not private
members. Inheritance will be covered shortly.

An object has an interface

An object has an interface
Aristotle was probably the first to begin a careful study of the concept of type. He was
known to speak of “the class of fishes and the class of birds.” The concept that all objects,
while being unique, are also part of a set of objects that have characteristics and behaviors in
common was directly used in the first object-oriented language, Simula-67, with its
fundamental keyword class that introduces a new type into a program (thus class and type
are often used synonymously3).
Simula, as its name implies, was created for developing simulations such as the classic “bank
teller problem.” In this, you have a bunch of tellers, customers, accounts, transactions, etc.
The members (elements) of each class share some commonality: every account has a balance,
every teller can accept a deposit, etc. At the same time, each member has its own state; each
account has a different balance, each teller has a name. Thus the tellers, customers,
accounts, transactions, etc. can each be represented with a unique entity in the computer
program. This entity is the object, and each object belongs to a particular class that defines
its characteristics and behaviors.
So, although what we really do in object-oriented programming is create new data types,
virtually all object-oriented programming languages use the “class” keyword. When you see
the word “type” think “class” and vice versa.
2 See Multiparadigm Programming in Leda by Timothy Budd (Addison-Wesley 1995).
3 Some people make a distinction, stating that type determines the interface while class is a particular
implementation of that interface.
44 Thinking in Java www.BruceEckel.com
Once a type is established, you can make as many objects of that type as you like, and then
manipulate those objects as the elements that exist in the problem you are trying to solve.
Indeed, one of the challenges of object-oriented programming is to create a one-to-one
mapping between the elements in the problem space (the place where the problem actually
exists) and the solution space (the place where you’re modeling that problem, such as a
computer).
But how do you get an object to do useful work for you? There must be a way to make a
request of that object so it will do something, such as complete a transaction, draw
something on the screen or turn on a switch. And each object can satisfy only certain
requests. The requests you can make of an object are defined by its interface, and the type is
what determines the interface. The idea of type being equivalent to interface is fundamental
in object-oriented programming.
A simple example might be a representation of a light bulb:
Light lt = new Light();
lt.on();
The name of the type/class is Light, and the requests that you can make of a Light object
are to turn it on, turn it off, make it brighter or make it dimmer. You create a “handle” for a
Light simply by declaring a name (lt) for that identifier, and you make an object of type
Light with the new keyword, assigning it to the handle with the = sign. To send a message
to the object, you state the handle name and connect it to the message name with a period
(dot). From the standpoint of the user of a pre-defined class, that’s pretty much all there is to
programming with objects.

The progress of abstraction

The progress of abstraction
All programming languages provide abstractions. It can be argued that the complexity of the
problems you can solve is directly related to the kind and quality of abstraction. By “kind” I
mean: what is it that you are abstracting? Assembly language is a small abstraction of the
underlying machine. Many so-called “imperative” languages that followed (such as
FORTRAN, BASIC, and C) were abstractions of assembly language. These languages are big
improvements over assembly language, but their primary abstraction still requires you to
think in terms of the structure of the computer rather than the structure of the problem you
are trying to solve. The programmer must establish the association between the machine
model (in the “solution space”) and the model of the problem that is actually being solved (in
the “problem space”). The effort required to perform this mapping, and the fact that it is
extrinsic to the programming language, produces programs that are difficult to write and
expensive to maintain, and as a side effect created the entire “programming methods”
industry.
The alternative to modeling the machine is to model the problem you’re trying to solve.
Early languages such as LISP and APL chose particular views of the world (“all problems are
ultimately lists” or “all problems are algorithmic”). PROLOG casts all problems into chains of
decisions. Languages have been created for constraint-based programming and for
programming exclusively by manipulating graphical symbols. (The latter proved to be too
restrictive.) Each of these approaches is a good solution to the particular class of problem
they’re designed to solve, but when you step outside of that domain they become awkward.
The object-oriented approach takes a step farther by providing tools for the programmer to
represent elements in the problem space. This representation is general enough that the
programmer is not constrained to any particular type of problem. We refer to the elements
in the problem space and their representations in the solution space as “objects.” (Of course,
you will also need other objects that don’t have problem-space analogs.) The idea is that the
program is allowed to adapt itself to the lingo of the problem by adding new types of
objects, so when you read the code describing the solution, you’re reading words that also
express the problem. This is a more flexible and powerful language abstraction than what
we’ve had before. Thus OOP allows you to describe the problem in terms of the problem,
rather than in the terms of the solution. There’s still a connection back to the computer,
though. Each object looks quite a bit like a little computer; it has a state, and it has
operations you can ask it to perform. However, this doesn’t seem like such a bad analogy to
objects in the real world; they all have characteristics and behaviors.
Chapter 1: Introduction to Objects 43
Alan Kay summarized five basic characteristics of Smalltalk, the first successful objectoriented
language and one of the languages upon which Java is based. These characteristics
represent a pure approach to object-oriented programming:
1. Everything is an object. Think of an object as a fancy variable; it stores data, but you can
also ask it to perform operations on itself by making requests. In theory, you can take any
conceptual component in the problem you’re trying to solve (dogs, buildings, services, etc.)
and represent it as an object in your program.
2. A program is a bunch of objects telling each other what to do by sending messages. To
make a request of an object, you “send a message” to that object. More concretely, you can
think of a message as a request to call a function that belongs to a particular object.
3. Each object has its own memory made up of other objects. Or, you make a new kind of
object by making a package containing existing objects. Thus, you can build up complexity in
a program while hiding it behind the simplicity of objects.
4. Every object has a type. Using the parlance, each object is an instance of a class, where
“class” is synonymous with “type.” The most important distinguishing characteristic of a
class is “what messages can you send to it?”
5. All objects of a particular type can receive the same messages. This is actually a very
loaded statement, as you will see later. Because an object of type circle is also an object of
type shape, a circle is guaranteed to receive shape messages. This means you can write code
that talks to shapes and automatically handle anything that fits the description of a shape.
This substitutability is one of the most powerful concepts in OOP.
Some language designers have decided that object-oriented programming itself is not
adequate to easily solve all programming problems, and advocate the combination of various
approaches into multiparadigm programming languages.2

1: Introduction to Objects

1: Introduction
to objects
Why has object-oriented programming had such a sweeping impact on the
software development community?
Object-oriented programming appeals at multiple levels. For managers, it promises faster
and cheaper development and maintenance. For analysts and designers, the modeling process
becomes simpler and produces a clear, manageable design. For programmers, the elegance
and clarity of the object model and the power of object-oriented tools and libraries makes
programming a much more pleasant task, and programmers experience an increase in
productivity. Everybody wins, it would seem.
If there’s a downside, it is the expense of the learning curve. Thinking in objects is a dramatic
departure from thinking procedurally, and the process of designing objects is much more
challenging than procedural design, especially if you’re trying to create reusable objects. In
the past, a novice practitioner of object-oriented programming was faced with a choice
between two daunting tasks:
1. Choose a language such as Smalltalk in which you had to learn a large library before
becoming productive.
2. Choose C++ with virtually no libraries at all,1 and struggle through the depths of the
language in order to write your own libraries of objects.
1 Fortunately, this has change significantly with the advent of third-party libraries and the Standard
C++ library.
42 Thinking in Java www.BruceEckel.com
It is, in fact, difficult to design objects well – for that matter, it’s hard to design anything
well. But the intent is that a relatively few experts design the best objects for others to
consume. Successful OOP languages incorporate not just language syntax and a compiler,
but an entire development environment including a significant library of well-designed, easy
to use objects. Thus, the primary job of most programmers is to use existing objects to solve
their application problems. The goal of this chapter is to show you what object-oriented
programming is and how simple it can be.
This chapter will introduce many of the ideas of Java and object-oriented programming on a
conceptual level, but keep in mind that you’re not expected to be able to write full-fledged
Java programs after reading this chapter. All the detailed descriptions and examples will
follow throughout the course of this book.

Acknowledgements

Acknowledgements
First of all, thanks to the Doyle Street Cohousing Community for putting up with me for the
two years that it took me to write this book (and for putting up with me at all). Thanks
very much to Kevin and Sonda Donovan for subletting their great place in gorgeous Crested
Butte, Colorado for the summer while I worked on the book. Also thanks to the friendly
residents of Crested Butte and the Rocky Mountain Biological Laboratory who made me feel
so welcome. The World Gym in Emeryville and its enthusiastic staff helped keep me sane
during the final months of the book.
This is my first experience using an agent, and I’m not looking back. Thanks to Claudette
Moore at Moore Literary Agency for her tremendous patience and perseverance in getting me
exactly what I wanted.
My first two books were published with Jeff Pepper as editor at Osborne/McGraw-Hill. Jeff
appeared at the right place and the right time at Prentice-Hall and has cleared the path and
made all the right things happen to make this the most pleasant publishing experience I’ve
ever had. Thanks, Jeff – it means a lot to me.
I’m especially indebted to Gen Kiyooka and his company Digigami, who have graciously
provided my Web server, and to Scott Callaway who has maintained it. This has been an
invaluable aid while I was learning about the Web.
Thanks to Cay Horstmann (co-author of Core Java, Prentice Hall 1997), D’Arcy Smith
(Symantec), and Paul Tyma (co-author of Java Primer Plus, The Waite Group 1996), for
helping me clarify concepts in the language.
Thanks to people who have spoken in my Java track at the Software Development
Conference, and students in my seminars, who ask the questions I need to hear in order to
make the material more clear.
Introduction 39
Special thanks to Larry and Tina O’Brien, who turned this book and my seminar into a
teaching CD ROM. (You can find out more at http://www.BruceEckel.com.)
Lots of people sent in corrections and I am indebted to them all, but particular thanks go to:
Kevin Raulerson (found tons of great bugs), Bob Resendes (simply incredible), John Pinto, Joe
Dante, Joe Sharp (all three were fabulous), David Combs (many grammar and clarification
corrections), Dr. Robert Stephenson, Franklin Chen, Zev Griner, David Karr, Leander A.
Stroschein, Steve Clark, Charles A. Lee, Austin Maher, Dennis P. Roth, Roque Oliveira,
Douglas Dunn, Dejan Ristic, Neil Galarneau, David B. Malkovsky, Steve Wilkinson, and a
host of others.
Prof. Ir. Marc Meurrens put in a great deal of effort to publicize and make the book available
in Europe.
There have been a spate of smart technical people in my life who have become friends and
have also been both influential and unusual in that they’re vegetarians, do yoga and practice
other forms of spiritual enhancement, which I find quite inspirational and instructional.
They are Kraig Brockschmidt, Gen Kiyooka and Andrea Provaglio, who helps in the
understanding of Java and programming in general in Italy.
It’s not that much of a surprise to me that understanding Delphi helped me understand
Java, since there are many concepts and language design decisions in common. My Delphi
friends provided assistance by helping me gain insight into that marvelous programming
environment. They are Marco Cantu (another Italian – perhaps being steeped in Latin gives
one aptitude for programming languages?), Neil Rubenking (who used to do the
yoga/vegetarian/Zen thing but discovered computers) and of course Zack Urlocker, a longtime
pal whom I’ve traveled the world with.
My friend Richard Hale Shaw’s insights and support have been very helpful (and Kim’s, too).
Richard and I spent many months giving seminars together and trying to work out the
perfect learning experience for the attendees. Thanks also to KoAnn Vikoren, Eric Faurot,
Deborah Sommers, Julie Shaw, Nicole Freeman, Cindy Blair, Barbara Hanscome, Regina
Ridley, Alex Dunne, and the rest of the cast and crew at MFI.
The book design, cover design, and cover photo were created by my friend Daniel Will-
Harris, noted author and designer (http://www.Will-Harris.com), who used to play with rubon
letters in junior high school while he awaited the invention of computers and desktop
publishing, and complained of me mumbling over my algebra problems. However, I
produced the camera-ready pages myself, so the typesetting errors are mine. Microsoft®
Word 97 for Windows was used to write the book and to create camera-ready pages. The
body typeface is Bitstream Carmina and the headlines are in Bitstream Calligraph 421
(www.bitstream.com). The symbols at the start of each chapter are Leonardo Extras from P22
(http://www.p22.com). The cover typeface is ITC Rennie Mackintosh.
Thanks to the vendors who supplied me with compilers: Borland, Microsoft, Symantec,
Sybase/Powersoft/Watcom, and of course, Sun.
A special thanks to all my teachers and all my students (who are my teachers as well). The
most fun writing teacher was Gabrielle Rico (author of Writing the Natural Way, Putnam
1983). I’ll always treasure the terrific week at Esalen.
The supporting cast of friends includes, but is not limited to: Andrew Binstock, Steve
Sinofsky, JD Hildebrandt, Tom Keffer, Brian McElhinney, Brinkley Barr, Bill Gates at Midnight
Engineering Magazine, Larry Constantine and Lucy Lockwood, Greg Perry, Dan Putterman,
Christi Westphal, Gene Wang, Dave Mayer, David Intersimone, Andrea Rosenfield, Claire
Sawyers, more Italians (Laura Fallai, Corrado, Ilsa, and Cristina Giustozzi), Chris and Laura
40 Thinking in Java www.BruceEckel.com
Strand, the Almquists, Brad Jerbic, Marilyn Cvitanic, the Mabrys, the Haflingers, the
Pollocks, Peter Vinci, the Robbins Families, the Moelter Families (and the McMillans), Michael
Wilk, Dave Stoner, Laurie Adams, the Cranstons, Larry Fogg, Mike and Karen Sequeira, Gary
Entsminger and Allison Brody, Kevin Donovan and Sonda Eastlack, Chester and Shannon
Andersen, Joe Lordi, Dave and Brenda Bartlett, David Lee, the Rentschlers, the Sudeks, Dick,
Patty, and Lee Eckel, Lynn and Todd, and their families. And of course, Mom and Dad.

Note on the cover design

Note on the cover design
The cover of Thinking in Java is inspired by the American Arts & Crafts Movement, which
began near the turn of the century and reached its zenith between 1900 and 1920. It began
in England as a reaction to both the machine production of the Industrial Revolution and the
highly ornamental style of the Victorian era. Arts & Crafts emphasized spare design, the
forms of nature as seen in the art nouveau movement, hand-crafting, and the importance of
38 Thinking in Java www.BruceEckel.com
the individual craftsperson, and yet it did not eschew the use of modern tools. There are
many echoes with the situation we have today: the impending turn of the century, the
evolution from the raw beginnings of the computer revolution to something more refined
and meaningful to individual persons, and the emphasis on software craftsmanship rather
than just manufacturing code.
I see Java in this same way: as an attempt to elevate the programmer away from an
operating-system mechanic and towards being a “software craftsman.”
Both the author and the book/cover designer (who have been friends since childhood) find
inspiration in this movement, and both own furniture, lamps and other pieces that are either
original or inspired by this period.
The other theme in this cover suggests a collection box that a naturalist might use to display
the insect specimens that he or she has preserved. These insects are objects, placed within the
box objects which are themselves placed within the “cover object,” which illustrates the
fundamental concept of aggregation in object-oriented programming. Of course, a
programmer cannot help but make the association with “bugs,” and here the bugs have been
captured and presumably killed in a specimen jar, and finally confined within a small display
box, as if to imply Java’s ability to find, display and subdue bugs (which is truly one of its
most powerful attributes).

Errors

Errors
No matter how many tricks a writer uses to detect errors, some always creep in and these
often leap off the page for a fresh reader. If you discover anything you believe to be an error,
please send the original source file (which you can find at http://www.BruceEckel.com)
with a clearly commented error (following the form shown on the Web page) and suggested
correction via electronic mail to Bruce@EckelObjects.com so that it might be fixed in the
electronic version on the Web site and in the next printing of the book. When you submit a
correction, please use the following format:
1. Put “TIJ Correction” (and nothing else) as the subject line – this way my email program
can route it to the right directory.
2. In the body of your email, please use the form:
find: one-line string to search for
comment:
multi-line comment, best starting with "here's how I think it
should read"
###
Where the ‘###’ is to indicate the end of comment. This way, my correction tools can do a
“find” using the original text, and your suggested correction will pop up in a window next
to it.
Suggestions for additional exercises or requests to cover specific topics in the next edition are
welcome. Your help is appreciated.

Seminars and mentoring

Seminars and mentoring
My company provides five-day, hands-on, public and in-house training seminars based on
the material in this book. Selected material from each chapter represents a lesson, which is
followed by a monitored exercise period so each student receives personal attention. The
lectures and slides for the introductory seminar are also captured on CD-ROM to provide at
least some of the experience of the seminar without the travel and expense. For more
information, go to:
http://www.BruceEckel.com
or email:
Bruce@EckelObjects.com
My company also provides consulting services to help guide your project through its
development cycle – especially your company’s first Java project.

Java versions

Java versions
Although I test the code in this book with several different vendor implementations of Java, I
generally rely on the Sun implementation as a reference when determining whether behavior
is correct.
By the time you read this, Sun will have released three major versions of Java: 1.0, 1.1 and
1.2 (Sun says it will make a major release about every nine months!). Version 1.1 represents
a significant change to the language and should probably have been labeled 2.0. (And if 1.1
is such a big change from 1.0, I shudder to think what will justify the number 2.0.)
However, it’s version 1.2 that seems to finally bring Java into the prime time, in particular
where user interface tools are concerned.
This book covers versions 1.0, 1.1 and selected parts of 1.2, although in situations where a
new approach is clearly superior to the old, I definitely favor the new approach, often
choosing to teach the better approach and completely ignore the old approach. However,
there are some cases where it’s unavoidable to teach the old approach before the new, in
particular with the AWT, since not only is there a lot of old Java 1.0 code out there, but
some platforms still support only Java 1.0. I will try to be scrupulous about pointing out
which features belong to which version.
One thing you’ll notice is that I don’t use the sub-revision numbers. At this writing, the
released version of 1.0 from Sun was 1.02 and the released version of 1.1 was 1.1.5 (Java
1.2 was in beta). In this book I will refer to Java 1.0, Java 1.1 and Java 1.2 only, to guard
against typographical errors produced by further sub-revisioning of these products.

Coding standards

Coding standards
In the text of this book, identifiers (function, variable and class names) will be set in bold.
Most keywords will also be set in bold, except for those keywords that are used so much that
the bolding can become tedious, such as “class.”
I use a particular coding style for the examples in this book. This style seems to be supported
by most Java development environments. It was developed over a number of years, and was
inspired by Bjarne Stroustrup’s style in his original The C++ Programming Language
(Addison-Wesley, 1991; 2nd ed.). The subject of formatting style is good for hours of hot
debate, so I’ll just say I’m not trying to dictate correct style via my examples; I have my
own motivation for using the style that I do. Because Java is a free-form programming
language, you can continue to use whatever style you’re comfortable with.
The programs in this book are files that are included by the word processor in the text,
directly from compiled files. Thus, the code files printed in the book should all work without
compiler errors. The errors that should cause compile-time error messages are commented
out with the comment //! so they can be easily discovered and tested using automatic
means. Errors discovered and reported to the author will appear first in the distributed
source code and later in updates of the book (which will also appear on the Web site
http://www.BruceEckel.com).

Source code

Source code
All the source code for this book is available as copyrighted freeware, distributed as a single
package, by visiting the Web site http://www.BruceEckel.com. To make sure that you get the
most current version, this is the official site for distribution of the code and the electronic
version of the book. You can find mirrored versions of the electronic book and the code on
other sites (some of these sites are found at http://www.BruceEckel.com), but you should
Introduction 35
check the official site to ensure that the mirrored version is actually the most recent edition.
You may distribute the code in classroom and other educational situations.
The primary goal of the copyright is to ensure that the source of the code is properly cited,
and to prevent you from republishing the code in print media without permission. (As long
as the source is cited, using examples from the book in most media is generally not a
problem.)
In each source code file you will find the following copyright notice:
//////////////////////////////////////////////////
// Copyright (c) Bruce Eckel, 1998
// Source code file from the book "Thinking in Java"
// All rights reserved EXCEPT as allowed by the
// following statements: You can freely use this file
// for your own work (personal or commercial),
// including modifications and distribution in
// executable form only. Permission is granted to use
// this file in classroom situations, including its
// use in presentation materials, as long as the book
// "Thinking in Java" is cited as the source.
// Except in classroom situations, you cannot copy
// and distribute this code; instead, the sole
// distribution point is http://www.BruceEckel.com
// (and official mirror sites) where it is
// freely available. You cannot remove this
// copyright and notice. You cannot distribute
// modified versions of the source code in this
// package. You cannot use this file in printed
// media without the express permission of the
// author. Bruce Eckel makes no representation about
// the suitability of this software for any purpose.
// It is provided "as is" without express or implied
// warranty of any kind, including any implied
// warranty of merchantability, fitness for a
// particular purpose or non-infringement. The entire
// risk as to the quality and performance of the
// software is with you. Bruce Eckel and the
// publisher shall not be liable for any damages
// suffered by you or any third party as a result of
// using or distributing software. In no event will
// Bruce Eckel or the publisher be liable for any
// lost revenue, profit, or data, or for direct,
// indirect, special, consequential, incidental, or
// punitive damages, however caused and regardless of
// the theory of liability, arising out of the use of
// or inability to use software, even if Bruce Eckel
// and the publisher have been advised of the
// possibility of such damages. Should the software
// prove defective, you assume the cost of all
// necessary servicing, repair, or correction. If you
// think you've found an error, please email all
// modified files with clearly commented changes to:
// Bruce@EckelObjects.com. (Please use the same
36 Thinking in Java www.BruceEckel.com
// address for non-code errors found in the book.)
/////////////////////////////////////////////////
You may use the code in your projects and in the classroom (including your presentation
materials) as long as the copyright notice that appears in each source file is retained.

Multimedia CD ROM

Multimedia CD ROM
To accompany this book a Multimedia CD ROM is available separately, but this is not like the
CDs that you’ll usually find packaged with books. Those often only contain the source code
for the book. (The code for this book is freely downloadable from the Web site
www.BruceEckel.com.) This CD ROM is a separate product and contains the entire contents
of the week-long “Hands-On Java” training seminar. This is more than 15 hours of lectures
given by Bruce Eckel, synchronized with 500 slides of information. The seminar is based on
this book so it is an ideal accompaniment.
The CD ROM contains two versions of this book:
1. A printable version identical to the one available for download.
2. For easy on-screen viewing and reference, a screen-formatted and hyperlinked version
which is available exclusively on the CD-ROM. These hyperlinks include:
 230 chapter, section, and sub-heading links
 3600 index links
The CD ROM contains over 600MB of content. We believe that it sets a new standard for
value.
The CD ROM contains everything in the printable version of the book and everything (with
the important exception of personalized attention!) from the five-day full-immersion
training seminars. We believe that it sets a new standard for quality.
The CD ROM is available only by ordering directly from the Web site www.BruceEckel.com.

Exercises

Exercises
I’ve discovered that simple exercises are exceptionally useful during a seminar to complete a
student’s understanding, so you’ll find a set at the end of each chapter.
Most exercises are designed to be easy enough that they can be finished in a reasonable
amount of time in a classroom situation while the instructor observes, making sure that all
the students are absorbing the material. Some exercises are more advanced to prevent
boredom for experienced students. The majority are designed to be solved in a short time and
test and polish your knowledge. Some are more challenging, but none present major
challenges. (Presumably, you’ll find those on your own – or more likely they’ll find you).

Appendix

Appendix A: Using non-Java code
A totally portable Java program has serious drawbacks: speed and the inability to
access platform-specific services. When you know the platform that you’re running
on, it’s possible to dramatically speed up certain operations by making them native
methods, which are functions that are written in another programming language
(currently, only C/C++ is supported). There are other ways that Java supports
non-Java code, including CORBA. This appendix gives you enough of an
introduction to these features that you should be able to create simple examples
that interface with non-Java code.
Appendix B: Comparing C++ and Java
If you’re a C++ programmer, you already have the basic idea of object-oriented
programming, and the syntax of Java no doubt looks very familiar to you. This
makes sense because Java was derived from C++. However, there are a surprising
number of differences between C++ and Java. These differences are intended to be
significant improvements, and if you understand the differences you’ll see why Java
is such a beneficial programming language. This appendix takes you through the
important features that make Java distinct from C++.
Appendix C: Java programming guidelines
This appendix contains suggestions to help guide you while performing low-level
program design and writing code.
Appendix D: Performance
This will allow you to find bottlenecks and improve speed in your Java program.
Appendix E: A bit about garbage collection
This appendix describes the operation and approaches that are used to implement
garbage collection.
Appendix F: Recommended reading
34 Thinking in Java www.BruceEckel.com
A list of some of the Java books I’ve found particularly useful.

Chapter 17: Projects

Chapter 17: Projects
This chapter includes a set of projects that build on the material presented in this
book, or otherwise didn’t fit in earlier chapters. These projects are significantly
more complex than the examples in the rest of the book, and they often
demonstrate new techniques and uses of class libraries.
There are subjects that didn’t seem to fit within the core of the book, and yet I find
that I discuss them during seminars. These are placed in the appendices.

Chapter 16: Design patterns

Chapter 16: Design patterns
This chapter introduces the very important and yet non-traditional “patterns”
approach to program design. An example of the design evolution process is studied,
starting with an initial solution and moving through the logic and process of
evolving the solution to more appropriate designs. You’ll see one way that a design
can materialize over time.

Chapter 15: Network programming

Chapter 15: Network programming
Introduction 33
All the Java features and libraries seem to really come together when you start
writing programs to work across networks. This chapter explores communication
across the Internet, and the classes that Java provides to make this easier. It also
shows you how to create a Java applet that talks to a common gateway interface
(CGI) program, shows you how to write CGI programs in C++ and covers Java
1.1’s Java DataBase Connectivity (JDBC) and Remote Method Invocation (RMI).

Chapter 14: Multiple threads

Chapter 14: Multiple threads
Java provides a built-in facility to support multiple concurrent subtasks, called
threads, running within a single program. (Unless you have multiple processors on
your machine, this is only the appearance of multiple subtasks.) Although these can
be used anywhere, threads are most powerful when trying to create a responsive
user interface so, for example, a user isn’t prevented from pressing a button or
entering data while some processing is going on. This chapter looks at the syntax
and semantics of multithreading in Java.

Chapter 13: Creating windows and applets

Chapter 13: Creating windows and applets
Java comes with the Abstract Window Toolkit (AWT), which is a set of classes that
handle windowing in a portable fashion; these windowing programs can either be
applets or stand-alone applications. This chapter is an introduction to the AWT and
the creation of World Wide Web applets. We’ll also look at pros and cons of the AWT
and the GUI improvements introduced in Java 1.1. The important “Java Beans”
technology is introduced. This is fundamental for the creation of Rapid-Application
Development (RAD) program-building tools. Finally, the new Java 1.2 “Swing”
library is introduced – this provides a dramatic improvement in UI components for
Java.

Chapter 12: Passing and returning objects

Chapter 12: Passing and returning objects
Since the only way you talk to objects in Java is through “handles,” the concepts of
passing an object into a function and returning an object from a function have
some interesting consequences. This chapter explains what you need to know to
manage objects when you’re moving in and out of functions, and also shows the
String class, which uses a different approach to the problem.

Chapter 11: Run-time type identification

Chapter 11: Run-time type identification
Java run-time type identification (RTTI) lets you find the exact type of an object
when you have a handle to only the base type. Normally, you’ll want to
intentionally ignore the exact type of an object and let Java’s dynamic binding
mechanism (polymorphism) implement the correct behavior for that type. But
occasionally it is very helpful to know the exact type of an object for which you
have only a base handle. Often this information allows you to perform a specialcase
operation more efficiently. This chapter explains what RTTI is for, how to use it
and how to get rid of it when it doesn’t belong there. In addition, the Java 1.1
reflection feature is introduced.

Chapter 10: The Java IO system

Chapter 10: The Java IO system
Theoretically, you can divide any program into three parts: input, process, and
output. This implies that IO (input/output) is a pretty important part of the
equation. In this chapter you’ll learn about the different classes that Java provides
for reading and writing files, blocks of memory, and the console. The distinction
between “old” IO and “new” Java 1.1 IO will be shown. In addition, this section
examines the process of taking an object, “streaming” it (so that it can be placed on
disk or sent across a network) and reconstructing it, which is handled for you in
Java version 1.1. Also, Java 1.1’s compression libraries, which are used in the Java
ARchive file format (JAR), are examined.

Chapter 9: Error handling with exceptions

Chapter 9: Error handling with exceptions
The basic philosophy of Java is that badly-formed code will not be run. As much as
possible, the compiler catches problems, but sometimes the problems – either
programmer error or a natural error condition that occurs as part of the normal
execution of the program – can be detected and dealt with only at run-time. Java
has exception handling to deal with any problems that arise while the program is
running. This chapter examines how the keywords try, catch, throw, throws, and
finally work in Java; when you should throw exceptions and what to do when you
catch them. In addition, you’ll see Java’s standard exceptions, how to create your
32 Thinking in Java www.BruceEckel.com
own, what happens with exceptions in constructors, and how exception handlers
are located.

Chapter 8: Holding your objects

Chapter 8: Holding your objects
It’s a fairly simple program that has only a fixed quantity of objects with known
lifetimes. In general, your programs will always be creating new objects at a variety
of times that will be known only while the program is running. In addition, you
won’t know until run-time the quantity or even the exact type of the objects you
need. To solve the general programming problem, you need to create any number of
objects, anytime, anywhere. This chapter explores in depth the tools that Java
supplies to hold objects while you’re working with them: the simple arrays and
more sophisticated collections (data structures) such as Vector and Hashtable.
Finally, the new and improved Java 1.2 collections library is explored in depth.

Chapter 7: Polymorphism

Chapter 7: Polymorphism
On your own, you might take nine months to discover and understand
polymorphism, a cornerstone of OOP. Through small, simple examples you’ll see
how to create a family of types with inheritance and manipulate objects in that
family through their common base class. Java’s polymorphism allows you to treat
all objects in this family generically, which means the bulk of your code doesn’t rely
on specific type information. This makes your programs extensible, so building
programs and code maintenance is easier and cheaper. In addition, Java provides a
third way to set up a reuse relationship through the interface, which is a pure
abstraction of the interface of an object. Once you’ve seen polymorphism, the
interface can be clearly understood. This chapter also introduces Java 1.1 inner
classes.

Chapter 6: Reusing classes

Chapter 6: Reusing classes
The concept of inheritance is standard in virtually all OOP languages. It’s a way to
take an existing class and add to its functionality (as well as change it, the subject of
Chapter 7). Inheritance is often a way to reuse code by leaving the “base class” the
same, and just patching things here and there to produce what you want. However,
inheritance isn’t the only way to make new classes from existing ones. You can also
embed an object inside your new class with composition. In this chapter you’ll learn
about these two ways to reuse code in Java, and how to apply them.

Chapter 5: Hiding the implementation

Chapter 5: Hiding the implementation
This chapter covers the way that code is packaged together, and why some parts of
a library are exposed while other parts are hidden. It begins by looking at the
package and import keywords, which perform file-level packaging and allow you
to build libraries of classes. The subject of directory paths and file names is also
examined. The remainder of the chapter looks at the public, private, and protected
keywords, the concept of “friendly” access, and what the different levels of access
control mean when used in various contexts.

Chapter 4: Initialization and cleanup

Chapter 4: Initialization and cleanup
This chapter begins by introducing the constructor, which guarantees proper
initialization. The definition of the constructor leads into the concept of function
overloading (since you might want several constructors). This is followed by a
discussion of the process of cleanup, which is not always as simple as it seems.
Normally, you just drop an object when you’re done with it and the garbage
collector eventually comes along and releases the memory. This portion explores the
garbage collector and some of its idiosyncrasies. The chapter concludes with a closer
look at how things are initialized: automatic member initialization, specifying
Introduction 31
member initialization, the order of initialization, static initialization and array
initialization.

Chapter 3: Controlling program flow

Chapter 3: Controlling program flow
This chapter begins with all of the operators that come to Java from C and C++. In
addition, you’ll discover common operator pitfalls, casting, promotion, and
precedence. This is followed by the basic control-flow and selection operations that
you get with virtually any programming language: choice with if-else; looping with
for and while; quitting a loop with break and continue as well as Java’s labeled
break and labeled continue (which account for the “missing goto” in Java); and
selection using switch. Although much of this material has common threads with C
and C++ code, there are some differences. In addition, all the examples will be full
Java examples so you’ll get more comfortable with what Java looks like.

Chapter 3: Controlling program flow

Chapter 3: Controlling program flow
This chapter begins with all of the operators that come to Java from C and C++. In
addition, you’ll discover common operator pitfalls, casting, promotion, and
precedence. This is followed by the basic control-flow and selection operations that
you get with virtually any programming language: choice with if-else; looping with
for and while; quitting a loop with break and continue as well as Java’s labeled
break and labeled continue (which account for the “missing goto” in Java); and
selection using switch. Although much of this material has common threads with C
and C++ code, there are some differences. In addition, all the examples will be full
Java examples so you’ll get more comfortable with what Java looks like.

Chapter 2: Everything is an object

Chapter 2: Everything is an object
This chapter moves you to the point where you can write your first Java program,
so it must give an overview of the essentials, including the concept of a “handle” to
an object; how to create an object; an introduction to primitive types and arrays;
scoping and the way objects are destroyed by the garbage collector; how everything
in Java is a new data type (class) and how to create your own classes; functions,
arguments, and return values; name visibility and using components from other
libraries; the static keyword; comments and embedded documentation.

Chapter 1: Introduction to objects

Chapter 1: Introduction to objects
This chapter is an overview of what object-oriented programming is all about,
including the answer to the basic question “What’s an object?”, interface vs.
implementation, abstraction and encapsulation, messages and functions, inheritance
and composition, and the all-important polymorphism. You’ll also be introduced to
issues of object creation such as constructors, where the objects live, where to put
them once they’re created, and the magical garbage collector that cleans up the
objects that are no longer needed. Other issues will be introduced, including error
handling with exceptions, multithreading for responsive user interfaces, and
networking and the Internet. You’ll also learn about what makes Java special, why
it’s been so successful, and about object-oriented analysis and design.

Chapters

Chapters
This book was designed with one thing in mind: the way people learn the Java language.
Seminar audience feedback helped me understand which parts were difficult and needed
30 Thinking in Java www.BruceEckel.com
illumination. In the areas where I got ambitious and included too many features all at once,
I came to know – through the process of presenting the material – that if you include a lot of
new features, you need to explain them all, and this easily compounds the student’s
confusion. As a result, I’ve taken a great deal of trouble to introduce the features as few at a
time as possible.
The goal, then, is for each chapter to teach a single feature, or a small group of associated
features, in such a way that no additional features are relied upon. That way you can digest
each piece in the context of your current knowledge before moving on.
Here is a brief description of the chapters contained in the book, which correspond to
lectures and exercise periods in my hands-on seminars.

Online documentation

Online documentation
The Java language and libraries from Sun Microsystems (a free download) come with
documentation in electronic form, readable using a Web browser, and virtually every third
party implementation of Java has this or an equivalent documentation system. Almost all
the books published on Java have duplicated this documentation. So you either already have
it or you can download it, and unless necessary, this book will not repeat that
documentation because it’s usually much faster if you find the class descriptions with your
Web browser than if you look them up in a book. (Plus it will be up-to-date.) This book will
provide extra descriptions of the classes only when it’s necessary to supplement the
documentation so you can understand a particular example.

Goals

Goals
Like my previous book Thinking in C++, this book has come to be structured around the
process of teaching the language. In particular, my motivation is to create something that
provides me with a way to teach the language in my own seminars. When I think of a
chapter in the book, I think in terms of what makes a good lesson during a seminar. My goal
is to get bite-sized pieces that can be taught in a reasonable amount of time, followed by
exercises that are feasible to accomplish in a classroom situation.
My goals in this book are to:
Introduction 29
1. Present the material one simple step at a time so that you can easily digest each
concept before moving on.
2. Use examples that are as simple and short as possible. This sometimes prevents me
from tackling “real world” problems, but I’ve found that beginners are usually
happier when they can understand every detail of an example rather than being
impressed by the scope of the problem it solves. Also, there’s a severe limit to the
amount of code that can be absorbed in a classroom situation. For this I will no doubt
receive criticism for using “toy examples,” but I’m willing to accept that in favor of
producing something pedagogically useful.
3. Carefully sequence the presentation of features so that you aren’t seeing something
that you haven’t been exposed to. Of course, this isn’t always possible; in those
situations, a brief introductory description is given.
4. Give you what I think is important for you to understand about the language, rather
than everything I know. I believe there is an information importance hierarchy, and
that there are some facts that 95 percent of programmers will never need to know
and just confuses people and adds to their perception of the complexity of the
language. To take an example from C, if you memorize the operator precedence table
(I never did), you can write clever code. But if you need to think about it, it will also
confuse the reader/maintainer of that code. So forget about precedence, and use
parentheses when things aren’t clear.
5. Keep each section focused enough so that the lecture time – and the time between
exercise periods – is small. Not only does this keep the audience’s minds more active
and involved during a hands-on seminar, but it gives the reader a greater sense of
accomplishment.
6. Provide you with a solid foundation so that you can understand the issues well
enough to move on to more difficult coursework and books.

Learning Java

Learning Java
At about the same time that my first book Using C++ (Osborne/McGraw-Hill 1989) came
out, I began teaching that language. Teaching programming languages has become my
profession; I’ve seen nodding heads, blank faces, and puzzled expressions in audiences all
over the world since 1989. As I began giving in-house training with smaller groups of
people, I discovered something during the exercises. Even those people who were smiling and
nodding were confused about many issues. I found out, by chairing the C++ track at the
Software Development Conference for the past few years (and now also the Java track), that
I and other speakers tended to give the typical audience too many topics too fast. So
eventually, through both variety in the audience level and the way that I presented the
material, I would end up losing some portion of the audience. Maybe it’s asking too much,
but because I am one of those people resistant to traditional lecturing (and for most people, I
believe, such resistance results from boredom), I wanted to try to keep everyone up to speed.
For a time, I was creating a number of different presentations in fairly short order. Thus, I
ended up learning by experiment and iteration (a technique that also works well in Java
program design). Eventually I developed a course using everything I had learned from my
teaching experience – one that I would be happy giving for a long time. It tackles the
learning problem in discrete, easy-to-digest steps and in a hands-on seminar (the ideal
learning situation), there are exercises following each of the short lessons. I now give this
course in public Java seminars, which you can find out about at http://www.BruceEckel.com.
(The introductory seminar is also available as a CD ROM. Information is available at the
same Web site.)
The feedback that I get from each seminar helps me change and refocus the material until I
think it works well as a teaching medium. But this book isn’t just a seminar handout – I
tried to pack as much information as I could within these pages and structured it to draw
you through onto the next subject. More than anything, the book is designed to serve the
solitary reader who is struggling with a new programming language.
Goals
Like my previous book Thinking in C++, this book has come to be structured around the
process of teaching the language. In particular, my motivation is to create something that
provides me with a way to teach the language in my own seminars. When I think of a
chapter in the book, I think in terms of what makes a good lesson during a seminar. My goal
is to get bite-sized pieces that can be taught in a reasonable amount of time, followed by
exercises that are feasible to accomplish in a classroom situation.

Java Introduction

Introduction
Like any human language, Java provides a way to express concepts. If
successful, this medium of expression will be significantly easier and more
flexible than the alternatives as problems grow larger and more complex.
You can’t look at Java as just a collection of features; some of the features make no sense in
isolation. You can use the sum of the parts only if you are thinking about design, not simply
coding. And to understand Java in this way, you must understand the problems with it and
with programming in general. This book discusses programming problems, why they are
problems, and the approach Java has taken to solve them. Thus, the set of features I explain
in each chapter are based on the way I see a particular type of problem being solved with the
language. In this way I hope to move you, a little at a time, to the point where the Java
mindset becomes your native tongue.
Throughout, I’ll be taking the attitude that you want to build a model in your head that
allows you to develop a deep understanding of the language; if you encounter a puzzle
you’ll be able to feed it to your model and deduce the answer.
Prerequisites
This book assumes that you have some programming familiarity; you understand that a
program is a collection of statements, the idea of a subroutine/function/macro, control
statements such as “if” and looping constructs such as “while,” etc. However, you might
have learned this in many places, such as programming with a macro language or working
with a tool like Perl. As long as you’ve programmed to the point where you feel comfortable
with the basic ideas of programming, you’ll be able to work through this book. Of course,
the book will be easier for the C programmers and more so for the C++ programmers, but
28 Thinking in Java www.Bruce's.com
don’t count yourself out if you’re not experienced with those languages (but come willing to
work hard). I’ll be introducing the concepts of object-oriented programming and Java’s basic
control mechanisms, so you’ll be exposed to those, and the first exercises will involve the
basic control-flow statements.
Although references will often be made to C and C++ language features, these are not
intended to be insider comments, but instead to help all programmers put Java in perspective
with those languages, from which, after all, Java is descended. I will attempt to make these
references simple and to explain anything that I think a non- C/C++ programmer would