An object is a component of an object-oriented program that encapsulates internal properties and responds to messages.
When an object receives a message, a message handler function is invoked. (Different types of messages may invoke different message handlers, or if no appropriate message handler can be found, some sort of error results.) The message handler responds to the message by some combination of:
1. Modifying its internal properties
2. Sending messages to other objects
3. Returning information to the sender
The properties of an object are also called its attributes or its state.
Program objects are actually composite variables. Often (but not always), attributes correspond to the values stored in the object's component variables. These component variables are also called fields (Java) or member variables (C++, C#).
Encapsulation means contains, but also implies access to attributes is strictly controlled by the object.
Message handlers are also called methods (Java) or member functions (C++).
The behavior of an object refers to the way it responds to all possible messages. In other words, the object's methods.
Here is a notation commonly used to refer to a particular property of an object:
objectName.propertyName
Of course this notation only produces the value of this property if the object grants the appropriate access rights.
Here is a notation commonly used to send a message to an object::
objectName.messageName(argument1, argument2, ...);
Here are some notations from other languages:
objectName->messageName(argument1, argument2, ...);
send(objectName, messageName, argument1, argument2, ...);
Assume checking and savings are names of objects representing bank accounts. The principle attribute of a bank account is its balance:
checking.balance = balance of checking account
savings.balance = balance of savings account
Bank accounts can receive deposit and withdraw messages. Of course these messages include the amount of money to be deposited or withdrawn. When a bank account object receives a withdraw messages, it invokes a method that deducts the specified amount from its balance. When a deposit message is received, a method is invoked that increments the balance by the specified amount:
savings.withdraw(500); // savings.balance = savings.balance - 500
checking.deposit(500); // checking.balance = checking.balance + 500
All Java objects reside on the heap. Java programs never access these objects directly. Instead, it accesses an object through implicit pointers called references. This results in reference or sharing semantics for assignments. For example, the assignment statement:
savings2 = savings;
results in savings and savings2 being names for the same bank account. Reference semantics is much simpler than copy semantics, but can lead to confusing aliasing problems that programmers must always guard against:
savings2.withdraw(500); // savings.balance -= 500!
A class is a template (i.e. a pattern or schema) for creating objects. The act of creating an object from a class is called instantiation. For this reason, objects are sometimes called instances. A class declares the attributes its instances posses, the types of messages they may receive, and the associated methods that will be invoked.
Example (Java):
class Account {
// fields:
double balance = 0;
// methods:
void withdraw(double amt) {
if (amt <= this.balance) {
this.balance -= amt;
}
}
void deposit(double amt) {
this.balance += amt;
}
}
1. Note comments and style conventions (indentation, alignment, etc.).
2. Note initialization of field.
3. The signature of the withdraw method is:
void withdraw(double amt)
The method body is the block:
{
if (amt <= this.balance) {
this.balance -= amt;
}
}
4. Note the use of the implicit parameter, this, which refers to the object receiving the message. It is unnecessary to qualify fields and method calls inside a method body unless the method has parameters or local variables that shadow the fields. For example:
void deposit(double balance) {
this.balance += balance;
}
Implicit parameters can be useful. Assume the following class is supplied:
class IRS {
public static final double SUSPICIOUS =
10000;
public static void report(Account acct)
{
//audit acct holder!
}
}
Here is how an account might report a suspiciously large deposit:
void deposit(double amt) {
balance += amt;
if (IRS.SUSPICIOUS <= amt) {
IRS.report(this);
}
}
Objects are created from classes using the new operator:
Account checking = new Account();
Account savings = new Account();
The argument to the new operator is a call to the default (parameterless) constructor. Constructors initialize the fields of the newly created object. This eliminates the dangerous gap between variable creation and variable initialization.
Of course programmers can define their own constructors. A word of warning: this causes the default constructor to disappear. This can cause problems, because the default constructor is assumed to exist in many situations.
Example:
class Account {
private double balance;
public Account(double balance) {
if (0 <= balance)
this.balance = balance;
else
this.balance = 0;
}
// replace default constructor:
public Account() { this(0); }
// etc.
}