- 3.1 Introduction
- 3.2 Instance Variables, set Methods and get Methods
- 3.3 Primitive Types vs. Reference Types
- 3.4 Account Class: Initializing Objects with Constructors
- 3.5 Account Class with a Balance; Floating-Point Numbers
- 3.6 Wrap-Up
3.2 Instance Variables, set Methods and get Methods
In this section, you’ll create two classes—Account (Fig. 3.1) and AccountTest (Fig. 3.2). Class AccountTest is an application class in which the main method will create and use an Account object to demonstrate class Account’s capabilities.
Fig. 3.1 | Account class that contains a name instance variable and methods to set and get its value.
1 // Fig. 3.1: Account.java
2 // Account class that contains a name instance variable
3 // and methods to set and get its value.
4
5 public class Account
6 {
7 private String name; // instance variable
8
9 // method to set the name in the object
10 public void setName(String name)
11 {
12 this.name = name; // store the name
13 }
14
15 // method to retrieve the name from the object
16 public String getName()
17 {
18 return name; // return value of name to caller
19 }
20 } // end class Account
Fig. 3.2 | Creating and manipulating an Account object.
1 // Fig. 3.2: AccountTest.java
2 // Creating and manipulating an Account object.
3 import java.util.Scanner;
4
5 public class AccountTest
6 {
7 public static void main(String[] args)
8 {
9 // create a Scanner object to obtain input from the command window
10 Scanner input = new Scanner(System.in);
11
12 // create an Account object and assign it to myAccount
13 Account myAccount = new Account();
14
15 // display initial value of name (null)
16 System.out.printf("Initial name is: %s%n%n", myAccount.getName()
);
17
18 // prompt for and read name
19 System.out.println("Please enter the name:");
20 String theName = input.nextLine(); // read a line of text
21 myAccount.setName(theName); // put theName in myAccount
22 System.out.println(); // outputs a blank line
23
24 // display the name stored in object myAccount
25 System.out.printf("Name in object myAccount is:%n%s%n",
26 myAccount.getName()
);
27 }
28 } // end class AccountTest
Initial name is: null Please enter the name: Jane Green Name in object myAccount is: Jane Green
3.2.1 Account Class with an Instance Variable, a set Method and a get Method
Different accounts typically have different names. For this reason, class Account (Fig. 3.1) contains a name instance variable. A class’s instance variables maintain data for each object (that is, each instance) of the class. Later in the chapter we’ll add an instance variable named balance so we can keep track of how much money is in the account. Class Account contains two methods—method setName stores a name in an Account object and method getName obtains a name from an Account object.
Class Declaration
The class declaration begins in line 5. The keyword public (which Chapter 8 explains in detail) is an access modifier. For now, we’ll simply declare every class public. Each public class declaration must be stored in a file having the same name as the class and ending with the .java filename extension; otherwise, a compilation error will occur. Thus, public classes Account and AccountTest (Fig. 3.2) must be declared in the separate files Account.java and AccountTest.java, respectively.
Every class declaration contains the keyword class followed immediately by the class’s name—in this case, Account. Every class’s body is enclosed in a pair of left and right braces as in lines 6 and 20 of Fig. 3.1.
Identifiers and Camel Case Naming
Class names, method names and variable names are all identifiers and by convention all use the same camel case naming scheme we discussed in Chapter 2. Also by convention, class names begin with an initial uppercase letter, and method names and variable names begin with an initial lowercase letter.
Instance Variable name
Recall that an object has attributes, implemented as instance variables and carried with it throughout its lifetime. Instance variables exist before methods are called on an object, while the methods are executing and after the methods complete execution. Each object (instance) of the class has its own copy of the class’s instance variables. A class normally contains one or more methods that manipulate the instance variables belonging to particular objects of the class.
Instance variables are declared inside a class declaration but outside the bodies of the class’s methods. Line 7
private String name; // instance variable
declares instance variable name of type String outside the bodies of methods setName (lines 10–13) and getName (lines 16–19). String variables can hold character string values such as "Jane Green". If there are many Account objects, each has its own name. Because name is an instance variable, it can be manipulated by each of the class’s methods.
Access Modifiers public and private
Most instance-variable declarations are preceded with the keyword private (as in line 7). Like public, private is an access modifier. Variables or methods declared with access modifier private are accessible only to methods of the class in which they’re declared. So, the variable name can be used only in each Account object’s methods (setName and getName in this case). You’ll soon see that this presents powerful software engineering opportunities.
setName Method of Class Account
Let’s walk through the code of setName’s method declaration (lines 10–13):
public void setName(String name) — This line is the method header
{
this.name = name; // store the name
}
We refer to the first line of each method declaration (line 10 in this case) as the method header. The method’s return type (which appears before the method name) specifies the type of data the method returns to its caller after performing its task. The return type void (line 10) indicates that setName will perform a task but will not return (i.e., give back) any information to its caller. In Chapter 2, you used methods that return information—for example, you used Scanner method nextInt to input an integer typed by the user at the keyboard. When nextInt reads a value from the user, it returns that value for use in the program. As you’ll soon see, Account method getName returns a value.
Method setName receives parameter name of type String. Parameters are declared in the parameter list, which is located inside the parentheses that follow the method name in the method header. When there are multiple parameters, each is separated from the next by a comma. Each parameter must specify a type (in this case, String) followed by a variable name (in this case, name).
Parameters Are Local Variables
In Chapter 2, we declared all of an app’s variables in the main method. Variables declared in a particular method’s body (such as main) are local variables which can be used only in that method. Each method can access only its own local variables, not those of other methods. When a method terminates, the values of its local variables are lost. A method’s parameters also are local variables of the method.
setName Method Body
Every method body is delimited by a pair of braces (as in lines 11 and 13 of Fig. 3.1) containing one or more statements that perform the method’s task(s). In this case, the method body contains a single statement (line 12) that assigns the value of the name parameter (a String) to the class’s name instance variable, thus storing the account name in the object.
If a method contains a local variable with the same name as an instance variable (as in lines 10 and 7, respectively), that method’s body will refer to the local variable rather than the instance variable. In this case, the local variable is said to shadow the instance variable in the method’s body. The method’s body can use the keyword this to refer to the shadowed instance variable explicitly, as shown on the left side of the assignment in line 12.
After line 12 executes, the method has completed its task, so it returns to its caller. As you’ll soon see, the statement in line 21 of main (Fig. 3.2) calls method setName.
getName Method of Class Account
Method getName (lines 16–19 of Fig. 3.1)
returns a particular Account object’s name to the caller. The method has an empty parameter list, so it does not require additional information to perform its task. The method returns a String. When a method that specifies a return type other than void is called and completes its task, it must return a result to its caller. A statement that calls method getName on an Account object (such as the ones in lines 16 and 26 of Fig. 3.2) expects to receive the Account’s name—a String, as specified in the method declaration’s return type.
The return statement in line 18 of Fig. 3.1 passes the String value of instance variable name back to the caller. For example, when the value is returned to the statement in lines 25–26 of Fig. 3.2, the statement uses that value to output the name.
3.2.2 AccountTest Class That Creates and Uses an Object of Class Account
Next, we’d like to use class Account in an app and call each of its methods. A class that contains a main method begins the execution of a Java app. Class Account cannot execute by itself because it does not contain a main method—if you type java Account in the command window, you’ll get an error indicating “Main method not found in class Account.” To fix this problem, you must either declare a separate class that contains a main method or place a main method in class Account.
Driver Class AccountTest
We use a separate class AccountTest (Fig. 3.2) containing method main to test class Account. Once main begins executing, it may call other methods in this and other classes; those may, in turn, call other methods, and so on. Class AccountTest’s main method creates one Account object and calls its getName and setName methods. Such a class is sometimes called a driver class—just as a Person object drives a Car object by telling it what to do (go faster, go slower, turn left, turn right, etc.), class AccountTest drives an Account object, telling it what to do by calling its methods.
Scanner Object for Receiving Input from the User
Line 10 creates a Scanner object named input for inputting the name from the user. Line 19 prompts the user to enter a name. Line 20 uses the Scanner object’s nextLine method to read the name from the user and assign it to the local variable theName. You type the name and press Enter to submit it to the program. Pressing Enter inserts a newline character after the characters you typed. Method nextLine reads characters (including whitespace characters, such as the blank in "Jane Green") until it encounters the newline, then returns a String containing the characters up to, but not including, the newline, which is discarded.
Class Scanner provides various other input methods, as you’ll see throughout the book. A method similar to nextLine—named next—reads the next word. When you press Enter after typing some text, method next reads characters until it encounters a white-space character (such as a space, tab or newline), 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 subsequent statements that call the Scanner’s methods later in the program.
Instantiating an Object—Keyword new and Constructors
Line 13 creates an Account object and assigns it to variable myAccount of type Account. Variable myAccount is initialized with the result of the class instance creation expression new Account(). Keyword new creates a new object of the specified class—in this case, Account. The parentheses to the right of Account are required. As you’ll learn in Section 3.4, those parentheses in combination with a class name represent a call to a constructor, which is similar to a method but is called implicitly by the new operator to initialize an object’s instance variables when the object is created. In Section 3.4, you’ll see how to place an argument in the parentheses to specify an initial value for an Account object’s name instance variable—you’ll enhance class Account to enable this. For now, we simply leave the parentheses empty. Line 10 contains a class instance creation expression for a Scanner object—the expression initializes the Scanner with System.in, which tells the Scanner where to read the input from (i.e., the keyboard).
Calling Class Account’s getName Method
Line 16 displays the initial name, which is obtained by calling the object’s getName method. Just as we can use object System.out to call its methods print, printf and println, we can use object myAccount to call its methods getName and setName. Line 16 calls getName using the myAccount object created in line 13, followed by a dot separator (.), then the method name getName and an empty set of parentheses because no arguments are being passed. When getName is called:
- The app transfers program execution from the call (line 16 in main) to method getName’s declaration (lines 16–19 of Fig. 3.1). Because getName was called via the my Account object, getName “knows” which object’s instance variable to manipulate.
- Next, method getName performs its task—that is, it returns the name (line 18 of Fig. 3.1). When the return statement executes, program execution continues where getName was called (line 16 in Fig. 3.2).
- System.out.printf displays the String returned by method getName, then the program continues executing at line 19 in main.
null—the Default Initial Value for String Variables
The first line of the output shows the name “null.” Unlike local variables, which are not automatically initialized, every instance variable has a default initial value—a value provided by Java when you do not specify the instance variable’s initial value. Thus, instance variables are not required to be explicitly initialized before they’re used in a program—unless they must be initialized to values other than their default values. The default value for an instance variable of type String (like name in this example) is null, which we discuss further in Section 3.3 when we consider reference types.
Calling Class Account’s setName Method
Line 21 calls myAccounts’s setName method. A method call can supply arguments whose values are assigned to the corresponding method parameters. In this case, the value of main’s local variable theName in parentheses is the argument that’s passed to setName so that the method can perform its task. When setName is called:
- The app transfers program execution from line 21 in main to setName method’s declaration (lines 10–13 of Fig. 3.1), and the argument value in the call’s parentheses (theName) is assigned to the corresponding parameter (name) in the method header (line 10 of Fig. 3.1). Because setName was called via the myAccount object, setName “knows” which object’s instance variable to manipulate.
- Next, method setName performs its task—that is, it assigns the name parameter’s value to instance variable name (line 12 of Fig. 3.1).
- When program execution reaches setName’s closing right brace, it returns to where setName was called (line 21 of Fig. 3.2), then continues at line 22.
The number of arguments in a method call must match the number of parameters in the method declaration’s parameter list. 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 see in Chapter 6, an argument’s type and its corresponding parameter’s type are not required to be identical.) In our example, the method call passes one argument of type String (theName)—and the method declaration specifies one parameter of type String (name, declared in line 10 of Fig. 3.1). So in this example, the type of the argument in the method call exactly matches the type of the parameter in the method header.
Displaying the Name That Was Entered by the User
Line 22 of Fig. 3.2 outputs a blank line. When the second call to method getName (line 26) executes, the name entered by the user in line 20 is displayed. When the statement at lines 25–26 completes execution, the end of method main is reached, so the program terminates.
3.2.3 Compiling and Executing an App with Multiple Classes
You must compile the classes in Figs. 3.1 and 3.2 before you can execute the app. This is the first time you’ve created an app with multiple classes. Class AccountTest has a main method; class Account does not. To compile this app, first change to the directory that contains the app’s source-code files. Next, type the command
javac Account.java AccountTest.java
to compile both classes at once. If the directory containing the app includes only this app’s files, you can compile both classes with the command
javac *.java
The asterisk (*) in *.java indicates that all files in the current directory ending with the filename extension “.java” should be compiled. If both classes compile correctly—that is, no compilation errors are displayed—you can then run the app with the command
java AccountTest
3.2.4 Account UML Class Diagram with an Instance Variable and set and get Methods
We’ll often use UML class diagrams to summarize a class’s attributes and operations. In industry, UML diagrams help systems designers specify a system in a concise, graphical, programming-language-independent manner, before programmers implement the system in a specific programming language. Figure 3.3 presents a UML class diagram for class Account of Fig. 3.1.
Fig. 3.3 | UML class diagram for class Account of Fig. 3.1.
Top Compartment
In the UML, each class is modeled in a class diagram as a rectangle with three compartments. In this diagram the top compartment contains the class name Account centered horizontally in boldface type.
Middle Compartment
The middle compartment contains the class’s attribute name, which corresponds to the instance variable of the same name in Java. Instance variable name is private in Java, so the UML class diagram lists a minus sign (-) access modifier before the attribute name. Following the attribute name are a colon and the attribute type, in this case String.
Bottom Compartment
The bottom compartment contains the class’s operations, setName and getName, which correspond to the methods of the same names in Java. The UML models operations by listing the operation name preceded by an access modifier, in this case + getName. This plus sign (+) indicates that getName is a public operation in the UML (because it’s a public method in Java). Operation getName does not have any parameters, so the parentheses following the operation name in the class diagram are empty, just as they are in the method’s declaration in line 16 of Fig. 3.1. Operation setName, also a public operation, has a String parameter called name.
Return Types
The UML indicates the return type of an operation by placing a colon and the return type after the parentheses following the operation name. Account method getName (Fig. 3.1) has a String return type. Method setName does not return a value (because it returns void in Java), so the UML class diagram does not specify a return type after the parentheses of this operation.
Parameters
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 after the operation name. The UML has its own data types similar to those of Java, but for simplicity, we’ll use the Java data types. Account method setName (Fig. 3.1) has a String parameter named name, so Fig. 3.3 lists name : String between the parentheses following the method name.
3.2.5 Additional Notes on Class AccountTest
static Method main
In Chapter 2, each class we declared had one method named main. Recall that main is a special method that’s always called automatically by the Java Virtual Machine (JVM) when you execute an app. You must call most other methods explicitly to tell them to perform their tasks.
Lines 7–27 of Fig. 3.2 declare method main. A key part of enabling the JVM to locate and call method main to begin the app’s execution is the static keyword (line 7), which indicates that main is a static method. A static method is special, because you can call it without first creating an object of the class in which the method is declared—in this case class AccountTest. We discuss static methods in detail in Chapter 6.
Notes on import Declarations
Notice the import declaration in Fig. 3.2 (line 3), which indicates to the compiler that the program uses class Scanner. As mentioned in Chapter 2, classes System and String are in package java.lang, which is implicitly imported into every Java program, so all programs can use that package’s classes without explicitly importing them. Most other classes you’ll use in Java programs must be imported explicitly.
There’s a special relationship between classes that are compiled in the same directory, like classes Account and AccountTest. 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 that package. Thus, an import declaration is not required when one class in a package uses another in the same package—such as when class AccountTest uses class Account.
The import declaration in line 3 is not required if we refer to class Scanner throughout this file 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 10 of Fig. 3.2 also could be written as
java.util.Scanner input = new
java.util.Scanner(System.in);
3.2.6 Software Engineering with private Instance Variables and public set and get Methods
As you’ll see, through the use of set and get methods, you can validate attempted modifications to private data and control how that data is presented to the caller—these are compelling software engineering benefits. We’ll discuss this in more detail in Section 3.5.
If the instance variable were public, any client of the class—that is, any other class that calls the class’s methods—could see the data and do whatever it wanted with it, including setting it to an invalid value.
You might think that even though a client of the class cannot directly access a private instance variable, the client can do whatever it wants with the variable through public set and get methods. You would think that you could peek at the private data any time with the public get method and that you could modify the private data at will through the public set method. But set methods can be programmed to validate their arguments and reject any attempts to set the data to bad values, such as a negative body temperature, a day in March out of the range 1 through 31, a product code not in the company’s product catalog, etc. And a get method can present the data in a different form. For example, a Grade class might store a grade as an int between 0 and 100, but a getGrade method might return a letter grade as a String, such as "A" for grades between 90 and 100, "B" for grades between 80 and 89, etc. Tightly controlling the access to and presentation of private data can greatly reduce errors, while increasing the robustness and security of your programs.
Declaring instance variables with access modifier private is known as data hiding or information hiding. When a program creates (instantiates) an object of class Account, variable name is encapsulated (hidden) in the object and can be accessed only by methods of the object’s class.
Conceptual View of an Account Object with Encapsulated Data
You can think of an Account object as shown in Fig. 3.4. The private instance variable name is hidden inside the object (represented by the inner circle containing name) and protected by an outer layer of public methods (represented by the outer circle containing getName and setName). Any client code that needs to interact with the Account object can do so only by calling the public methods of the protective outer layer.
Fig. 3.4 | Conceptual view of an Account object with its encapsulated private instance variable name and protective layer of public methods.