Unit 4: Object-Oriented Programming: Basics
Topics covered in this unit
After completing this unit you will:
- have a working understanding of objects, classes, attributes, and methods
- know what "overloading" is
- know the syntax for constructing an object of a given class
- be able to write a tester/runner that constructs and uses a given class
- know how to distinguish between an accessor and mutator method
- know how to use the Java API to look up classes and their methods
- be able to define and give examples of encapsulation
- be able to define and give examples of abstraction
- be able to define and give examples of the public interface for a class
- be able to write an implementation of a class, given its public interface
- be able to write JavaDoc comments for a class
4.0. Overview
This unit covers develops the concept of "objects" and "object-oriented programming," and examines how one goes about implementing a class based on its programming interface. If you don't know what an object, a class, or a method is yet, you will very soon.Let's get started.
- An Introduction to Objects
- Objects and Methods
- Abstraction; Object-Oriented Design
- Comments, Instance Variables, Implementing the Class, Testing
4.1. Overview - An Introduction to Objects
This is an important session. Today we're going to be looking at types and variables, the assignment operator, as well as describing what an object is and seeing how one goes about constructing an object.
4.1.1. Objects, Classes, and Methods
Object-oriented programming has to do with objects. But what does that mean?
Objects
Objects are specific "instances" of a class of things. Qualities or characteristics that describe the state of an object are called attributes. Objects can be interacted with by calling their methods.
In object-oriented programming, objects belong to a class. Some examples:
mrWhite
is an object that belongs to the classTeacher
dRosato
is an object that belongs to the classTeacher
caroline
,jack
, anddhalia
are three objects that belongs to the classStudent
Teacher
objects andStudent
objects belong to the classPerson
Class
A class is a general category of a thing—a data construct, or a representation of a real life thing—with specific individual objects, or instances, belonging to that class.
A class, as part of its definition, describes both the attributes (or fields) that describe an object of that class, and the methods that can be used to interact with objects of that class.
Definition: Attribute
An attribute is a value that describes the state of the object.
Example: A Person
object might have an age
variable that keeps track of the person's age. We say that age
is an attribute or field of the Person
class.
Definition: Method
A method is a sequence of instructions that interacts with an object's internal data.
Example: .celebrateBirthday()
is a method of the Person
class. Calling that method adds one to the object's age
.
Other examples:
- The
Teacher
class has a.takeAttendance()
method. - The
Teacher
class has a.collectHomework()
method. - The
Student
class has a.doHomework()
method. - The
Student
class has a.studyForTest()
method. - The
Person
class has a.eatDinner()
method. - The
Person
class has a.sleep()
method.
These objects, classes, and methods that we've seen here are kind of abstract, and we're not yet to the point where we could design and code them yet. We've discussed the abstract concept of a Person
class, but that's not something that we can actually work with on the computer, at least not yet.
4.1.2. Overloaded Methods
Sometimes a class will be capable of doing two different things with two different methods, but the methods will have the same name. For example:
System.out.println(3.14); System.out.println("Hey there.");
The println
method is used to print a double
type number in the first line, and the same method (actually a different method with the same name) is used to print a string in the second line. Although it may not be surprising to you that the println
method would be able to do this, Java is very particular about what it will allow a method to do. Once you learn to write methods like this, you'll see that you actually have to write two methods: one to handle the String values and one to handle the double values. (And yes, a third to handle the int
values.)
When the same println
method can handle multiple situations—multiple types of parameters—we say that the method is overloaded.
4.2. Overview - Objects and Methods
In this session we'll be looking at how you construct objects and use accessor and mutator methods. In addition we'll be looking at implementing a test program, API documentation.
4.2.1. Constructing Objects
Sometimes, when you're writing a program, you'll need to write your own class that describes a type of object. Java doesn't have a Person
, a Teacher
, or a Student
class already written, so if you wanted to work with data like that, you've have to write your own classes. We'll learn how to do that soon.
But Java does supply some classes that have already been defined for you, and you're free to use these in writing programs. The String
class allows you to create String objects. The Scanner
class allows you to create a Scanner in your program that you can use to accept input. The Rectangle
class allows you to create virtual Rectangles that might be used in a program.
So, let's create a Rectangle object. It's important to note at this point that these are all abstractions of objects. We're going to create a rectangle here, but it's not a real rectangle—it's not even a picture of a real rectangle. It's an abstract model of a rectangle, in coded form. That's still going to be enormously useful to us.
Let's construct an object of our own and see how to manipulate it to find out information. The Rectangle
class has already been defined for us, and is available to us in a package called awt
("Abstract Windowing Toolkit").
How to Construct an Object
Here's how you construct an object in general.
<Class name> <identifier> = new <Class name>([explicit parameters]);
So what does that look like in a program? How would we, say, construct a Rectangle
object that we can manipulate?
Program: AreaTester
Write an AreaTester program that constructs a Rectangle
object and then computes and prints its area. Use the getWidth
and getHeight
methods. Also print the expected answer.
Here's the code to solve this problem.
/** * AreaTester program for class * @author Richard White * @version 2013-09-09 */ import java.awt.Rectangle; // import the Rectangle class public class AreaTester { public static void main(String[] args) { Rectangle myRect = new Rectangle(10, 30, 50, 60); double theArea = myRect.getWidth() * myRect.getHeight(); System.out.print("The area of the rectangle is: "); System.out.println(theArea); System.out.println("Expected answer: 3000"); } }
Problem: PerimeterTester
Now, based on this program as an example, on paper, write PerimeterTester
, a program that creates a Rectangle
object and then calculates the perimeter of that rectangle.
4.2.2. The Public Interface of a class
Each object we construct (such as myRect
above) belongs to a class, and the class defines the methods for the objects.
There are other methods defined for these classes, as you might expect. The methods that you can use with objects of any given class are collectively referred to as the public interface for the class.
Public Interface
The public interface for a class describes what public constructors and methods a programmer can use to interact with objects of that class.
You'll get lots of practice working with public interfaces in upcoming assignments.
4.2.3. Accessor Methods and Mutator Methods
We've already seen methods, which are defined for objects in a class. Methods can be loosely organized into two categories.
Accessor methods
Accessor methods provide access to the information in an object.
box.getWidth()
, for example, returns the width of the Rectangle box
.
Mutator methods
Mutator methods alter, or "mutate," the information in an object.
box.translate(x,y);
for example, causes the Rectangle box
to be relocated x
pixels to the right and y
pixels down on the screen.
All methods access information in an object, but some of them also change the object's state. If all a method does is access information, it's considered an accessor, but if it also causes a change in the object, it is considered a mutator.
Accessor or Mutator?
What about this statement? Is the Rectangle
method .getWidth()
an accessor method or a mutator method?
System.out.println(myRect.getWidth());
What about this statement? Is .length()
an accessor method or a mutator method?
System.out.println("Hello!".length());
What about this statement? Is .translate(-2,4)
an accessor method or a mutator method?
System.out.println(myRect.translate(-2,4);
Trick question! Is .toUpperCase()
an accessor method or a mutator method?
System.out.println(riverName.toUpperCase());
4.2.4. The Java API
We're still working our way around to starting to write some serious programs, and something that will help you along that path is the Application Programming Interface (API) for the language.
Definition: API
An Application Programming Interface lists the classes and methods that are available to a programmer for a given language or library.
Just as the Public Interface for a class that you write will describe what methods a programmer can use in interacting with objects of that class, the API documents the thousands of classes and their methods that are available to you in the Java library.
One of the things that makes Java amazing is that a lot of people have been working on it for a very long time, and if you need a programming tool to help you solve a certain type of problem, the chances are very good that somebody else has already addressed that problem, and made their solution available.
Those solutions are available to you in the Java Application Programming Interface!
We won't be taking full advantage of the Java API, because we're actually trying to learn how to do computer science, and not just write applications in Java. Still, you'll want to keep the online version of the Java API handy.
Research the Rectangle class
Do an online search for the Java API for your version of Java and find the Rectangle class in there. What methods are available to you for interacting with Rectangle objects?
Steps:
- Launch a browser
- Search for keywords "Java 7 API"
- Load Oracle's official API page
- Press ctrl-f (command-f on Apple) to Find on the page the package you're looking for. (Rectangle? Ellipse? Random?)
- In the menu on the left side, click on the class you're interested in—hit ctrl-g/command-g to search for the next occurrence of the term if you don't immediately find what you're looking for—and read the documentation there in the main window
Up to this point we've written programs of our own that perform a series of calculations. And now that we know that some types of data can be represented as objects that are organized into classes, we can use classes that other people have written (like the Rectangle
class) in our own programs.
The next thing that we'd really like to be able to do is to write our own classes.
4.3. Overview: Abstraction; Object-Oriented Design
We've been using classes that someone at some point wrote, including the Rectangle
class for rectangles and the String
class for sequences of characters.
As part of this course, ultimately, we're going to be designing and writing our own classes, which is one of the single most powerful things that you can do as a programmer. Designing classes requires a whole lot more experience than you currently have, but implementing a class—writing a class that someone else has designed and given us specifications for—we should be able to manage.
In this section we'll consider levels of abstraction, and how one goes about implementing the Public Interface of a class.
4.3.1. Object-Oriented concepts
A Black Box is a device for which the inner workings are hidden. We don't need to know how things work inside a black box—we just need to know how to use it. Think of a car: You don't need to know how a car works to be able to drive it. The Black Box is based on the idea of encapsulation: the hiding of unimportant details.
OOP Term: Encapsulation
Encapsulation in Object-Oriented Programming refers to the hiding of details that the user doesn't need to know in order to be able to use an object or feature.
Example: It isn't necessary for you to know how Google's search feature was programmed—you can just use it. The implementation details of that search feature are encapsulated.
Example: It isn't necessary for you to know how your car's four-stroke engine works. The details of that operation are encapsulated. The car's engine is a Black Box, whose details are effectively hidden from you.
Here, an engineer programs the "black box" on a MotoGP motorcycle which will help determine how the motorcycle's engine, throttle, braking, and steering work. The rider gives feedback to the engineers on how the motorcycle is "riding," but isn't aware of all the software details that control the bike.
Somewhat related to the Black Box idea is the concept of abstraction.
OOP Term: Abstraction
Abstraction allows us to model only the details of a process that are most important, interesting, or relevant to us. Other details of the model are ignored.
Example: An abstraction of a car may consist of considering only its velocity (speed and direction). We might consider its engine temperature or how many cylinders it has, or whether the windows are up or down, but if that's not important for our problem, we're going to ignore those aspects of the car. Our abstraction of the car will only include velocity.
Example: An abstraction of a rectangle might include its length and width, while ignoring its position in space and its color.
Example: An abstraction of a playing card might include the suit and the value of the card, while ignoring the color of the suit, the design on the back of the card, the manufacturer, the material the card is made of, etc.
4.3.2. Object-Oriented Design
Computer used to use only primitive data types: numbers, and maybe characters. As programs become increasingly complex, the need for more sophisticated data structures arose: "strings" of characters, "lists" of numbers, etc.
At this point in the history of computer science, one of the predominant paradigms for computer programming is object-oriented design and programming, in which "black boxes"—objects—--are designed, programmed, and used to solve complex problems.
Objects get designed and built by one person, and used by another.
- Example: Automotive
- Example: Programming
As a programmer, you may find yourself designing an object at one point, programming it at another, and then using the object itself a few moments later to write a program.
There are two challenges associated with these ideas. One, you have to be flexible, and look at the objects you're working with from different perspectives. And two, writing good programs and bad programs is equally easy to do. You have to work hard to write good programs, with well-designed projects.
4.3.3. The Public Interface of a Class (Reviewed)
The Public Interface
The public interface of a class describes the behaviors of that class: the methods that will allow programmers to interact with that class, the features that we're going to implement.
When we create a class to describe some object, some features of the real life object are essential and will become part of our abstraction of the class; other features of the real life object aren't significant, and we won't need to worry about those when we create our class.
Take a guess
What features are most important for, say, a BankAccount
class?
Answers will vary depending on how detailed our class is, but all bank accounts should include information about deposits, withdrawals, and allow you to get the balance.
What features are perhaps distantly related to the bank, but not really important for the function of our BankAccount
class?
The bank's logo design, branch locations, ATM construction, Human Resources hiring practices, etc.
When designing a class, you want to imagine how someone would carry out each operation interacting with that class.
4.3.4. Writing the Person
class
Work with the instructor to implement the Person
class.
It's often helpful to summarize the attributes and methods that you plan to implement for a class, using a diagram similar to the one here. We'll often use something like this to develop our classes before we start coding.
4.3.5. Writing the Counter
class
A little tally counter is used to count how many people walk into a concert, exhibition, or amusement park. Let's write a Counter
class that will model the behavior of a tally counter.
What will this counter need to keep track of?
How will we need to be able to interact with this counter?
4.3.6. Writing the BankAccount
class
If we have a BankAccount
class, we'd want to be able to make a deposit:
myChecking.deposit(5000);
We also want to get our money back out:
myChecking.withdraw(40);
And we'd want to be able to figure out how much money we have in our account:
System.out.println(myChecking.getBalance());
In each of these examples above, what class are we working with? What are the objects? Which are the accessor methods? Which are the mutator methods?
For each of the three methods that we've been given to design here, we need to write code that will define these methods.
Let's see how to do that.
4.3.7. Writing a Method
How to write a Method
When you write a method description, you have to provide a header that specifies some important information about the method, and a body that describes exactly what to do when that method is called.
Here's a comparison of the headers for the three methods that we're going to write, along with some comments.
The
deposit
method which takes a parameteramount
, the amount to be deposited.public void deposit (double amount) { }
NOTES:
- The access specifier is
public
, which means that other objects will have access to this method (we'll clarify this later). - The return type is
void
, which means that there will be no value returned from calling this method. It's a mutator method that does something to our object. - In the parentheses is a list of explicit parameters, along with their types.
- After this, enclosed in curly braces
{ }
, the body of the method will be written.
- The access specifier is
The
widthdraw
method takes a parameteramount
, the amount to be withdrawn.public void withdraw(double amount) { }
NOTES:
- The access specifier is
public
, which means that other objects will have access to this method. - The return type is
void
tells us that there will be no value returned from calling this method. - The parentheses enclose the explicit parameter
amount
, which is of the typedouble
. - After this, enclosed in curly braces
{ }
, the body of the method will be written.
- The access specifier is
The
getBalance
method allows us to view the balance of the account.public double getBalance() { }
NOTES:
- The access specifier is
public
. - The return type is
double
, which means that the method, when called, will return a floating-point number. The method is an accessor method that retrieves information about theBankAccount
object. - There are no explicit parameters in the parentheses because we don't need to send any information in, but the parentheses need to be included in your header just the same.
- After this, enclosed in curly braces
{ }
, the body of the method will be written.
- The access specifier is
4.3.8. Writing a Constructor
We're almost to the point where we can write a class, but there's one more thing that we need to be able to provide in addition to the methods. We need to write the code that will allow a BankAccount
object to be constructed in the first place. Before we can move money around in a bank account we have to initiate the bank account. To do that, we're going to write a constructor that will make that happen.
You already know how to use a constructor to create a new object. To create a new Rectangle object, you'd write something like this:
Rectangle myRect = new Rectangle(20, 30, 100, 50);
For our BankAccount
class we want to allow a programmer to write something like this:
BankAccount momsSavings = new BankAccount();
So how do we go about writing a constructor that will allow a programmer to create BankAccount
objects?
How to write a constructor
A constructor definition includes a header for the constructor that has a public
access type, no return type (not even void
), and a name that is the same as the class. Parentheses enclose the type and name of any parameters, followed by curly braces enclosing any instructions that will be executed when the new object gets constructed. This typically includes initializing variables in the new object.
In our example, we could write the constructor for our BankAccount in a couple of different ways:
public BankAccount() { }
You can imagine that we might also want to construct a BankAccount that has some initial balance, in which case we would write something like this.
public BankAccount(double startingBalance) { }
4.3.9. The BankAccount
class
At this point, we can create a skeleton version of our BankAccount
class. It's missing a lot of pieces, but we're going to fill those in soon.
/** * BankAccount * * This class allows the user to manipulate a BankAccount object. * * @author Richard White * @version 2014-09-21 */ public class BankAccount() { // instance variables go here later // ******* Constructors ******* public BankAccount() { // body goes here } public BankAccount(double balance) { // body goes here } // ******* Methods ******* public void deposit(double amount) { // body goes here } public void withdraw(double amount) { // body goes here } public double getBalance() { // body goes here } }
4.0. Overview - Comments, Instance Variables, Implementing the Class, Testing
Last time we used a given Public Interface to create the initial outline of a class description.
In this section, we'll fill in all the missing pieces: the Public Interface comments, instance fields, and constructors and methods. We'll also look at unit testing; examine different categories of variables, and learn about implicit and explicit parameters.
4.4.1. Commenting the Public Interface
Java has three ways of writing comments, and you need to be familiar with all three.
3 types of Comments in Java
- A single line comment or in-line comment is indicated by a
//
Anything on the line after a//
is ignored by compiler.balance = balance + amount; // increase the balance
- A multiline comment begins with a
/*
and ends with a*/
As you might expect, a single comment can extend over multiple lines. Everything between the/*
and the*/
is ignored by the compiler./* This section of the class is still being developed. Still to do: * Implement user interface * error-check for negative values * clean up code--it's a mess! */
- A documentation comment begins with a
/**
and ends with a*/
Doc comments are placed before the class or method that they describe, and have a special form. (You've actually already seen these, although you haven't known what they were.)/** * HelloWorld.java * The classic first program written by all computer students. * @author Richard White * @version 2014-09-10 */
Both the multiline comments and JavaDoc comments can occur over multiple lines. The important difference is that JavaDoc comments will be used both by us, the programmers, to help us write our code, and by the Java Development Kit (JDK) to create user documentation for our code.
How to write a JavaDoc comment
In the JavaDoc comments:
- Describe the method's purpose briefly (on a single line?)
- For each parameter, write
@param <parameterName>
and give a short explanation of the parameter's purpose. You can omit this line if there are no parameters. - If the method returns a value, in the JavaDoc write
@return
and describe the value that is being returned. You can omit this line if there is no return value (ie. the method returnsvoid
).
There are really two purposes for these comments. You already know that comments are useful for other people who are trying to learn how to use the class that you've written.
Even more importantly for us, though, is the fact that writing your JavaDoc comments before you write your program makes program writing a LOT easier. Writing the JavaDoc comments before you code helps to ensure that you have a good idea of what the method is going to do. For much of what you write, if you've already written the JavaDoc, writing the actual method itself is almost trivial in comparison.
Let's add JavaDoc comments to our BankAccount class...
4.4.2. Instance Fields, aka Instance Variables
We're getting very close to finishing up our object. We've got the object itself more or less planned out, we've got an idea of the 4 methods that we have to write—one constructor method, two mutator methods, and one accessor method. We've even got documentation written for the class, which means we'll have less clean-up documenting to do at the end. Just two things left to do...
Most objects have data in them that needs to be stored, and the places that these data are stored are called instance fields, or instance variables. These are variables that are created when an instance of your class is created, and they're used to track information about your object.
For our BankAccount
class, there's really only one thing that we need to keep track of for our account at this point: the balance!
An instance field is almost always a private
location, as opposed to a public
one, meaning that nobody outside of the object has direct access to this information. If people want to see if, they're going to have to access it via the accessor methods that we've written which allow them to have access. Keeping the value of this field private is part of the concept of encapsulation: the hiding of details that the user shouldn't have to worry about.
Here, there's only one thing that we really need to keep track of in our BankAccount: the balance. You declare the private instance field in the class like this, usually just before the constructor method:
private double balance;
4.4.3. Implementing our Constructors and Methods
Okay, so far we have given some thought to designing our class, and designed the public interface for our class by considering what methods we want our class to have.
We've also commented that public interface—written JavaDoc comments for our methods—that have described in some detail what those methods will do.
All that's left is to actually write the constructors and methods that make up our program. And at this point, that's easy.
In the code below, comments are indicated in normal weight text, while Java syntax is in bold.
/** * BankAccount.java * @author Richard White * @version 2013-04-12 * * A bank account has a balance can be changed by deposits * and withdrawals. */ public class BankAccount { // declares the instance field here! /** * Constructor1: Constructs a bank account with a zero balance. */ public BankAccount() { } /** * Constructor2: Constructs a bank account with a given balance. * Because there are two different constructors for this class, * we say that the constructor is "overloaded." * @param initialBalance the initial balance */ public BankAccount(double initialBalance) { } /** * Deposits money into the bank account. * @param amount the amount to be deposited */ public void deposit(double amount) { } /** * Withdraw money from the bank account. * @param amount the amount to be withdrawn */ public void withdraw(double amount) { } /** * Return the balance in the account. * @return the current balance */ public double getBalance() { } }
And what does the completed class look like?
In the listing below, the added Java statements are listed in bold.
/** * BankAccount.java * @author Richard White * @version 2013-04-12 * * A bank account has a balance can be changed by deposits * and withdrawals. */ public class BankAccount { private double balance; // declares the instance field! /** * Constructor1: Constructs a bank account with a zero balance. */ public BankAccount() { balance = 0; } /** * Constructor2: Constructs a bank account with a given balance. * Because there are two different constructors for this class, * we say that the constructor is "overloaded." * @param initialBalance the initial balance */ public BankAccount(double initialBalance) { balance = initialBalance; } /** *Deposits money into the bank account. * @param amount the amount to be deposited */ public void deposit(double amount) { double newBalance = balance + amount; balance = newBalance; } /** Withdraw money from the bank account. @param amount the amount to be withdrawn */ public void withdraw(double amount) { balance = balance - amount; } /** Return the balance in the account. @return the current balance */ public double getBalance() { return balance; } }
4.4.4. Testing the Class
Okay, we've written a class. Does it compile? Enter the code into BlueJ and make sure it compiles. If it doesn't compile, you'll need to go through and correct your syntax errors until it compiles without errors.
Does it run? It does not run, because what we've written isn't really a program—it's a class description, a template that will be used for creating BankAccount
objects by a program that IS running.
So how do we know if our class works? We need to test it. We need to run it using another program that we'll write. That program will include a main
program that does run, and we'll use that main program to create BankAccount
objects and manipulate them, to see if they work as we intended.
You'll usually call our test program something like BankAccountTester
or BankAccountRunner
.
The Testing process
For our example, assume we've written the class BankAccount
and we want to test it with a program BankAccountTester
.
- Put the Class you've just written,
BankAccount.java
and the Tester program (BankAccountTester.java
) in the same folder. - Compile the Class you've written. Make sure that there are no compilation (syntax) errors.
$ javac BankAccount.java
- Compile the Tester program. Make sure that there are no compilation (syntax) errors.
$ javac BankAccountTester.java
- Run the Tester program.
$ java BankAccountTester
The tester program will construct and manipulate the Class that you've written, and print out the results of using that class. If your Class passes all the tests, then that's a good sign that you might have done it correctly!
For today, I've already written a Tester program for your class. Here's what you should do.
Download the Tester and run it
To download the BankAccountTester.java
and use it to test your class:
- Use the Terminal to navigate to the folder where you want to download the file, possibly your Desktop.
- Use
scp
to copy the fileBankAccountTester.java
from my Public folder on the server to your local computer.
$ scp studentID@servername:/home/rwhite/Public/BankAccountTester.java .
- Move the Tester into your BlueJ project folder for this assignment; you can either click-drag the file into that directory, or use the command-line to
mv
it there. - Double-click on the
package.bluej
file in your BlueJ project folder to launch this project in BlueJ. The Tester file will be recognized by BlueJ and automatically included on your BlueJ desktop. - Right-click (PC) or Ctrl-click (Mac) on the Tester file and select Compile to compile the Tester.
- Right-click (PC) or Ctrl-click (Mac) on the Tester file again and select void main(String[] args) to run the program, and see what kind of results are produced.
- Diagnose errors as necessary to get your BankAccount class working correctly.