[Previous]
Into Java, Part II - Lab Work - by Simon Gronlund
[Next]
OS/2 e-Zine!

Go to a Printer Friendly version of this page



Lash Back! - Join in the ongoing public discussion with our interactive forum. Be frank, be vicious, you can even be anonymous.

- or -

Blast Back! Send a private message directly to Simon Gronlund with your thoughts:

Contact OS/2 e-Zine!

Sponsored links:

Summary: Simon continues his exploration of the Java language... with sample code.

Into Java, Part II - Lab Work

By: Simon Gronlund

Welcome to the hands-on part of this Java column. Up to now we know of classes, methods, constructors and data fields, and that really seems to be a lot, isn't it? In spite of that, we cannot rush of and use the more subtle parts of Java this time, we will control ourselves and stick to text mode. Nevertheless, we will make ourselves one class and a simple test application to bring our class alive.

A few more details will be discussed, like header, body, toString, new and a few syntax details. Yes, I am well aware of this being a veeeeery long column. Isn't it always so, at first there are quite a few things to get the hang of, and later it doesn't feel much. A few years back I started out learning the Persian language, a spoken language, not a programming language. In the beginning the curve was steep, believe me. Not a single character was alike our western alphabet, but as soon as I got hold of it, everything seemed much easier. So this Java column will, too.

Again I'd like to repeat that a basic programming knowledge is assumed. Most of the code is understood by the context, but a good source of general programming knowledge is the REXX column by Dr. Dirk Terell, began in OS/2 e-Zine! v3n4. The general coding style is coherent within this family of programming languages.

The Bank Account

In any object oriented language, the Bank Account (BA) is one of the most commonly used illustrations to discuss the basics of classes and objects, and we will use it as well. A BA is an object, in this case a record of our means. Creating a BA as a class is wise. Whenever the senior accountant finds out that we have to change a few things in the BA's, it's an easy task. Do it once in the classes affected, and we're almost done. In languages not object oriented, we have to search most of the code to find out if we need to adjust anything.

The first thing we always will do is to make a rough outline of what we expect of a new class. This technique is always useful, my first computing lecturer pointed out that "application code is 90 percent penciled and the remainder is typed by the keyboard". I start to believe it's true, most of the 90 percent anyway. If we skip this part we soon will have to pay with blood, sweat and tears.

Any BA uses a name for the account holder, a balance, a way to deposit and redraw money. For a lite BA that's all, the first pencil part is done. Let's make ourselves a new folder within our OS/2-eZine_Java folder, named Bank. Now we will continue with writing ourselves a class named BankAccount using our favorite editor, and we will save it into this new folder using the name BankAccount.java.

import java.io.*;
public class BankAccount {
    protected String name;
    protected int balance = 0;
}
// 1
// 2
// 3
// 4
// 5

Line 1: A statement that informs the Java compiler to import from the io class of the common Java environment whenever needed to. In fact this line is redundant. Our applications will always have full access to the io class, and a few more classes -- the fundamental Java classes -- but we use this line more as an illustration.

Line 2: The class header, providing the class name, BankAccount. The public part tells the system that anyone, practically, could use this class to create their own instances from. In spite of that, anyone will not have access to our objects, as we soon will see.

At the end of line two there is the { , the beginning of the body of the class. Along with the finishing } it encompasses the entire class body. This far the body is nothing more than two data fields, the name and the balance.

Line 3: We assume name will be spelled with alphabet characters, as we usually write names. Therefore, we declare name to be of the String class. At this time we don't know anything about the holder, even more less his name, hence we do no initialization.

Line 4: Preferable we will only handle real dollars and never cents, that's why we declare balance to be an integer, an int. Unless this bank will credit us with an initial amount as a welcome gift, we must initiate the balance to zero at the creation.

This initialization to zero is not necessary to do here, but it's convenient. Even though the bank will use welcome gifts in the future, or only for certain BA's, it doesn't hurt to initiate to zero. On the contrary, think of what could happen if we didn't initiate to zero, and we'll forget to give balance a value upon creating an instance of this class. What will happen if we later will try to redraw money from it? There is no money, so we couldn't rob the bank, but the redraw method will get in trouble, since it will find null, which is more nonexistent than zero.

Protected

What about that protected at the beginning of line three and four? We said that the class is made public, so anyone could use it to make their own BA's from. But we will certainly not let anyone else touch the stuff we have within our BA object. Therefore, we hide the information within the class, we protect it so that no-one could not "see" or manipulate the data fields. Now only methods within the class could change or access the data. There are other access modifiers as well, that we'll discuss when we encounter them.

Methods

Now we have to make ourselves several methods, a few already mentioned, which we could start with. Any method is put within the body of the class. In the same manner as a class, methods too have headers, and bodies encompassed by { and }. Let's start.

The very first method, a method we will always use, is the toString(). It is an easy method to write and it's self-explanatory. Why use this method? If any thing goes wrong Java tries to give us a clue of what happened. At that time it will present us the brief, or long if we like to, information we have stuffed in toString(). This is why it's a good practice to use toString(), it will surely save you expensive hours debugging your code.

toString() will not print by itself through the System.out, but it's invoked by the Java run-time environment. Or by ourselves if we like to. Usually it returns a String, telling us a few details, and so give us an identifying description.

public String toString () {
    return "Class BankAccount: " + name;
}
public void deposit (int value) {
    if (value >= 0)
        balance = balance + value;
    else {
        System.out.print("You cannot deposit " +
        "a negative sum of money: ");
        System.out.println(value);
    }
}
// 6
// 7
// 8
// 9
// 10
// 11
// 12
// 13
// 14
// 15
// 16
// 17

Line 6 and 9: As the class is, methods are made public. A future column will discuss protected methods. String tells the compiler that toString() will return a text string, and void that this method will return nothing. Why so? you may ask. That way the compiler makes sure we know what we are doing. We will not be allowed to request a String from a method that will not return nothing.

What about the (int value) part? As discussed in the theory chapter, this is the information receiver. We have to tell the compiler what kind of information to expect, if any. We will leave it with that for now, but there are really useful concepts out of that. Here we say that the sum to be received, and assigned to value, must be an int.

Please note, whenever we mention a method, usually we will write x(), and the parenthes denotes we are discussing a method. This is because we are allowed using exactly the same name for data fields as well, e.g. price and price() are a data field and a method respectively.

Line 10 and 12: Here we do a simple if - else test, and I think the purpose is obvious. It's always good to know that the cashier cannot mistakenly do a typo of minus one million dollars, and drive you bankrupt the very week you sold your Intel shares. She will be directed to line 12 if she did wrong.

An else is not necessary, we use it only whenever we actually need anything to be done as an alternative. Another time we will learn more about how to use if, else, else if and a few more.

Please note the { } encompassing the body of the else. These are necessary as soon as more than one statement are to be executed. Therefore, we do not need them for if, which only have one statement.

Line 11: Add the sum of money to the balance. Within the object the data fields are visible. They are only invisible to objects and methods outside the object they belong to, because we made them protected.

Line 13: A line with this text will be printed if the cashier made that typo, and nothing will happen to your balance. Try to deposit again.

Line 15: The wrong value will be printed. Compared to the HelloWorld, we see that it would have been possible to use the + sign at line 14, to add further information to be printed. The only reason of this new line is to show a tiny difference between print() and println(). In the HelloWorld app we used print(), but then we had to finish the line with the "\n" operator, 'end-of-line'. The subtle difference between these two methods is that println() does a line feed by itself.

What is a BA if we cannot withdraw money from it? Let's continue:

public void withdraw (int value) {
    if (value <= balance && value >= 0)
        balance = balance - value;
    else {
        System.out.print("You cannot withdraw " +
        "that sum of money: ");
        System.out.println(value);
    }
}
// 18
// 19
// 20
// 21
// 22
// 23
// 24
// 25
// 26

Line 18: Despite we withdraw money, it isn't the withdraw() method that hands them to us. Hopefully we'll get them from the cashier. That's why it have void as return type, but withdraw() will justify the balance.

Line 19: There is no big difference from the deposit(): if - else line, except two things, a small matter of taste, and a great opportunity to discuss how comparisons will evaluate. The most obvious comparison is whether we try to withdraw more money than we are credited. If so, we are directed to the else alternative printout. Maybe you ask, "Why not tell the balance at the same time?" The simple answer is, don't let a method do more than it's supposed to do. It's better to create another method doing that part of the work, a querying method that we could use for other purposes as well. Admittedly, it's a matter of taste.

In the middle of the comparison line there is a double &&. This notation is used for AND. It will guarantee us that both comparisons are true, we still have credit left AND the amount we want to withdraw is correctly typed, a negative value would actually be a deposit. Cool, but unfortunately the bank will soon find out.

Why did we write the "do we have enough money" check before the "typo" check? Despite all those bugs we see too often, developers actually have been thinking. One thing with an AND comparison is really trivial: both left side and right side have to be true. If the if finds the left part false, it is redundant to check the right side, so it does not. Smart, he? Then it's up to us to figure out which comparison is most frequently false, and put that one first, and that way saving a few CPU cycles.

Exactly the same reasoning is true about OR comparisons, it's enough finding one true left OR true right. Put the most frequently true to the left. Every composite comparison will evaluate from left to right, and stop whenever a conclusion is made.

public String name () {
    return name;
}
public int balance () {
    return balance;
}
// 27
// 28
// 29
// 30
// 31
// 32

These two useful methods will give us an opportunity, the only one indeed, to have a look at the two data fields. Since the fields are protected it isn't possible for anything, except public querying methods within the object, to return those data.

Line 27 and 30: The double parentheses ( ) are empty, nothing is needed to be able to return the data stored in the data fields. To show the difference, we will make a method used whenever we would like to change a name. Maybe the customer got married, or whatever.

public void name (String newName) {
    name = newName;
}
// 33
// 34
// 35

How come both methods could use the same name name()? Because the compiler is viewing them as two different methods, actually name(void) (although void is not written, but understood) and name(String). As Jones, James is not Jones, John. Powerful, isn't it?

Now we have a complete class, with two data fields and six methods. Everything is written within the encompassing { } parentheses of the class.

Constructor

Could we now make an instance out of it? Actually not! The constructor will come to play an important role. At first, we could always make the basic constructor, which takes no parameters at all. That's the easiest one, but not always to recommend. Think of a database record, why create a new record but without data stored in it. Mostly we press Cancel those times, don't we? Other times it suits our intention very well to make zombie objects, and later bring them alive.

public BankAccount () {
    super();
}
// 36
// 37
// 38

Constructors looks a lot as methods, and they are in many respects. But we cannot invoke them, as we invoke methods. And they never returns anything. Constructors will only listen to a special operator, the new operator discussed below. And they are named by the class name as we see.

What is super()? Without going too far, we can say that it's a call to the System parent, to let the environment constructor create a new general object. super() is sometimes not written explicitly, but it is always there, it's implied. If we from now on leave it out, it's understood.

Anything else isn't done within this constructor. The object is now created, we have our BankAccount instance at hand. However, if we didn't initiate balance to zero at line 4, we can do it here, in the constructor. If so, we have to add a line balance = 0; within the body of the constructor.

public BankAccount (String n) {
    name = n;
}
public BankAccount (int amount) {
    balance = amount;
}
public BankAccount (String n, int amount) {
    name = n;
    balance = amount;
}
// 39
// 40
// 41
// 42
// 43
// 44
// 45
// 46
// 47


With these three more constructors we can create a BA any way we like, but maybe that one with a balance but no owner is more for Swiss banks to use. The BankAccount class became 47 lines long, but it surely is a lite BA. It's clear anyway that classes don't need to be several hundreds of lines. A few weeks ago I made one only 10 lines long, but on the other side, I needed about 2,500 instances of it for my lab work.

To make this class work, we have to use the code found in MyBank.java. Objects like BA are rarely running by themselves. MyBank is a short test application. It will create a few BA objects, assign them owners and do a few transactions.

new

OS/2 - Window
[X:\]javac MyBank.java
[X:\]java MyBank
...
...
...
[X:\]
Compile and run your Home Java bank.
The only new thing in MyBank is the new operator. Above I stated that the constructors only listened to a special operator, that's new. This operator will order the creation of a new instance of the actual class. It's as simple as that. Next time we will discuss it a little bit more.

After you have edited MyBank.java, copy it into the Bank folder. As we did the last month, we compile this Java file into Java byte code, and then run it.

Why don't we compile the class BankAccount.java? Simply because javac is smart enough to understand that it needs to compile that class too, since it is used by the MyBank class. That's convenient whenever making minor editing on any class in a bigger app, only run javac on the main class, and the compiler finds out by itself that only a few *.java files are actually edited, and compiles them.

Please, experiment with MyBank as much as you like, nothing will be crashed or screwed up, the nasty and foul-mouthed compiler will amuse you. Ctrl + C will stop every process out of control.

Summary

We have labored hard this time, examining the difference between class and object, the blueprint vs. the real object, created by the new operator invoking the constructor. Both classes and methods are made by headers and bodies, the headers providing vital information to us and the compiler.

The data fields have to be declared and maybe initialized. Initiation could be made at the declaration, or in the constructor. Neither is absolutely necessary, we might give data field's values later on.

The importance of the penciling phase and of toString() was emphasized, since it will save us a lot of late nights and brain work in the future. With this knowledge we now are prepared to advance into the Java world of living creatures, objects and ways to master them all. Have a nice adventure. See you next month!

Introduction
Java Theory
Java On-Line Manual
[Previous]
 [Index]
 [Feedback]
 [Next]
Copyright © 1999 - Falcon Networking ISSN 1203-5696
November 1, 1999