95.105 - Introduction to Programming |
Fall 2001
|
3 Object Oriented Programming |
3.1 Important Terms |
Logically,
Logically, an instance:
0__
__0__
/|
liftRightArm |
|
|
/ \
/ \
/ \
/ \
_____
/ \
. o O | BOB |
__0__
__0__ \_____/
| whatIsYourName
|
|
|
/ \
/ \
/ \
/ \
Instance methods represent the "behaviour" of a particular kind of object. So, sending a message to an object really means calling one of its methods. We send messages to objects (i.e., instances) in Java by using the dot operator:
anObject.methodName();This means: "send the message called methodName to anObject". We can also supply parameter values within the round brackets.
An instance variable:
Note that both instances above have the same instance variables, but these variables are holding different values. Hence, the values vary between instances of the class and therefore are called instance variables.
A class method:
Class methods represent static (or fixed) "behaviour" which may not depend on instances. So, sending a message to a classt really means calling one of its functions. We send messages to classes in Java by using the dot operator, making sure the class name is on the left side:
Classname.methodName();
A class variable:
A package:
3.2 Approach to Object-Oriented Problem Solving |
So ... you'll find that much of your time is spent in defining objects,
their state and their behaviour. Once you have defined these objects,
you can start using them ... this is the application's responsibility.
In your assignments, the application is merely your testing that you'll
be doing.
There are 3 basic steps to follow when solving problems in an object-oriented manner:
Complex Number
state:
real, imaginary
operations:
+, -, ...., realPart, imaginaryPart
Bank Account
state:
account number, owner name, account type,
account balance, account transactions
operations:
balance, deposit, withdraw
3.3 Creating Simple Methods |
Some methods return an answer, and some do not. Those that return answers are also known as functions. Those that do not return an answer are typically called procedures. When writing a method, the method must have a return type which is the kind of value that is returned from the method upon its completion. The return type must ALWAYS be specified. All procedures have a return type of void. Hence, if no value is to be returned, then the return type should be void.
When methods are created, they are called (used) differently depending
on where the method is declared.
Here is the format for calling the method (this is not actual code,
just a template):
methodName(parameters) // when the method with name methodName is declared in this classNote that in the writing of the method, we must declare the types of each parameter as well (we'll see this later).
object.methodName(parameters) // when the method with name methodName is declared in another class
classname.methodName(parameters) // when the method with name methodName is declared static in another class
When Java encounters a method call with some Java code, it stops what it is doing, goes and does the code within the method and then comes back (i.e., returns) to what it was doing before the method call. If the method was a function, the returned value is then used in the computations.
For example, consider what happens when we call the Math.sin() function in this code:
System.out.println("Sine of PI is " + Math.sin(3.14159265));Note that Java does not print anything from the println() method until it has the entire String to be printed. Java will come across the Math.sin(3.14159265) method call and then go get the answer. This answer (happens to be 0.0) then replaces the call and so this is what Java now sees:
System.out.println("Sine of PI is " + 0.0);Just remember that all method calls get replaced by their return value, and this is then used in the Java expressions.
System.out.println("Sine of PI is 0.0");Finally, Java evaluates the println method and the output appears in the console.
Consider the following code which converts a centigrade (Celsius) temperature to fahrenheit:
public class CentFahr {
public static void main(String args[]) {
System.out.println("Enter a centigrade temperature:");
int cent = KeyboardPrompter.getInteger();
int fahr = cent * 9 / 5 + 32;
System.out.println(cent + "C is " + fahr + "F");
}
}
Here is the output if 28 is entered :
Enter
a centigrade temperature:
28 28C is 82F. |
Here are some things to note:
We must create our own class (we'll call it Convert) and make a message (perhaps called fahrenheit() ) . If we define the method as static, then it is a class method for that class and we can call it as follows:
Here is the class definition and method:
System.out.println(a + "C is " + centigradeToFahrenheit(a)
+ "F");
System.out.println(b + "C is " + centigradeToFahrenheit(b)
+ "F");
System.out.println(c + "C is " + centigradeToFahrenheit(c)
+ "F");
}
}
34C
is 93F
0C is 32F 24C is 75F |
Example:
Now consider a program that needs to display a name and email address for several people as follows:
We can write a method that will print out information for one person and then merely call it repeatedly with different names and email addresses. We'll call the method printInfo().
Here is the method:
static void printInfo(String name, String email) {Notice that the method does not return anything (declared as void). Also, notice that there are two parameters that are required. Of course, to test it, we'll need some test code:
System.out.println("Name: " + name);
System.out.println("E-mail: " + email);
System.out.println("----------------------------------------");
}
public class Labels {
static void printInfo(String name, String email) {
System.out.println("Name: " + name);
System.out.println("E-mail: " + email);
System.out.println("----------------------------------------");
}public static void main(String args[]) {
printInfo("Rob Banks","rbanks@bns.nortel.ca");
printInfo("Ben Dover","dover@abb.ca");
printInfo("Hugh Jass","hjass@npp.springfield.ca");
printInfo("Phil Meeyup","pm@itt.nrc.ca");
}
}
3.4 Creating a Simple Bank Account Class |
When creating a new type of object (a class), there are always a set
of steps that need to be made:
We "call" the constructor by using the new keyword:
BankAccount
marksAccount;
marksAccount
= new BankAccount(432883,"Mark Lanthier");
//
These are the set (modifying) methods. They allow other
//
classes and methods to alter the internal components of
//
the bank account.
public void setAccountNumber(int
aNumber) { accountNumber = aNumber; }
public void setOwnerName(String
aName) { ownerName = aName; }
public void setBalance(float
aNumber) { balance = aNumber; }
BankAccount bobsAccount;
bobsAccount
= new BankAccount(0, "?");
bobsAccount.setOwnerName("Bob");
System.out.println("The
Bank Account's owner is " +
bobsAccount.getOwnerName());
bobsAccount.setAccountNumber(554343);
System.out.println("The
Bank Account's number is " +
bobsAccount.getAccountNumber());
bobsAccount.setBalance(3500.00f);
System.out.println("The
Bank Account's balance is " +
bobsAccount.getBalance());
Note that the following code does the same thing, can you explain why
?:
//
Withdraw some money from the account and return the amount withdrawn
public float
withdraw(float anAmount) {
balance
-= anAmount;
return(anAmount);
}
Of course, as usual, to make sure everything works, we'll need to
write some test code ... so we make a main method as follows:
marksAccount
= new BankAccount(432883,"Mark Lanthier");
System.out.println(marksAccount);
marksAccount.deposit(253.67f);
marksAccount.deposit(300.00f);
System.out.println(marksAccount);
marksAccount.withdraw(400.17f);
System.out.println(marksAccount);
}
Account
#432883 with balance $0.0
Account #432883 with balance $553.67 Account #432883 with balance $153.49997 |
Seeing the results, we realize that the printing method does not round off to two decimal places. Also, we notice that the math is slightly off. We can easily fix this:
Account
#432883 with balance $0.00
Account #432883 with balance $553.67 Account #432883 with balance $153.50 |
We can even make more than one bank account:
jimsAccount
= new BankAccount(431881, "Jim Dandy");
bettysAccount
= new BankAccount(549432, "Betty Boop");
jimsAccount.deposit(300.00f);
System.out.println(jimsAccount);
bettysAccount.withdraw(100.00f);
System.out.println(bettysAccount);
}
Account
#431881 with balance $300.00
Account #549432 with balance -$100.00 |
Woops! Looks like we forgot to make a balance check before withdrawing
fro the account : ).
Can we fix this ? Certainly.
Clearly, we needed more testing before stating that our code was correct.// Withdraw some money from the account and return the amount withdrawn
public float withdraw(float anAmount) {
if (anAmount <= balance)
balance -= anAmount;
return(anAmount);
}
bobsAccount
= new BankAccount(0, "?");
bobsAccount.setOwnerName("Bob");
System.out.println("The
Bank Account's owner is " +
bobsAccount.getOwnerName());
bobsAccount.setAccountNumber(554343);
System.out.println("The
Bank Account's number is " +
bobsAccount.getAccountNumber());
bobsAccount.setBalance(3500.00f);
System.out.println("The
Bank Account's balance is " +
bobsAccount.getBalance());
}
The
Bank Account's owner is Bob
The Bank Account's number is 554343 The Bank Account's balance is 3500.0 |
Hey! How come the balance has no dollar sign ? And how come it's not showing two decimal places ? You should be able to explain this easily.
We can create special "example" methods which are used to create example of BankAccounts. Example methods:
public static BankAccount example1() {Notice a few things:
BankAccount ba = new BankAccount();
ba.setOwnerName("Bob");
ba.setAccountNumber(554343);
ba.deposit(1000);
return ba;
}
bobsAccount
= BankAccount.example1();
System.out.println("The
Bank Account's owner is " +
bobsAccount.getOwnerName());
System.out.println("The
Bank Account's number is " +
bobsAccount.getAccountNumber());
System.out.println("The
Bank Account's balance is " +
bobsAccount.getBalance());
}
What if we call the method twice ? Is it the same account returned ? NO WAY! Every time you call the method a new account is created. It will just happen to have the same initial values in it:
publicstatic void main(String args[]) {This prints out (depending on the toString method though):
BankAccount bobsAccount, marysAccount;bobsAccount = BankAccount.example1();
marysAccount = BankAccount.example1();
marysAccount.setOwnerName("Mary");
marysAccount.setAccountNumber(431881);
System.out.println(bobsAccount);
System.out.println(marysAccount);
}
Account
#554343 with balance $1000.00
Account #431881 with balance $1000.00 |
In fact, since the example method is static and we are calling this it from another tatic method (i.e., the main method), we do not need to specify the class:
publicstatic void main(String args[]) {However, if we wanted to use the example methods from outside of the BankAccount class, we would need to supply the classname.
BankAccount bobsAccount, marysAccount;bobsAccount = example1();
marysAccount = example1();
marysAccount.setOwnerName("Mary");
marysAccount.setAccountNumber(431881);
System.out.println(bobsAccount);
System.out.println(marysAccount);
}
Just to confuse you even further....it is actually possible to call a class method from an instance of that class!!! This is very weird and counter-intuitive...but hey....the java guys like to come up with weird stuff:
bobsAccount
= new BankAccount().example1();
//this works!
marysAccount
= bobsAccount.example1();
//this works too! bobsAccount is used to get a new account !
VERY Weird!
Lastly, we can make many more example methods and use them similarily:
public static BankAccount example2() {
BankAccount ba = new BankAccount();
ba.setOwnerName("Biff");
ba.setAccountNumber(121212);
ba.deposit(1000);
ba.deposit(2000);
return ba;
}
public static BankAccount emptyAccountExample() {
BankAccount ba = new BankAccount();
ba.setOwnerName("John Doe");
ba.setAccountNumber(554343);
return ba;
}
3.5 Adding a Class Variable (i.e., static field) |
Looking at the BankAccount code, we notice something unrealistic. When creating a new bank account, we probably should not be allowed to specify the account number. Usually, these are assigned automatically to the customer. What number does a new customer get anyway ?
Let us assume that the first customer gets 000001, the second gets 000002, the third 000003 and so on. That means, that if we keep a count as to how many customers there are, we will know which number to give the next customer. We'll keep a counter called LastAccountNumber which will store the account number that was given out last. The next customer will get one more than this.
How do we do this ? Well, just make a class variable (i.e., static) called LastAccountNumber which has an initial value of zero. Then, when a new BankAccount is created, give it an account number which is one more than the LastAccountNumber and increment it for the next time.
We must make a modification to the constructor so that it does not allow
the user to specify the account number.
public class BankAccount2
{
// This class variable remembers the last account number that was given
// out the last time a new bank account was created.
static int LastAccountNumber = 0;
//
These are the instance variables
private
int accountNumber;
private
String ownerName;
private
float balance;
// This is the constructor. It initializes the bank account
// to have the name as specified by the given parameter.
// The account number is set by a class variable.
public BankAccount2(String name) {
accountNumber = ++LastAccountNumber;
ownerName = name;
balance = 0.0f;
}
...
//
I left out the get/set/toString/deposit/withdraw methods
...
//
This is the testing
public
static void main(String args[]) {
BankAccount2 marksAccount, jimsAccount, bettysAccount;
marksAccount = new BankAccount2("Mark Lanthier");
System.out.println(marksAccount);
jimsAccount = new BankAccount2();
System.out.println(jimsAccount);
bettysAccount = new BankAccount2("Betty Boop");
System.out.println(bettysAccount);
}
}
Note as well that we had to change two constructors (the default one, and the other one). To reduce the amount of code changes required during code maintenance, often a programmer may do what is known as "chaining" methods (in this case chaining constructors). What we will do is have one constructor call the other one:
public BankAccount2() {
this("unknown");
//
Calls the constructor below.
}
public BankAccount2(String name) {
accountNumber = ++LastAccountNumber;
ownerName = name;
balance = 0.0f;
}
In addition to these changes, we will probably want to remove the setAccountNumber() set method for setting the account number since it is no longer allowed. In fact, in this case, we probably do not want the set method for the balance either since we have a deposit() and withdraw() methods that make the changes as necessary.
Also, if we want to display account numbers with leading zeros, we can use the decimal format class on the account number as well:
public String toString() {
return("Account #" + new java.text.DecimalFormat("000000").format(accountNumber) +
" with balance " + new java.text.DecimalFormat("$0.00").format(balance));
}
Here are the results of testing. Notice that successively
created bank accounts have unique numbers.
Account
#000001 with balance $0.00
Account #000002 with balance $0.00 Account #000003 with balance $0.00 |
3.6 Testing Requirements |
After creating a class, make a main function that tests at least the following:
But what about when multiple Bank Accounts are used ? Apply common sense again. You must always indicate which account has been changed and how:
3.7 Investigating a Simple Class (Random) |
We've seen earlier how to get a random number using the Math class. Now we'll see a richer form of generating random numbers. To do this, we'll make use of the Random class in the java.util package. This class represents a random number generator to which we can repeatedly ask for random numbers.
There are two ways to make a random number generator. The first
is the default constructor, the second allows the user to specify a seed.
Random
r = new Random();
Random r = new Random(seedValue); |
Random numbers are actually not random at all. It is impossible to get a truly random number from a computer. Instead, the random number generator generates a sequence of numbers that only appear to be random (to the average person). The seedValue is the starting point for this sequence of random numbers. If we make a generator with the same seed value each time we run the program, we'll get the same sequence of random numbers (this is good for testing).
We can set the seed of a generator rafter we make it as follows:
3.8 Investigating the Date and Calendar Classes |
To make a new instance of Date we do the following:
Date today = new Date(); |
This generates an instance of Date with the current time and date of the computer's clock. In the class Date itself, there is no easy way to create a specific date (e.g., February 29, 1992). To do this, we'll have to make use of another class called Calendar.
What does a date look like ? Well, consider the following code:
import java.util.Date;
public class DateTest {
public static void main (String args[]) {
System.out.println(new Date());
}
}
Here is the output:
Wed Sep 09 11:05:35 EDT 1998 |
It shows the day, month, date, hours, minutes, seconds, zone and year of the Date object.
Now how do we make a specific date ? Use the Calendar class.
The class message getInstance()
returns an instance of Calendar:
Calendar today = Calendar.getInstance(); |
Once we have a Calendar instance, we can use the get() method to obtain information about some specific attribute of the date as follows:
aCalendar.set(int
year, int month, int day);
aCalendar.set(int year, int month, int day, int hour, int minute); aCalendar.set(int year, int month, int day, int hour, int minute, int seconds); |
In order to display the date and time, we must get the time from the Calendar using getTime().
Example
Here is a simple example that creates two dates. One representing today, the other representing a future date:
//Display Information about today's date and time
System.out.println("Here is today:");
System.out.println(today.getTime());
System.out.println(today.get(Calendar.YEAR));
System.out.println(today.get(Calendar.MONTH));
System.out.println(today.get(Calendar.DAY_OF_MONTH));
//Display Information about a future day's date and
time
future = Calendar.getInstance();
future.set(2010, Calendar.MARCH, 5);
System.out.println("Here is the future:");
System.out.println(future.getTime());
System.out.println(future.get(Calendar.YEAR));
System.out.println(future.get(Calendar.MONTH));
System.out.println(future.get(Calendar.DAY_OF_MONTH));
}
}
Here
is today:
Wed Sep 09 15:15:50 EDT 1998 1998 8 9 Here is the future: Fri Mar 05 00:00:00 EST 2010 2010 2 5 |
Notice that the months start at 0, not 1. Although we can create and display simple dates, we have not done any manipulation at all. For instance, we may want to know how many working days there are between two dates. There are many more functions in the Calendar and Date classes, but we will not discuss them any further here. You'd have to look at the API for the Date, Calendar and SimpleDateFormat classes.
For instance, the following program tests out some simple date formats:
import java.util.Date;Here is the output from my testing that I ran the afternoon of September 11th:
import java.text.SimpleDateFormat;
public class DateFormatTest {
public static void main (String args[]) {
Date aDate = new Date();
System.out.println(aDate);
System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(aDate));
System.out.println(new SimpleDateFormat("yy/MM/dd").format(aDate));
System.out.println(new SimpleDateFormat("MM/dd").format(aDate));
System.out.println(new SimpleDateFormat("MMM dd,yyyy").format(aDate));
System.out.println(new SimpleDateFormat("MMMM dd,yyyy").format(aDate));
}
}
Mon Sep 11 14:33:19 EDT 2000
2000/09/11 00/09/11 09/11 Sep 11,2000 September 11, 2000 |