• 沒有找到結果。

M ost useful programs don’t just manipulate numbers and strings.

N/A
N/A
Protected

Academic year: 2022

Share "M ost useful programs don’t just manipulate numbers and strings. "

Copied!
44
0
0

加載中.... (立即查看全文)

全文

(1)

33

C h a p t e r 2

An

Introduction to

Objects and Classes

 To realize the difference between objects and object references

 To become familiar with the process of implementing classes

 To be able to implement simple methods

 To understand the purpose and use of constructors

 To understand how to access instance fields and local variables

 To appreciate the importance of documentation comments C H A P T E R G O A L S

To understand the concepts of classes and objects

M ost useful programs don’t just manipulate numbers and strings.

Instead, they deal with data items that are more complex and that more closely represent entities in the real world. Examples of these data items include bank accounts, employee records, and graphical shapes.

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. As

you learn more about Java program-

ming in subsequent chapters, you will

be able to implement classes whose

objects carry out more sophisticated

actions.

(2)

34 CHAPTER 2 An Introduction to Objects and Classes

34

Objects and classes are central concepts for Java programming. It will take you some time to master these concepts fully, but since every Java program uses at least a couple of objects and classes, it is a good idea to have a basic understanding of these concepts right away.

An object is an entity that you can manipulate in your program, generally by calling methods. For example, you saw in Chapter 1 that System.out refers to an object, and you saw how to manipulate it by calling the println method. (Actually, several different methods are available, all called println: one for printing strings, one for printing integers, one for printing floating-point numbers, and so on; the rea- son is discussed in Section 2.8.) When you call the println method, some activities occur inside the object, and the ultimate effect is that the object causes text to appear in the console window.

You should think of the object as a “black box” with a public inter- face (the methods you can call) and a hidden implementation (the code and data that are necessary to make these methods work).

Different objects support different sets of methods. For example, you can apply the println method to the System.out object, but not to the string object "Hello, World!". That is, it would be an error to call

"Hello, World!".println(); // This method call is an error

2.1 Using and Constructing Objects

2.1 Using and Constructing Objects 34 Syntax 2.1: Object Construction 37 2.2 Object Variables 37

Syntax 2.2: Variable Definition 40 Syntax 2.3: Importing a Class from

a Package 40

Common Error 2.1: Forgetting to Initialize Variables 41

Advanced Topic 2.1: Importing Classes 41 2.3 Defining a Class 42

Syntax 2.4: Method Implementation 43 Syntax 2.5: The return Statement 44 2.4 Testing a Class 44

Productivity Hint 2.1: Using the Command Line Effectively 46

2.5 Instance Fields 47

Syntax 2.6: Instance Field Declaration 49 2.6 Constructors 49

Syntax 2.7: Constructor Implementation 51 2.7 Designing the Public Interface of

a Class 51

Advanced Topic 2.2: Overloading 53 2.8 Commenting the Public Interface 54 Productivity Hint 2.2: The javadoc Utility 56 Productivity Hint 2.3: Keyboard Shortcuts for

Mouse Operations 57

2.9 Specifying the Implementation of a Class 58

Common Error 2.2: Trying to Reset an Object by Calling a Constructor 62

HOWTO 2.1: Designing and Implementing a Class 62

2.10 Variable Types 66

Common Error 2.3: Forgetting to Initialize Object References in a Constructor 67 2.11 Explicit and Implicit Method

Parameters 67

Common Error 2.4: Trying to Call a Method Without an Implicit Parameter 68

Advanced Topic 2.3: Calling One Constructor from Another 69

Random Fact 2.1: Mainframes—When Dinosaurs Ruled the Earth 70 C H A P T E R C O N T E N T S

Objects are entities in your program that you manipulate by invoking methods.

The public interface of a class specifies what you can do with its objects. The hidden implementation describes how these actions are carried out.

(3)

2.1 Using and Constructing Objects 35

The reason is simple. The System.out and "Hello, World!" objects belong to dif- ferent classes. The System.out object is an object of the class PrintStream, but the

"Hello, World!" object is an object of class String. You can apply the println method to any object of the PrintStream class, but the String class does not support the println method. The String class supports a good number of other methods;

you will see many of them in Chapter 3. For example, the length method counts the number of characters in a string. You can apply that method to any object of type String. Thus,

"Hello, World!".length() // This method call is OK

is a correct method call—it computes the number of characters in the string object

"Hello, World!" and returns the result, 13. (The quotation marks are not counted.) You can verify that the length method does return the length of a String object by writing a short test program

public class LengthTest {

public static void main(String[] args) {

System.out.println("Hello, World!".length());

} }

Every object belongs to a class. The class defines the methods for the objects. Thus, the PrintStream class defines the print and println methods. The String class defines the length method and many other methods.

The System.out object is created automatically when a Java pro- gram loads the System class. String objects are created when you specify a string enclosed in quotation marks. However, in most Java programs, you want to create more objects.

To see how to create new objects, let us turn to another class: the Rectangle class in the Java class library. Objects of type Rectangle describe rectangular shapes—see Figure 1.

Note that a Rectangle object isn’t a rectangular shape—it is a set of numbers that describe the rectangle (see Figure 2). Each rectangle is described by the x- and y- coordinates of its top left corner, its width, and its height. To make a new rectangle, you

F i g u r e 1

Rectangular Shapes Classes are factories for objects. You construct a new object of a class with the new operator.

(4)

need to specify these four values. For example, you can make a new rectangle with top left corner at (5, 10), width 20, and height 30 as follows:

new Rectangle(5, 10, 20, 30)

The new operator causes the creation of an object of type Rectangle. The process of creat- ing a new object is called construction. The four values 5, 10, 20, and 30 are called the construc- tion parameters. Different classes will require different construction parameters. For example, to construct a Rectangle object, you supply four numbers that describe the position and size of the rectangle. To construct a Car object, you might supply the model name and year.

Actually, some classes let you construct objects in multiple ways. For example, you can also obtain a Rectangle object by supplying no construction parameters at all (but you must still supply the parentheses):

new Rectangle()

This constructs a (rather useless) rectangle with top left corner at the origin (0, 0), width 0, and height 0.

To construct any object, you do the following:

1. Use the new operator.

2. Give the name of the class.

3. Supply construction parameters (if any) inside parentheses.

What can you do with a Rectangle object? Not much, for now. In Chapter 4, you will learn how to display rectangles and other shapes in a window. You can pass a rectan- gle object to the System.out.println or print method, which just prints a descrip- tion of the rectangle object onto the console window:

public class RectangleTest {

public static void main(String[] args) {

System.out.println(new Rectangle(5, 10, 20, 30));

} }

This program prints the line

java.awt.Rectangle[x=5,y=10,width=20,height=30]

More specifically, this program creates an object of type Rectangle, then passes that object to the println method. Afterward, that object is no longer used.

F i g u r e 2

A Rectangle Object

x = 5

y = 10

width = 20 height = 30

Rectangle

(5)

2.2 Object Variables 37

Of course, usually you want to do something more to an object than just create it, print it, and forget it. To remember an object, you need to hold it in an object variable. As was mentioned in Chapter 1, a variable is an item of information in memory whose location is identified by a sym- bolic name. An object variable is a container that stores the location of an object.

In Java, every variable has a particular type that identifies what kind of information it can contain. You create a variable by giving its type followed by a name for the variable. For example,

Rectangle cerealBox;

This statement defines an object variable, cerealBox. The type of this variable is Rectangle. That is, after the cerealBox variable has been defined by the preceding statement, thereafter in the program it must always contain the location of a Rectangle object, never a Car or String object.

Syntax 2.1 : Object Construction

new ClassName (parameters) Example:

new Rectangle(5, 10, 20, 30) new Car("BMW 540ti", 2004) Purpose:

To construct a new object, initialize it with the construction parameters, and return a reference to the constructed object

2.2 Object Variables

F i g u r e 3

An Uninitialized Object Variable

cerealBox =

F i g u r e 4

An Object Variable Containing an Object Reference cerealBox =

x = 5

y = 10

width = 20 height = 30

Rectangle You store object locations

in object variables.

(6)

You can choose any variable names you like, provided you follow a few simple rules.

 Names can be made up of letters, digits, and the underscore (_) character. They cannot start with a digit, though.

 You cannot use other symbols such as ? or % in variable names.

 Spaces are not permitted inside names, either.

 Furthermore, you cannot use reserved words such as public as names; these words are reserved exclusively for their special Java meanings. Appendix A4 lists all reserved words.

 Variable names are also case-sensitive; that is, cerealBox and Cerealbox are dif- ferent names.

Look again at the declaration of the cerealBox variable. So far, the variable is not initialized—it doesn’t yet contain any object loca- tion at all (see Figure 3). You need to set cerealBox to an object location. How do you get an object location? The new operator cre- ates a new object and returns its location. Use that value to initialize the variable.

Rectangle cerealBox = new Rectangle(5, 10, 20, 30);

You may wonder what happens if you leave the cerealBox variable uninitialized. See Common Error 2.1 for an answer. Figure 4 shows the result.

An object location is also often called an object reference. When a variable contains the location of an object, we say that it refers to an object. For example, cerealBox refers to the Rectangle object that the new operator constructed.

It is very important that you remember that the cerealBox vari- able does not contain the object. It refers to the object. You can have two object variables refer to the same object:

Rectangle r = cerealBox;

Now you can access the same Rectangle object both as cerealBox and as r, as shown in Figure 5.

Usually, your programs use objects in the following ways:

1. Construct an object with the new operator.

2. Store the object reference in an object variable.

3. Call methods on the object variable.

The Rectangle class has over 50 methods, some useful, some less so. To give you a flavor of manipulating Rectangle objects, let us look at a method of the Rectangle class. The translate method moves a rectangle by a certain distance in the x- and y- directions. For example,

cerealBox.translate(15, 25);

All object variables must be initialized before you access them.

An object reference describes the location of an object.

Multiple object variables can contain references to the same object.

(7)

2.2 Object Variables 39

moves the rectangle by 15 units in the x-direction and 25 units in the y-direction. Mov- ing a rectangle doesn’t change its width or height, but it changes the top left corner. The code fragment

Rectangle cerealBox = new Rectangle(5, 10, 20, 30);

cerealBox.translate(15, 25);

System.out.println(cerealBox);

prints

java.awt.Rectangle[x=20,y=35,width=20,height=30]

Let’s turn this code fragment into a complete program. As with the Hello program, you need to carry out three steps:

1. Invent a new class, say MoveTest. 2. Supply a main method.

3. Place instructions inside the main method.

For this program, you need to carry out another step in addition to those: You need to import the Rectangle class from a package. A package is a collection of classes with a related purpose. All classes in the standard library are contained in packages. The Rectangle class belongs to the package java.awt (where awt is an abbreviation for

“Abstract Windowing Toolkit”), which contains many classes for drawing windows and graphical shapes.

To use the Rectangle class from the java.awt package, simply place the following line at the top of your program:

import java.awt.Rectangle;

Why didn’t you have to import the System and String classes that were used in the Hello program? The reason is that the System and String classes are in the java.lang package, and all classes from this package are automatically imported, so you never need to import them yourself.

F i g u r e 5

Two Object Variables Referring to the Same Object

cerealBox = r =

x = 5

y = 10

width = 20 height = 30

Rectangle

Java classes are grouped into packages. If you use a class from another package (other than the java.lang package), you must import the class.

(8)

Thus, the complete program is:

File MoveTest.java

1 import java.awt.Rectangle;

2

3 public class MoveTest 4 {

5 public static void main(String[] args)

6 {

7 Rectangle cerealBox = new Rectangle(5, 10, 20, 30);

8

9 // move the rectangle

10 cerealBox.translate(15, 25);

11

12 // print the moved rectangle

13 System.out.println(cerealBox);

14 }

15 }

Syntax 2.2 : Variable Definition

TypeName variableName;

TypeName variableName = expression;

Example:

Rectangle cerealBox;

String name = "Dave";

Purpose:

To define a new variable of a particular type and optionally supply an initial value

Syntax 2.3 : Importing a Class from a Package

import packageName.ClassName;

Example:

import java.awt.Rectangle;

Purpose:

To import a class from a package for use in a program

(9)

2.2 Object Variables 41

Forgetting to Initialize Variables

You just learned how to store an object reference in a variable so that you can manipulate the object in your program. This is a very common step, and it can lead to one of the most common programming errors—using a variable that you forgot to initialize.

Suppose your program contains the lines Rectangle cerealBox;

cerealBox.translate(15, 25);

Now you have a variable cerealBox. An object variable is a container for an object refer- ence. But you haven’t put anything into the variable—it is not initialized. Thus, there is no rectangle to translate.

The compiler spots these problems. If you make this mistake, the compiler will com- plain that you are trying to use an uninitialized variable.

The remedy is to initialize the variable. You can initialize a variable with any object reference, either a reference to a new object or an existing object.

// initialize with new object reference

Rectangle cerealBox = new Rectangle(5, 10, 20, 30);

// initialize with existing object reference Rectangle cerealBox = anotherRectangle;

Importing Classes

You have seen the simplest and clearest method for importing classes from packages.

Simply use an import statement that names the package and class for each class that you want to import. For example,

import java.awt.Rectangle;

import java.awt.Point;

There is a shortcut that many programmers find convenient. You can import all classes from a package name with the construct

import packagename.*;

For example, the statement import java.awt.*;

imports all classes from the java.awt package. This is less trouble to type, but we won’t use this style in this book, for a simple reason. If a program imports multiple packages

Common Error 2.1

















Advanced Topic 2.1













(10)

and you encounter an unfamiliar class name, then you have to look up all of those pack- ages to find the class. For example, suppose you see a program that imports

import java.awt.*;

import java.io.*;

Furthermore, suppose you see a class name Image. You would not know whether the

Image class is in the java.awt package or the java.io package. Why do you care in which package it is? You need to know if you want to use the class in your own programs.

Note that you cannot import multiple packages with a single import statement. For example, import java.*.*; // Error

is a syntax error.

You can avoid all import statements by using the full name (both package name and class name) whenever you use a class. For example,

java.awt.Rectangle cerealBox =

new java.awt.Rectangle(5, 10, 20, 30);

That is pretty tedious, and you won’t find many programmers doing it.

In this section, you will learn how to define your own classes. Recall that a class defines the methods that you can apply to its objects. We will start with a very simple class that contains a single method.

public class Greeter {

public String sayHello() {

String message = "Hello, World!";

return message;

} }

A method definition contains the following parts:

 An access specifier (such as public)

 The return type of the method (such as String)

 The name of the method (such as sayHello)

 A list of the parameters of the method, enclosed in parentheses (the sayHello method has no parameters)

 The body of the method: a sequence of statements enclosed in braces

The access specifier controls which other methods can call this method. Most methods should be declared as public. That way, all other methods in your program can call them.













2.3 Defining a Class

(11)

2.3 Defining a Class 43

(Occasionally, it can be useful to have methods that are not so widely callable—turn to Chapter 11 for more information on this issue.)

The return type is the type of the value that the method returns to its caller. The sayHello method returns an object of type String (namely, the string "Hello, World!").

Some methods just execute some statements without returning a value. Those methods are tagged with a return type of void.

Many methods depend on other information. For example, the translate method of the Rectangle class needs to know how far you want to move the rectangle horizontally and vertically. These items are called the parameters of the method. Each parameter is a variable, with a type and a name. Parameter variables are separated by commas. For exam- ple, the implementors of the Java library defined the translate method like this:

public class Rectangle { . . .

public void translate(int x, int y) {

method body }

. . . }

Syntax 2.4 : Method Implementation

public class ClassName {

. . .

accessSpecifier returnType methodName(parameterType parameterName, ... ) {

method body }

. . . }

Example:

public class Greeter {

public String sayHello() {

String message = "Hello, World!";

return message;

} }

Purpose:

To define the behavior of a method A method definition specifies

the method name, parameters, and the statements for carrying out the method's actions.

(12)

The method body contains the statements that the method executes. The sayHello method body, for example, contains two statements. The first statement initializes a String variable with a String object:

String message = "Hello, World!";

The second statement is a special statement that terminates the method. When the return statement is executed, the method exits. If the method has a return type other than void, then the return state- ment must contain a return value, namely the value that the method sends back to its caller. The sayHello method returns the object refer- ence stored in message—that is, a reference to the "Hello, World!" string object.

Now you have seen how to define a class that contains a method. In the next section, you will see what you can do with the class.

In the preceding section, you saw the definition of a simple Greeter class. What can you do with it? Of course, you can compile the file Greeter.java. However, you can’t exe- cute the resulting Greeter.class file. It doesn’t contain a main method. That is normal—most classes don’t contain a main method.

To do something with your class, you have two choices. Some development environments, such as the excellent BlueJ program, let you create objects of a class and call methods on those objects. Figure 6 shows the result of creating an object of the Greeter class and invok- ing the sayHello method. The dialog box contains the return value of the method.

Alternatively, if you don’t have a development environment that lets you test a class interactively, you can write a test class. A test class is a class with a main method that con- tains statements to test another class. A test class typically carries out the following steps:

1. Construct one or more objects of the class that is being tested.

2. Invoke one or more methods.

3. Print out one or more results.

Syntax 2.5 : The

return

Statement

return expression; or

return;

Example:

return message;

Purpose:

To specify the value that a method returns, and exit the method immediately.

The return value becomes the value of the method call expression.

2.4 Testing a Class

Use the return statement to specify the value that a method returns to its caller.

To test a class, use an environment for interactive testing, or write a second class to execute test instructions.

(13)

2.4 Testing a Class 45

The RectangleTest class in Section 2.1 is a good example of a test class. That class tests the Rectangle class—a class in the Java library.

Here is a class to test the Greeter class. The main method constructs an object of type Greeter, invokes the sayHello method, and displays the result on the console.

public class GreeterTest {

public static void main(String[] args) {

Greeter worldGreeter = new Greeter();

System.out.println(worldGreeter.sayHello());

} }

To produce a program, you need to combine these two classes. The details for building the program depend on your compiler and development environment. In most environ- ments, you need to carry out these steps:

1. Make a new subfolder for your program.

2. Make two files, one for each class.

3. Compile both files.

4. Run the test program.

F i g u r e 6

Testing a Class in the BlueJ Environment

(14)

For example, if you use the Java SDK command line tools, the steps are like this:

mkdir greeter cd greeter

edit Greeter.java edit GreeterTest.java javac Greeter.java javac GreeterTest.java java GreeterTest

Many students are surprised that such a simple program contains two classes. However, this is normal. The two classes have entirely different purposes. The Greeter class (which we will make more interesting in the next section) describes objects that can utter greetings. The GreeterTest class runs a test that puts a Greeter object through its paces. The GreeterTest program is necessary only if your development environment does not have a facility for interactive testing.

Using the Command Line Effectively

If your programming environment lets you accomplish all routine tasks with menus and dialog boxes, you can skip this note. However, if you need to invoke the editor, the compiler, the linker, and the program to test manually, then it is well worth learning about command line editing.

Most operating systems (UNIX, DOS, OS/2) have a command line interface to inter- act with the computer. (In Windows, you can use the DOS command line interface by double-clicking the “MS-DOS Prompt” icon, or, if that icon doesn’t appear on your

“Programs” menu, clicking “Run...” and typing command.com.) You launch commands at a prompt. The command is executed, and on completion you get another prompt.

When you develop a program, you find yourself executing the same commands over and over. Wouldn’t it be nice if you didn’t have to type beastly commands like

javac MyProg.java

more than once? Or if you could fix a mistake rather than having to retype the command in its entirety? Many command line interfaces have an option to do just that, but they don’t always make it obvious. If you use Windows, you need to install a program called doskey. If you use UNIX, some shells let you cycle through your old commands. If your default configuration does not have that feature, ask how you can change to a better shell, such as bash or tcsh.

Once you have your shell configured properly, you can use the up and down arrow keys to recall old commands and the left and right arrow keys to edit lines. You can also perform command completion. For example, to reissue the same javac command, type javac and press F8 (Windows) or type !javac (UNIX).

Productivity Hint 2.1



















(15)

2.5 Instance Fields 47

Right now, our Greeter class isn’t very interesting, because all objects act in the same way. Suppose you construct two objects:

Greeter greeter1 = new Greeter();

Greeter greeter2 = new Greeter();

Then both greeter1 and greeter2 return exactly the same result when you invoke the sayHello method. Let’s modify the Greeter class so that one object can return the message "Hello, World!"

and another can return "Hello, Dave!".

To achieve this purpose, each Greeter object must store state. The state of an object is the set of values that determine how an object reacts to method calls. In the case of our improved Greeter object, the state is the name that we want to use in the greeting, such as "World" or "Dave".

An object stores its state in one or more variables called instance fields. You declare the instance fields for an object in the class.

public class Greeter {

. . .

private String name;

}

An instance field declaration consists of the following parts:

 An access specifier (usually private)

 The type of the variable (such as String)

 The name of the variable (such as name)

Each object of a class has its own set of instance fields. For example, if worldGreeter and daveGreeter are two objects of the Greeter class, then each object has its own name field, called worldGreeter.name and

daveGreeter.name (see Figure 7).

Instance fields are generally declared with the access specifier private. That specifier means that they can be accessed only by the methods of the same class, not by any other method. In particular, the name variable can be accessed only by the sayHello method.

In other words, if the instance fields are declared private, then all data access must occur through the public methods. Thus, the instance fields 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 the data and providing methods for data access is called encapsulation. Although it is theoretically possible in Java to leave instance fields public, that is very uncommon in prac- tice. We will always make all instance fields private in this book.

2.5 Instance Fields

An object uses instance fields to store its state — the data that it needs to execute its methods.

Each object of a class has its own set of instance fields.

Encapsulation is the process of hiding object data and providing methods for data access.

You should declare all instance fields as private.

(16)

For example, because the name instance field is private, you cannot access the instance field in methods of another class:

public class GreeterTest {

public static void main(String[] args) {

. . .

System.out.println(daveGreeter.name); // ERROR }

}

Only the sayHello method can access the private name variable. If we later add other methods to the Greeter class, such as a sayGoodbye method, then those methods can access the private instance field as well.

Here is the implementation of the sayHello method of the improved Greeter class.

public String sayHello() {

String message = "Hello, " + name + "!";

return message;

}

The + symbol denotes string concatenation, an operation that forms a new string by put- ting shorter strings together one after another.

This method computes a string message by combining three strings: "Hello, ", the string stored in the name instance field, and the string consisting of an exclamation

F i g u r e 7

Instance Fields worldGreeter =

name = “World”

Greeter

daveGreeter =

name = “Dave”

Greeter

(17)

2.6 Constructors 49

point "!". If the name variable refers to the string "Dave", then the resulting string is

"Hello, Dave!".

Note that this method uses two separate object variables: the local variable message and the instance field name. A local variable belongs to an individual method, and you can use it only in the method in which you declare it. An instance field belongs to an object, and you can use it in all methods of its class.

To complete the improved Greeter class, we need to be able to con- struct objects with different values for the name instance field. We want to specify the name when constructing the object:

Greeter worldGreeter = new Greeter("World");

Greeter daveGreeter = new Greeter("Dave");

To accomplish this, we need to supply a constructor in the class defini- tion. A constructor specifies how an object should be initialized. In our example, we have one construction parameter—a string describing the name. Here is the code for the constructor.

public Greeter(String aName) {

name = aName;

}

Syntax 2.6 : Instance Field Declaration

accessSpecifier class ClassName {

. . .

accessSpecifier fieldType fieldName; . . .

}

Example:

public class Greeter {

. . .

private String name;

. . . }

Purpose:

To define a field that is present in every object of a class

2.6 Constructors

Constructors contain instructions to initialize objects. The constructor name is always the same as the class name.

(18)

A constructor always has the same name as the class of the objects it constructs. Similar to methods, constructors are generally declared as public to enable any code in a program to construct new objects of the class. Unlike meth- ods, though, constructors do not have return types.

The new operator invokes the constructor:

new Greeter("Dave")

This expression constructs a new object whose name instance field is set to the string

"Dave".

Constructors are not methods. You cannot invoke a constructor on an existing object.

For example, the call

worldGreeter.Greeter("Harry"); // Error

is illegal. You can use a constructor only in combination with the new operator.

Here is the complete code for the enhanced Greeter class.

File Greeter.java

1 public class Greeter 2 {

3 public Greeter(String aName)

4 {

5 name = aName;

6 }

7

8 public String sayHello()

9 {

10 String message = "Hello, " + name + "!";

11 return message;

12 }

13

14 private String name;

15 }

Here is a test class that you can use to confirm that the Greeter class works correctly.

File GreeterTest.java

1 public class GreeterTest 2 {

3 public static void main(String[] args)

4 {

5 Greeter worldGreeter = new Greeter("World");

6 System.out.println(worldGreeter.sayHello());

7

8 Greeter daveGreeter = new Greeter("Dave");

9 System.out.println(daveGreeter.sayHello());

10 }

11 } The new operator invokes the constructor.

(19)

2.7 Designing the Public Interface of a Class 51

The purpose of the Greeter class was to show you the mechanics of defining classes, meth- ods, instance fields, and constructors. Frankly, that class was not very useful. In this section we will create a more interesting class that describes the behavior of a bank account. More impor- tantly, we will go through the thought process that is required when you design a new class.

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 account. You can

 Deposit money

 Withdraw money

 Get the current balance

In Java, these operations are expressed as method calls. Let’s suppose the variable harrys- Checking contains a reference to an object of type BankAccount. You’ll want to be able to call methods such as the following:

harrysChecking.deposit(2000);

harrysChecking.withdraw(500);

System.out.println(harrysChecking.getBalance());

Syntax 2.7 : Constructor Implementation

accessSpecifier class ClassName {

. . .

accessSpecifier ClassName(parameterType parameterName, . . .) {

constructor implementation }

. . . }

Example:

public class Greeter {

. . .

public Greeter(String aName) {

name = aName;

} . . . }

Purpose:

To define the behavior of a constructor, which is used to initialize the instance fields of newly created objects

2.7 Designing the Public Interface of a Class

(20)

That is, the BankAccount class should define three methods:

 deposit

 withdraw

 getBalance

Next, you need to determine the parameters and return types of these methods. As you can see from the code samples, the deposit and withdraw methods receive a num- ber (the dollar amount) and return no values. The getBalance method has no parame- ter and returns a number.

Java has several number types—you will learn about them in the next chapter. The most flexible number type is called double, which stands for “double precision floating- point number”. Think of a number in double format as any number that can appear in the display panel of a calculator, such as 250, 6.75, or -0.333333333.

Now that you know that you can use the double type for numbers, you can write down the methods of the BankAccount class:

public void deposit(double amount) public void withdraw(double amount) public double getBalance()

Now let’s do the same for the constructors of the class. How do we want to construct a bank account? It seems reasonable that a call

BankAccount harrysChecking = new BankAccount();

should construct a new bank account with a zero balance. What if we want to start out with another balance? A second constructor would be useful that sets the balance to an initial value:

BankAccount harrysChecking = new BankAccount(5000);

That gives us two constructors:

public BankAccount()

public BankAccount(double initialBalance)

The compiler figures out which constructor to call by looking at the parameters. For example, if you call

new BankAccount()

then the compiler picks the first constructor. If you call new BankAccount(5000)

then the compiler picks the second constructor. But if you call new BankAccount("lotsa moolah")

then the compiler generates an error message—for this class there is no constructor that takes a parameter of type String.

You may think that it is strange to have two constructors that have the same name and that differ only in the parameter type. (The first constructor has no parameters; the second one has one parameter, a Overloaded methods are

methods with the same name but different parameter types.

(21)

2.7 Designing the Public Interface of a Class 53

number.) If a name is used to denote more than one constructor or method, that name is overloaded. See Advanced Topic 2.2 for more information on name overloading. Name overloading is common in Java, especially for constructors. After all, we have no choice what to call the constructor. The name of a constructor must be identical to the name of the class.

The constructors and methods of a class form the public interface of the class. These are the operations that any code in your program can access to create and manipulate BankAccount objects. Here is a complete listing of the public interface of the BankAccount class:

public BankAccount()

public BankAccount(double initialBalance) public void deposit(double amount)

public void withdraw(double amount) public double getBalance()

The behavior of our BankAccount class is simple, but it lets you carry out all of the 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);

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. The process of determining the feature set for a class is called abstraction.

Think about how an abstract painting strips away extraneous details and tries to represent only the essential features of an object. When you design the public interface of a class, you also need to find what operations are essential to manipulate objects in your program.

Overloading

When the same name is used for more than one method or constructor, the name is over- loaded. This is particularly common for constructors, because all constructors must have the same name—the name of the class. In Java you can overload methods and constructors, provided the parameter types are different. For example, the PrintStream class defines many methods, all called println, to print various number types and to print objects:

class PrintStream {

Advanced Topic 2.2









Abstraction is the process of finding the essential feature set for a class.

(22)

public void println(String s) { . . . } public void println(double a) { . . . } . . .

}

When the println method is called, system.out.println(x);

the compiler looks at the type of x. If x is a String value, the first method is called. If x is a double value, the second method is called. If x does not match the parameter type of any of the methods, the compiler generates an error.

For overloading purposes, the type of the return value does not matter. You cannot have two methods with identical names and parameter types but different return values.

When you define classes and methods, you should get into the habit of thor- oughly commenting their behavior. In Java there is a very useful standard form for documentation comments. If you use this form in your classes, a program called javadoc can automatically generate a neat set of HTML pages that describe them. (See Productivity Hint 2.2 for a description of this utility.)

A documentation comment starts with a /**, a special comment delimiter used by the javadoc utility. Then you describe the method’s purpose. Then, for each method parameter, you supply a line that starts with @param, followed by the param- eter name and a short explanation. Finally, you supply a line that starts with @return, describing the return value. You omit the @param tag for methods that have no parameters, and you omit the @return tag for methods whose return type is void.

The javadoc utility copies the first sentence of each comment to a summary table.

Therefore, it is best to write that first sentence with some care. It should start with an upper- case letter and end with a period. It does not have to be a grammatically complete sentence, but it should be meaningful when it is pulled out of the comment and displayed in a summary.

Here are two typical examples.

/**

Withdraws money from the bank account.

@param amount the amount to withdraw

*/

public void withdraw(double amount) {

implementation—filled in later }

/**

Gets the current balance of the bank account.

@return the current balance

*/

public double getBalance() {

implementation—filled in later }









2.8 Commenting the Public Interface

Use documentation comments to describe the classes and public methods of your programs.

(23)

2.8 Commenting the Public Interface 55

The comments you have just seen explain individual methods. You should also supply a brief comment for each class, explaining its purpose. The comment syntax for class comments is very simple: Just place the documentation comment above the class.

/**

A bank account has a balance that can be changed by deposits and withdrawals.

*/

public class BankAccount {

. . . }

Your first reaction may well be “Whoa! Am I supposed to write all this stuff?” These comments do seem pretty repetitive. But you should still take the time to write them, even if it feels silly at times. There are three reasons.

First, the javadoc utility will format your comments into a neat set of documents that you can view in a web browser. It makes good use of the seemingly repetitive phrases. The first sen- tence of the comment is used for a summary table of all methods of your class (see Figure 8).

The @param and @return comments are neatly formatted in the detail description of each method (see Figure 9). If you omit any of the comments, then javadoc generates docu- ments that look strangely empty.

Next, it is actually easy to spend more time pondering whether a comment is too trivial to write than it takes just to write it. In practical programming, very simple methods are rare. It is harmless to have a trivial method overcommented, whereas a complicated method without

F i g u r e 8

A Method Summary Generated by javadoc

(24)

any comment can cause real grief to future maintenance programmers.

According to the standard Java documentation style, every class, every method, every parameter, and every return value should have a comment.

Finally, it is always a good idea to write the method comment first, before writing the method code. This is an excellent test to see that you firmly understand what you need to program. If you can’t explain what a class or method does, you aren’t ready to implement it.

The javadoc Utility

You should always insert documentation comments in your code, whether or not you use javadoc to produce HTML documentation. But most people find the HTML docu- mentation convenient, so it is worth learning how to run javadoc.

From a command shell, you invoke the javadoc utility with the command javadoc MyClass.java

or

javadoc *.java F i g u r e 9

Method Details Generated by javadoc

Productivity Hint 2.2









Provide documentation comments for every class, every method, every parameter, and every return value.

(25)

2.8 Commenting the Public Interface 57

The javadoc utility then produces files MyClass.html in HTML format, which you can inspect in a browser. If you know HTML (see Chapter 4), you can embed HTML tags into the comments to specify fonts or add images. Perhaps most importantly, javadoc automatically provides hyperlinks to other classes and methods.

You can actually run javadoc before implementing any methods. Just leave all the method bodies empty. Don’t run the compiler—it would complain about missing return values. Simply run javadoc on your file to generate the documentation for the public interface that you are about to implement.

The javadoc tool is wonderful because it does one thing right: It lets you put the documentation together with your code. That way, when you update your programs, you can see right away which documentation needs to be updated. Hopefully, you will update it right then and there. Afterward, run javadoc again and get updated information that is both timely and nicely formatted.

Keyboard Shortcuts for Mouse Operations

Programmers spend a lot of time with the keyboard and the mouse. Programs and docu- mentation are many pages long and require a lot of typing. The constant switching among the editor, compiler, and debugger takes up quite a few mouse clicks. The design- ers of programs such as a Java integrated development environment have added some features to make your work easier, but it is up to you to discover them.

Just about every program has a user interface with menus and dialog boxes. Click on a menu and click on a submenu to select a task. Click on each field in a dialog box, fill in the requested answer, and click on the OK button. These are great user interfaces for the beginner, because they are easy to master, but they are terrible user interfaces for the reg- ular user. The constant switching between the keyboard and the mouse slows you down.

You need to move a hand off the keyboard, locate the mouse, move the mouse, click the mouse, and move the hand back onto the keyboard. For that reason, most user interfaces have keyboard shortcuts: combinations of keystrokes that allow you to achieve the same tasks without having to switch to the mouse at all.

All Microsoft Windows applications use the following conventions:

 The Alt key plus the underlined letter in a menu name (such as the F in “File”) pulls down that menu. Inside a menu, just type the underlined character in the name of a submenu to activate it. For example, Alt+F followed by O selects “File”

“Open”. Once your fingers know about this combination, you can open files faster than the fastest mouse artist.

 Inside dialog boxes, the Tab key is important; it moves from one option to the next. The arrow keys move within an option. The Enter key accepts the entire dia- log, and Esc cancels it.

 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.











Productivity Hint 2.3





















(26)

 Alt+Tab toggles between applications, letting you toggle quickly between, for example, the text editor and a command shell window.

 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 most text editors.

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 dur- ing 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.”

Now that you understand the public interface of the BankAccount class, let’s provide the implementation. As you already know, you need to supply a class with these ingredients:

public class BankAccount {

constructors methods fields }

We have seen which constructors and methods we need. Let us turn to the instance fields. The instance fields are used to store the object state. 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, the date for mailing out the next statement, and so on.) For now, a sin- gle instance field suffices:

public class BankAccount {

. . .

private double balance;

}

Note that the instance field is declared with the access specifier private. That means, the bank balance can be accessed only by the constructors and methods of the same class—namely, deposit, withdraw, and getBalance—and not by any constructor or method of another class. How the state of a bank account is maintained is a private













2.9 Specifying the Implementation of a Class

(27)

2.9 Specifying the Implementation of a Class 59

implementation detail of the class. Recall that the practice of hiding the implementation details and providing methods for data access is called encapsulation.

The BankAccount class is so simple that it is not obvious what benefit you gain from the encapsulation. After all, you can always find out the current balance by calling the getBalance method. You can set the balance to any value by calling deposit with an appropriate amount.

The primary benefit of the encapsulation mechanism is the guarantee that an object cannot accidentally be put into an incorrect state. For example, 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.

(You will need to wait until Chapter 5 to see how to implement that protection.) On the other hand, if any code could freely modify the balance instance field of a BankAccount object, then it would be an easy matter to store a negative number in the variable.

Now that you know what methods you need, and how the object state is represented, it is an easy matter to implement each of the methods. For example, here is the deposit method:

public void deposit(double amount) {

double newBalance = balance + amount;

balance = newBalance;

}

Here is the constructor with no parameters.

public BankAccount() {

balance = 0;

}

You will find the complete BankAccount class, with the implementations of all methods, at the end of this section.

If your development environment lets you construct objects interactively, then you can test this class immediately. Figures 10 and 11 show how to test the class in BlueJ.

Otherwise, you need to supply a test class. The BankAccountTest class at the end of this section constructs a bank account, deposits and withdraws some money, and prints the remaining balance.

File BankAccount.java

1 /**

2 A bank account has a balance that can be changed by 3 deposits and withdrawals.

4 */

5 public class BankAccount 6 {

7 /**

8 Constructs a bank account with a zero balance.

9 */

10 public BankAccount() 11 {

12 balance = 0;

(28)

13 } 14

15 /**

16 Constructs a bank account with a given balance.

17 @param initialBalance the initial balance 18 */

19 public BankAccount(double initialBalance) 20 {

21 balance = initialBalance;

22 } 23

24 /**

25 Deposits money into the bank account.

26 @param amount the amount to deposit 27 */

28 public void deposit(double amount) 29 {

30 double newBalance = balance + amount;

31 balance = newBalance;

32 } 33 34 /**

F i g u r e 1 0

Calling the withdraw Method in BlueJ

(29)

2.9 Specifying the Implementation of a Class 61

35 Withdraws money from the bank account.

36 @param amount the amount to withdraw 37 */

38 public void withdraw(double amount) 39 {

40 double newBalance = balance - amount;

41 balance = newBalance;

42 } 43

44 /**

45 Gets the current balance of the bank account.

46 @return the current balance 47 */

48 public double getBalance() 49 {

50 return balance;

51 } 52

53 private double balance;

54 }

F i g u r e 1 1

The Return Value of the getBalance Method in BlueJ

參考文獻

相關文件

It’s easy to check that m is a maximal ideal, called the valuation ideal. We can show that R is a

The hashCode method for a given class can be used to test for object equality and object inequality for that class. The hashCode method is used by the java.util.SortedSet

To complete the “plumbing” of associating our vertex data with variables in our shader programs, you need to tell WebGL where in our buffer object to find the vertex data, and

Structured programming 14 , if used properly, results in programs that are easy to write, understand, modify, and debug.... Steps of Developing A

this: a Sub-type reference variable pointing to the object itself super: a Base-type reference variable pointing to the object itself. same reference value, different type

final instance variable: accessed through instance, and assigned once (in declaration or every instance constructor) final instance method: cannot be overriden (≈ assigned once)

/** Class invariant: A Person always has a date of birth, and if the Person has a date of death, then the date of death is equal to or later than the date of birth. To be

• Any node that does not have a local replica of the object periodically creates a QoS-advert message contains (a) its δ i deadline value and (b) depending-on , the ID of the node