java
*
Chapter 6 - Object-Oriented Programming
- Object-oriented programming overview
- objects
- classes
- encapsulation
- UML Class Diagram
- First OOP Class
- private and public Access
- Driver Class
- Reference Variables and Instantiation
- Calling a Method
- Calling Object
1
*
1. In this chapter, I demo these files:
Mouse.java
MouseDriver.java
*
- The this Reference
- Default Values
- Variable Persistence
- OOP Tracing Procedure (hidden)
- UML Class Diagram for Next Version of the Mouse Program
- Local Variables
- return statement
- void Return Type
- Empty return Statement
- Argument Passing
- Specialized methods:
- accessor methods
- mutator methods
- boolean methods
Chapter 6 - Object-Oriented Programming
*
*
Object-Oriented Programming Overview
- In the old days, the standard programming technique was called "procedural programming."
- That's because the emphasis was on the procedures or tasks that made up a program.
- You'd design your program around what you thought were the key procedures.
- Today, the most popular programming technique is object-oriented programming (OOP).
- With OOP, instead of thinking first about procedures, you think first about the things in your problem. The things are called objects.
1
*
1. With OOP, procedures are still important, but objects are even more important.
*
Object-Oriented Programming Overview
- An object is:
A set of related data which identifies the current state of the object.
+ a set of behaviors
- Example objects:
- Car object in a traffic-flow simulation:
- data = ?
- methods = ?
3
4
2
1
| human entities | physical objects | mathematical entities |
| employees | cars in a traffic-flow simulation | points on coordinate system |
| customers | Aircraft in an air-traffic control system | Complex numbers |
| students | Electrical components in a circuit-design program | time |
*
1. In Java, you implement an object's data with variables.
2. An object's behaviors refer to the activities associated with the object.
In Java, you implement an object's behaviors as methods.
3. Let's think about the first human entity example.
Suppose you're writing a program that keeps track of employee salaries:
You'd probably want to have employee objects, where an employee object's state consists of the employee's name and current salary.
And you'd probably want to define a method that adjusts an employee's salary.
4. Let's think about the first physical object example...
data = position, speed, ...
methods = start, stop, slowDown, ...
*
Object-Oriented Programming Overview
- Benefits of OOP:
- Programs are more understandable -
- Since people tend to think about problems in terms of objects, it's easier for people to understand a program that's split into objects.
- Fewer errors -
- Since objects provide encapsulation (isolation) for the data, it's harder for the data to get messed up.
1
2
*
1. How does an object provide this encapsulation?
An object's data can only be accessed by using one of the object's methods so it's hard for a programmer to accidentally mess up an object's data.
Note how the diagram illustrates encapsulation.
2. Returning to the employee-salaries program example, the only way to change an employee object's salary is to use its adjustSalary method.
Thus, if there's a bug relating to an employee's salary, the programmer immediately knows where to look for the problem - in the adjustSalary method or in one of the calls to the adjustSalary method.
��
methods�
data�
rest of program�
object�
*
Object-Oriented Programming Overview
- A class is a description for a set of objects.
- On the next slide, note the three computers on a conveyer belt in a manufacturing plant:
- The three computers represent objects, and the specifications document represents a class. The specifications document is a blueprint that describes the computers: it lists the computers' components and describes the computers' features.
- Think of an object as a physical example for a class's description. More formally, we say that an object is an instance of a class.
1
2
*
1. Having discussed objects, it's now time to talk about an intimately related entity – a class.
We'll start with a broad definition of a class, and we'll refine it later.
2. What is the relationship between a class and an object?
Is it a one-to-one relationship? No.
One class can have any number of objects associated with it.
A class can even have zero objects associated with it.
This should make sense if you think about the computer-manufacturing example.
Isn't it possible to have a blueprint for a computer but not yet have any computers manufactured from that blueprint?
*
Object-Oriented Programming Overview
*
( com puter objects Specifications for a com puter )
�
*
Object-Oriented Programming Overview
- A class is a description for a set of objects. The description consists of:
a list of variables
+ a list of methods
- Classes can define two types of variables – instance variables and class variables. And classes can define two types of methods – instance methods and class methods. Instance variables and instance methods are more common than class variables and class methods, and we'll focus on instance variables and instance methods in this chapter and the next several chapters.
1
2
*
1. We'll now present a more complete definition of a class.
2. Let's define instance variables and instance methods….
*
Object-Oriented Programming Overview
- A class's instance variables specify the type of data that an object can store.
- For example, if you have a class for computer objects, and the Computer class contains a hardDiskSize instance variable, then each computer object stores a value for the size of the computer's hard disk.
- A class's instance methods specify the behavior that an object can exhibit.
- For example, if you have a class for computer objects, and the Computer class contains a printSpecifications instance method, then each computer object can print a specifications report (the specifications report shows the computer's hard disk size, CPU speed, cost, etc.).
*
*
Object-Oriented Programming Overview
- Note the use of the term “instance” in “instance variable” and “instance method.” That reinforces the fact that instance variables and instance methods are associated with a particular object instance. For example, each computer object has its own value for the hardDiskSize instance variable.
- That contrasts with class variables and class methods, which you saw in Chapter 5. Class variables and class methods are associated with an entire class. For example, the Math class contains the PI class variable and the round class method. PI and round are associated with the entire Math class, not with a particular instance of the Math class. We'll cover class variables and class methods in more detail in Chapter 9.
1
*
1. It doesn't even make sense to have a "particular instance of the Math class."
*
UML Class Diagram
- UML:
- Stands for Unified Modeling Language.
- It's a diagrammatic methodology for describing classes, objects, and the relationships between them.
- It is widely accepted in the software industry as a standard for modeling OOP designs.
- Example:
- UML class diagram for a Mouse class:
1
2
3
4
5
6
| Mouse | | class name |
| age : int weight : double percentGrowthRate : double | | attributes / variables |
| setPercentGrowthRate(percentGrowthRate : double) grow() display() | | operations / methods |
*
1. Next, we'll put what you've learned into practice by implementing a complete OOP program.
The program will contain a Mouse class, and it will simulate the growth of two Mouse objects.
As is customary with OOP programs, we start the implementation process by describing the solution pictorially with a Unified Modeling Language (UML) class diagram.
2. Note that a UML class diagram box is divided into three parts - class name at the top, attributes in the middle, and operations at the bottom.
With Java programs, attributes equate to variables and operations equate to methods.
Henceforth, we'll use the Java terms, variables and methods, rather than the formal UML terms, attributes and operations.
Collectively, we refer to a class's variables and methods as the class's members.
Let's now describe each Mouse member….
3. The Mouse class has three instance variables – age, weight, and percentGrowthRate.
The age instance variable keeps track of how old a Mouse object is, in days.
The weight instance variable keeps track of a Mouse object's weight, in grams.
The percentGrowthRate instance variable is the percentage of its current weight that gets added to its weight each day.
If the percentGrowthRate is 10% and the mouse's weight is 10 grams, then the mouse gains 1 gram by the next day.
4. The Mouse class has three instance methods – setPercentGrowthRate, grow, and display.
The setPercentGrowthRate method assigns the passed-in argument (I circle the percentGrowthRate parameter in the diagram) to the percentGrowthRate instance variable (I circle the percentGrowthRate instance variable in the diagram).
The grow method simulates one day of weight gain for a mouse.
The display method prints a mouse's age and weight.
5. Some programmers use UML class diagrams as a means to document programs after they've already been written.
That's not how class diagrams were intended to be used.
I want you to use class diagrams as a first step in your solution implementation.
6. Now let's get a more detailed understanding of the Mouse class by looking at the Mouse class's source code….
*
First OOP Class
/************************************************************
* Mouse.java
* Dean & Dean
*
* This class models a mouse for a growth simulation program.
************************************************************/
public class Mouse
{
private int age = 0; // age of mouse in days
private double weight = 1.0; // weight of mouse in grams
private double percentGrowthRate; // % weight increase per day
//*********************************************************
// This method assigns the mouse's percent growth rate.
public void setPercentGrowthRate(double percentGrowthRate)
{
this.percentGrowthRate = percentGrowthRate;
} // end setPercentGrowthRate
instance variable declarations
To access instance variables, use this dot.
1
2
3
4
parameter
method body
*
1. Remember that a class is a description/blueprint.
On this slide, I don't show the creation of Mouse objects, I only show you the blueprint for a Mouse.
On an upcoming slide, I'll show you how to create Mouse objects.
2. Note the class's three instance variable declarations.
Instance variable declarations are very similar to variable declarations you've seen in the past:
The variable's type goes at the left of the variable, and you can optionally assign an initial value to the variable.
What's it called if you assign a value to a variable as part of a declaration?
That's called an initialization.
3. Note the initializations for age and weight.
We initialize age to 0 because newborn mice are zero days old.
We initialize weight to 1 because newborn mice weigh approximately 1 gram.
4. The primary difference between instance variable declarations and variable declarations you've seen in the past is the private access modifier.
I go to the private and public access slide, read the entire thing, and return to the next slide.
*
First OOP Class (hidden)
/************************************************************
* Mouse.java
* Dean & Dean
*
* This class models a mouse for a growth simulation program.
************************************************************/
public class Mouse
{
private int age = 0; // age of mouse in days
private double weight = 1.0; // weight of mouse in grams
private double percentGrowthRate; // % weight increase per day
//*********************************************************
// This method assigns the mouse's percent growth rate.
public void setPercentGrowthRate(double percentGrowthRate)
{
this.percentGrowthRate = percentGrowthRate;
} // end setPercentGrowthRate
instance variable declarations
1
2
3
4
To access instance variables, use this dot.
parameter
method body
5
*
1. Note that we initialize age and weight to 0 and 1.0, respectively, but we don't initialize percentGrowthRate.
That's because we're comfortable with age = 0 and weight = 1.0 for all newborn Mouse objects, but we're not comfortable with a predefined initial value for percentGrowthRate.
Presumably, we'll want to use different percentGrowthRate values for different Mouse objects (mice in a doughnut-eating study might have higher percentGrowthRate values than mice in a cigarette-smoking study).
2. With no initialization for the percentGrowthRate instance variable, how do we set the growth rate for a Mouse object?
Have the Mouse object call the setPercentGrowthRate method with a growth rate value as an argument.
For example, here's how a Mouse object can set its growth rate to 10 (percent) by calling setPercentGrowthRate:
setPercentGrowthRate(10);
3. As you may recall from Chapter 5, a method call's parenthetical values are referred to as arguments.
Thus, in this example, 10 is an argument.
The 10 gets passed into the percentGrowthRate variable in setPercentGrowthRate's heading.
A method heading's parenthetical variables are referred to as parameters.
In this example, percentGrowthRate is a parameter.
4. Within the setPercentGrowthRate method body, the percentGrowthRate parameter is assigned into the percentGrowthRate instance variable.
To access an instance variable, preface the instance variable with this dot.
[Actually, you don't always have to use this when referring to an instance variable, but in the interest of getting you used to this (which is very important), we'll use this in the next couple chapters for all instance variable accesses.
We'll explain the meaning of this dot later on in this chapter.
In Chapter 8, we'll explain when it's acceptable to omit this when accessing class members.]
5. In this example, the parameter and the instance variable have the same name.
That's fairly common, and there's no problem distinguishing between the two variables because the instance variable uses this dot and the parameter does not.
*
First OOP Class
//*********************************************************
// This method simulates one day of growth for the mouse.
public void grow()
{
this.weight += (.01 * this.percentGrowthRate * this.weight);
this.age++;
} // end grow
//*********************************************************
// This method prints the mouses's age and weight.
public void display()
{
System.out.printf(
"Age = %d, weight = %.3f\n", this.age, this.weight);
} // end display
} // end class Mouse
1
2
3
*
1. What does the grow method do?
It simulates one day of weight gain for a mouse.
The weight-gain formula adds a certain percentage of the current weight to the current weight.
That means that the Mouse will continue to grow every day of its life.
That's not a very accurate portrayal of normal weight gain.
I've intentionally kept the weight-gain formula simple in order to avoid getting bogged down in complicated math.
2. What does the display method do?
It prints a mouse's age and weight.
3. Notice the descriptions above each method ...
Style requirement - above each method, you must have:
blank line, line of stars, blank line, description, blank line !!!
*
private and public Access
- private and public are access modifiers. When you apply an access modifier to a member of a class, you determine how easy it is for the member to be accessed. Accessing a member refers to either reading the member's value or modifying it.
- If you declare a member to be private, then the member can be accessed only from within the member's class. Instance variables are almost always declared with the private modifier because you almost always want an object's data to be hidden. Making the data hard to access is what encapsulation is all about and it's one of the cornerstones of OOP.
- If you declare a member to be public, then the member can be accessed from anywhere (from within the member's class, and also from outside the member's class). Methods are usually declared with the public modifier because you normally want to be able to call them from anywhere.
1
2
3
4
*
1. See the private access modifier in the Mouse class's instance variable declarations.
2. With instance variables being private, how can the outside world ever access an object's instance variables?
It can't do it directly, but it can do it with the help of the object's public methods….
3. The word public should make sense if you think about public members being available to the "general public."
4. Return to the hidden code slide and note the public access modifiers in the Mouse class's method declarations.
*
Driver Class
/****************************************
* MouseDriver.java
* Dean & Dean
*
* This is a driver for the Mouse class.
****************************************/
import java.util.Scanner;
public class MouseDriver
{
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
double growthRate;
Mouse gus = new Mouse();
Mouse jaq = new Mouse();
1
4
3
2
*
1. This class is in charge of using the Mouse class to instantiate Mouse objects.
Since this class runs or "drives" the other class, we say this class is a driver class.
Note the class's name - MouseDriver.
"Driver" is not a reserved word in Java.
It's a common computer term that applies to a piece of software that tests or drives something else.
For example, a printer driver is a program that is in charge of running a printer.
2. Can you find the code that creates a new Mouse object?
new Mouse()
3. With primitive variables, you just declare them and that's good enough.
See the growthRate variable; it's a primitive variable because it holds a primitive value, a double.
But with objects, you have to declare them and also create them with the new operator!
Read through the first “Reference Variables and Instantiation” slide and then return here.
4. After you create a Mouse object, how do you read the object's data or modify the object's data?
You must call a method!
Where are the method calls?
I go to the next slide and show the gus and jaq method calls.
*
Driver Class
System.out.print("Enter growth rate as a percentage: ");
growthRate = stdIn.nextDouble();
gus.setPercentGrowthRate(growthRate);
jaq.setPercentGrowthRate(growthRate);
gus.grow();
jaq.grow();
gus.grow();
gus.display();
jaq.display();
} // end main
} // end class MouseDriver
1
2
*
1. I note the gus.setPercentGrowth(growthRate) method call.
When we instantiated the gus object, we initialized the age and weight instance variables to 0 and 1, but we did not initialize the setPercentGrowthRate instance variable (I verify that in the Mouse class's slide).
We assign a value to the percentGrowthRate instance variable by calling the setPercentGrowthRate method.
2. Go to the slide that's two away ...
*
Reference Variables and Instantiation
- To declare a reference variable (which holds the address in memory where an object is stored):
<class-name> <reference-variable>;
- To instantiate/create an object and assign its address into a reference variable:
<reference-variable> = new <class-name>()
- Example code:
Mouse gus;
gus = new Mouse();
- This single line is equivalent to the above two lines:
Mouse gus = new Mouse();
1
2
3
4
declaration
instantiation
initialization
*
1. I've been a little loose with the terminology.
I said that you declare an object; actually, you declare a reference variable, which holds the address of the object.
2. See the example declaration below.
3. See the example instantiation below.
4. Return to the program slide and show this initialization line.
*
Calling a Method
- After instantiating an object and assigning its address into a reference variable, call/invoke an instance method using this syntax:
<reference-variable>.<method-name>(<comma-separated-arguments>);
- Here are three example instance method calls from the MouseDriver class:
gus.setPercentGrowthRate(growthRate);
gus.grow();
gus.display();
1
2
3
4
*
1. Note how the syntax is used in the three example method calls.
The first method call has one argument and the next two method calls have zero arguments.
If we had a method with two parameters, we'd call it with two arguments separated by a comma.
2. Now that we've looked at the Mouse program's source code, let's compile it and run it.
I demo the Mouse program by compiling and running Mouse.java and MouseDriver.java with a growth rate input of 10.
In the demo, I point out that we must put the two files in the same directory.
[The output is:
Enter growth rate as a percentage: 10
Age = 2, weight = 1.210
Age = 1, weight = 1.100]
3. Why are gus's age and weight greater than jaq's age and weight?
I go back to the driver slide and show how gus.grow was called twice for two days of growth and jaq.grow was called only once.
Why is jaq's weight 1.1?
Because 10% of the initial weight, 1, is .1.
And 1 + .1 is 1.1.
Why is gus's weight 1.21?
Because on the second day of growth, 10% of 1.1 is .11.
And 1.1 + .11 is 1.21.
I show the math on the board.
4. Let's now cover some more OOP terminology and concepts….
*
- A calling object is the object that appears at the left of the dot in a call to an instance method.
- Can you find the calling objects below?
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
double growthRate;
Mouse gus = new Mouse();
System.out.print("Enter growth rate as a percentage: ");
growthRate = stdIn.nextDouble();
gus.setPercentGrowthRate(growthRate);
gus.grow();
gus.display();
} // end main
Calling Object
1
2
3
*
1. I circle gus in the setPercentGrowthRate, grow, and display method calls.
2. Can anyone see any other calling objects?
I circle stdIn in the nextDouble method call.
[3. What's wrong with this sentence?
"The calling object is the object that appears at the left of the dot …"
An object never appears at the left of the dot; a reference variable appears at the left of the dot.
This would be more precise:
The calling object is the object who's reference variable appears at the left of the dot.
I used the original wording because I think that wording is clearer.
Aside:
Most people in industry don't use the term reference variable at all.
Instead, they just use the term "object" instead of reference variable.
Despite industry's use of the term object as a substitute for reference variable, we'll try to avoid that temptation (except when referring to a calling object) because the truth is that an object and a reference variable are indeed two separate entities - an object is a grouping of data stored internally by the operating system and a reference variable is a variable that holds an address.
Understanding the difference between an object and a reference variable will help you to understand the behavior of certain Java code.]
*
- The this reference:
- When used in conjunction with a dot and an instance variable, "this" is referred to as the this reference. Note this example from the Mouse class's grow method:
this.weight += (.01 * this.percentGrowthRate * this.weight);
- The this reference is used inside of a method to refer to the object that called the method; in other words, the this reference refers to the calling object.
So what’s so great about having a special name for the calling object inside of a method? Why not just use the original name, gus or jaq, inside the method?
- Because if the original name were to be used, then the method would only work for the one specified calling object. By using a generic name (this) for the calling object, then the method is more general purpose. For example, by using this, the grow method is able to specify weight gain for any Mouse object that calls it. If gus calls grow, then gus’s weight is updated, whereas if jaq calls grow, then jaq’s weight is updated.
The this Reference
1
2
*
1. Let me repeat that since it's so important – the this reference refers to the calling object!
2. Why did the Java designers choose the word "this" as the generic term for the calling object?
Like much of the Java language, the word this is borrowed from Java's parent language, C++.
For both languages, this is used inside of a method to refer to the object that called the method.
*
- A variable's default value is the value that the variable gets if there's no explicit initialization.
- Mouse class's instance variable declarations:
private int age = 0;
private double weight = 1.0;
private double percentGrowthRate;
- Here are the default values for instance variables:
- integer types get 0
- floating point types get 0.0
- boolean types get false
- reference types get null
- Note that a String is a reference type so it gets null by default.
Default Values
explicit initializations
percentGrowthRate gets default value of 0.0
1
2
3
4
5
6
*
1. See the Mouse class's variable declarations below.
age and weight are explicitly initialized so they do not get default values.
percentGrowthRate is not explicitly initialized so it does get a default value.
2. What are the integer types?
I write down on the board:
int, long <-- 0
[Actually, byte, short, and char are also considered to be integer types and they also get 0 by default (char <-- \u0000)]
3. What are the floating-point types?
I write on the board:
float, double <-- 0.0
4. What type of thing does a reference variable normally hold?
The address of an object!
We say that the reference variable “points” to an object (via the address).
Having a null value in a reference variable means that the reference variable doesn't point to anything.
5. It's easy to think of String as a built-in primitive type, just like int and float, because we use it so often.
But you should know that it's not a primitive type; it's actually a reference type and that means it's a class.
You can tell it's a reference type and a class because the “S" in String is capitalized.
6. What would age's default value be if there was no initialization?
0
So the program would work the same if you omitted age's initialization.
I prefer to show age's initialization rather than omit it because it makes the program more self documenting.
It makes it clear that a mouse’s age starts at 0.
*
- A variable's persistence is how long a variable's value survives before it's wiped out.
- Instance variables persist for the duration of a particular object. Thus, if an object makes two method calls, the second called method does not reset the calling object's instance variables to their initialized values. Instead, the object's instance variables retain their values from one method call to the next.
Variable Persistence
1
2
*
1. I go back to the Mouse class and note how weight is updated in grow.
It's important for weight's value to persist after the method is done.
Why?
So that if a Mouse object calls grow a second time, the weight from the first simulation is saved and the weight from the second simulation can add to it.
2. After variable persistence, the next section in the book shows you how to trace an OOP program.
Read it on your own, but I’m skipping it in class so we can focus on other concepts.
*
OOP Tracing Procedure (hidden)
- OOP tracing procedure:
Provide a trace setup.
Starting with the first line in main, trace the program in the
normal fashion, but follow these additional rules:
When starting a method:
- Under the method's local variable headings, write local variable initial values. Use a question mark for local variables that are uninitialized.
When an object is instantiated:
- Under the object's class-name heading, provide a column heading named "obj#", where # is a unique number.
- Under the obj# heading, provide an underlined column heading for each of the object's instance variables.
- Under the instance variable headings, write instance variable initial values.
1
3
4
5
2
*
[1. Tracing an OOP program is needed if tracing exercises are assigned, but otherwise, OOP tracing can be skipped.
To save time and to be able to cover other, more important, material, I skip the OOP tracing slides.]
2. To ensure that you fully understand OOP semantics, we'll do a trace of the Mouse program.
This slide and the next 2 slides present step-by-step instructions on how to set up and execute a trace for an OOP program.
The trace procedure is pretty long and complicated, so reading it straight would probably be confusing.
Probably the best way to learn it is to read and apply the steps one at a time.
3. I go to the trace setup slide and read the procedure and relate the steps to the provided setup.
I copy the trace setup onto the side board so I can view the program and the trace simultaneously.
I then return here.
4. I note main's local variables on the program slide and then on the trace, I write down ?'s under growthRate, gus, and jaq.
5. I go to the program slide and write down:
Under Mouse: obj1
Under obj1: age, weight, percentGrowthRate
Under age: 0 because age is initialized to 0
Under weight: 1.0 because weight is initialized to 0
Under percentGrowthRate: 0.0 because percentGrowthRate's default value is 0.0
*
OOP Tracing Procedure (hidden)
When there's an assignment into a reference variable:
- Rather than writing the actual address that's assigned into the reference variable (and there's no way to know the actual address), write obj# under the reference variable's column heading, where obj# matches up with the associated obj# in the object portion of the trace.
- When there's a method call:
- Under the called method's this column heading, write the calling object's obj#.
- If the method call contains an argument, write the argument's value under the called method's associated parameter.
- On your program listing, jump to the first statement in the called method.
1
2
3
4
*
1. I go to the program slide and write down:
Under gus: obj1
2. I go to the program slide and write down:
Under setPercentGrowthRate's this heading: obj1
3. I go to the program slide and write down:
Under setPercentGrowthRate's rate heading: 10 (from the growthRate parameter)
4. I go to the program slide and jump to the setPercentGrowthRate method.
*
OOP Tracing Procedure (hidden)
When there's a this reference:
- Find the obj# under the current method's this column heading.
- Go to the found obj#'s heading and read or update the obj#’s value accordingly.
When finishing a method:
- In the method's section, draw a horizontal line under the last row that contains a transaction generated by that method. This indicates that the method has finished and the values in the method's local variables are wiped out.
- On your program listing, jump to the code that immediately follows the method call that called the just-finished method.
1
2
3
*
1. I go to the program slide, find setPercentGrowthRate's obj1 value, and go to the Mouse heading's obj1 heading.
Under obj1's percentGrowthRate heading: 10
2. On the trace, I draw a horizontal line under the setGrowthRate method's this value.
3. I continue with the trace by looking at the program slide and referring back to the trace procedure if there are questions.
*
Tracing the Mouse Program (hidden)
import java.util.Scanner;
public class MouseDriver2
{
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
double growthRate;
Mouse gus, jaq;
System.out.print("Enter % growth rate: ");
growthRate = stdIn.nextDouble();
gus = new Mouse();
gus.setPercentGrowthRate(growthRate);
gus.grow();
gus.display();
jaq = new Mouse();
jaq.grow();
jaq.display();
} // end main
} // end class MouseDriver2
There's a logic error here. We “accidentally” forget to initialize the growth rate in jaq!
1
*
1. When we get to this point in the trace, I read the green box.
What do you think will be printed by jaq.display?
I remember their answers so we can compare them to the trace results.
*
Tracing the Mouse Program (hidden)
public class Mouse
{
private int age = 0; // age of mouse in days
private double weight = 1.0; // weight of mouse in grams
private double percentGrowthRate; // % weight increase per day
public void setPercentGrowthRate(double percentGrowthRate)
{
this.percentGrowthRate = percentGrowthRate;
} // end setPercentGrowthRate
public void grow()
{
this.weight +=
(.01 * this.percentGrowthRate * this.weight);
this.age++;
} // end grow
public void display()
{
System.out.printf("Age = %d, weight = %.3f\n",
this.age, this.weight);
} // end display
} // end class Mouse
*
*
Tracing the Mouse Program (hidden)
Use this trace setup for the Mouse program:
Trace setup procedure:
- Provide a heading for each class.
- Under each class-name heading, provide a heading for each of the class's methods.
- Under each method-name heading:
- Provide a heading named this for the method's calling object. Exception: Since main doesn't have a calling object, don't write this under main.
- Provide a heading for each of the method's parameters and local variables.
1
2
3
*
1. What's a parameter?
A parameter is a variable declared within a method heading's parentheses.
It receives an argument's passed-in value.
What's the parameter in the setup?
Note percentGrowthRate in the trace setup and then find the parameter declaration in the program.
2. I'll discuss local variables in detail later, but for now, just realize that growthRate, gus, and jaq are considered to be local variables because they're declared and used within one particular method (the main method).
That's different from the age, weight, and percentGrowthRate instance variables, which are declared outside of all methods, at the top of the class.
Note the local variables in the trace setup and then find them in the program.
3. Note the vacant area under the Mouse heading.
We'll fill in more headings there as we do the trace.
Sheet1
| input | |||||||||||
| 10 | |||||||||||
| MouseDriver2 | Mouse | ||||||||||
| main | set%grRate | grow | display | ||||||||
| grRate | gus | jaq | this | (%grRate) | this | this | output |
Sheet2
Sheet3
*
Tracing the Mouse Program (hidden)
1
2
3
*
1. The below notes are supposed to go with the next slide.
Unfortunately, when I print the next slide using notes mode, it's messed up.
The solution is to print the next slide in slide mode.
But then the notes don't appear; thus, they're here.
2. Here's the finished trace.
Note the line numbers.
As you know, the book shows a long form of the trace, with line numbers.
The line numbers help when you're reading from a book, but they're not all that helpful when you're tracing in a lecture environment.
For homework traces, it's OK to include or omit the line numbers.
3. Let's run the Mouse program on the computer to verify that we calculated the output correctly in our trace.
I demo the Mouse program, using the Mouse.java and MouseDriver2.java files.
*
Tracing the Mouse Program (hidden)
*
Sheet1
| input | |||||||||||||
| 10 | |||||||||||||
| MouseDriver2 | Mouse | ||||||||||||
| main | set%grRate | grow | display | obj1 | obj2 | ||||||||
| grRate | gus | jaq | this | (%grRate) | this | this | age | weight | %grRate | age | weight | %grRate | output |
| ? | |||||||||||||
| ? | |||||||||||||
| ? | |||||||||||||
| Enter % growth rate: | |||||||||||||
| 10.0 | |||||||||||||
| 0 | |||||||||||||
| 1.000 | |||||||||||||
| 0.0 | |||||||||||||
| obj1 | |||||||||||||
| obj1 | 10.0 | ||||||||||||
| 10.0 | |||||||||||||
| obj1 | |||||||||||||
| 1.100 | |||||||||||||
| 1 | |||||||||||||
| obj1 | |||||||||||||
| Age = 1, weight = 1.100 | |||||||||||||
| 0 | |||||||||||||
| 1.000 | |||||||||||||
| 0.0 | |||||||||||||
| obj2 | |||||||||||||
| obj2 | |||||||||||||
| 1.000 | |||||||||||||
| 1 | |||||||||||||
| obj2 | |||||||||||||
| Age = 1, weight = 1.000 |
Sheet2
Sheet3
*
UML Class Diagram for Next Version of the Mouse Program
Member accessibility:
Use "-" for private access and "+" for public access.
Method notes:
We use them here to specify local variables.
1
2
3
4
5
Initialization values:
Use "= <value>".
6
7
*
1. Let's now look at the next version of the mouse program.
The Mouse2 class has the same three instance variables as the original Mouse class -- age, weight, and percentGrowthRate.
And the same setPercentGrowthRate method.
But the getAge and getWeight methods are new and the grow method is improved.
2. The getAge method retrieves a mouse's age.
Remember that age is private, so the only way for the outside world to read a mouse object's age is to use a public method - the getAge method!
3. The getWeight method retrieves a mouse's weight.
4. The grow method simulates a mouse's growth for a specified number of days.
Note the days parameter; the number of days is passed into the days parameter and that's how the method knows how many days to simulate.
Remember what the grow method did in the previous Mouse program?
It simulated one day of growth.
This version of grow is more flexible because it handles multiple days of growth.
5. Now let's focus on some UML class diagram syntax.
Note the "= 0" and "=1.0" for the age and weight instance variables in the UML class diagram.
In our previous mouse program, we didn't bother to show the initializations in the class diagram.
UML is flexible: you're allowed to omit or include items, as you see fit.
6. Note the –'s for the instance variables and the +'s for the methods and read the bottom green box.
The -'s indicate private, and the +'s indicate public.
7. Note the notes.
Notes are depicted by rectangles with the "folded-in" top-right corners.
Why folded-in corners?
They are supposed to give the impression of a piece of paper with its corner folded and a piece of paper is indicative of a "note."
Including notes for your UML class diagram methods is purely optional.
We'll sometimes use them and we'll sometimes refrain from using them.
For this example, we use them because we want to demonstrate how local variables can be represented in a class diagram.
Local variables? What in the world are they? …
*
Local Variables
- A local variable is a variable that's declared inside a method. That's different from an instance variable, which is declared at the top of a class, outside all the methods.
- A local variable is called "local" because it can be used only inside of the method in which it is declared – it is completely local to the method.
- In the Mouse2Driver class on the next slide, note how the main method has three local variables - stdIn , mickey, and days. And in the Mouse2 class, note how the grow method has one local variable - i.
1
*
1. I first go back to the UML diagram and show the local variables, then I go forward to the program slides and identify the local variables.
*
Mouse2Driver Class
import java.util.Scanner;
public class Mouse2Driver
{
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
Mouse2 mickey = new Mouse2();
int days;
mickey.setPercentGrowthRate(10);
System.out.print("Enter number of days to grow: ");
days = stdIn.nextInt();
mickey.grow(days);
System.out.printf("Age = %d, weight = %.3f\n",
mickey.getAge(), mickey.getWeight());
} // end main
} // end class Mouse2Driver
local variables
1
2
3
4
5
*
1. Note the three local variables.
As you perhaps now realize, all the variables we defined in prior chapters were local variables.
They were all declared within main methods so they were accessible only from within the main method.
We didn't bother to explain the term “local variable” until now because there were no other methods besides main, so the idea of a variable being local to main wouldn't have made much sense.
2. I walk through this code without bothering to step into the setPercentGrowthRate, grow, getAge, and getWeight method bodies.
I just explain what the methods do.
3. The setPercentGrowthRate method is unchanged from the original mouse program.
But this time we're passing in a constant, 10, instead of a variable.
Normally, you'll use variables for your arguments.
But I wanted to show you that it is legal to use constants as well.
4. We prompt the user for the number of days of simulated growth for mickey, and we pass in the days value to the grow method.
We'll look at the grow method shortly.
5. We print mickey's age and weight by embedding getAge and getWeight method calls within a printf statement.
We'll look at the getAge and getWeight method definitions later on.
*
Mouse2 Class
import java.util.Scanner;
public class Mouse2
{
private int age = 0; // age in days
private double weight = 1.0; // weight in grams
private double percentGrowthRate; // % daily weight gain
//********************************************************
public void setPercentGrowthRate(double percentGrowthRate)
{
this.percentGrowthRate = percentGrowthRate;
} // end setPercentGrowthRate
//********************************************************
public int getAge()
{
return this.age;
} // end getAge
1
2
*
1. Any local variables here?
No! age, weight, and percentGrowthRate are instance variables, not local variables, because they're declared outside of all the methods, at the top of the class.
2. I'll explain the return statement formally later on, but the basic idea is that getAge returns the age of the mouse calling object to the place where getAge was called.
*
Mouse2 Class
//********************************************************
public double getWeight()
{
return this.weight;
} // end getWeight
//********************************************************
public void grow(int days)
{
for (int i=0; i<days; i++)
{
this.weight +=
(.01 * this.percentGrowthRate * this.weight);
}
this.age += days;
} // end grow
} // end class Mouse2
local variable
1
2
3
*
1. Note the i local variable.
Since i is declared within the for loop heading, it's local to the for loop.
So you can read and update i only within the for loop.
If you try to access i outside of the for loop, you'll get a compilation error.
2. This grow method is similar to the previous mouse program's grow method.
To simulate a mouse's growth, we're still increasing the mouse's weight and age instance variables.
But this time, we're simulating multiple days of growth rather than just one day.
So we use a for loop to simulate multiple days.
The days parameter determines how many times the loop will repeat.
3. Note that we add days to age, so if days is 3, then age increases by 3 days.
*
- The default value for a local variable is garbage.
- Garbage means that the variable's value is unknown - it's whatever just happens to be in memory at the time that the variable is created.
- When doing a trace, use a "?" to indicate garbage.
- If a program attempts to access a variable that contains garbage, the compiler generates an error. For example, what happens if you remove =0 from the grow method's for loop heading? Will the following code work?
for (int i; i<days; i++)
{
this.weight +=
(0.01 * this.percentGrowthRate * this.weight);
}
- Since i is no longer assigned zero, i contains garbage when the i<days condition is tested. That causes the compiler to generate this error message:
variable i might not have been initialized
Local Variable Default Values
2
1
*
1. Previously we covered the default values for instance variables:
0's for numeric instance variables, null for reference instance variables.
Now let's cover the default values for local variables.
2. You can see that in the book, in the section that traces the Mouse program.
*
- Local variables persist only for the duration of the method (or for loop) in which the local variable is defined. The next time the method (or for loop) is executed, the local variable's value resets to its initial value.
Local Variable Persistence
2
1
*
1. Previously we covered persistence for instance variables:
An instance variable persists for as long as the instance variable's object remains alive.
2. I go back to the Mouse2 class slide and show grow's i variable and mention that each time grow is called, the i gets reset to its initial value of 0.
And that's OK because there's no need to remember the old value of i.
*
return Statement
- The return statement allows you to pass a value from a method back to the place where the method was called. Note the following example.
- From the Mouse2 class:
public int getAge()
{
return this.age;
} // end getAge
- From the Mouse2Driver class:
System.out.printf("Age = %d, weight = %.3f\n",
mickey.getAge(), mickey.getWeight());
- Note the return type in the above example. It has to match the type of the value that's being returned in the return statement.
return type
return statement
method call
2
1
3
4
*
1. I go back to the Mouse2 program and verify that these code fragments came from there.
2. The getAge method returns age to Mouse2Driver's printf statement.
To perform a mental trace, imagine that the method call is overlaid by the returned value.
So if mickey's age is 2, then 2 is returned and 2 overlays the method call.
On the board, I cross off the method call and write 2 on top of it.
After the 2 is returned, the 2 gets printed as part of the printf statement.
3. The return statement returns this.age.
I verify on the Mouse2 slide that the age instance variable is declared to be an int, and that matches getAge's return type, so all is well.
4. Note that you can have an expression following the word return; you aren't limited to just having a simple variable.
But the expression must evaluate to a value with the return type's type.
For example, would return this.age + 1 be legal?
Yes, because this.age + 1 evaluates to an int type, and that matches getAge's return type.
*
void Return Type
- As shown in the below grow method from the Mouse2 class, if a method does not return a value, then the method must specify void for its return type.
public void grow(int days)
{
for (int i=0; i<days; i++)
{
this.weight +=
(0.01 * this.percentGrowthRate * this.weight);
}
this.age += days;
} // end grow
1
*
1. Note the void return type and note that there's no return statement.
*
Empty return Statement
- For methods with a void return type, it's legal to have an empty return statement. The empty return statement looks like this:
return;
- The empty return statement does what you'd expect:
- It terminates the current method and causes control to be passed to the calling module at the point that immediately follows the method call that called the current method.
*
*
Empty return Statement
- Suppose you'd like to model mouse growth only up through mouse adolescence. This grow method does that by stopping a mouse's growth after 100 days:
public void grow(int days)
{
int endAge;
endAge = this.age + days;
while (this.age < endAge)
{
if (this.age >= 100)
{
return;
}
this.weight +=
.01 * this.percentGrowthRate * this.weight;
this.age++;
} // end while
} // end grow
empty return statement
1
2
3
*
1. Here's an example that uses an empty return statement.
2. I walk through the entire code and note:
a) We find the endAge value and within the loop, we increment this.age until it gets up to endAge.
b) To cut off the aging process at 100 days, we check the age inside the loop and return if the age is at the 100 value cutoff.
c) Note the empty return statement. Since nothing is returned, the method heading must specify void for its return type.
3. Note that it's illegal to have an empty return statement and a non-empty return statement in the same method.
That should make sense when you realize that empty and non-empty return statements require different return types (void for an empty return statement and some other type for a non-empty return statement).
And you can't specify more than one return type in a method's heading.
*
Empty return Statement
- Code that uses an empty return statement(s) can always be replaced by code that does not use the empty return statement(s). For example, here's a return-less version of the previous grow method:
public void grow(int days)
{
int endAge;
endAge = this.age + days;
if (endAge > 100)
{
endAge = 100;
}
while (this.age < endAge)
{
this.weight +=
(.01 * this.percentGrowthRate * this.weight);
this.age++;
} // end while
} // end grow
1
2
*
1. The empty return statement is a helpful statement in that it provides an easy way to quickly exit from a method.
However, it does not provide unique functionality.
2. I show on this slide and the previous slide how I added the if statement above the loop and removed the if and return statements inside the loop.
*
Empty return Statement
- Software engineering observation:
- Real-world programmers are often asked to maintain (fix and improve) other people's code. In doing that, they oftentimes find themselves having to examine the loops and, even more specifically, the loop termination conditions in the program they're working on. Therefore, it's important that the loop termination conditions are clear. Normally, loop termination conditions appear in standard places: while loop heading, do loop closing, for loop heading's condition part. However, in using a return statement inside a loop, the return statement introduces a loop termination condition that's not in one of the standard places. For example, in the grow method two slides ago, the return statement is "hidden" inside an if statement that's embedded in a while loop.
- In the interest of maintainability, you should use restraint when considering the use of a return statement inside of a loop. Based on the context, if inserting a return statement(s) inside a loop improves clarity, then feel free to insert. However, if it simply makes the coding chores easier and it does not add clarity, then don't insert.
1
*
1. So for this grow method, which implementation is better?
I prefer the second implementation for maintainability reasons.
However, since the loop is so short and straightforward, it doesn't really make much of a difference here.
*
Argument Passing
- What is the output for the following Mouse3Driver and Mouse3 classes?
public class Mouse3Driver
{
public static void main(String[] args)
{
Mouse3 minnie = new Mouse3();
int days = 365;
minnie.grow(days);
System.out.println("# of days aged = " + days);
} // end main
} // end class Mouse3Driver
1
2
*
1. Here's another version of the mouse program.
As you can see, the Mouse3Driver class calls the grow method with a days value of 365 and then prints the days variable.
2. Let's examine what happens to days in the Mouse3 class….
*
Argument Passing
public class Mouse3
{
private int age = 0; // age in days
private double weight = 1.0; // weight in grams
private double percentGrowthRate = 10; // % daily weight gain
//********************************************************
public void grow(int days)
{
this.age += days;
while (days > 0)
{
this.weight +=
(.01 * this.percentGrowthRate * this.weight);
days--;
}
} // end grow
} // end class Mouse3
1
*
1. As you can see, the days parameter decrements down to zero.
So what happens to the days variable in the main method in Mouse3Driver? What do you think the calling module on the previous slide prints?
I show the Mouse3Driver class.
# of aged days = 0 is a good guess, but it's wrong.
The actual answer is “# of aged days = 365.”
Why? Next slide….
*
Argument Passing
- Java uses the pass-by-value mechanism to pass arguments to methods.
- Pass-by-value means that the JVM passes a copy of the argument's value (not the argument itself) to the parameter.
- Thus, if the parameter's value changes within the method, the argument in the calling module is unaffected.
- For example, in the previous two program slides, even though the days value within the grow method changes, the main method's days value is unaffected.
*
*
Argument Passing
- An argument and its associated parameter often use the same name. For example, we use days for the argument in Mouse3Driver's grow method call, and we also use days for the parameter in Mouse3's grow method heading.
- But be aware that an argument and its associated parameter don't have to use the same name. The only requirement is that an argument and its associated parameter are the same type.
- For example, if num is an int variable, then this method call successfully passes num's value into the days parameter:
minnnie.grow(num);
- As another example, since 365 is an int value, the following method call successfully passes 365 into the days parameter:
minnie.grow(365);
1
2
*
1. I return to the Mouse3 class and verify that grow's parameter is named days.
2. As shown earlier (in the original MouseDriver class), it's legal to use a constant value for an argument.
If you use a constant for an argument, make sure that the constant is the same type as the parameter.
*
Specialized Methods
- Accessor methods -
- They simply get/access the value of an instance variable.
- Example:
public int getAge()
{
return this.age;
}
- Mutator methods -
- They simply set/mutate the value of an instance variable.
- Example:
public void setPercentGrowthRate(double percentGrowthRate)
{
this.percentGrowthRate = percentGrowthRate;
} // end setPercentGrowthRate
2
1
3
5
4
6
*
1. Let's discuss some of the common types of specialized methods.
This won't be new syntax - I'm just introducing new terms.
2. You've already seen this getAge accessor method in the Mouse class.
It retrieves a Mouse's age.
3. Accessor methods should be named with a "get" prefix.
That's why accessor methods are often called "get" methods.
4. What's the origin of the term "mutator"?
“Mutate" is a form of mutation, which means a change in something.
And mutator methods are in charge of changing/setting an instance variable.
5. Mutator methods should be named with a "set" prefix.
That's why mutator methods are often called "set" methods.
In my opinion, "set method" is a better name than "mutator method," but you should follow the standards, not my personal opinions.
6. You've already seen this setPercentGrowthRate mutator method in the Mouse class.
It assigns a value to a Mouse's percentGrowthRate instance variable.
*
Specialized Methods
- boolean methods -
- They check the truth or falsity of some condition.
- They always return a boolean value.
- They should normally start with "is".
- For example, here's an isAdolescent method that determines whether a Mouse object's age is ≤ 100:
public boolean isAdolescent()
{
if (this.age <= 100)
{
return true;
}
else
{
return false;
}
} // end isAdolescent
- Here's how the isAdolescent method might be used in main:
Mouse pinky = new Mouse();
...
if (pinky.isAdolescent() == false)
{
System.out.println(
"The Mouse's growth is no longer" +
" being simulated - too old.");
}
2
1
3
*
1. How can this code be shortened?
I coax the students into replacing the method body with this:
return this.age <= 100;
2. How does it work?
If age ≤ 100, we want the method to return true in order to indicate adolescence.
If age is 50, what's returned?
true – so it works!
3. How can this code be shortened?
I coax the students into replacing the condition with this:
if (!pinky.isAdolescent())
*
Chapter 6 - Quiz Questions
Which of the following is a data item in a class?
instance variable
method
public modifier
- Given this method call. What is the thing called that's inside the parentheses?
car.assignSpeed(speed);
- an argument
- a passenger
- a cargo
- What word is used to create an object?
- new
- create
- allocate
*
1. a
2. a
3. a
*
Chapter 6 - Quiz Questions
What's the formal name for a method like setAge that sets the value of an instance variable?
accessor method
mutator method
protracted method
*
4. b
methods
data
rest of program
object
computer objects
Specifications for a computer
input
10
growdisplay
grRategusjaqthis(%grRate)thisthis output
MouseDriver2 Mouse
set%grRatemain
input
10
growdisplay
grRategusjaqthis(%grRate)thisthisageweight%grRateageweight%grRateoutput
?
?
?
Enter % growth rate:
10.0
0
1.000
0.0
obj1
obj110.0
10.0
obj1
1.100
1
obj1
Age = 1, weight = 1.100
0
1.000
0.0
obj2
obj2
1.000
1
obj2
Age = 1, weight = 1.000
MouseDriver2 Mouse
obj2set%grRatemain obj1