An Introduction To Classes: Chapter Goals
An Introduction To Classes: Chapter Goals
3
CHAPTER
Chapter Goals
◆ To become familiar with the process of implementing classes
103
104 Chapter 3. An Introduction to Classes
You have learned about the number and string data types of Java. Although it is
possible to write interesting programs using nothing but numbers and strings, most
useful programs need to manipulate data items that are more complex and more
closely represent entities in the real world. Examples of these data items are bank
accounts, employee records, graphical shapes, and so on.
The Java language is ideally suited for designing and manipulating such data items,
or objects. In Java, you define classes that describe the behavior of these objects. In this
chapter, you will learn how to define classes that describe objects with very simple
behavior. To implement more complex behavior, you will need to know more about
control structures in Java, which is the topic of Chapters 5 and 7.
In this section, you will learn how to create a simple class that describes the behavior
of a bank account. First, you will see how to use the class, assuming that someone has
already defined it. Next, you will see how to implement the class and its methods.
Before you start programming, you need to understand how the objects of your
class behave. Consider what kind of operations you can carry out with a bank ac-
count. You can
◆ Deposit money
◆ Withdraw money
◆ Get the current balance
In Java, these operations are expressed as method calls. Let’s suppose you have an
object harrysChecking of type BankAccount. Then you’ll want to be able to call
methods such as the following:
harrysChecking.deposit(2000);
harrysChecking.withdraw(500);
System.out.println("Balance: " + harrysChecking.getBalance());
That is, the set of methods
◆ deposit
◆ withdraw
◆ getBalance
forms the behavior of the BankAccount class. The behavior is the complete list of
methods that you can apply to objects of a given class. (This is also called the interface
of the class, but in Java, the word interface has a more restricted meaning; see Chapter
9.) You can think of an object of type BankAccount as a “black box” that can carry
out its methods.
For a more complex class, it takes some amount of skill and practice to discover the
appropriate behavior that makes the class useful and is reasonable to implement. You
3.1 Determining Object Behavior 105
will learn more about this important aspect of class design in Chapter 14. For now,
we will work with simple classes for which the behavior is very straightforward.
The behavior of our BankAccount class is simple, but it lets you carry out all
important operations that commonly occur with bank accounts. For example, here
is how you can transfer an amount from one bank account to another:
// transfer from one account to another
double transferAmount = 500;
momsSavings.withdraw(transferAmount);
harrysChecking.deposit(transferAmount);
And here is how you can add interest to a savings account:
double interestRate = 5; // 5% interest
double interestAmount =
momsSavings.getBalance() * interestRate / 100;
momsSavings.deposit(interestAmount);
Finally, we have to discuss how to construct objects of the BankAccount class. Recall
from chapter 1 that object variables such as harrysChecking are references to objects.
Suppose you declare an object variable:
BankAccount harrysChecking;
This object variable does not refer to any object at all. If you were to try to invoke a
method on this variable, the compiler would tell you that the variable has not been
initialized. To initialize the variable, you need to create a new BankAccount object.
You do this by calling
new BankAccount()
This call creates a new object and returns a reference to the newly created object (see
Figure 1). To use the object, you must assign that reference to an object variable (see
Figure 2):
BankAccount harrysChecking = new BankAccount();
We will implement the BankAccount so that a newly created bank account has a
zero balance. Here is a scenario to open a new account and to deposit some money:
// open a new account
double initialDeposit = 1000;
BankAccount harrysChecking = new BankAccount();
// constructed with zero balance
harrysChecking.deposit(initialDeposit);
Figure 1
BankAccount
harrysChecking
BankAccount
balance 0
Figure 2
As you can see, you can use objects of the BankAccount class to carry out meaningful
tasks, without knowing how the BankAccount objects store their data or how the
BankAccount methods do their work. This is an important aspect of object-oriented
programming.
Now that you understand how to use objects of the BankAccount class, let’s get
started designing the Java class that implements this behavior. As you already know,
you need to implement a class to describe object behavior:
public class BankAccount
{ BankAccount methods
BankAccount data
}
We have identified three methods:
public class BankAccount
{ public void deposit(double amount)
{ method implementation
}
public void withdraw(double amount)
{ method implementation
}
public double getBalance()
{ method implementation
}
BankAccount data
}
3.2 Defining Methods 107
Here we start out with the headers of methods. A method header consists of the
following parts:
harrysChecking.deposit(500); // Ok
To indicate that a method does not return a value, use the special type void. Both
the deposit and withdraw methods are declared with return type void.
The parameters are the inputs to the method. The deposit and withdraw meth-
ods each have one parameter: the amount of money to deposit or withdraw. You
need to specify the type of the parameter, such as double, and a name for the pa-
rameter, such as amount. The getBalance method has no parameters. In that case,
you still need to supply a pair of parentheses () behind the method name.
If a method has more than one parameter, you separate them by commas. For
example,
Once you have specified the method header, you must supply the implementation
of the method, in a block that is delimited by braces { . . . }. You will see how to
implement the BankAccount methods in Section 3.4.
108 Chapter 3. An Introduction to Classes
Java Syntax
3.1 Method Implementation
public class ClassName
{ . . .
accessSpecifier returnType methodName
(parameterType parameterName, . . . )
method implementation
}
. . .
}
Example:
public class BankAccount
{ . . .
public void deposit(double amount)
{ balance = balance + amount;
}
. . .
}
Purpose:
To define the behavior of a method
Each object must store its current state: the set of values that describe the object and
that influence how an object reacts to method calls. In the case of our simple bank
account objects, the state is the current balance of the bank account. (A more complex
bank account might have a richer state, perhaps the current balance together with
the interest rate paid on the current balance.) Each object stores the state in one or
more instance variables:
public class BankAccount
{ . . .
private double balance;
}
An instance variable declaration consists of the following parts:
Each object of a class has its own copy of the instance variables. For exam-
ple, if harrysChecking and momsSavings are two objects of the BankAccount
3.3 Instance Variables 109
harrysChecking
BankAccount
balance 0
momsSavings
BankAccount
balance 7500
Figure 3
Instance Variables
class, then each object has its own balance field, harrysChecking.balance and
momsSavings.balance (see Figure 3).
Instance variables are generally declared with the access specifier private. That
means, they can be accessed only by the methods of the same class, not by any other
method. In particular, the balance variable can be accessed only by the deposit,
withdraw, and getBalance methods.
In other words, if the instance variables are declared private, then all data access
must occur through the public methods. Thus, the instance variables of an object are
effectively hidden from the programmer who uses a class. They are of concern only
to the programmer who implements the class. The process of hiding data is called
encapsulation. Although it is theoretically possible in Java to leave instance variables
unencapsulated (by defining them as public), that is very uncommon in practice.
We will always make all instance variables private in this book.
For example, because the balance instance variable is private, you cannot access
the instance variable in other code:
double b = harrysChecking.balance; // Error
But you can call the public getBalance method to inquire about the balance:
double b = harrysChecking.getBalance(); // OK
110 Chapter 3. An Introduction to Classes
Java Syntax
3.2 Instance Variable Declaration
class ClassName
{ . . .
accessSpecifier type variableName;
}
Example:
public class BankAccount
{ . . .
private double balance;
}
Purpose:
To define a variable that is present in every ob-
ject of a class
Note that there is no equivalent setBalance method to set the balance to a par-
ticular value. With a real bank account, you cannot simply set the balance to any
desired value; you must update the balance the hard way, through withdrawals and
deposits. The class behavior captures this not always convenient fact.
The BankAccount class is so simple that it is not obvious what benefit you gain
from the encapsulation. The primary benefit of the encapsulation mechanism is the
guarantee that an object cannot accidentally be put into an incorrect state. For exam-
ple, suppose you want to make sure that a bank account is never overdrawn. You can
simply implement the withdraw method so that it refuses to carry out a withdrawal
that would result in a negative balance. On the other hand, if any code could freely
modify the balance instance variable of a BankAccount object, then it would be an
easy matter to store a negative number in the variable.
We will have more to say about the importance of encapsulation in later chapters.
For now, we will simply require that all instance variables be declared private. Hence
their values can be examined and set only by the methods of their own class.
You must provide an implementation for every method of the class. Here is the im-
plementation of the three methods of the BankAccount class. Note that these meth-
ods do not protect the account from being overdrawn. We did not yet introduce the
necessary Java statement to carry out that check. You will need to wait until Chapter
5 to see how to implement this enhancement.
public class BankAccount
{ public void deposit(double amount)
{ balance = balance + amount;
}
3.4 Implementing Methods 111
Java Syntax
3.3 The return Statement
return expression ;
or
return;
Example:
public class BankAccount
{ public double getBalance()
{ return balance;
}
. . .
}
Purpose:
To obtain the value that a method returns, and
exit the method immediately. The return value
becomes the value of the method call expres-
sion.
parameter amount is set to 500. This parameter is called an explicit parameter, be-
cause it is explicitly named in the method definition. However, the bank account
reference is not explicit in the method definition—it is called the implicit parameter of
the method. Inside each method, the object reference whose name is the keyword
this refers to the implicit parameter object. For example, in the preceding method
invocation, this was set to momsSavings.
Every method has one implicit parameter. The type of the implicit parame-
ter is the class that defines the method. You don’t give the implicit parameter a
name. It is always called this. (There is one exception to the rule that every meth-
od has an implicit parameter: static methods do not. We will discuss them in Chap-
ter 6.)
Methods can have any number of explicit parameters, or no explicit parameter at
all. You must give a type and a name for each explicit parameter. When you call a
method, you supply the value for the implicit parameter before the method name,
separated by a dot (.), and you supply the values for the explicit parameters inside
parentheses after the method name:
implicitParameterValue.methodName ( explicitParameterValues );
Next, look again closely at the implementation of the withdraw method. In the
statement
balance = balance - amount;
it is clear what amount means; it is the value of the explicit amount parameter. But
what is balance? There is a balance instance variable in the BankAccount class,
denoting the balance of a particular account. Which account? Java uses a convenient
shorthand. When you refer to an instance variable in a method, you automatically
refer to the instance variable of the object for which the method was called (implicit param-
eter). In other words, the withdraw method actually executes the statement
this.balance = this.balance - amount;
For example, when called with
momsSavings.withdraw(500);
the method call computes
momsSavings.balance = momsSavings.balance - 500;
3.5 Constructors
There is only one remaining issue with the BankAccount class. We need to define
the default constructor.
The code for a constructor sets all instance variables of the object. The purpose of
a constructor is to initialize the instance variables of an object.
3.5 Constructors 113
Constructors always have the same name as their class. Similar to methods, con-
structors are generally declared as public to enable any code in a program to con-
struct new objects of the class. Unlike methods, though, constructors do not have
return types.
Constructors are always invoked together with the new operator:
new BankAccount()
The new operator allocates memory for the object, and the constructor initializes
it. The value of the new operator is the reference to the newly allocated and con-
structed object. In most cases, you want to store that object reference in an object
variable:
BankAccount harrysChecking = new BankAccount();
// sets harrysChecking to a new account with zero balance
Constructors are not methods. You cannot invoke a constructor on an existing object.
For example, the call
harrysChecking.BankAccount(); // Error
is illegal. You can use a constructor only in combination with the new operator.
The constructor that we just defined is a default constructor—it takes no parame-
ters. The BankAccount default constructor sets the bank balance to 0. Some default
constructors work harder than that. For example, the default constructor of the Date
class in the java.util package constructs an object that stores the current date and
time.
If you do not initialize an instance variable that is a number, it is initialized au-
tomatically to zero. In this regard, instance variables act differently than local vari-
ables! Therefore, you didn’t actually have to initialize the balance instance variable
to zero. Nevertheless, it is a matter of good style to initialize every instance variable
explicitly. On the other hand, an object variable is initialized to a special value called
null, which indicates that the object variable does not yet refer to an actual object.
(See Section 3.9.)
Many classes have more than one constructor. For example, you can supply a
second constructor for the BankAccount class that sets the balance instance variable
to an initial balance, which is a parameter of the constructor:
public class BankAccount
{ public BankAccount()
{ balance = 0;
}
114 Chapter 3. An Introduction to Classes
Now there are two constructors with the same name. (You have no choice how
to name a constructor—it must have the same name as the class.) Whenever you
have multiple methods (or constructors) with the same name, the name is said to be
overloaded. The compiler figures out which one to call by looking at the parameters.
For example, if you call
new BankAccount()
then the compiler picks the second constructor. But if you call
new BankAccount("lotsa moolah")
then the compiler generates an error message—there is no constructor that takes a
parameter of type String.
We have now completed the implementation of the BankAccount class. Here is
the complete source code for this class.
Class BankAccount.java
public class BankAccount
{ public BankAccount()
{ balance = 0;
}
Java Syntax
3.4 Constructor Implementation
class ClassName
{ . . .
accessSpecifier ClassName
(parameterType parameterName, . . .)
{ constructor implementation
}
. . .
}
Example:
public class BankAccount
{ . . .
public BankAccount(double initialBalance)
{ balance = initialBalance;
}
. . .
}
Purpose:
To define the behavior of a constructor. Construc-
tors are used to initialize newly created objects.
◆ If you find it confusing to have an invisible parameter, you can always use the this param-
◆ eter to make the method easier to read:
◆
◆ class BankAccount
◆ { . . .
◆ public void monthlyFee()
◆ { final double MONTHLY_FEE = 10;
◆ this.withdraw(MONTHLY_FEE);
// withdraw from this account
◆
}
◆
}
◆
◆ several instance variables, it can be convenient to have the default constructor call another
◆ constructor of the same class instead. There is a shorthand notation to achieve this:
◆
◆ class BankAccount
◆ { public BankAccount (double initialBalance)
◆ { balance = initialBalance;
◆ }
◆ public BankAccount()
◆ { this(0);
◆ }
◆ . . .
◆ }
◆
◆ The command this(0); means “Call another constructor of this class and supply the value
◆ 0.” Such a constructor call can occur only as the first line in another constructor.
◆ This syntax is a minor convenience. We will not use it in this book. Actually, the use of
◆ the this keyword is a little confusing. Normally, this denotes a reference to the implicit
◆ parameter, but if this is followed by parentheses, it denotes a call to another constructor of
◆ this class.
◆ ◆ In a program with multiple windows, Ctrl + Tab usually toggles through the windows
◆ managed by that program, for example between the source and error window.
◆
◆ Alt + Tab toggles between applications, letting you toggle quickly between, for exam-
◆
ple, the compiler and a folder explorer program.
◆
◆ ◆ Hold down the Shift key and press the arrow keys to highlight text. Then use Ctrl +
◆ X to cut the text, Ctrl + C to copy it, and Ctrl + V to paste it. These keys are easy to
◆ remember. The V looks like an insertion mark that an editor would use to insert text.
◆ The X should remind you of crossing out text. The C is just the first letter in “copy”.
◆ (OK, so it is also the first letter in “Cut”—no mnemonic rule is perfect.) You find these
◆ reminders in the Edit menu.
◆
◆ Of course, the mouse has its use in text processing: to locate or select text that is on the same
◆ screen but far away from the cursor.
◆ Take a little bit of time to learn about the keyboard shortcuts that the designers of your
◆ programs provided for you, and the time investment will be repaid many times during your
◆ programming career. When you blaze through your work in the computer lab with keyboard
◆ shortcuts, you may find yourself surrounded by amazed onlookers who whisper, “I didn’t
◆ know you could do that.”
In the last five sections, you saw how a class implementer designs and implements
a class: by determining the desired behavior and implementing the methods of the
class. Now let’s see how a class user can make use of the designer’s efforts. The class
user is still a programmer who wants to solve a particular problem with a class,
without necessarily having to think about how the class was implemented. (The
class user is not the same as the program user, a nonprogrammer who has no interest
at all in how the program is put together.) Of course, as a beginning Java programmer,
you are often simultaneously the implementer and user of your own classes. You
should nevertheless develop a “split personality”. When designing a class, think of the
concept that the class represents (such as a bank account), and think of the behavior
that you—or any other programmers who might need to write programs involving
bank accounts—need to utilize. Conversely, when using a class, whether it is one of
your own creation or supplied in a library, you should not think of the internals of
the class.
Let’s try this out by writing a program that puts the BankAccount class to work.
We want to study the following scenario:
A savings account is created with a balance of $10,000. For two years in a row, add 5%
interest. How much money is in the account after two years?
Now you need two classes: the BankAccount class that we developed in the preceding
sections, and a second class that we will call BankAccountTest. The main method
120 Chapter 3. An Introduction to Classes
Program BankAccountTest.java
public class BankAccountTest
{ public static void main(String[] args)
{ BankAccount account = new BankAccount(10000);
double interest;
When you have a programming problem, you need to discover one or more classes
that help you solve the problem. Discovering useful classes is an important skill that
you will practice throughout your first Java programming course. In Chapter 14 we
will approach this issue systematically, but for now we just want to give you a useful
3.7 Discovering Classes 121
rule of thumb how to go about discovering classes. That rule of thumb is to look for
nouns in the description of the problem that you want to solve. Some of the nouns may
suggest useful classes. Conversely, some of the verbs may suggest useful methods of
those classes.
Let’s try this out with one of the programs of the preceding chapter. Recall that
you studied a program that asks a user to specify the number of coins in a purse and
then prints out the total amount of money in the purse.
There are three nouns in the problem description: coin, purse, and money. What is
a coin? A coin has a name (such as “quarter”) and a dollar value (such as $0.25). A
purse contains a collection of coins.
We will turn these nouns into classes in a moment. The noun money can lead to
a useful class, for example, if you want to model different currencies. In a simple
example, though, you can just use a floating-point number to store a dollar amount,
so we won’t make a separate money class now.
A Coin object needs to remember its name and dollar value. A constructor sets
the name and value, and you need two methods to find out the name and value of a
coin. Actually, there is a complexity with coin names. Given a singular name (such as
“penny” or “quarter”), the computer has no way of determining the plural form (such
as “pennies” and “quarters”). Simply store the plural form. (A better way would be to
store both names and pick the correct one, depending on the coin count. However,
you don’t yet know enough Java to implement this enhancement.)
The Coin class is a very simple class, consisting of a constructor, two methods,
and two instance variables. Here it is, without much further ado.
class Coin
{ // a constructor for constructing a coin
Let’s put this class to work. How many coins of that type does a user have?
System.out.println("How many " + coinType.getName()
+ " do you have?");
int coinCount = console.readInt();
public Purse()
{ total = 0;
}
Now that you have discovered and implemented these simple classes, the
program is easy. First, make a Purse object. For each of the four coin types,
find out how many coins the user has, and call addCoins. Finally, print the total
value.
public class Coins6
{ public static void main(String[] args)
{ Purse thePurse = new Purse();
ConsoleReader console = new ConsoleReader(System.in);
thePurse.addCoins(coin1Count, coin1);
thePurse.addCoins(coin2Count, coin2);
thePurse.addCoins(coin3Count, coin3);
thePurse.addCoins(coin4Count, coin4);
nothing about United States coin types (pennies, nickels, dimes, or quarters). You
could easily add a few pesos or Zorkmids to the purse.
Designing and implementing appropriate classes is an important part of writing
professional Java programs. It takes a bit longer to come up with the classes, and the
resulting programs are longer than programs that don’t use classes, but experience
has shown that such programs are more likely to be correct and are easier to maintain
over time. Those considerations are considerably more important in practice than
simply trying to write short programs quickly.
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆
◆ Figure 4
◆
◆
◆ A Mainframe Computer
◆
◆
◆ Instead, they sold about 1,500 machines of their System 650 model and went on to build and
◆ sell more powerful computers.
◆ The so-called mainframe computers of the 1950s, 1960s, and 1970s were huge. They filled
◆ up whole rooms, which had to be climate-controlled to protect the delicate equipment (see
◆ Figure 4). Today, because of miniaturization technology, even mainframes are getting smaller,
◆ but they are still very expensive. (At the time of this writing, the cost for a midrange IBM 3090
◆ is approximately 4 million dollars.)
◆
These huge and expensive systems were an immediate success when they first appeared,
◆
because they replaced many roomfuls of even more expensive employees, who had previously
◆
performed the tasks by hand. Few of these computers do any exciting computations. They
◆
◆ keep mundane information, such as billing records or airline reservations; they just keep lots
◆ of them.
◆ IBM was not the first company to build mainframe computers; that honor belongs to
◆ the Univac Corporation. However, IBM soon became the major player, partially because
◆ of technical excellence and attention to customer needs and partially because it exploited
◆ its strengths and structured its products and services in a way that made it difficult for cus-
◆ tomers to mix them with those of other vendors. In the 1960s IBM’s competitors, the so-called
◆ “Seven Dwarfs”—GE, RCA, Univac, Honeywell, Burroughs, Control Data, and NCR—fell
◆ on hard times. Some went out of the computer business altogether, while others tried
◆ unsuccessfully to combine their strengths by merging their computer operations. It was
◆
126 Chapter 3. An Introduction to Classes
◆ generally predicted that they would eventually all fail. It was in this atmosphere that the
◆ U.S. government brought an antitrust suit against IBM in 1969. The suit went to trial in 1975
◆ and dragged on until 1982, when the Reagan Administration abandoned it, declaring it “with-
◆ out merit”.
◆ Of course, by then the computing landscape had changed completely. Just as the dino-
◆ saurs gave way to smaller, nimbler creatures, three new waves of computers had appeared:
◆ the minicomputers, workstations, and microcomputers, all engineered by new companies,
◆
not the Seven Dwarfs. Today, the importance of mainframes in the marketplace has dimin-
◆
ished, and IBM, while still a large and resourceful company, no longer dominates the com-
◆
◆ puter market.
◆ Mainframes are still in use today for two reasons. They still excel at handling large data
◆ volumes. More importantly, the programs that control the business data have been refined
◆ over the last 20 or more years, fixing one problem at a time. Moving these programs to less
◆ expensive computers, with different languages and operating systems, is difficult and error-
◆ prone. Sun Microsystems, a leading manufacturer of workstations, was eager to prove that its
◆ mainframe system could be “downsized” and replaced by its own equipment. Sun eventually
◆ succeeded, but it took over five years—far longer than it expected.
Consider the following code that copies a number and then adds an amount to the
copy:
double balance1 = 1000;
double balance2 = balance1; // see Figure 5
balance2 = balance2 + 500;
Unlike the preceding code, now both account1 and account2 have a balance of 1500.
In Java, there is a big difference between the assignment of numbers,
double balance2 = balance1;
Each number variable is a memory location that holds a value. If you change the
contents of either variable, then the other is not affected. Object variables, how-
ever, do not hold values; they hold references to objects. The actual object is stored
elsewhere, and the object variable remembers where it is stored. When you copy an
3.9 The null Reference 127
balance2 1000
Copying Numbers
Figure 6 account1
account2
Copying Object Ref- BankAccount
erences
balance 1000
object variable, you do not make a copy of the object; you merely make a copy
of the reference. After the copy, both variables refer to the same object. If you
use a method that changes the object, then both variables access the changed ob-
ject.
What can you do if you actually need to make a true copy of an object, that is, a
new object whose state is identical to an existing object? As you will see in Chapter
9, you can define a clone method for your classes to make such a copy. But in the
meantime, you will simply have to construct a new object:
BankAccount account2 = new BankAccount(account1.getBalance());
account2
A null Reference BankAccount
balance 1000
128 Chapter 3. An Introduction to Classes
If you try to invoke a method on a variable containing a null reference, the program
terminates. Clearly you must avoid doing so in your programs.
It is important to note that null is not the same as the number 0. The number 0
is a valid number, just like 1000 or ⫺13. A number variable always contains some
number. However, object variables have the choice between referring to an actual
object or referring to no object at all.
Unlike numbers, strings are objects in Java. A String variable can refer either to
an actual string or to no string at all:
Note that the empty string and a null reference are different (see Figure 8), just as
there is a difference between an empty message (say, on an answering machine) and
no message at all (such as “no comment” by a spokesman for a politician).
Figure 8 greeting
message
String References String
comment null
"Hello"
String
""
3.9 The null Reference 129
◆ apparent only when the program runs and dies with a null-reference error.
◆ To avoid this problem, make it a habit to initialize every instance variable in every con-
◆ structor.
In this section you will learn how to implement the ConsoleReader class that we use
for reading keyboard input in this book. If you are interested in the implementation
details, you should first read through Section 2.8 to understand the technical issues of
reading keyboard input in Java. If you aren’t interested in the implementation details
of the ConsoleReader class, you can safely skip this section.
A central aspect of class design is encapsulation: the hiding of unimportant details
from the class user. In Section 2.8 you saw how cumbersome it is in Java to read
a number from System.in: You must construct a BufferedReader, read a line of
input as a string, and convert it to a number, being careful about exceptions in the
process. That is just the kind of detail that lends itself to encapsulation.
To build the actual ConsoleReader class, let us use the same method as with the
BankAccount class:
Step 1: Let’s think of the behavior that you want for reading input. As the user of
the class, you are not particularly interested in buffered readers, number conversions,
or exceptions. You just want the value that the user is entering, converted to the right
type. Here is a set of methods that achieves this goal:
◆ Read an integer
◆ Read a floating-point number
◆ Read a string
Step 2: Translated to Java notation, the method and constructor definitions are as
follows:
public class ConsoleReader
{ public ConsoleReader(InputStream inStream)
{ constructor implementation
}
public int readInt()
{ method implementation
}
public double readDouble()
{ method implementation
}
public string readLine()
3.10 Implementing the ConsoleReader Class (Advanced) 133
{ method implementation
}
instance variables
}
Step 3: As you know from Section 2.8, you need a BufferedReader to read a
line of input. We will store that BufferedReader object in an instance variable of
the ConsoleReader class:
public class ConsoleReader
{ . . .
private BufferedReader reader;
}
This instance variable is set in the constructor. Recall from Section 2.8 that an input
stream can read only individual bytes. To read lines of characters, you must first turn
an input stream (such as system.in) into an input stream reader and then turn the
reader into a buffered reader
public class ConsoleReader
{ public ConsoleReader(InputStream inStream)
{ reader = new BufferedReader(new InputStreamReader(inStream));
}
. . .
}
Step 4: Now, let us turn to the implementation of the methods. We will first im-
plement the readLine method; it calls the readLine method of the reader object.
Now, though, we have to worry about exceptions. As we did in Chapter 2, we will
terminate the program when an I/O exception is encountered. Here is the code:
public String readLine()
{ String inputLine = "";
try
{ inputLine = reader.readLine();
}
catch(IOException e)
{ System.out.println(e);
System.exit(1);
}
return inputLine;
}
In other words, the readLine method of the ConsoleReader class simply calls the
readLine method of the BufferedReader class, and deals with the possibility of
the IOException, so that you don’t have to.
Having implemented this method, the other methods are very simple. To read
in an integer, simply read in an input line, using the readLine method that we just
134 Chapter 3. An Introduction to Classes
defined. Then parse the input into an integer and return that value. Floating-point
numbers are read in the same fashion.
public int readInt()
{ String inputString = readLine();
int n = Integer.parseInt(inputString);
return n;
}
Chapter Summary
1. You use objects in your program when you need to manipulate data that are more
complex than just numbers and strings. Every object belongs to a class. A class de-
termines the behavior of its objects.
3. Every instance method has one implicit parameter—the object on which the
method is invoked—and zero or more explicit parameters.
5. Number variables hold values. Object variables hold references. When a number is
copied into a number variable, the variable gets a copy of the value. When an object is
copied into an object variable, the variable gets another reference to the same object.
Review Exercises
Exercise R3.2. Give the Java code for an object of class BankAccount and for an object
variable of class BankAccount.
Exercise R3.3. Explain the differences between an instance variable and a local vari-
able.
Exercise R3.5. What are the construction parameters for a BankAccount object?
Exercise R3.8. Repeat the preceding exercise, but now define object variables that
are initialized with the required objects.
double x = BankAccount(10000).getBalance();
BankAccount b;
b.deposit(10000);
b = new BankAccount(10000);
b.addCoins(new Coin(0.25, "quarters"));
Purse p = null;
p.addCoins(new Coin(0.25, "quarters"));
Exercise R3.10. Describe all constructors of the BankAccount class. List all methods
that can be used to change a BankAccount object. List all methods that don’t change
the BankAccount object.
136 Chapter 3. An Introduction to Classes
Exercise R3.12. If b1 and b2 store objects of class BankAccount, consider the fol-
lowing instructions.
b1.deposit(b2.getBalance());
b2.deposit(b1.getBalance());
Are the balances of b1 and b2 now identical? Explain.
Programming Exercises
Exercise P3.1. Write a program that asks for an initial balance amount. Create a
BankAccount object with that amount. Then ask for a deposit amount and a with-
drawal amount. Carry out the deposit and withdrawal, then print the remaining bal-
ance.
Exercise P3.2. Implement a class Employee. An employee has a name (a string) and
a salary (a double). Write a default constructor, a constructor with two parameters
(name and salary), and methods to return the name and salary. Write a small program
that tests your class.
Exercise P3.3. Enhance the class in the preceding exercise by adding a method
raiseSalary(double byPercent) that raises the employee’s salary by a certain
percentage. Sample usage:
Employee harry = new Employee("Hacker, Harry", 55000);
harry.raiseSalary(10); // Harry gets a 10% raise
Exercise P3.4. Implement a class Car with the following properties. A car has a
certain fuel efficiency (measured in miles/gallon or liters/km—pick one) and a certain
amount of fuel in the gas tank. The efficiency is specified in the constructor, and the
initial fuel level is 0. Supply a method drive that simulates driving the car for a
certain distance, reducing the fuel level in the gas tank, and methods getFuelLevel,
returning the current fuel level, and tank, to tank up. Sample usage:
Car myBeemer = new Car(29); // 29 miles per gallon
myBeemer.tank(20); // tank 20 gallons
myBeemer.drive(100); // drive 100 miles
System.out.println(myBeemer.getFuelLevel());
// print fuel remaining
Programming Exercises 137
Exercise P3.5. Change the purse program Coins6 to ask the user to supply coins
in a different currency. For example, you can use the following collection of German
coins:
new Coin(0.01, "Pfennig");
new Coin(0.1, "Groschen");
new Coin(1.0, "Mark");
What changes did you have to make? What changes would you have to make to the
Coins4 program to change the currency? Which is easier?
Exercise P3.7. Implement a class Student. For the purpose of this exercise, a student
has a name and a total quiz score. Supply an appropriate constructor and methods
getName(), addQuiz(int score), getTotalScore(), and getAverageScore().
To compute the latter, you also need to store the number of quizzes that the student
took.
Exercise P3.8. Implement a class Product. A product has a name and a price, for
example new Product("Toaster", 29.95). Supply methods printProduct(),
getPrice(), and setPrice(). Write in a program that makes two products, prints
them, reduces their prices by $5.00, and then prints them again.
Exercise P3.9. Implement a class Circle that has methods getArea() and
getCircumference(). In the constructor, supply the radius of the circle.