Académique Documents
Professionnel Documents
Culture Documents
Context
The purpose of this unit is to clarify and extend the concepts of object orientation as
they apply to Java programs. You will already have come across the basic concepts of
'class', 'object', 'variable' and 'method'; this unit gives more details and explains how
new classes are implemented.
Objectives
Write simple Java applets that define and use methods and variables
Explain the difference between public and private variables and methods
Explain the difference between class and instance variables and methods
Understand the scope of class, instance and local variables
Be able to write programs that make use of classes in packages
Study Planning
You should expect to spend approximately 9 hours studying this unit. You may find it
convenient to break up your study as follows:
Equipment/software required
1. Java: how to program. H. M. Deitel and P. J. Deitel, 3rd edition, Prentice-Hall, ISBN: 0-
13-012507-5
Online Resources:
1. Frequently-asked questions
2. Additional Source Code Examples
Introduction to unit 6
In this unit we return to the object concepts introduced at the beginning of the module.
We are now able to consider the detailed aspects of the implementation of classes with
methods and variables based on the programming concepts and skills covered in the
units up to this one.
In fact you have already been working with the implementation of several Java object
concepts, in the process of creating applets (and some applications) for investigating
and solving problems with such programming features as iteration statements and
strings.
It is worth keeping in mind that with object orientation there is a stark contrast between
the complexity of the theory and the simplicity of the practice.
Object technology is a relatively new field, and therefore it is changing very rapidly. This
means that different people, textbooks and Web sites use different terms for the same
thing. In this module we have tried to be consistent in the use of terminology, but our
use of terminology is not necessarily compatible with anyone else.
A conceptual class is one that does not have a physical presence. Here are some
examples:
a bank account
a mortgage
a musical recording
a picture
Note the difference between a musical recording, which is conceptual, and a compact disk,
which is concrete. In everyday speech we happily mix these up. We may talk, for example, of
'playing Beethoven's fifth symphony' and not, 'playing the compact disk which embodies a
recording of Beethoven's fifth symphony'.
However, if you were designing software to catalogue a large music library, you would
have to ensure that the difference between these concepts was perfectly clear in your
mind. This concept is considered in more detail in the 'discussion' topics.
Although there is a good correspondence between the use of classes for modelling and
the use of classes to organize a computer program, historically these uses of classes
have been developed by different people, and use different terminology. The Unified
Modelling Language (UML) has started to impose a standard terminology on the
modelling use of classes, but the use of classes in programming is still subject to a
variety of different terms. There is a summary of the terminology used in object
orientation at the end of this unit.
class Customer
// variables
<variable1>
<variable2>
<variable...>
// methods
<method1>
<method2>
<method...>
Classes are central to a Java program. Every program must have at least one. The
program may use classes to represent real world (concrete) categories of object, or the
program classes may be entirely internal (such as user interface classes like Button,
MenuItem etc.) Program instructions can only exist inside methods, which are inside
classes.
The program designer decides what variables and methods to define as part of a class
after analysis and modelling activities, prior to coding at a computer.
In Java, classes are the main organizational unit of a program. By their very nature
classes tend to be self-contained. This leads to easier management of large projects,
and easier maintenance of the software. You should spend some time thinking about
these issues at the end of this unit (there is a question to that effect in the `reflection'
section).
Variables
For example, in a program concerned with company accounts, we may define a class
called Customer, representing and managing the details of the company's customers.
Variables of Customer may include name, address, credit limit, etc. Each object of class
customer may have different values of these variables.
Taking the 'Customer' example, let's assume that the important features of a customer
are name, address, account balance and credit limit. We might define this as below:
class Customer
{
// Variables of customer
String name;
String address;
int accountBalance;
int creditLimit;
// Methods of Customer go here
}
Considering a library records system, we shall define a class for text items as follows:
class TextItem
{
// variables
String title;
int numPages;
String shelfMark;
boolean onLoan;
int daysLate;
// methods
void TextItem( String itemTitle,int itemNumPages,
String itemShelfMark)
{ // method implementation }
For now the implementation of the methods has been replaced with comments.
As can be seen from the class definition, the class defines 5 variables:
title
numPages
shelfMark
onLoan
daysLate
These correspond to attributes identified during analysis and modelling. However,
another attribute was also identified, which has not been implemented as a variable in
this class. That other attribute was the fine due on a TextItem object.
A general rule of thumb, when performance is not a major issue, is never to store a
value that can be calculated when it is needed. If we assume that the fine for a TextItem
is always 0.05 pounds for each day late, since we have the variable daysLate already
stored as a variable, we can always calculate the fine when needed.
A method that needs to find out the fine is the getFine() method, which we might
implement as follows:
double getFine()
{
return (0.05 * daysLate);
}
This method calculates the fine and returns the result of the calculation as a reply.
Implementation observation
Not every attribute identified in analysis and modelling is implemented as a variable.
If a value can be calculated form other variables, then a get() method is all that is
needed — the implemented variable will be set via changes to the variables that make
up its calculation.
An object is an instance of a class. In Java we can create an object using the keyword
new as follows:
In the example below, 'aMenu' is the name of a particular object of class Menu. Note the
brackets after the name of the class; their significance is explained later this unit:
new Applet();
a specific object (of class Applet) is created, but it never gets assigned to a variable.
This object must be entirely self-contained, as the method that creates it has no way of
communicating with it after creation.
Constructor methods
The statement:
TextItem object
numPages = 1041
onLoan = false
daysLate = 0
Notice that the onLoan status is false and the value of daysLate is zero, even thought
these values were not part of the statement. In fact, even though values like "Deitel
& Deitel" were part of the statement, how did they get into an object?
Statements that create objects are actually invoking methods called constructor
methods. Each class has a constructor method that executes when a new object is to
be created of that class. We might define a constructor method for our TextItem class
as follows:
}
As can be seen, for this constructor the arguments to the method (title, number of pages
and shelf mark) are used to initialise some of this new object's variables. In addition,
some variables (onLoan and daysLate) are automatically initialised to values
appropriate for a new object of the class.
All classes have constructor methods that state what should be done to create a new
object of the class. Sometimes constructors are not explicit in the definition of the class
(they may be inherited from another class — we'll look more at inheritance in the next
unit).
Constructors do not have to have arguments — for some classes of objects there is
some reasonable way create an object that does not need to have any initial values
provided.
The example below shows how to provide a class 'Customer' with a constructor.
class Customer
{
String name;
String address;
int accountBalance;
int creditLimit;
// Customer constructor
Customer ()
{
// Statements to initialize the object go here
}
}
Note that the constructor in this case is defined as Customer(), so to create a new
Customer object we would write:
new Customer();
Alternatively, let's suppose that when we create a new customer we will always specify
the customer's name, address, account balance and credit limit. In this case we would
define the constructor as follows:
// Customer constructor
name = _name;
address = _address;
accountBalance = _accountBalance;
creditLimit = _creditLimit;
}
Then we might create a new Customer like this:
new Customer ("Fred Bloggs", "13 Acacia Avenue", 0, 2000);
Use of '_' prefix
Notice in the example above, rather than using a prefix like 'new' or 'item' for each
argument, the prefix '_' was used. This is another convention widely used to distinguish
between the names of arguments and the names of the variables they are setting. It is
important, whichever convention you adopt for the identifiers of arguments, that you do
not write methods that have either arguments or local variables that have the same
identifiers as your object's variables.
Arguments (parameters)
Arguments can be though of as the `input' to methods.
By `input' is meant an input from another part of the program, not from the user.
Arguments, like variables, have a name and a type.
Suppose that when a customer pays some money into an account, it increases the
account balance. We want to define a method called payIn() which is called whenever
the customer pays some money in. This method cannot add a fixed amount to the
balance, as it must be able to cope with any amount of money paid in. For example, we
would like to be able to write
payIn (300);
to pay in 300 pounds, and
payIn (10000);
to pay in 10,000 pounds, etc. The number in parentheses (brackets) is called an
argument of this method. We can define the payIn() method like this:
public void payIn (int amount)
{
accountBalance = accountBalance + amount;
}
Don't forget that accountBalance is the name of the variable that stores the account
balance for this customer.
Again, the name of the argument `amount' is only meaningful inside the method. We
could have used any name for this argument (provided all instances were the same)
and it would work the same. Of course, common sense suggests that you should
choose identifiers that make the program easier to understand.
Return value
The return value is the 'output' of a method in the same way that the arguments are the
'input'.
The return value is the result that a method sends back as a reply (to the message that
caused the method to be performed on an object). This is particularly important when
the purpose of the method is to allow other objects to find out something about this
object (i.e. get methods). The ‘something’ becomes the return value.
The return value of a method is the reply returned to the message statement that
invoked the method.. Writing data to the screen, to a window or to a file is not a return
value. The return value has a type, like a variable.
Continuing with the customer example again: suppose we need to calculate the interest
on a customer's account balance. Suppose further that we don't want to print this
interest, but use it in a later calculation. What we need here is a method that returns the
interest on the account. Then we can write something like:
Novice programmers often become confused about the difference between `output' as a
return value, and `output' to, for example, the screen. For example, the operation
`println' is defined as having no return value (technically called a void return). But it
does produce an `output' to the display. Remember that a return value is an `output'
only to another part of the program, not to some other part of the computer.
Methods in general
Methods define what a class can do, or can have done to it. For example, a Mortgage
class may have the following methods:
deduct payment
calculate interest
foreclose
etc.
Note that it is not very clear which of these methods the class ‘does’, and which are
‘done to’ it (i.e. change the state of the object). This is often the case, and modern
practice is not to worry too much about this.
In Java, all program instructions must be inside methods. Methods can read and
change the values of instance variables as well as their own (local) variables.
Continuing with the Customer example: suppose we need the program to be able to
print each customer's account balance on the screen (perhaps using
System.out.println() or Graphics.drawString() ). A sensible place to put the
instructions for this is in a method called `printBalance' (for example). We might define
this method like this:
class Customer
{
String name;
String address;
int accountBalance;
int creditLimit;
// printBalance method
// prints the customer's balance
In a specific object of this class, assume the customer's name is Fred Bloggs and the
account balance is 2,000 pounds, then executing this method would print:
Current balance for Fred Bloggs is 2000 pounds
Look carefully at the start of the definition of printBalance:
public void printBalance ()
The keyword `public' loosely means `can be called by methods outside this class'.
This concept is discussed in more later this unit.
The keyword `void' means that this method has no return value ('output'). The empty
parentheses (brackets) mean that the method has no arguments ('inputs').
x = printBalance();
because nothing useful would be put into the variable x in this case. In other words,
there is no output in the form of a reply returned to the message statement that invoked
the method.
Although an object can be created using a very simple new statement such as:
The new statement is actually an expression, which evaluates to a reference to the new
object.
Therefore it is very easy to store the reference in a variable. We can declare a variable
able to refer to an object of a certain class as follows:
<class> <variableIdentifier>;
For example:
String name1;
Ball ball1;
BankAccount customerAccount1;
At this point, these three variables do not refer to any objects (they have the value
null).
It is now possible to assign to such a variable the reference to a newly created object:
both declare the variables name and ball1, and also make these variables refer to newly
created objects of the String and Ball classes respectively.
By their nature, a variable can change its value. A variable declared for referring to an
object of a certain class can refer to any objects of that class. So such a variable might
at one point be referring to one object, and at another point be referring to a different
object.
For example:
BankAccount accountToBalance;
...
accountToBalance = account1;
display( accountToBalance.getBalance() );
accountToBalance = account2;
display( accountToBalance.getBalance() );
This illustrates:
Object individuality
Each object has individuality. So when a new object is created, it takes up its own part
of the computer's memory. The individuality of an object is not related to the values of
its variables (i.e. its state).
LibrayItem book1;
LibrayItem book2;
Two new objects have been created, which are instances of the class LibraryItem. The
first new object was created, and the variable book1 assigned the reference to its
location. The second new object was created in a different place in the computer's
memory, and the variable book2 assigned the reference to that object.
LibrayItem book3;
LibrayItem book4;
Just the same thing has happened as before — two different objects have been
created, and each one is stored in a different location in the computers memory.
Variable book3 refers to the first of these 2 new objects, and book4 refers to the second
of these new objects. Although both these objects have been created with the same
arguments (and so most likely have the same state) they are not the same object. It is
perfectly reasonable to consider that there might be two copies of the same book in a
library, but they are not the same objects.
Therefore, book3 does not have the same value (i.e. reference) as book4. Were we to
use the boolean test for equality:
(book 3 == book4)
What is intended by the term variable can become a little confusing sometimes, since
there are actually a number of different kinds of variable classes and objects can have.
instance variables
class variables
local variables (in methods)
Local variable
A local variable is one that can only be referred to inside a single method, or part of a
method. For example some local variables are local to a particular method, others are
local to only part of a method (such as in a compound loop statement).
An example of a local variable is the loop variable 'i' in this for loop:
The error says 'undefined variable i' since the compiler does not know what variable you
mean.
The part of a class where a variable can be referred to is called the scope of the
variable. An instance or class variable (see later this unit) has a scope comprising every
method of a class. A local variable may have a scope of a whole method, or may, as in
the loop above, have a scope of just one compound statement.
The full listing of our Scope class (without the offending line) is given below:
// Scope.java
int total = 0;
average
x
y
total
i
The variable average has a scope of all methods, since it is an instance variable.
The variables x and y and total have a scope of all the statements that occur after
them in the paint() method.
The issue is scope is not covered in great detail here, however, be aware that an error
relating to an undefined variable is often one of either scope or a misspelling of the
variable identifier.
For an object to evoke some system behaviour not implemented as one of its methods,
it must send a message to another object (or invoke a class method — see later this
unit).
To send a message, both the receiving object and the message to send need to be
provided. This is achieved using the 'dot' notation, e.g.:
anObject.aMessage();
Every message must include an argument list in parentheses ( ... ), although there
may not be any arguments, so the parentheses may be empty: ().
The sending of a message to an object results in a method of that object being invoked.
invoked.
Keyword 'this'
When an object wishes to send itself a message to invoke one of its own methods, it is
not necessary to use the dot notation.
oldBalance = m1();
This statement means send the message m1() to itself, to result in its corresponding
m1() method being invoked.
In the absence of the dot notation Java assumes that a message is to be send to the
sending object itself.
Java actually provides a keyword to mean 'the object sending the message'. The
keyword is:
this
So we could replace the line above, with the following:
oldBalance = this.m1();
and the result would be the same. We have simply made it explicit that the message is
to be sent to the object itself.
In fact, when Java spots that no object (or class) has been specified to receive a
message, it inserts the sending object itself to be the recipient of the message (i.e. Java
will automatically insert the this. so that it know where every message is to be sent).
The this keyword is also useful when an object wishes to send a reference to itself as
part of a message or reply — in such a situation this is passed as a message
argument or a return value.
Java uses the dot notation for the sending of messages to other objects (and methods).
For example, if a method in a different class wants to send a message to invoke the
method printBalance() for a particular object of class customer, we could write
(assuming the object is called aCustomer):
aCustomer.printBalance();
This means `send the message printBalance() to the object aCustomer'. This
would, had we defined the Customer class appropriately, result in the
printBalance() method being invoked for the object aCustomer.
The dot notation can also be used to send messages to receive values of variables
from objects. Thus the statement:
aCustomer.familyName;
is a message that will result in the aCustomer object returning the value of its
familyName variable (as long as this variable is public — see later this unit).
Access control
When a class of objects is defined, it is not the case that we wish all the methods or
variables of an object of the class to be available to other objects in the software
system. A general design rule for classes is that they should be self-contained, and
control access to its variables and methods
There is little point in creating careful get and set methods, if we do not prevent the
direct access of variables by other objects.
Java provides several of levels of access to the variables and methods defined in a
class. We shall investigate the access control provided by the following keywords:
public
private
You may, as an optional extension of your reading, wish to look into a third keyword
related to access:
protected
Those variables and methods we wish to hide from other objects should be declared as
private. Those variables and methods we wish other objects to have access to should
be declared as public. For example, a MyCircle object may be defined as follows:
class MyCircle
{
// private variables
private int x;
private int y;
private int radius;
//... etc.
// public methods
public int getX(){ return x; }
public void setX( int newX ) {x = newX; }
//... etc.
}
This MyCircle class has only private variables, and only public methods — this
arrangement is quite common:
class MyCircle
Variables Methods
Private int x
Private int y
//AcessApplet.java
//<APPLET code = "AccessApplet.class" width=275 height=200></APPLET>
import java.applet.Applet;
import java.awt.*;
Note that classes themselves, as well as variable and methods, can have their access
defined. Thus a public class must be defined in a file with the same name as the
class, an is one that can be used by other instances of other classes. Classes can be
private, and only accessible to some other class defined in the same file.
Math.PI;
g.drawString ("Math.PI =: " + Math.PI, 20, 20);
There is no need for any instance of the class to be created for some other other object
to be able to refer to this value. Notice that the dot notation is used for class variables
and constants, but with the class identifier rather than an object variable first. The
general form for referring to class constants and variables is as follows:
<classIdentifier>.<variable>
or if the class is within a package:
<package>.<classIdentifier>.<variable>
A class constant or variable is declared in the same way as an instance constant or
variable, except that the static keyword is included. So for example, a Client class might
declare a class variable to count the number of Client objects that have been created.
Such a variable would be declared as follows:
private static int numClients = 0;
Regardless of how many objects of the class existing, since this is a static variable,
this will be the only occurance of the variable numClients. Objects of the Client class
can update or read the variable, but since the variable is private, objects of other
classes will not be able to read or update this variable.
There are a number of situations where it is useful to have class variables, although
they are not that relevant to the programs you will be developing for this module. It is
are recommended you carefully study Deitel & Deitel to learn more about the use of the
static keyword to define class variables and constants.
In the same way that a class can provide variables and constants, a class can also
provide useful methods that do not require an object of the class.
nextGuess = Math.random();
This example is a method that returns a random value from 0.0 up to (but not including)
1.0. There is no need for an instance of the Math class for some other object to use this
public static method.
We could define a class method addUp() for a class Counter, that when provided with
an integer returns the total of numbers. For example, if provided with the number 3,
would return (1 + 2 + 3) = 6. Such a class and its public static method would look as
follows:
int total = 0;
int i = 1;
total += i;
i = i + 1;
return total;
An applet to make use of this class method, without creating an instance of the class, is
as follows:
import java.applet.Applet;
import java.awt.*;
int x = 20;
int y = 20;
Notice how there is never an instance of the class Counter created. The class method
is invoked through the sending of a message to the Counter class itself:
Counter.addUp( 4 )
Exercise 6 — DiceThrower
In Java, it is usual for classes to be grouped into a package, i.e., a set of related
classes. A `packaged' class must be identified by its package name and class name, or
the 'import' instruction must be used to tell the compiler to look for classes in a particular
package.
You may have noticed at the start of many of the example programs a line like this:
import java.applet.Applet;
This is an indication that the program will make use of the class Applet which is in the
package java.applet.
The Java developers have designed the compiler to automatically import the standard
Java package java.lang. This defines important classes such as String. You will
need to refer to a Java reference, such as Deitel & Deitel or the on-line documentation
to identify what to import to use those classes that are not 'built-in' through automatic
importing.
import java.awt.*;
This means 'search every class in the package java.awt'. When the compiler comes
across a class it does not recognise in the current program, it will see if it exists inside
the package java.awt.
If we did not use the import line, the program would still compile as long as I wrote
If you are creating a large, sophisticated program, you may well want to group your
classes up into packages of related classes.
Summary of terminology
For reference, I have included a table comparing the terminology used in object-
oriented modelling and object-oriented programming. In the table below all the terms on
a given row are equivalent, or almost equivalent. The terms shown in bold are the
preferred terms for Java programming, as defined by the Java language specification
(available at http://java.sun.com/docs/books/jls/html/index.html)
object-oriented modelling Object-oriented programming
generalization/specialization inheritance
specializes extends
Activities
(1) Create a class Shape which defines the following class constants:
numSidesSquare = 4
numSidesTriangle = 3;
numSidesPentagon = 5;
(2) Write an applet to test your class constants, that displays the following output on
screen:
class Shape
public
since they are not much use if they are not readable by objects of other classes
static
Each constant is constant (i.e. an unchanging variable) because it is defined as:
final
int x = 20;
int y = 20;
int n1 = Shapes.numSidesTriangle;
int n2 = Shapes.numSidesSquare;
int n3 = Shapes.numSidesPentagon;
In an earlier unit you will have written a program to calculate the factorial of a number —
a model answer is Factorial1.java.
Modify this program so that it uses a class called Factorial to do the calculation. This
class should have a method called calculateFactorial() which contains the code to do
the calculation itself. In the program's main class, you should get the answer by
executing:
Factorial.calculateFactorial(50);
Discussion/Suggested solution to Activity 2
Factorial class
Factorial2.java
// Factorial1.java
// A program that works out the factorial of 50, that is, 50 * 49 * 48.... * 2
* 1.
// This version implements an operation called factorial' that can get the
factorial
// of any specified number
// Kevin Boone, June 1999
import java.applet.Applet;
import java.awt.*;
public class Factorial2 extends Applet
{
public void paint (Graphics g)
{
g.drawString ("Factorial of 50 is...", 20, 20);
double result = factorial(50);
g.drawString (Double.toString(result), 20, 40);
}
// Here we define the `factorial' operation
public double factorial (int factorialRequired)
{
double result = 1;
Note, the calculateFactorial() method was made static because there is no reason
to need to create an instance of class Factorial in order to calculate a factorial.
Discussion of Exercise 1
Examples of classes
Here are a few examples. You may well have come up with many more, and many
different examples;
Lightbulb
Car
Compact disk
Audio CD database
Control equipment for any production plant that uses valves, e.g., chemical process
Complete the following statements that summarise the concepts explored in the
'Classes' section of this unit:
Program instructions can only exist inside _______, which are inside classes.
Classes do not usually have to be explicitly removed — the Java system gets rid of
them when they are no longer accessible. This is known as _______ __________.
Discussion of Exercise 2
Program instructions can only exist inside methods, which are inside classes.
Classes do not usually have to be explicitly removed — the Java system gets rid of
them when they are no longer accessible. This is known as garbage collection.
Methods in classes can (and usually do) create new objects.
Create a Java source file defining a Person class with the following variables and
methods:
Variables
Methods
`get' and `set' methods for the inspection and setting of the `height' variable
Discussion of Exercise 3
Class definition
The class overall needs to look as follows (remember, class names should start with a
capital letter):
class Person
// variables
// methods
// variables
int age;
double height;
String name;
(remember, convention dictates that variables start with a lower case letter).
return type
identifier
arguments
statements to be performed
When a method does not return a value, its return type is the keyword void.
The 'set' method for the height variable will return no value (void), can have any valid
identifier, we shall call it setHeight following the convention to have a set method
named with "set" followed by the variable name, and will have one argument — the new
value of height. The action to perform is simply to assign the value of the argument to
the variable height:
(remember, except for constructor methods, methods are spelt with a lower case first
letter).
The 'get' method will return a value, since this is the role of 'get' methods. The type of
value returned will be that of the height variable. The identifier of the method follows the
convention of "get" followed by the variable identifier (i.e. getHeight). The method
requires no arguments, so there is an empty argument list between the parentheses '()'
— note that a method definition must have parentheses following the method identifier,
even when there are no arguments.
The action for the method is to return the value of the height variable:
double getHeight()
return height;
}
The whole class looks as follows:
class Person
// variables
int age;
double height;
String name;
// methods
height = newHeight;
double getHeight()
return height;
Add to your Person class from Exercise 3 — Class definition a constructor that requires
arguments of age and name, and uses these arguments to set the initial values of these
variables.
Discussion of Exercise 4
Constructor method
The constructor method does not return a value (void), must have an identifier the same
as the class (i.e. Person), and has arguments of the new age and name. The actions to
be performed are to assign the values of the arguments to the variables for the new
object:
// constructor
age = newAge;
name = newName;
We have named the arguments appropriately since they will have the new values to be
assigned to the object's variables when created.
class Person
// variables
int age;
double height;
String name;
// methods
// constructor
age = newAge;
name = newName;
height = newHeight;
double getHeight()
return height;
Complete the following statements that summarise the concepts explored in the
'Packages and Imports' section of this unit:
Discussion of Exercise 5
Methods Summary
A constructor method is indicated by a method that has the same identifier (name) as
the class.
It is common practice to provide every class with a both a constructor and a toString()
method.
Exercise 6 — DiceThrower
/*
DiceThrower.java
This applet simulates the rolling of a pair of dice. Whenever the user
clicks the mouse in the applet window, a number pair of numbers is
displayed. This display looks something like this:
(the image will only be visible if you are looking at the HTML version of
this program).
There are three classes making up this program. The main class, which is
a sub-type of applet, creates two objects of class `Die', of for each
Die to display. When the `paint' operation of the applet is called, it
in turn calls the `draw' operation in each of the Die object.
A not on English usage: one `die', two `dice'. The singuar form of `dice' is
`die'. So we define an object called `Die' (not Dice) as it is responsible
for drawing a single die. Sorry about this, but I didn't invent the
language.
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
/*
DiceThrower class
*/
/*
paint
As always, `paint' is called automatically whenever the display needs to
be updated. In this case the applet only paints the text message
`Click mouse anywhere to throw dice again'. The real painting is done
by the `draw' operation in the Die class
*/
public void paint (Graphics g)
{
die1.draw(g);
die2.draw(g);
g.drawString ("Click mouse anywhere", 55, 140);
g.drawString ("to throw dice again", 65, 160);
}
/*
init
init is called automatically when the Applet is initialized by the Web
browser
or applet viewer. In this case we create two Die objects. Their values are
stored as attributes of this class. We also create an object of class
DiceThrowerMouseListener which will detect when the user has clicked the
mouse on this applet.
*/
public void init()
{
die1 = new Die();
die2 = new Die();
// `throw' the dice so that they start off with random numbers
showing
throwBothDice();
DiceThrowerMouseListener diceThrowerMouseListener =
new DiceThrowerMouseListener();
diceThrowerMouseListener.setDiceThrower(this);
addMouseListener(diceThrowerMouseListener);
}
/*
throwBothDice
This operation calls the `throw' operation in each die object and then
calls `repaint' to cause the display to be updated. In effect, repaint()
leads to `paint' being called
*/
public void throwBothDice()
{
die1.throwDie();
die2.throwDie();
repaint();
}
}
/*
Die class
The `Die' class is responsible for throwing the die (i.e., generating a
random
number between 1 and 6, and drawing the spots in the appropriate
places.
For simplicity I assume that there are only 7 possible places where a `spot'
can
be displayed, and I have numbered them from 0 to 6, as shown in the diagram
below:
0 1
2 3 4
5 6
So the number `1' is shown by drawing spot `3'. The number `2' is shown by
drawing spots `0' and `6'. The number `3' is spots `0', `3' and `6' and
so on.
*/
class Die
{
/* Attributes of the Die class */
int topLeftX;
int topLeftY;
/*
throwDie
Generates a new random number for the Die to display.
The random number is between 1 and 6
*/
public void throwDie()
{
/// QUESTION: why do we multiply by 6 and add 1?
// perhaps try a few other numbers and see what
// happens. You may need to look up what
// range of values Math.random() returns
numberShowing = (int)(Math.random() * 6 + 1);
}
/*
draw
Draw the spots on the die, according to the value stored in the
attribute `numberShowing'
*/
public void draw(Graphics g)
{
/// QUESTION: why do we pass `g' to each drawSpot
// operation, as well as the spot number?
switch(numberShowing)
{
case 1:
drawSpot(g, 3);
break;
case 2:
drawSpot(g, 0);
drawSpot(g, 6);
break;
case 3:
drawSpot(g, 0);
drawSpot(g, 3);
drawSpot(g, 6);
break;
case 4:
drawSpot(g, 0);
drawSpot(g, 1);
drawSpot(g, 5);
drawSpot(g, 6);
break;
case 5:
drawSpot(g, 0);
drawSpot(g, 1);
drawSpot(g, 3);
drawSpot(g, 5);
drawSpot(g, 6);
break;
case 6:
drawSpot(g, 0);
drawSpot(g, 1);
drawSpot(g, 2);
drawSpot(g, 4);
drawSpot(g, 5);
drawSpot(g, 6);
break;
}
}
/*
drawSpot
The drawSpot operation draws the specified spot. The numbering of the
spots is described above
*/
/*
DiceThrowerMouseListener class
This class has an operation called `mouseClicked' that is called
automatically whenever the user clicks the mouse over the
object that this object is associated with. In the definition
of the main applet (above) we call `addMouseListener' to make
this association. These issues will be described in more detail
in a later unit of this course.
*/
class DiceThrowerMouseListener extends MouseAdapter
{
DiceThrower diceThrower;
There are a number of questions embedded in the comments in this code listing —
answer these questions.
Discussion of Exercise 6
DiceThrower
1. setTopLeftPosition is an method in class Die. It sets the position on the screen that
the die will appear. In this application, only the horizontal position needs to be set, since
both dice are at the same vertical position. However, it makes the class Die more
general if both horizontal and vertical positions can be set
2. The variables topLeftX and topLeftY are private to class Die. This is the default in
Java. To make them public would require that the public keyword be specified
3. setTopLeftPosition simply sets the values of the variables topLeftX and topLeftY.
These variables are used in the paint method to set the position where the spots will be
drawn
5. g is an object of class Graphics. This object contains the basic methods needed to
draw graphics. As this object is passed automatically to the paint method by the Java
system, it makes sense to pass it along to every method that does drawing. It is
possible to create a new Graphics object whenever required, but it is more efficient not
to.
Complete the following statements that summarise the concepts explored in the 'Packages and Imports'
section of this unit:
It is usual for classes to be grouped into a _______, i.e., a set of related classes.
A packaged class must be identified explicitly by its _______ ____and _____ ____.
Alternatively the ______ instruction tells the compiler to look for classes in a particular
package.
The use of the asterisk '*' after a package name, rather than a specific class name,
instructs the compiled to ______________
_______________________.
Discussion of Exercise 7
It is usual for classes to be grouped into a package, i.e., a set of related classes.
A packaged class must be identified explicitly by its package name and class
name.
Alternatively the import instruction tells the compiler to look for classes in a particular
package.
The use of the asterisk '*' after a package name, rather than a specific class name,
instructs the compiled to search the package for classes it does not
recognise.
Review Questions
Review Question 1
In what ways might the object-oriented strategy adopted by Java be superior to this? In
what ways might an object-oriented approach be a disadvantage?
Answer
Since there are strict rules about how classes can interact with one another, then a
program based on classes will tend to exhibit encapsulation. Good use of encapsulation
leads to the following advantages:
In activity 1 I asked you to think of computer applications that would need to know about
specific objects. Probably you were able to think of two or three different applications for
each object. However, in all likelihood the things they would need to know about each
class are likely to be similar. This is one of the great strengths of object orientation. By
focussing on the `real-world' features of the application, we encourage the selection of
components that can readily be re-used in new applications. As developing good quality
software is difficult and expensive, the ability to re-use it has to be an advantage.
A sophisticated piece of software may have millions of lines of program code. If you are
new to this subject (and as a student on this program you probably are), then you are
probably struggling at present with the detailed syntax of the Java programming
language. After a few months this becomes second nature, and you can begin to
struggle with the problem of managing and maintaining large programs. The class is a
natural way to group together those parts of a program that naturally belong together.
So in the class called BankAccount we would put all the program instructions concerned
with the management of bank accounts.
Easier fault-finding
If there is a fault in a program, the fact that classes are self-contained means that it is
relatively easy to decide which class is at fault.
Improved teamwork
Discussion Topics
It is normal for a Java class to restrict access to its class and instance variables. For
example, if class X wants to get the value of a variable in class Y, it would normally call
a method (an accessor method) in class Y to do this.
Although it is possible to read an instance variable in a different object directly, like this:
Working this way leads to a slight increase in the complexity of every class, as accessor
methods need to be provided to control access to a number of variables.
There are two important reasons, a philosophical one and a practical one.
Philosophically, an object of a class `owns' its instance variables, and can supply or
withhold details as it sees fit (or rather as its programmer sees fit). Similar examples
abound in real life. If I want to know a person's name I can either ask him, or grab his
wallet and look through it for his name on a credit card. Clearly the latter approach is
rather invasive, and would not normally be acceptable. Of course, if I ask a person his
name he could lie, and in the same way the Calendar object could respond to
get (Calendar.MINUTE);
Practically, if the only way to get access to the current number of minutes past the hour
is by calling the appropriate `get' operation, then it is very easy for the programmer to
control how that data is stored and used. Suppose, for example, that the Calendar class
stores its time internally as the number of seconds past midnight. In this case there is
no minutes variable; the class calculates it on demand from the number of seconds. If I
want to change the internal operation of Calendar, provided other classes get access to
it by its `get' methods, there is no problem in doing this. If other classes get access to its
instance variables directly, it can become very difficult to modify the class without
affecting other classes.
At the start of this unit the ideas of 'concrete' and 'conceptual' objects were introduced.
It was suggested that a 'picture' was a 'conceptual' object, not a concrete one. However,
it could be argued that a picture is 'concrete': you may have a photograph which you
can hold in your hand, so obviously it is a 'real' thing. On the other hand, you can copy a
photograph many times, and each copy would be the 'same' picture. So in that sense
there is a 'picture' which is different from the physical embodiment of the picture. Which
point of view do you think is correct here? Is a picture concrete or conceptual? This is
not an abstract academic discussion; in some computer applications an understanding
of this issue is essential to efficient use of the
computer.
The purpose of this exercise is to help ensure that students are familiar with the
distinction between class and object, and between 'conceptual' and 'abstract' classes. It
is probably a good idea to steer discussion away from the philosophical issues of
platonic reality, etc., that tend to surface at this point if the students are well-read.
In some senses this issue is related to the distinction between software and hardware. If
I buy a piece of software from a supplier, I may receive a CD-ROM. But the CD-ROM
containing the software is different from the software itself. I could receive the same
software in a number of different forms, e.g., on a floppy disk. I could even download
the software from a Web site in which case there is no physical medium of delivery at
all. The software is `conceptual' while the medium it delivered on is `concrete'.
Why is this important? Consider the case of a records system for a large library. It has,
say, 100,000 different books in stock, of which there are an average of 5 copies of each
book. A book has both a conceptual and a concrete component. A particular copy of a
book is definitely concrete: it is a real, physical object. This is what a library user will
borrow and pay fines on if not returned on time. However, the `conceptual book' is the
content of the book, its author, date of publication etc. We could represent this in Java
as follows:
class ConceptualBook
class ConcreteBook
ConceptualBook conceptualBook;
boolean onLoan;
int daysLate;
100,000 * 6 + 500,000 * 3
or 2.1 million variables. This will require a lot of memory. However, consider the case
where we don't distinguish between the concrete and conceptual books. Now we need
to define the class like this, as one class must contain all the data:
class Book
boolean onLoan;
int daysLate;
So now we have 8 variables in each class, and 500,000 objects of this class. So the
system must now deal with 4 million variables.
In your Learning Journal write up your experience of your learning on this unit. Say
what you thought was good or bad, what you had difficulty understanding, and how you
resolved your problems.
Additional Content
Destructor methods
Objects can be created and destroyed by programs. Part of a class definition can be a
method, called a destructor, that is executed when the object is to be destroyed. In Java
such a method must have the identifier finalise, and an example might be:
This particular destructor method is not very useful, however, it illustrates that it is
possible to add a destructor method to classes, so that actions can be performed by an
object being destroyed.
Abandoned objects
If an object is created, but no variable refers to it, then this object is actually wasting
computer memory, since it can never be used. It is not possible to 'search' through
computer memory for objects of a given class — who knows, your program might find
part of some word processor document, or part of a database, stored in memory by
some application, and think it was an object
Of course, it is very rare that a situation might occur where objects would be created
and never referred to. However, what is much more common is that objects get created,
refereed to by a variable, used for some purpose, and then the variable is used to refer
to some other object. This is the abandoning of objects.
When a system needs memory, it can perform a task called garbage-collection to find
all abandoned objects, destroy them (i.e. call their destructor methods) and free up their
memory for other uses.
Subclasses
There are some additional exercises in this section. They provide an opportunity to
recap on some general object concepts, and explore some notation ideas for graphical
representing object concepts.
In implementing a piece of software, all of the following are potentially ‘bad’ classes.
By 'bad' it is meant that they may not be useful things to represent as classes in a
program.
Bad classes
Central processing unit: this is a bad class because it's got nothing to do with what the
program does. It's about what the computer does. In programming, we don't normally
have to be concerned what's going on inside the central processing unit. Exception: a
piece of software for designing computers.
Joe Bloggs, a customer: a particular customer is an object, not a class. The class
Customer might be useful.
Linked list: if you've never heard of a linked list, then this is a good reason for not
allowing it as a class. Classes are useful for explaining how programs work to a non-
specialist. The use of classes that no-one understands does not help with this aspect of
design. In fact, a linked list is a mechanism for a computer to group together lists of
data. Again, at the design stage you should not be worrying about how your program's
data is structured internally.
Data file: essentially this is bad for the same reason as `linked list'. In addition it is
vague. What is `data'? Exception: a program for checking for computer viruses. Files
are very important here.
Miscellaneous: this is a bad class because it is unnecessary. If you have used object
oriented design correctly, then everything should fit neatly into one class or another.
You should have no bits left over that have to go into a `miscellaneous' class
The reference site for UML notation is held by Rational Software Inc. Try
http://www.rational.com/uml. However, their documents are extremely long and
complex, as you would expect for the specification of a major modelling language. More
useful sites on UML can be found by doing a search for `UML overview' or `UML
tutorial'.
Before proceeding to the next unit you should work through this set of review questions.
When you have completed the questions you will be able to obtain the answers for
future reference.
Your performance with these questions will not affect your grade for the module, but
may be monitored so that your tutor can be alerted if you are having difficulty.
Please contact your tutor if you feel you have not done as well as you expected.
Now complete the end of unit review questions online.