3. Introduction to Classes and Objects

Objectives

In this chapter you’ll learn:

• What classes, objects, methods and instance variables are.

• How to declare a class and use it to create an object.

• How to declare methods in a class to implement the class’s behaviors.

• How to declare instance variables in a class to implement the class’s attributes.

• How to call an object’s methods to make those methods perform their tasks.

• The differences between instance variables of a class and local variables of a method.

• How to use a constructor to ensure that an object’s data is initialized when the object is created.

• The differences between primitive and reference types.

You will see something new. Two things. And I call them Thing One and Thing Two.

Dr. Theodor Seuss Geisel

Nothing can have value without being an object of utility.

Karl Marx

Your public servants serve you right.

Adlai E. Stevenson

Knowing how to answer one who speaks, To reply to one who sends a message.

Amenemope

Outline

3.1   Introduction

3.2   Classes, Objects, Methods and Instance Variables

3.3   Declaring a Class with a Method and Instantiating an Object of a Class

3.4   Declaring a Method with a Parameter

3.5   Instance Variables, set Methods and get Methods

3.6   Primitive Types vs. Reference Types

3.7   Initializing Objects with Constructors

3.8   Floating-Point Numbers and Type double

3.9   (Optional) Software Engineering Case Study: Identifying the Classes in a Requirements Document

3.10   Wrap-Up

3.1 Introduction

We introduced the basic terminology and concepts of object-oriented programming in Section 1.9. In Chapter 2, you began to use those concepts to create simple applications that displayed messages to the user, obtained information from the user, performed calculations and made decisions. One common feature of every application in Chapter 2 was that all the statements that performed tasks were located in method main. Typically, the applications you develop in this book will consist of two or more classes, each containing one or more methods. If you become part of a development team in industry, you might work on applications that contain hundreds, or even thousands, of classes. In this chapter, we present a simple framework for organizing object-oriented applications in Java.

First, we motivate the notion of classes with a real-world example. Then we present five complete working applications to demonstrate creating and using your own classes. The first four of these examples begin our case study on developing a grade-book class that instructors can use to maintain student test scores. This case study is enhanced over the next several chapters, culminating with the version presented in Chapter 7, Arrays. The last example in the chapter introduces floating-point numbers—that is, numbers containing decimal points, such as 0.0345, –7.23 and 100.7—in the context of a bank account class that maintains a customer’s balance.

3.2 Classes, Objects, Methods and Instance Variables

Let’s begin with a simple analogy to help you understand classes and their contents. Suppose you want to drive a car and make it go faster by pressing down on its accelerator pedal. What must happen before you can do this? Well, before you can drive a car, someone has to design it. A car typically begins as engineering drawings, similar to the blueprints used to design a house. These engineering drawings include the design for an accelerator pedal to make the car go faster. The pedal “hides” from the driver the complex mechanisms that actually make the car go faster, just as the brake pedal “hides” the mechanisms that slow the car and the steering wheel “hides” the mechanisms that turn the car. This enables people with little or no knowledge of how engines work to drive a car easily.

Unfortunately, you cannot drive the engineering drawings of a car. Before you can drive a car, the car must be built from the engineering drawings that describe it. A completed car has an actual accelerator pedal to make the car go faster, but even that’s not enough—the car won’t accelerate on its own, so the driver must press the accelerator pedal.

Now let’s use our car example to introduce the key programming concepts of this section. Performing a task in a program requires a method. The method describes the mechanisms that actually perform its tasks. The method hides from its user the complex tasks that it performs, just as the accelerator pedal of a car hides from the driver the complex mechanisms of making the car go faster. In Java, we begin by creating a program unit called a class to house a method, just as a car’s engineering drawings house the design of an accelerator pedal. In a class, you provide one or more methods that are designed to perform the class’s tasks. For example, a class that represents a bank account might contain one method to deposit money to an account, another to withdraw money from an account and a third to inquire what the current balance is.

Just as you cannot drive an engineering drawing of a car, you cannot “drive” a class. Just as someone has to build a car from its engineering drawings before you can actually drive a car, you must build an object of a class before you can get a program to perform the tasks the class describes how to do. That is one reason Java is known as an object-oriented programming language.

When you drive a car, pressing its gas pedal sends a message to the car to perform a task—that is, make the car go faster. Similarly, you send messages to an object—each message is known as a method call and tells a method of the object to perform its task.

Thus far, we’ve used the car analogy to introduce classes, objects and methods. In addition to a car’s capabilities, a car also has many attributes, such as its color, the number of doors, the amount of gas in its tank, its current speed and its total miles driven (i.e., its odometer reading). Like the car’s capabilities, these attributes are represented as part of a car’s design in its engineering diagrams. As you drive a car, these attributes are always associated with the car. Every car maintains its own attributes. For example, each car knows how much gas is in its own gas tank, but not how much is in the tanks of other cars. Similarly, an object has attributes that are carried with the object as it is used in a program. These attributes are specified as part of the object’s class. For example, a bank account object has a balance attribute that represents the amount of money in the account. Each bank account object knows the balance in the account it represents, but not the balances of the other accounts in the bank. Attributes are specified by the class’s instance variables.

The remainder of this chapter presents examples that demonstrate the concepts we introduced in the context of the car analogy. The first four examples incrementally build a GradeBook class to demonstrate these concepts:

1. The first example presents a GradeBook class with one method that simply displays a welcome message when it is called. We then show how to create an object of that class and call the method so that it displays the welcome message.

2. The second example modifies the first by allowing the method to receive a course name as an argument and by displaying the name as part of the welcome message.

3. The third example shows how to store the course name in a GradeBook object. For this version of the class, we also show how to use methods to set the course name and obtain the course name.

4. The fourth example demonstrates how the data in a GradeBook object can be initialized when the object is created—the initialization is performed by the class’s constructor.

The last example in the chapter presents an Account class that reinforces the concepts presented in the first four examples and introduces floating-point numbers. For this purpose, we present an Account class that represents a bank account and maintains its balance as a floating-point number. The class contains two methods—one that credits a deposit to the account, thus increasing the balance, and another that retrieves the balance. The class’s constructor allows the balance of each Account object to be initialized as the object is created. We create two Account objects and make deposits into each to show that each object maintains its own balance. The example also demonstrates how to input and display floating-point numbers.

3.3 Declaring a Class with a Method and Instantiating an Object of a Class

We begin with an example that consists of classes GradeBook (Fig. 3.1) and GradeBookTest (Fig. 3.2). Class GradeBook (declared in file GradeBook.java) will be used to display a message on the screen (Fig. 3.2) welcoming the instructor to the grade-book application. Class GradeBookTest (declared in file GradeBookTest.java) is an application class in which the main method will use class GradeBook. Each class declaration that begins with keyword public must be stored in a file that has the same name as the class and ends with the .java file-name extension. Thus, classes GradeBook and GradeBookTest must be declared in separate files, because each class is declared public.

Fig. 3.1. Class declaration with one method.

Image

Fig. 3.2. Creating an object of class GradeBook and calling its displayMessage method.

Image

Common Programming Error 3.1

Image

Declaring more than one public class in the same file is a compilation error.

Class GradeBook

The GradeBook class declaration (Fig. 3.1) contains a displayMessage method (lines 7–10) that displays a message on the screen. Line 9 of the class performs the work of displaying the message. Recall that a class is like a blueprint—we’ll need to make an object of this class and call its method to get line 9 to execute and display its message.

The class declaration begins in line 4. The keyword public is an access modifier. For now, we’ll simply declare every class public. Every class declaration contains keyword class followed immediately by the class’s name. Every class’s body is enclosed in a pair of left and right braces ({ and }), as in lines 5 and 12 of class GradeBook.

In Chapter 2, each class we declared had one method named main. Class GradeBook also has one method—displayMessage (lines 7–10). Recall that main is a special method that is always called automatically by the Java Virtual Machine (JVM) when you execute an application. Most methods do not get called automatically. As you’ll soon see, you must call method displayMessage to tell it to perform its task.

The method declaration begins with keyword public to indicate that the method is “available to the public”—that is, it can be called from outside the class declaration’s body by methods of other classes. Keyword void indicates that this method will perform a task but will not return (i.e., give back) any information to its calling method when it completes its task. You have already used methods that return information—for example, in Chapter 2 you used Scanner method nextInt to input an integer typed by the user at the keyboard. When nextInt inputs a value, it returns that value for use in the program.

The name of the method, displayMessage, follows the return type. By convention, method names begin with a lowercase first letter and all subsequent words in the name begin with a capital letter. The parentheses after the method name indicate that this is a method. An empty set of parentheses, as shown in line 7, indicates that this method does not require additional information to perform its task. Line 7 is commonly referred to as the method header. Every method’s body is delimited by left and right braces ({ and }), as in lines 8 and 10.

The body of a method contains statement(s) that perform the method’s task. In this case, the method contains one statement (line 9) that displays the message "Welcome to the Grade Book!" followed by a newline in the command window. After this statement executes, the method has completed its task.

Next, we’d like to use class GradeBook in an application. As you learned in Chapter 2, method main begins the execution of every application. A class that contains method main is a Java application. Such a class is special because the JVM can use main as an entry point to begin execution. Class GradeBook is not an application because it does not contain main. Therefore, if you try to execute GradeBook by typing java GradeBook in the command window, you’ll receive an error message like:

Exception in thread "main" java.lang.NoSuchMethodError: main

This was not a problem in Chapter 2, because every class you declared had a main method. To fix this problem for the GradeBook, we must either declare a separate class that contains a main method or place a main method in class GradeBook. To help you prepare for the larger programs you’ll encounter later in this book and in industry, we use a separate class (GradeBookTest in this example) containing method main to test each new class we create in this chapter.

Class GradeBookTest

The GradeBookTest class declaration (Fig. 3.2) contains the main method that will control our application’s execution. Any class that contains main declared as shown in line 7 can be used to execute an application. The GradeBookTest class declaration begins in line 4 and ends in line 16. The class contains only a main method, which is typical of many classes that begin an application’s execution.

Lines 7–14 declare method main. Recall from Chapter 2 that the main header must appear as shown in line 7; otherwise, the application will not execute. A key part of enabling the JVM to locate and call method main to begin the application’s execution is the static keyword (line 7), which indicates that main is a static method. A static method is special because it can be called without first creating an object of the class in which the method is declared. We thoroughly explain static methods in Chapter 6, Methods: A Deeper Look.

In this application, we’d like to call class GradeBook’s displayMessage method to display the welcome message in the command window. Typically, you cannot call a method that belongs to another class until you create an object of that class, as shown in line 10. We begin by declaring variable myGradeBook. Note that the variable’s type is GradeBook—the class we declared in Fig. 3.1. Each new class you create becomes a new type that can be used to declare variables and create objects. Programmers can declare new class types as needed; this is one reason why Java is known as an extensible language.

Variable myGradeBook is initialized with the result of the class instance creation expression new GradeBook(). Keyword new creates a new object of the class specified to the right of the keyword (i.e., GradeBook). The parentheses to the right of GradeBook are required. As you’ll learn in Section 3.7, those parentheses in combination with a class name represent a call to a constructor, which is similar to a method, but is used only at the time an object is created to initialize the object’s data. In that section you’ll see that data can be placed in parentheses to specify initial values for the object’s data. For now, we simply leave the parentheses empty.

Just as we can use object System.out to call methods print, printf and println, we can use object myGradeBook to call method displayMessage. Line 13 calls the method displayMessage (lines 7–10 of Fig. 3.1) using myGradeBook followed by a dot separator (.), the method name displayMessage and an empty set of parentheses. This call causes the displayMessage method to perform its task. This method call differs from those in Chapter 2 that displayed information in a command window—each of those method calls provided arguments that specified the data to display. At the beginning of line 13, “myGradeBook.” indicates that main should use the myGradeBook object that was created in line 10. Line 7 of Fig. 3.1 indicates that method displayMessage has an empty parameter list—that is, displayMessage does not require additional information to perform its task. For this reason, the method call (line 13 of Fig. 3.2) specifies an empty set of parentheses after the method name to indicate that no arguments are being passed to method displayMessage. When method displayMessage completes its task, method main continues executing in line 14. This is the end of method main, so the program terminates.

Compiling an Application with Multiple Classes

You must compile the classes in Fig. 3.1 and Fig. 3.2 before you can execute the application. First, change to the directory that contains the application’s source-code files. Next, type the command

javac GradeBook.java GradeBookTest.java

to compile both classes at once. If the directory containing the application includes only this application’s files, you can compile all the classes in the directory with the command

javac *.java

The asterisk (*) in *.java indicates that all files in the current directory that end with the file name extension “.java” should be compiled.

UML Class Diagram for Class GradeBook

Figure 3.3 presents a UML class diagram for class GradeBook of Fig. 3.1. Recall from Section 1.9 that the UML is a graphical language used by programmers to represent object-oriented systems in a standardized manner. In the UML, each class is modeled in a class diagram as a rectangle with three compartments. The top compartment contains the name of the class centered horizontally in boldface type. The middle compartment contains the class’s attributes, which correspond to instance variables in Java. In Fig. 3.3, the middle compartment is empty because the version of class GradeBook in Fig. 3.1 does not have any attributes. The bottom compartment contains the class’s operations, which correspond to methods in Java. The UML models operations by listing the operation name preceded by an access modifier and followed by a set of parentheses. Class GradeBook has one method, displayMessage, so the bottom compartment of Fig. 3.3 lists one operation with this name. Method displayMessage does not require additional information to perform its tasks, so the parentheses following the method name in the class diagram are empty, just as they were in the method’s declaration in line 7 of Fig. 3.1. The plus sign (+) in front of the operation name indicates that displayMessage is a public operation in the UML (i.e., a public method in Java). We’ll often use UML class diagrams to summarize a class’s attributes and operations.

Fig. 3.3. UML class diagram indicating that class GradeBook has a public displayMessage operation.

Image

3.4 Declaring a Method with a Parameter

In our car analogy from Section 3.2, we discussed the fact that pressing a car’s gas pedal sends a message to the car to perform a task—make the car go faster. But how fast should the car accelerate? As you know, the farther down you press the pedal, the faster the car accelerates. So the message to the car actually includes the task to perform and additional information that helps the car perform the task. This additional information is known as a parameter—the value of the parameter helps the car determine how fast to accelerate. Similarly, a method can require one or more parameters that represent additional information it needs to perform its task. A method call supplies values—called arguments—for each of the method’s parameters. For example, the method System.out.println requires an argument that specifies the data to output in a command window. Similarly, to make a deposit into a bank account, a deposit method specifies a parameter that represents the deposit amount. When the deposit method is called, an argument value representing the deposit amount is assigned to the method’s parameter. The method then makes a deposit of that amount.

Our next example declares class GradeBook (Fig. 3.4) with a displayMessage method that displays the course name as part of the welcome message. (See the sample execution in Fig. 3.5.) The new displayMessage method requires a parameter that represents the course name to output.

Fig. 3.4. Class declaration with one method that has a parameter.

Image

Fig. 3.5. Creating a GradeBook object and passing a String to its displayMessage method.

Image

Before discussing the new features of class GradeBook, let’s see how the new class is used from the main method of class GradeBookTest (Fig. 3.5). Line 12 creates a Scanner named input for reading the course name from the user. Line 15 creates an object of class GradeBook and assigns it to variable myGradeBook. Line 18 prompts the user to enter a course name. Line 19 reads the name from the user and assigns it to the nameOfCourse variable, using Scanner method nextLine to perform the input. The user types the course name and presses Enter to submit the course name to the program. Note that pressing Enter inserts a newline character at the end of the characters typed by the user. Method nextLine reads characters typed by the user until the newline character is encountered, then returns a String containing the characters up to, but not including, the newline. The newline character is discarded. Class Scanner also provides a similar method—next—that reads individual words. When the user presses Enter after typing input, method next reads characters until a white-space character (such as a space, tab or newline) is encountered, then returns a String containing the characters up to, but not including, the white-space character (which is discarded). All information after the first white-space character is not lost—it can be read by other statements that call the Scanner’s methods later in the program.

Line 24 calls myGradeBooks’s displayMessage method. The variable nameOfCourse in parentheses is the argument that is passed to method displayMessage so that the method can perform its task. The value of variable nameOfCourse in main becomes the value of method displayMessage’s parameter courseName in line 7 of Fig. 3.4. When you execute this application, notice that method displayMessage outputs the name you type as part of the welcome message (Fig. 3.5).

Software Engineering Observation 3.1

Image

Normally, objects are created with new. One exception is a string literal that is contained in quotes, such as "hello". String literals are references to String objects that are implicitly created by Java.

More on Arguments and Parameters

When you declare a method, you must specify whether the method requires data to perform its task. To do so, you place additional information in the method’s parameter list, which is located in the parentheses that follow the method name. The parameter list may contain any number of parameters, including none at all. Empty parentheses following the method name (as in Fig. 3.1, line 7) indicate that a method does not require any parameters. In Fig. 3.4, displayMessage’s parameter list (line 7) declares that the method requires one parameter. Each parameter must specify a type and an identifier. In this case, the type String and the identifier courseName indicate that method displayMessage requires a String to perform its task. At the time the method is called, the argument value in the call is assigned to the corresponding parameter (in this case, courseName) in the method header. Then, the method body uses the parameter courseName to access the value. Lines 9–10 of Fig. 3.4 display parameter courseName’s value, using the %s format specifier in printf’s format string. Note that the parameter variable’s name (Fig. 3.4, line 7) can be the same or different from the argument variable’s name (Fig. 3.5, line 24).

A method can specify multiple parameters by separating each parameter from the next with a comma (we’ll see an example of this in Chapter 6). The number of arguments in a method call must match the number of parameters in the parameter list of the called method’s declaration. Also, the argument types in the method call must be “consistent with” the types of the corresponding parameters in the method’s declaration. (As you’ll learn in subsequent chapters, an argument’s type and its corresponding parameter’s type are not always required to be identical.) In our example, the method call passes one argument of type String (nameOfCourse is declared as a String in line 19 of Fig. 3.5) and the method declaration specifies one parameter of type String (line 7 in Fig. 3.4). So in this example the type of the argument in the method call exactly matches the type of the parameter in the method header.

Common Programming Error 3.2

Image

A compilation error occurs if the number of arguments in a method call does not match the number of parameters in the method declaration.

Common Programming Error 3.3

Image

A compilation error occurs if the types of the arguments in a method call are not consistent with the types of the corresponding parameters in the method declaration.

Updated UML Class Diagram for Class GradeBook

The UML class diagram of Fig. 3.6 models class GradeBook of Fig. 3.4. Like Fig. 3.1, this GradeBook class contains public operation displayMessage. However, this version of displayMessage has a parameter. The UML models a parameter a bit differently from Java by listing the parameter name, followed by a colon and the parameter type in the parentheses following the operation name. The UML has its own data types similar to those of Java (but as you’ll see, not all the UML data types have the same names as the corresponding Java types). The UML type String does correspond to the Java type String. GradeBook method displayMessage (Fig. 3.4) has a String parameter named courseName, so Fig. 3.6 lists courseName : String between the parentheses following displayMessage.

Fig. 3.6. UML class diagram indicating that class GradeBook has a displayMessage operation with a courseName parameter of UML type String.

Image

Notes on import Declarations

Notice the import declaration in Fig. 3.5 (line 4). This indicates to the compiler that the program uses class Scanner. Why do we need to import class Scanner, but not classes System, String or GradeBook? Most classes you’ll use in Java programs must be imported. Classes System and String are in package java.lang, which is implicitly imported into every Java program, so all programs can use package java.lang’s classes without explicitly importing them.

There is a special relationship between classes that are compiled in the same directory on disk, like classes GradeBook and GradeBookTest. By default, such classes are considered to be in the same package—known as the default package. Classes in the same package are implicitly imported into the source code files of other classes in the same package. Thus, an import declaration is not required when one class in a package uses another in the same package—such as when class GradeBookTest uses class GradeBook.

The import declaration in line 4 is not required if we always refer to class Scanner as java.util.Scanner, which includes the full package name and class name. This is known as the class’s fully qualified class name. For example, line 12 could be written as

java.util.Scanner input = new java.util.Scanner( System.in );

Software Engineering Observation 3.2

Image

The Java compiler does not require import declarations in a Java source code file if the fully qualified class name is specified every time a class name is used in the source code. But most Java programmers consider using fully qualified names to be cumbersome, and instead prefer to use import declarations.

3.5 Instance Variables, set Methods and get Methods

In Chapter 2, we declared all of an application’s variables in the application’s main method. Variables declared in the body of a particular method are known as local variables and can be used only in that method. When that method terminates, the values of its local variables are lost. Recall from Section 3.2 that an object has attributes that are carried with the object as it is used in a program. Such attributes exist before a method is called on an object and after the method completes execution.

A class normally consists of one or more methods that manipulate the attributes that belong to a particular object of the class. Attributes are represented as variables in a class declaration. Such variables are called fields and are declared inside a class declaration but outside the bodies of the class’s method declarations. When each object of a class maintains its own copy of an attribute, the field that represents the attribute is also known as an instance variable—each object (instance) of the class has a separate instance of the variable in memory. The example in this section demonstrates a GradeBook class that contains a courseName instance variable to represent a particular GradeBook object’s course name.

GradeBook Class with an Instance Variable, a set Method and a get Method

In our next application (Fig. 3.7Fig. 3.8), class GradeBook (Fig. 3.7) maintains the course name as an instance variable so that it can be used or modified at any time during an application’s execution. The class contains three methods—setCourseName, getCourseName and displayMessage. Method setCourseName stores a course name in a GradeBook. Method getCourseName obtains a GradeBook’s course name. Method displayMessage, which now specifies no parameters, still displays a welcome message that includes the course name; as you’ll see, the method now obtains the course name by calling another method in the same class—getCourseName.

Fig. 3.7. GradeBook class that contains a courseName instance variable and methods to set and get its value.

Image

Fig. 3.8. Creating and manipulating a GradeBook object.

Image

A typical instructor teaches more than one course, each with its own course name. Line 7 declares that courseName is a variable of type String. Because the variable is declared in the body of the class but outside the bodies of the class’s methods (lines 10–13, 16–19 and 22–28), line 7 is a declaration for an instance variable. Every instance (i.e., object) of class GradeBook contains one copy of each instance variable. For example, if there are two GradeBook objects, each object has its own copy of courseName (one per object). A benefit of making courseName an instance variable is that all the methods of the class (in this case, GradeBook) can manipulate any instance variables that appear in the class (in this case, courseName).

Access Modifiers public and private

Most instance variable declarations are preceded with the keyword private (as in line 7). Like public, keyword private is an access modifier. Variables or methods declared with access modifier private are accessible only to methods of the class in which they are declared. Thus, variable courseName can be used only in methods setCourseName, getCourseName and displayMessage of (every object of) class GradeBook.

Software Engineering Observation 3.3

Image

Precede every field and method declaration with an access modifier. As a rule of thumb, instance variables should be declared private and methods should be declared public. (We’ll see that it is appropriate to declare certain methods private, if they will be accessed only by other methods of the class.)

Good Programming Practice 3.1

Image

We prefer to list the fields of a class first, so that, as you read the code, you see the names and types of the variables before you see them used in the methods of the class. It is possible to list the class’s fields anywhere in the class outside its method declarations, but scattering them tends to lead to hard-to-read code.

Good Programming Practice 3.2

Image

Place a blank line between method declarations to separate the methods and enhance program readability.

Declaring instance variables with access modifier private is known as data hiding. When a program creates (instantiates) an object of class GradeBook, variable courseName is encapsulated (hidden) in the object and can be accessed only by methods of the object’s class. In class GradeBook, methods setCourseName and getCourseName manipulate the instance variable courseName.

Method setCourseName (lines 10–13) does not return any data when it completes its task, so its return type is void. The method receives one parameter—name—which represents the course name that will be passed to the method as an argument. Line 12 assigns name to instance variable courseName.

Method getCourseName (lines 16–19) returns a particular GradeBook object’s courseName. The method has an empty parameter list, so it does not require additional information to perform its task. The method specifies that it returns a String—this is known as the method’s return type. When a method that specifies a return type is called and completes its task, the method returns a result to its calling method. For example, when you go to an automated teller machine (ATM) and request your account balance, you expect the ATM to give you back a value that represents your balance. Similarly, when a statement calls method getCourseName on a GradeBook object, the statement expects to receive the GradeBook’s course name (in this case, a String, as specified in the method declaration’s return type). If you have a method square that returns the square of its argument, you would expect the statement

int result = square( 2 );

to return 4 from method square and assign 4 to the variable result. If you have a method maximum that returns the largest of three integer arguments, you would expect the following statement

int biggest = maximum( 2711451 );

to return 114 from method maximum and assign 114 to variable biggest.

Note that the statements in lines 12 and 18 each use courseName even though it was not declared in any of the methods. We can use courseName in the methods of class GradeBook because courseName is a field of the class. Also note that the order in which methods are declared in a class does not determine when they are called at execution time. So method getCourseName could be declared before method setCourseName.

Method displayMessage (lines 22–28) does not return any data when it completes its task, so its return type is void. The method does not receive parameters, so the parameter list is empty. Lines 26–27 output a welcome message that includes the value of instance variable courseName. Once again, we need to create an object of class GradeBook and call its methods before the welcome message can be displayed.

GradeBookTest Class That Demonstrates Class GradeBook

Class GradeBookTest (Fig. 3.8) creates one object of class GradeBook and demonstrates its methods. Line 11 creates a Scanner that will be used to obtain a course name from the user. Line 14 creates a GradeBook object and assigns it to local variable myGradeBook of type GradeBook. Lines 17–18 display the initial course name calling the object’s getCourseName method. Note that the first line of the output shows the name “null.” Unlike local variables, which are not automatically initialized, every field has a default initial value—a value provided by Java when the programmer does not specify the field’s initial value. Thus, fields are not required to be explicitly initialized before they are used in a program—unless they must be initialized to values other than their default values. The default value for a field of type String (like courseName in this example) is null, which we say more about in Section 3.6.

Line 21 prompts the user to enter a course name. Local String variable theName (declared in line 22) is initialized with the course name entered by the user, which is returned by the call to the nextLine method of the Scanner object input. Line 23 calls object myGradeBook’s setCourseName method and supplies theName as the method’s argument. When the method is called, the argument’s value is assigned to parameter name (line 10, Fig. 3.7) of method setCourseName (lines 10–13, Fig. 3.7). Then the parameter’s value is assigned to instance variable courseName (line 12, Fig. 3.7). Line 24 (Fig. 3.8) skips a line in the output, then line 27 calls object myGradeBook’s displayMessage method to display the welcome message containing the course name.

set and get Methods

A class’s private fields can be manipulated only by methods of that class. So a client of an object—that is, any class that calls the object’s methods—calls the class’s public methods to manipulate the private fields of an object of the class. This is why the statements in method main (Fig. 3.8) call the setCourseName, getCourseName and displayMessage methods on a GradeBook object. Classes often provide public methods to allow clients of the class to set (i.e., assign values to) or get (i.e., obtain the values of) private instance variables. The names of these methods need not begin with set or get, but this naming convention is highly recommended in Java and is required for special Java software components called JavaBeans that can simplify programming in many Java integrated development environments (IDEs). The method that sets instance variable courseName in this example is called setCourseName, and the method that gets the value of instance variable courseName is called getCourseName.

UML Class Diagram for class GradeBook with an Instance Variable and set and get Methods

Figure 3.9 contains an updated UML class diagram for the version of class GradeBook in Fig. 3.7. This diagram models class GradeBook’s instance variable courseName as an attribute in the middle compartment of the class. The UML represents instance variables as attributes by listing the attribute name, followed by a colon and the attribute type. The UML type of attribute courseName is String. Instance variable courseName is private in Java, so the class diagram lists a minus sign () in front of the corresponding attribute’s name. Class GradeBook contains three public methods, so the class diagram lists three operations in the third compartment. Recall that the plus (+) sign before each operation name indicates that the operation is public. Operation setCourseName has a String parameter called name. The UML indicates the return type of an operation by placing a colon and the return type after the parentheses following the operation name. Method getCourseName of class GradeBook (Fig. 3.7) has a String return type in Java, so the class diagram shows a String return type in the UML. Note that operations setCourseName and displayMessage do not return values (i.e., they return void in Java), so the UML class diagram does not specify a return type after the parentheses of these operations.

Fig. 3.9. UML class diagram indicating that class GradeBook has a courseName attribute of UML type String and three operations—setCourseName (with a name parameter of UML type String), getCourseName (which returns UML type String) and displayMessage.

Image

3.6 Primitive Types vs. Reference Types

Data types in Java are divided into two categories—primitive types and reference types (sometimes called nonprimitive types). The primitive types are boolean, byte, char, short, int, long, float and double. All nonprimitive types are reference types, so classes, which specify the types of objects, are reference types.

A primitive-type variable can store exactly one value of its declared type at a time. For example, an int variable can store one whole number (such as 7) at a time. When another value is assigned to that variable, its initial value is replaced. Primitive-type instance variables are initialized by default—variables of types byte, char, short, int, long, float and double are initialized to 0, and variables of type boolean are initialized to false. You can specify your own initial values for primitive-type variables. Recall that local variables are not initialized by default.

Error-Prevention Tip 3.1

Image

Any attempt to use a local variable that has not been initialized results in a compilation error.

Programs use variables of reference types (normally called references) to store the locations of objects in the computer’s memory. Such a variable is said to refer to an object in the program. Objects that are referenced may each contain many instance variables and methods. Line 14 of Fig. 3.8 creates an object of class GradeBook, and the variable myGradeBook contains a reference to that GradeBook object. Reference-type instance variables are initialized by default to the value null—a reserved word that represents a “reference to nothing.” This is why the first call to getCourseName in line 18 of Fig. 3.8 returned null—the value of courseName had not been set, so the default initial value null was returned. The complete list of reserved words and keywords is listed in Appendix C, Keywords and Reserved Words.

A reference to an object is required to invoke (i.e., call) the object’s methods. In the application of Fig. 3.8, the statements in method main use the variable myGradeBook to send messages to the GradeBook object. These messages are calls to methods (like setCourseName and getCourseName) that enable the program to interact with the GradeBook object. For example, the statement in line 23 uses myGradeBook to send the setCourseName message to the GradeBook object. The message includes the argument that setCourseName requires to perform its task. The GradeBook object uses this information to set the courseName instance variable. Note that primitive-type variables do not refer to objects, so such variables cannot be used to invoke methods.

Software Engineering Observation 3.4

Image

A variable’s declared type (e.g., int, double or GradeBook) indicates whether the variable is of a primitive or a reference type. If a variable’s type is not one of the eight primitive types, then it is a reference type. For example, Account account1 indicates that account1 is a reference to an Account object).

3.7 Initializing Objects with Constructors

As mentioned in Section 3.5, when an object of class GradeBook (Fig. 3.7) is created, its instance variable courseName is initialized to null by default. What if you want to provide a course name when you create a GradeBook object? Each class you declare can provide a constructor that can be used to initialize an object of a class when the object is created. In fact, Java requires a constructor call for every object that is created. Keyword new calls the class’s constructor to perform the initialization. The constructor call is indicated by the class name followed by parentheses—the constructor must have the same name as the class. For example, line 14 of Fig. 3.8 first uses new to create a GradeBook object. The empty parentheses after “new GradeBook" indicate a call to the class’s constructor without arguments. By default, the compiler provides a default constructor with no parameters in any class that does not explicitly include a constructor. When a class has only the default constructor, its instance variables are initialized to their default values. Variables of types char, byte, short, int, long, float and double are initialized to 0, variables of type boolean are initialized to false, and reference-type variables are initialized to null.

When you declare a class, you can provide your own constructor to specify custom initialization for objects of your class. For example, you might want to specify a course name for a GradeBook object when the object is created, as in

GradeBook myGradeBook =
      new GradeBook( "CS101 Introduction to Java Programming" );

In this case, the argument "CS101 Introduction to Java Programming" is passed to the GradeBook object’s constructor and used to initialize the courseName. The preceding statement requires that the class provide a constructor with a String parameter. Figure 3.10 contains a modified GradeBook class with such a constructor.

Fig. 3.10. GradeBook class with a constructor to initialize the course name.

Image

Lines 9–12 declare the constructor for class GradeBook. A constructor must have the same name as its class. Like a method, a constructor specifies in its parameter list the data it requires to perform its task. When you create a new object (as we’ll do in Fig. 3.11), this data is placed in the parentheses that follow the class name. Line 9 indicates that class GradeBook’s constructor has a String parameter called name. The name passed to the constructor is assigned to instance variable courseName in line 11 of the constructor’s body.

Fig. 3.11. GradeBook constructor used to specify the course name at the time each GradeBook object is created.

Image

Figure 3.11 demonstrates initializing GradeBook objects using the constructor. Lines 11–12 create and initialize the GradeBook object gradeBook1. The constructor of class GradeBook is called with the argument "CS101 Introduction to Java Programming" to initialize the course name. The class instance creation expression to the right of the = in lines 11–12 returns a reference to the new object, which is assigned to the variable gradeBook1. Lines 13–14 repeat this process for another GradeBook object gradeBook2, this time passing the argument "CS102 Data Structures in Java" to initialize the course name for gradeBook2. Lines 17–20 use each object’s getCourseName method to obtain the course names and show that they were indeed initialized when the objects were created. In the introduction to Section 3.5, you learned that each instance (i.e., object) of a class contains its own copy of the class’s instance variables. The output confirms that each GradeBook maintains its own copy of instance variable courseName.

Like methods, constructors also can take arguments. However, an important difference between constructors and methods is that constructors cannot return values, so they cannot specify a return type (not even void). Normally, constructors are declared public. If a class does not include a constructor, the class’s instance variables are initialized to their default values. If a programmer declares any constructors for a class, the Java compiler will not create a default constructor for that class.

Error-Prevention Tip 3.2

Image

Unless default initialization of your class’s instance variables is acceptable, provide a constructor to ensure that your class’s instance variables are properly initialized with meaningful values when each new object of your class is created.

Adding the Constructor to Class GradeBook’s UML Class Diagram

The UML class diagram of Fig. 3.12 models class GradeBook of Fig. 3.10, which has a constructor that has a name parameter of type String. Like operations, the UML models constructors in the third compartment of a class in a class diagram. To distinguish a constructor from a class’s operations, the UML requires that the word “constructor” be placed between guillemets (« and ») before the constructor’s name. It is customary to list constructors before other operations in the third compartment.

Fig. 3.12. UML class diagram indicating that class GradeBook has a constructor that has a name parameter of UML type String.

Image

3.8 Floating-Point Numbers and Type double

In our next application, we depart temporarily from our GradeBook case study to declare a class called Account that maintains the balance of a bank account. Most account balances are not whole numbers (e.g., 0, –22 and 1024). For this reason, class Account represents the account balance as a floating-point number (i.e., a number with a decimal point, such as 7.33, 0.0975 or 1000.12345). Java provides two primitive types for storing floating-point numbers in memory—float and double. The primary difference between them is that double variables can store numbers with larger magnitude and finer detail (i.e., more digits to the right of the decimal point—also known as the number’s precision) than float variables.

Floating-Point Number Precision and Memory Requirements

Variables of type float represent single-precision floating-point numbers and have seven significant digits. Variables of type double represent double-precision floating-point numbers. These require twice as much memory as float variables and provide 15 significant digits—approximately double the precision of float variables. For the range of values required by most programs, variables of type float should suffice, but you can use double to “play it safe.” In some applications, even variables of type double will be inadequate—such applications are beyond the scope of this book. Most programmers represent floating-point numbers with type double. In fact, Java treats all floating-point numbers you type in a program’s source code (such as 7.33 and 0.0975) as double values by default. Such values in the source code are known as floating-point literals. See Appendix D, Primitive Types, for the ranges of values for floats and doubles.

Although floating-point numbers are not always 100% precise, they have numerous applications. For example, when we speak of a “normal” body temperature of 98.6, we do not need to be precise to a large number of digits. When we read the temperature on a thermometer as 98.6, it may actually be 98.5999473210643. Calling this number simply 98.6 is fine for most applications involving body temperatures. Due to the imprecise nature of floating-point numbers, type double is preferred over type float because double variables can represent floating-point numbers more accurately. For this reason, we use type double throughout the book.

Floating-point numbers also arise as a result of division. In conventional arithmetic, when we divide 10 by 3, the result is 3.3333333..., with the sequence of 3s repeating infinitely. The computer allocates only a fixed amount of space to hold such a value, so clearly the stored floating-point value can be only an approximation.

Common Programming Error 3.4

Image

Using floating-point numbers in a manner that assumes they are represented precisely can lead to logic errors.

Account Class with an Instance Variable of Type double

Our next application (Figs. 3.133.14) contains a class named Account (Fig. 3.13) that maintains the balance of a bank account. A typical bank services many accounts, each with its own balance, so line 7 declares an instance variable named balance of type double. Variable balance is an instance variable because it is declared in the body of the class but outside the class’s method declarations (lines 10–16, 19–22 and 25–28). Every instance (i.e., object) of class Account contains its own copy of balance.

Fig. 3.13. Account class with an instance variable of type double.

Image

Fig. 3.14. Inputting and outputting floating-point numbers with Account objects.

Image

Image

Class Account contains a constructor and two methods. Since it is common for someone opening an account to place money in the account immediately, the constructor (lines 10–16) receives a parameter initialBalance of type double that represents the account’s starting balance. Lines 14–15 ensure that initialBalance is greater than 0.0. If so, initialBalance’s value is assigned to instance variable balance. Otherwise, balance remains at 0.0—its default initial value.

Method credit (lines 19–22) does not return any data when it completes its task, so its return type is void. The method receives one parameter named amount—a double value that will be added to the balance. Line 21 adds amount to the current value of balance, then assigns the result to balance (thus replacing the prior balance amount).

Method getBalance (lines 25–28) allows clients of the class (i.e., other classes that use this class) to obtain the value of a particular Account object’s balance. The method specifies return type double and an empty parameter list.

Once again, note that the statements in lines 15, 21 and 27 use instance variable balance even though it was not declared in any of the methods. We can use balance in these methods because it is an instance variable of the class.

AccountTest Class to Use Class Account

Class AccountTest (Fig. 3.14) creates two Account objects (lines 10–11) and initializes them with 50.00 and -7.53, respectively. Lines 14–17 output the balance in each Account by calling the Account’s getBalance method. When method getBalance is called for account1 from line 15, the value of account1’s balance is returned from line 27 of Fig. 3.13 and displayed by the System.out.printf statement (Fig. 3.14, lines 14–15). Similarly, when method getBalance is called for account2 from line 17, the value of the account2’s balance is returned from line 27 of Fig. 3.13 and displayed by the System.out.printf statement (Fig. 3.14, lines 16–17). Note that the balance of account2 is 0.00 because the constructor ensured that the account could not begin with a negative balance. The value is output by printf with the format specifier %.2f. The format specifier %f is used to output values of type float or double. The .2 between % and f represents the number of decimal places (2) that should be output to the right of the decimal point in the floating-point number—also known as the number’s precision. Any floating-point value output with %.2f will be rounded to the hundredths position—for example, 123.457 would be rounded to 123.46, and 27.333 would be rounded to 27.33.

Line 20 creates a Scanner that will be used to obtain deposit amounts from a user. Line 21 declares local variable depositAmount to store each deposit amount entered by the user. Unlike the instance variable balance in class Account, local variable depositAmount in main is not initialized to 0.0 by default. However, this variable does not need to be initialized here because its value will be determined by the user’s input.

Line 23 prompts the user to enter a deposit amount for account1. Line 24 obtains the input from the user by calling Scanner object input’s nextDouble method, which returns a double value entered by the user. Lines 25–26 display the deposit amount. Line 27 calls object account1’s credit method and supplies depositAmount as the method’s argument. When the method is called, the argument’s value is assigned to parameter amount (line 19 of Fig. 3.13) of method credit (lines 19–22 of Fig. 3.13), then method credit adds that value to the balance (line 21 of Fig. 3.13). Lines 30–33 (Fig. 3.14) output the balances of both Accounts again to show that only account1’s balance changed.

Line 35 prompts the user to enter a deposit amount for account2. Line 36 obtains the input from the user by calling Scanner object input’s nextDouble method. Lines 37–38 display the deposit amount. Line 39 calls object account2’s credit method and supplies depositAmount as the method’s argument, then method credit adds that value to the balance. Finally, lines 42–45 output the balances of both Accounts again to show that only account2’s balance changed.

UML Class Diagram for Class Account

The UML class diagram in Fig. 3.15 models class Account of Fig. 3.13. The diagram models the private attribute balance with UML type Double to correspond to the class’s instance variable balance of Java type double. The diagram models class Account’s constructor with a parameter initialBalance of UML type Double in the third compartment of the class. The class’s two public methods are modeled as operations in the third compartment as well. The diagram models operation credit with an amount parameter of UML type Double (because the corresponding method has an amount parameter of Java type double) and operation getBalance with a return type of Double (because the corresponding Java method returns a double value).

Fig. 3.15. UML class diagram indicating that class Account has a private balance attribute of UML type Double, a constructor (with a parameter of UML type Double) and two public operations—credit (with an amount parameter of UML type Double) and getBalance (returns UML type Double).

Image

3.9 (Optional) Software Engineering Case Study: Identifying the Classes in a Requirements Document

Now we begin designing the ATM system that we introduced in Chapter 2. In this section, we identify the classes that are needed to build the ATM system by analyzing the nouns and noun phrases that appear in the requirements document. We introduce UML class diagrams to model the relationships between these classes. This is an important first step in defining the structure of our system.

Identifying the Classes in a System

We begin our OOD process by identifying the classes required to build the ATM system. We’ll eventually describe these classes using UML class diagrams and implement these classes in Java. First, we review the requirements document of Section 2.8 and identify key nouns and noun phrases to help us identify classes that comprise the ATM system. We may decide that some of these nouns and noun phrases are attributes of other classes in the system. We may also conclude that some of the nouns do not correspond to parts of the system and thus should not be modeled at all. Additional classes may become apparent to us as we proceed through the design process.

Figure 3.16 lists the nouns and noun phrases found in the requirements document of Section 2.8. We list them from left to right in the order in which we first encounter them in the requirements document. We list only the singular form of each noun or noun phrase.

Fig. 3.16. Nouns and noun phrases in the ATM requirements document.

Image

We create classes only for the nouns and noun phrases that have significance in the ATM system. We don’t model “bank” as a class, because the bank is not a part of the ATM system—the bank simply wants us to build the ATM. “Customer” and “user” also represent outside entities—they are important because they interact with our ATM system, but we do not need to model them as classes in the ATM software. Recall that we modeled an ATM user (i.e., a bank customer) as the actor in the use case diagram of Fig. 2.16.

We do not model “$20 bill” or “deposit envelope” as classes. These are physical objects in the real world, but they are not part of what is being automated. We can adequately represent the presence of bills in the system using an attribute of the class that models the cash dispenser. (We assign attributes to the ATM system’s classes in Section 4.12.) For example, the cash dispenser maintains a count of the number of bills it contains. The requirements document does not say anything about what the system should do with deposit envelopes after it receives them. We can assume that simply acknowledging the receipt of an envelope—an operation performed by the class that models the deposit slot—is sufficient to represent the presence of an envelope in the system. (We assign operations to the ATM system’s classes in Section 6.19.)

In our simplified ATM system, representing various amounts of “money,” including an account’s “balance,” as attributes of classes seems most appropriate. Likewise, the nouns “account number” and “PIN” represent significant pieces of information in the ATM system. They are important attributes of a bank account. They do not, however, exhibit behaviors. Thus, we can most appropriately model them as attributes of an account class.

Though the requirements document frequently describes a “transaction” in a general sense, we do not model the broad notion of a financial transaction at this time. Instead, we model the three types of transactions (i.e., “balance inquiry,” “withdrawal” and “deposit”) as individual classes. These classes possess specific attributes needed for executing the transactions they represent. For example, a withdrawal needs to know the amount of money the user wants to withdraw. A balance inquiry, however, does not require any additional data. Furthermore, the three transaction classes exhibit unique behaviors. A withdrawal includes dispensing cash to the user, whereas a deposit involves receiving deposit envelopes from the user. [Note: In Section 10.8, we “factor out” common features of all transactions into a general “transaction” class using the object-oriented concept of inheritance.]

We determine the classes for our system based on the remaining nouns and noun phrases from Fig. 3.16. Each of these refers to one or more of the following:

• ATM

• screen

• keypad

• cash dispenser

• deposit slot

• account

• bank database

• balance inquiry

• withdrawal

• deposit

The elements of this list are likely to be classes we’ll need to implement our system.

We can now model the classes in our system based on the list we’ve created. We capitalize class names in the design process—a UML convention—as we’ll do when we write the actual Java code that implements our design. If the name of a class contains more than one word, we run the words together and capitalize each word (e.g., MultipleWordName). Using this convention, we create classes ATM, Screen, Keypad, CashDispenser, DepositSlot, Account, BankDatabase, BalanceInquiry, Withdrawal and Deposit. We construct our system using these classes as building blocks. Before we begin building the system, however, we must gain a better understanding of how the classes relate to one another.

Modeling Classes

The UML enables us to model, via class diagrams, the classes in the ATM system and their interrelationships. Figure 3.17 represents class ATM. In the UML, each class is modeled as a rectangle with three compartments. The top compartment contains the name of the class centered horizontally in boldface. The middle compartment contains the class’s attributes. (We discuss attributes in Section 4.12 and Section 5.9.) The bottom compartment contains the class’s operations (discussed in Section 6.19). In Fig. 3.17, the middle and bottom compartments are empty because we have not yet determined this class’s attributes and operations.

Fig. 3.17. Representing a class in the UML using a class diagram.

Image

Class diagrams also show the relationships between the classes of the system. Figure 3.18 shows how our classes ATM and Withdrawal relate to one another. For the moment, we choose to model only this subset of classes for simplicity. We present a more complete class diagram later in this section. Notice that the rectangles representing classes in this diagram are not subdivided into compartments. The UML allows the suppression of class attributes and operations in this manner to create more readable diagrams, when appropriate. Such a diagram is said to be an elided diagram—one in which some information, such as the contents of the second and third compartments, is not modeled. We’ll place information in these compartments in Section 4.12 and Section 6.19.

Fig. 3.18. Class diagram showing an association among classes.

Image

In Fig. 3.18, the solid line that connects the two classes represents an association—a relationship between classes. The numbers near each end of the line are multiplicity values, which indicate how many objects of each class participate in the association. In this case, following the line from one end to the other reveals that, at any given moment, one ATM object participates in an association with either zero or one Withdrawal objects—zero if the current user is not currently performing a transaction or has requested a different type of transaction, and one if the user has requested a withdrawal. The UML can model many types of multiplicity. Figure 3.19 lists and explains the multiplicity types.

Fig. 3.19. Multiplicity types.

Image

An association can be named. For example, the word Executes above the line connecting classes ATM and Withdrawal in Fig. 3.18 indicates the name of that association. This part of the diagram reads “one object of class ATM executes zero or one objects of class Withdrawal.” Note that association names are directional, as indicated by the filled arrowhead—so it would be improper, for example, to read the preceding association from right to left as “zero or one objects of class Withdrawal execute one object of class ATM.”

The word currentTransaction at the Withdrawal end of the association line in Fig. 3.18 is a role name, which identifies the role the Withdrawal object plays in its relationship with the ATM. A role name adds meaning to an association between classes by identifying the role a class plays in the context of an association. A class can play several roles in the same system. For example, in a school personnel system, a person may play the role of “professor” when relating to students. The same person may take on the role of “colleague” when participating in an association with another professor, and “coach” when coaching student athletes. In Fig. 3.18, the role name currentTransaction indicates that the Withdrawal object participating in the Executes association with an object of class ATM represents the transaction currently being processed by the ATM. In other contexts, a Withdrawal object may take on other roles (e.g., the previous transaction). Notice that we do not specify a role name for the ATM end of the Executes association. Role names in class diagrams are often omitted when the meaning of an association is clear without them.

In addition to indicating simple relationships, associations can specify more complex relationships, such as objects of one class being composed of objects of other classes. Consider a real-world automated teller machine. What “pieces” does a manufacturer put together to build a working ATM? Our requirements document tells us that the ATM is composed of a screen, a keypad, a cash dispenser and a deposit slot.

In Fig. 3.20, the solid diamonds attached to the association lines of class ATM indicate that class ATM has a composition relationship with classes Screen, Keypad, CashDispenser and DepositSlot. Composition implies a whole/part relationship. The class that has the composition symbol (the solid diamond) on its end of the association line is the whole (in this case, ATM), and the classes on the other end of the association lines are the parts—in this case, classes Screen, Keypad, CashDispenser and DepositSlot. The compositions in Fig. 3.20 indicate that an object of class ATM is formed from one object of class Screen, one object of class CashDispenser, one object of class Keypad and one object of class DepositSlot. The ATM has a screen, a keypad, a cash dispenser and a deposit slot. The has-a relationship defines composition. (We’ll see in the Software Engineering Case Study section in Chapter 10 that the is-a relationship defines inheritance.)

Fig. 3.20. Class diagram showing composition relationships.

Image

According to the UML specification (www.uml.org), composition relationships have the following properties:

1. Only one class in the relationship can represent the whole (i.e., the diamond can be placed on only one end of the association line). For example, either the screen is part of the ATM or the ATM is part of the screen, but the screen and the ATM cannot both represent the whole in the relationship.

2. The parts in the composition relationship exist only as long as the whole, and the whole is responsible for the creation and destruction of its parts. For example, the act of constructing an ATM includes manufacturing its parts. Also, if the ATM is destroyed, its screen, keypad, cash dispenser and deposit slot are also destroyed.

3. A part may belong to only one whole at a time, although the part may be removed and attached to another whole, which then assumes responsibility for the part.

The solid diamonds in our class diagrams indicate composition relationships that fulfill these three properties. If a has-a relationship does not satisfy one or more of these criteria, the UML specifies that hollow diamonds be attached to the ends of association lines to indicate aggregation—a weaker form of composition. For example, a personal computer and a computer monitor participate in an aggregation relationship—the computer has a monitor, but the two parts can exist independently, and the same monitor can be attached to multiple computers at once, thus violating the second and third properties of composition.

Figure 3.21 shows a class diagram for the ATM system. This diagram models most of the classes that we identified earlier in this section, as well as the associations between them that we can infer from the requirements document. [Note: Classes BalanceInquiry and Deposit participate in associations similar to those of class Withdrawal, so we have chosen to omit them from this diagram to keep the diagram simple. In Chapter 10, we expand our class diagram to include all the classes in the ATM system.]

Fig. 3.21. Class diagram for the ATM system model.

Image

Figure 3.21 presents a graphical model of the structure of the ATM system. This class diagram includes classes BankDatabase and Account, and several associations that were not present in either Fig. 3.18 or Fig. 3.20. The class diagram shows that class ATM has a one-to-one relationship with class BankDatabase—one ATM object authenticates users against one BankDatabase object. In Fig. 3.21, we also model the fact that the bank’s database contains information about many accounts—one object of class BankDatabase participates in a composition relationship with zero or more objects of class Account. Recall from Fig. 3.19 that the multiplicity value 0..* at the Account end of the association between class BankDatabase and class Account indicates that zero or more objects of class Account take part in the association. Class BankDatabase has a one-to-many relationship with class Account—the BankDatabase stores many Accounts. Similarly, class Account has a many-to-one relationship with class BankDatabase—there can be many Accounts stored in the BankDatabase. [Note: Recall from Fig. 3.19 that the multiplicity value * is identical to 0..*. We include 0..* in our class diagrams for clarity.]

Figure 3.21 also indicates that if the user is performing a withdrawal, “one object of class Withdrawal accesses/modifies an account balance through one object of class BankDatabase.” We could have created an association directly between class Withdrawal and class Account. The requirements document, however, states that the “ATM must interact with the bank’s account information database” to perform transactions. A bank account contains sensitive information, and systems engineers must always consider the security of personal data when designing a system. Thus, only the BankDatabase can access and manipulate an account directly. All other parts of the system must interact with the database to retrieve or update account information (e.g., an account balance).

The class diagram in Fig. 3.21 also models associations between class Withdrawal and classes Screen, CashDispenser and Keypad. A withdrawal transaction includes prompting the user to choose a withdrawal amount and receiving numeric input. These actions require the use of the screen and the keypad, respectively. Furthermore, dispensing cash to the user requires access to the cash dispenser.

Classes BalanceInquiry and Deposit, though not shown in Fig. 3.21, take part in several associations with the other classes of the ATM system. Like class Withdrawal, each of these classes associates with classes ATM and BankDatabase. An object of class BalanceInquiry also associates with an object of class Screen to display the balance of an account to the user. Class Deposit associates with classes Screen, Keypad and DepositSlot. Like withdrawals, deposit transactions require use of the screen and the keypad to display prompts and receive input, respectively. To receive deposit envelopes, an object of class Deposit accesses the deposit slot.

We have now identified the classes in our ATM system (although we may discover others as we proceed with the design and implementation). In Section 4.12, we determine the attributes for each of these classes, and in Section 5.9, we use these attributes to examine how the system changes over time.

Software Engineering Case Study Self-Review Exercises

3.1 Suppose we have a class Car that represents a car. Think of some of the different pieces that a manufacturer would put together to produce a whole car. Create a class diagram (similar to Fig. 3.20) that models some of the composition relationships of class Car.

3.2 Suppose we have a class File that represents an electronic document in a standalone, non-networked computer represented by class Computer. What sort of association exists between class Computer and class File?

a. Class Computer has a one-to-one relationship with class File.

b. Class Computer has a many-to-one relationship with class File.

c. Class Computer has a one-to-many relationship with class File.

d. Class Computer has a many-to-many relationship with class File.

3.3 State whether the following statement is true or false, and if false, explain why: A UML diagram in which a class’s second and third compartments are not modeled is said to be an elided diagram.

3.4 Modify the class diagram of Fig. 3.21 to include class Deposit instead of class Withdrawal.

Answers to Software Engineering Case Study Self-Review Exercises

3.1 [Note: Answers may vary.] Figure 3.22 presents a class diagram that shows some of the composition relationships of a class Car.

Fig. 3.22. Class diagram showing composition relationships of a class Car.

Image

3.2 c. [Note: In a computer network, this relationship could be many-to-many.]

3.3 True.

3.4 Figure 3.23 presents a class diagram for the ATM including class Deposit instead of class Withdrawal (as in Fig. 3.21). Note that Deposit does not access CashDispenser, but does access DepositSlot.

Fig. 3.23. Class diagram for the ATM system model including class Deposit.

Image

3.10 Wrap-Up

In this chapter, you learned the basic concepts of classes, objects, methods and instance variables—these will be used in most Java applications you create. In particular, you learned how to declare instance variables of a class to maintain data for each object of the class, and how to declare methods that operate on that data. You learned how to call a method to tell it to perform its task and how to pass information to methods as arguments. You learned the difference between a local variable of a method and an instance variable of a class and that only instance variables are initialized automatically. You also learned how to use a class’s constructor to specify the initial values for an object’s instance variables. Throughout the chapter, you saw how the UML can be used to create class diagrams that model the constructors, methods and attributes of classes. Finally, you learned about floating-point numbers—how to store them with variables of primitive type double, how to input them with a Scanner object and how to format them with printf and format specifier %f for display purposes. In the next chapter we begin our introduction to control statements, which specify the order in which a program’s actions are performed. You’ll use these in your methods to specify how they should perform their tasks.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.222.195