Spring Semester 2003


Lecture Notes Seven: Introduction to user-defined types. Classes.
You have now learned about the number and string data types of Java. Although it is possible to write interesting programs using nothing but numbers and strings, most useful programs need to manipulare data items that are more complex and more closely represent entities in the real world.

Examples of these data items are bank accounts, employee records, graphical shapes, and so on. The Java language is ideally suited for designing and manipulating such data items, or objects.

In Java, you define classes that describe the behaviour of these objects. (Classes are blueprints). You will now learn how to define classes that describe objects with very simple behaviour. This will be a very good start.

Let's create a simple class that describes the behaviour of a bank account. Before we can describe what a bank account is in Java we need to be clear (in plain English) what we mean by it.

In other words we have to sell it first. Exactly.

Well, consider what kinds of operations you can carry out with a bank account. You can:...
  • deposit money
  • withdraw money
  • get the current balance

Sounds like a bank account to me. In Java these operations are expressed as method calls.

So the set of methods that an object of type... ... BankAccount (sounds like a good name to me)

... will support, could be...
  • deposit
  • withdraw
  • getBalance

That's what BankAccounts do best! OK, before we get too euphoric we need to imagine such an object in action.

Objects are agents. Exactly. If they have methods (and most objects do) they have a behaviour, defined by what their methods can do. Now, how do you envision BankAccounts in action?

To start with, opening a new bank account should look like this:
BankAccount myChecking = new BankAccount();
... and the initial balance should be zero. How do you put, let's say, $1,000.00 in your account when you create it?

It would be something like this:
myChecking.deposit(1000.00);
Isn't this very similar to translate for Rectangles?

It is, indeed, the very same thing: we're translating the balance, in one dimension. How would you check your current balance?

I'd ask myChecking to report the balance, which I could then print:
System.out.println(myChecking.getBalance());
Isn't this very similar to printing the length of a String?

It sure is. As for the third method, I just realized I don't even need it... How come? What if you want to withdraw $300.00 from your checking?

I can already express that as: Then it won't be too hard though to come up with an extra method...
myChecking.deposit(-300.00);

.. that could be called as follows: This should make more sense to the user of your class.Are we done now?
myChecking.withdraw(300.00);

I think we are. These three methods form the complete list of what you can do with an object of type BankAccount, ... ... at least from the point of view of what we wanted in a bank account. We could certainly add methods to compute interest, etc., and enhance our model (or design), but for now...

... these three methods are more than enough for what we have in mind with this class. We want to implement it and see it used in a Java program.

Nothing less. For a more complex class, it takes some amount of skill and practice to discover the appropriate behaviour that makes the class useful and is reasonable to implement. We will learn more about this important aspect when we get to the chapter on "Object oriented design".

The behaviour of our BankAccount class is very simple, and we have described it completely. We can now implement it. Yes, let's go for it.

Although I think it's worth pointing out one thing, before we even write a single line of code of implementation... What is it?

In our description of the methods we have used objects of type BankAccount without knowing (or caring too much to know) about their implementation, which we are only now about to describe. Yes, that's an important aspect of object-oriented programming. But now, that we are completely clear on how to use objects of the BankAccount class, we really need to get started...

.. to describe the Java class that implements the described behaviour. I can get us started and in the following way:
public class BankAccount 
{ ...
  ...
} 

What do we put inside? Methods and data.

I don't understand what you mean by data. It's what makes tiggers remember where they have bounced last. Do you remember Rectangles?

I certainly do. Their diagrams were always containing four slots, in which we were writing the current values of their x, y, width, and height. That's the data that they have, which helps them remember where they are, and how big they are. Could you draw a diagram to illustrate what's happening after we create a new BankAccount?

The code would be... ... and the diagram?
BankAccount mySavings = new BankAccount(); 
Very good. What's that in which you put the initial balance of 0 (zero)?

It's a location, like we've seen for Rectangles. It looks like a variable, probably of type double or of a similar or related type. Each object must store its current state; for objects of type BankAccount the state is the current balance of the bank account.

Each object stores the state in one or more instance variables . An instance variable declaration consists of the following three parts:

a) an access specifier ... such as private

b) the type of the variable ... such as double

c) the name of the variable ... such as balance.

So far we have: ... and the diagram:
public class BankAccount
{ private double balance; 
  ... 
}
The balance field is all we need, as far as data goes.

Each object of a class has its own copy of the instance variables. We've seen that with Rectangles and it's the same with BankAccounts.

Each object has its own balance field. And in our example they hold different values.

Instance variables are generally declared with the access specifier private. That means that they can be accessed only by methods of the same class. In particular, the balance variable can be accessed only by the deposit, withdraw, and getBalance methods.

Let's write these methods in Java. OK. Let's start by defining them.

Yes, let's start by describing their headers. A method header consists of the following parts:

a) an access specifier ... such as public

b) the return type of the method ... such as double or void

c) the name of the method ... such as deposit

d) a list of the parameters of the method ... describing the method's initial data. Let's consider each of these parts in detail.

The access specifier controls which other methods can call this method. Most methods should be declared public. This way, all other methods in your program can call them. (Ocasionally, it can be useful to have methods that are not so widely callable, but we will look at that later).

The return type is the type of the value that the method computes. For example, the getBalance method returns the current account balance, which will be a floating-point number, so its return type is double. On the other hand the deposit and withdraw methods don't return any value. They just update the current balance but don't return it.

To indicate that a method does not return a value, use the special type void. Both the deposit and withdraw are declared with return type void.

The parameters are inputs to the method. The deposit and withdraw methods each have one parameter: the amount of money to deposit or withdraw.

You need to specify the type of the parameter, such as double, and the name for the parameter, such as amount. The getBalance method has no parameters. In that case, you still need to supply a pair of parentheses () behind the method name.

If a method has more than one parameter, you separate them by commas. And once you have specified the method header, you must supply the implementation of the method, in a block that is delimited by braces ({...}).

Putting all this together we have: Looks good.
public class BankAccount
{ public void deposit(double amount)
  { ...
    ...
  }
  public void withdraw(double amount)
  { ...
    ...
  }
  public double getBalance() 
  { ...
    ...
  } 
  private double balance;
} 

We now must provide an implementation for each method of the class. The implementation of these three methods is straightforward: when some amount of money is deposited or withdrawn, the balance increases or decreases by that amount.
public class BankAccount
{ public void deposit(double amount)
  { balance = balance + amount; 
  }
  public void withdraw(double amount)
  { balance = balance - amount;    
  }
  public double getBalance() 
  { return balance; 
  }
  private double balance;
} 

The getBalance simply returns the current balance. But wait: how can we use balance and amount without declaring or initializing them, as we said we should be doing every single time?

They are not method (local) variables. amount is a method parameter. When the method is called the value that will be passed to the method will be placed in a location called amount of type double that represents the initial data of the method. So amount, as a method parameter is like a variable... ... that is, a symbolic name that refers to a memory location...

... and will have been initialized when the method is called. How about balance?

It is also a variable, only not a method variable. It is an instance variable, visible to every instance method (such as deposit, withdraw, and getBalance). For these methods it is a (somewhat) global variable (which they share). Instance variables are initialized when the object to which they belong is constructed. In this respect they are really different from method variables.

They are initialized with default values, that depend on their types. Numbers, for example, are initialized with a value of 0 (zero).

Of course, you can also initialize them in a certain, customized way, if you define constructors. We will talk about constructors tomorrow.

Meanwhile let's see what we mean by return. Well, when you ask someone a question, and you pay for the question to be answered, ...

... you expect an answer, ... ... in return.

When a function, that is supposed to provide a value as an answer, has the answer ready... ... (by computing an expression, or by referring to a location where the final result has been stored, ready to be reported)...

... and when it wants to report it,... ... to whoever called it in the first place...

... it simply states that it's ready to finish and returns the value,... ... after which the function (or method, as it's known in Java) ends.

Interesting. So this is basically syntax. Very much so.

OK, can we see the full program? Yes, you need to have two classes.

One is the class we designed.
public class BankAccount
{ public void deposit(double amount)
  { balance = balance + amount; 
  }
  public void withdraw(double amount)
  { balance = balance - amount;    
  }
  public double getBalance() 
  { return balance; 
  }
  private double balance;
} 
While the other one has the main.
public class Experiment 
{ public static void main(String[] args) 
  { BankAccount a = new BankAccount(); 
    BankAccount b = new BankAccount();   
    a.deposit(200); 
    b.deposit(300); 
    System.out.println(a.getBalance()); 
    System.out.println(b.getBalance()); 
    a.withdraw(100); 
    b.withdraw(200); 
    System.out.println(a.getBalance()); 
    System.out.println(b.getBalance()); 
  }
}

Place them in the same directory... ... compile and run them, and enjoy!

Not many problems couldn't be much harder, ... I think. I agree. By the way, ...

"I hope you're not much tired, are you?" "Nohow. And thank you very much for asking!"

Last updated: Jan 31, 2002 by Adrian German for A201