1.  Classes and Objects

Learning Outcomes

On completion of this section you will know:

Download pdf version

Introduction

Java is an object oriented language, yet we have progressed this far without ever mentioning the word object.  We have managed to do this by exploiting the fact that the method main can run without creating an object. Thus we were able to do very limited programming that still enabled us to become familiar with the essential ideas and structures that underlie all programming such as:

Variables

Operators and operands

Decision constructs

Loop constructs

Methods

Now that we have got those under out belt we shall move on to examine the first OO topic which is objects themselves and their underlying classes.  In order to do so we shall revert to the payroll application we had examined before, but this time we shall put all of the processing into a class.

 A Class for a Payroll Application

Go to top

Listing 6.1 below shows our payroll application structured as a class.  The processing is simplified somewhat. There is no superannuation calculation, for example, and the calculation of the tax has reverted to a flat 25% tax rate.  The reason for this is that at this point we assume that you are familiar with the decision constructs, and since removing them simplifies the processing of the class, we can concentrate better on examining the features of object oriented programming.

Listing 6.1

 1

public class Pay

 2

{

 3

  private double hours, rate, gross, tax, nett;

 4

  public void setHours(double h)

 5

  {

 6

    hours = h;

 7

  }

 8

  public void setRate(double r)

 9

  {

 10

    rate = r;

 11

  }

 12

  public double getHours()

 13

  {

 14

    return hours;

 15

  }

 16

  public double getRate()

 17

  {

 18

    return rate;

 19

  }

 20

  public double getGross()

 21

  {

 22

    return gross;

 23

  }

 24

  public double getTax()

 25

  {

 26

    return tax;

 27

  }

 28

  public double getNett()

 29

  {

 30

    return nett;

 31

  }

 32

  public void calculateGross()

 33

  {

 34

    gross = hours * rate;

 35

  }

 36

  public void calculateTax()

 37

  {

 38

    tax = gross * .25;

 39

  }

 40

  public void calculateNett()

 41

  {

 42

    nett = gross - tax;

 43

  }

 44

}

 

The first thing to notice about Listing 6.1 is that there is no method main to be seen anywhere. Don’t worry!  We shall later see how to get around that problem. The other thing to notice is that there is quite a large number of very small methods.

One more thing to notice is that the variables are not declared inside a method, but in the body of the class itself, that is at line 3. They are also declared as being private.  This means that they are visible in all of the class but are invisible to any other class. Also notice that the methods have all been declared as public.  This means that they are visible to any other class that wishes to interact with the class Pay.

The reason that the variables have been declared private is that since they cannot be seen outside of the class then they cannot be accidentally corrupted by external code.  This is one of the features of object oriented programming that we shall return to later. It is called encapsulation.

Of course it is pointless having a class where all of the variables are private and nothing outside of the class can do anything to them.  It would be similar to having K1,000,000 inside a burglar-proof safe that nobody knows the combination of. We must therefore allow some limited access to those variables.  In our case this is done by making all of the methods of the class public.  This gives the outside world limited access to those variables since that world can call the methods of the class, which in turn can process the internal variables.

In the previous version of the payroll application we processed the data by putting values into the variables hours and rate, then calculating the values of gross, tax and nett and finally printing the results.  We can in fact do the same thing with our class Pay. In order to put a value into the variable hours we simply call the method setHours, which spans the lines 4 – 7.  This method takes an argument h and in the body of the same method the value of the argument is copied into the variable hours.  Thus the outside world can put value into the private variable hours through the setHours method.

The method setRate, which spans lines 8 – 11, can update the variable rate in exactly the same manner.

Again notice that there is no method that allows an outsider to set the values for gross, tax or nett since those variables should be updated from inside the class itself.

In order to calculate the value of the gross the outsider can simply call the method calculateGross, which spans lines 32 – 35. In the body of this method the gross is calculated by multiplying the hours by the rate.  In a similar manner the values of the tax and nett can be calculated.

Once the values of the gross tax and nett are calculated the results need to be able to be read.  For this we have a range of get methods that span lines 12 – 31. In our case we notice that all of them are methods that return a double value.  The method getHours, which spans the lines 12 – 15, simply uses the return keyword to return the value of hours to the outside world.  The other get methods do exactly the same.

Now let us look at how we may use the class Pay for processing an actual payroll. In order to do this we have to create another class as shown in Listing 6.2 below.

Listing 6.2

 1

public class Untitled1

 2

{

 3

  public static void main(String[] args)

 4

  {

 5

    Pay p;

 6

    p = new Pay();

 7

    p.setHours(40);

 8

    p.setRate(20);

 9

    p.calculateGross();

 10

    p.calculateTax();

 11

    p.calculateNett();

 12

    System.out.println("Hours " + p.getHours());

 13

    System.out.println("Rate " + p.getRate());

 14

    System.out.println("Gross " + p.getGross());

 15

    System.out.println("Tax " + p.getTax());

 16

    System.out.println("Nett " + p.getNett());

 17

  }    

 18

}

 

At line 5 we declare a pointer that will eventually point to an object of the class Pay.  A pointer is simply a space in the computer’s memory that will hold the address of a different memory location where an object of Pay is stored.  At line 6 new Pay() creates an object of Pay somewhere else in memory and stores the starting address of that object in the pointer p.

What exactly do we mean by creating an object and what is the difference between it and the class Pay that we have been examining up to now?  Essentially a class is a blueprint or template that is used to define the variables to be used for an application and the methods that will process those variables.  Creating an object of this class means finding a block of free space in memory where we can store the variables and the code that processes them as one unit. Once this space has been found and divided up between the variable spaces and the code that will process them, the starting address of that block is stored in a pointer so that any code that requires to use the newly created object will be able to access it by referring to the pointer.

With that out of the way let us look at the rest of the processing. At line 7 the setHours method of the object p is called and 40 is passed to its argument.  As we have seen when examining that method earlier the value 40 will be stored will be stored in the variable hours. The value for the hourly rate is set in exactly the same manner. In lines 9 – 11 the calculating methods are called for processing the values of the gross tax and nett, while in lines 12 – 16 the results of the calculation are displayed by calling the appropriate get methods.

In the second sentence of the above paragraph I said At line 7 the setHours method of the object p is called. This is not strictly correct. The pointer p is not the object. The object resides in a reserved block somewhere else in memory and the pointer simply holds the address of the first register of that block.  Thus it would be more correct to say At line 7 the setHours method of the object whose memory address is stored in the pointer p is called. Although correct, this very cumbersome to write or say or even to read frequently and thus we shall use the shorter, although slightly incorrect expression which refers to p as an object.

Encapsulation

Go to top

When discussing Listing 6.1 we stated that the variables in the class were declared as private so that they could not be corrupted by code that was external to the class.  This is certainly true. None of them can be corrupted directly by any external code.  On the other hand, as Listing 6.1 currently stands, the variables can certainly be corrupted indirectly.  Some examples of external corruption are as follows:

Someone can change line 7 of Listing 6.2 to p.setHours(9000) and be on the plane to Rio before the fraud is discovered.

At the opposite end of the scale the same line can be changed to p.setHours(-9), which, although complete nonsense, will still be accepted by the programme.

The calculation of the gross, tax and nett can be performed in any order even though they should be performed in the strict order of gross, tax and nett.

The same calculations could occur even when no values haven been given to hours and rate.

The values of all of the variables can be read through the get methods, even if no calculation has ever occurred.

In order to remedy the above weaknesses we have to make the following adjustments to our class:

The methods setHours and setRate must validate the incoming data to ensure that it is within reasonable limits.  We can set the range 5 – 60 as an acceptable range for hours and 8 – 50 as an acceptable range for rate.  The same methods will set flag variables to indicate whether correct data has been received or not

The methods calculateGross, calculateTax and calculateNett are to be made private so that they cannot be called directly, while a new public method – calculate – will be used to call them in the correct order.

The method calculate will first check if correct values have been received for hours and rate before performing the calculation of the payroll.  At the end a flag will be set indicating whether the calculation was successful or not.

The get methods will first check if a successful calculation has occurred before returning the values of the appropriate variables.

The code below in Listing 6.3 is our original class which has been modified according to the suggestions above.

Listing 6.3

 1

public class Pay

 2

{

 3

  private double hours, rate, gross, tax, nett;

 4

  private boolean validHours, validRate, calculateOK;

 5

  public void setHours(double h)

 6

  {

 7

    if((h>=5) && (h<=60))

 8

    {

 9

      hours = h;

 10

      validHours = true;

 11

    }

 12

    else

 13

      validHours = false;

 14

  }

 15

  public void setRate(double r)

 16

  {

 17

    if((r>=8) && (r<=50))

 18

    {

 19

      rate = r;

 20

      validRate = true;

 21

    }

 22

    else

 23

      validRate = false;

 24

  }

 25

  public double getHours()

 26

  {

 27

    if(calculateOK)

 28

      return hours;

 29

    else

 30

      return -1;

 31

  }

 32

  public double getRate()

 33

  {

 34

    if(calculateOK)

 35

      return rate;

 36

    else

 37

      return -1;

 38

  }

 39

  public double getGross()

 40

  {

 41

    if(calculateOK)

 42

      return gross;

 43

    else

 44

      return -1;

 45

  }

 46

  public double getTax()

 47

  {

 48

    if(calculateOK)

 49

      return tax;

 50

    else

 51

      return -1;

 52

  }

 53

  public double getNett()

 54

  {

 55

    if(calculateOK)

 56

      return nett;

 57

    else

 58

     return -1;

 59

 }

 60

  private void calculateGross()

 61

  {

 62

    gross = hours * rate;

 63

  }

 64

  private void calculateTax()

 65

  {

 66

    tax = gross * .25;

 67

  }

 68

  private void calculateNett()

 69

  {

 70

    nett = gross - tax;

 71

  }

 72

  public void calculate()

 73

  {

 74

    if(validHours && validRate)

 75

    {

 76

      calculateGross();

 77

      calculateTax();

 78

      calculateNett();

 79

      calculateOK = true;

 80

    }

 81

    else

 82

      calculateOK = false;

 83

  }

 84

}

 

The first change we notice here is that at line 4, three new boolean variables validHours, validRate and calculateOk have been added. A boolean variable is one that can have only one of two values true or false. The three above will be used by the methods setHours, setRate and calculate to set the appropriate flags as we shall see shortly.

The method setHours has undergone considerable changes and now extends from line 5 to line 15.  The entire method now consists of an if..else construct. At line 7 the value of the argument h is checked fro being in the range 5 – 60 inclusive.  If it is, then, at line 9 that value is copied into the variable hours and validHours is set to true. If h is outside of the range 5 – 60 then validHours is set to false.

Identical alterations have been made to the method setRate.

Here we begin to notice the power of encapsulation. Data from outside can only enter the class through setHours and setRate.  Those two methods now have complete control of what data is allowed in.  Any data not in the allowed range is rejected.

Since setHours and setRate set the values of validHours and validRate when we get to calculate, which spans lines 72 – 83, we see that the first thing this method does is to test those two variable for being true.  This occurs at line 74.  If both are true then the three methods for calculating the payroll are called in the correct sequence at lines 76, 77 and 78 and at line 79 calculateOK is set to true.  If both of the boolean variables are not true then at line 82 calculateOK is set to false.

What we notice here regarding encapsulation is that if an attempt is made to perform the payroll calculation before correct data has been entered for hours and rate the calculation is actually skipped and the method simply sets calculateOK to false.  Thus, when we try to read any of the variables via one of the get methods, all those methods will have to do is check the value of calculateOK. If it is true then they will return the value of the appropriate variable, otherwise they will return -1 to indicate an error situation. If we examine getGross which spans lines 39 – 45 we notice that this is how the method behaves. 

The reason we can use -1 as a value to indicate an error situation is that since we are dealing with a payroll situation the lowest value we can have for any of our variables is 0. Thus, if we wish to indicate an error situation, we can use any negative number we wish. The number -1 is as good as any other one.

Below at Listing 6.4 we have the modified version of our main method. The only difference between it and its predecessor at Listing 6.2 is that the calls to calculateGross, calculateTax and calculateNett have been replaced by a single call to calculate.

Listing 6.4

 1

public class Untitled1

 2

{

 3

  public static void main(String[] args)

 4

  {

 5

    Pay p;

 6

    p = new Pay();

 7

    p.setHours(40);

 8

    p.setRate(20);

 9

    p.calculate();

 10

    System.out.println("Hours " + p.getHours());

 11

    System.out.println("Rate " + p.getRate());

 12

    System.out.println("Gross " + p.getGross());

 13

    System.out.println("Tax " + p.getTax());

 14

    System.out.println("Nett " + p.getNett());

 15

  }

 16

}

 

public static void main

Go to top

We now come to a point where we can explain a little more about the keywords that precede our main method – in this case the public keyword.  If a method is to be seen outside of its class it has to be declared as public.  The method main is that startup method of any application, and as it has to be called by the operator from outside of the class that contains it, it has to be declared as public.

Summary

Go to top

A class is a template for an object. At runtime an object of a class is created using the keyword new. The object is simply a block of reserved memory which is set up to contain the variables and the code specified in the class.

When an object is created its memory address is returned and is usually stored in a pointer.  This pointer is subsequently used to access any of the methods of the class.

One of the advantages of using an object is that we can encapsulate our data inside it. Some of the guidelines of encapsulation are:

Declare all variables as private

Allow external data into the object via set methods.

Incoming data should be validated by the set methods and rejected if not correct

only essential input data should be allowed in

any methods required only for internal processing should be declared as private

any variables used only for internal processing should not have either get or set methods

set methods should return only valid data

Practice

Go to top

Copy listings 6.3 and 6.4 into your computer, then compile and run them. Next experiment with listing 6.4 by passing values that are outside of the range for hours and rate and check that -1’s are returned.

Finally try to make calls to the private methods or variables to check how the system handles these error situations.


Exercises

Go to top

Exercise 6.1

1.      what is the difference between a class and an object?

2.      what is a pointer?

3.      describe in detail the workings of the keyword new.

4.      what is the meaning of encapsulation?

5.      describe the structure and the use of set methods in a class

6.      describe the structure and the use of get methods in a class

7.      list some techniques for enhancing the encapsulation of data

Exercise 6.2

Modify exercise 5.2 as follows:

  1. Create a class called Sale and create private data members for holding values for lawnmower category, price of mower, amount sold, discount, price after discount, GST, price including GST and customer total.
  2. The lawnmower category and amount sold should have get and set methods so that they can be updated from outside the class and their values can be read from outside the class.  The lawnmower category should be validated as being in the range 1 – 6.  In order to do this there should be another private data member validCategory which should be of type Boolean. The body of the get method for the category should first test if the incoming value is between 1 and 6 inclusive.  If it is then the value is copied into the private data member for lawnmower category and the member validCategory should be set to true, otherwise validCategory should be set to false. Check setHours or setRate in listing 6.3 above to see how to do this validation.  There is no need to validate the amount sold.
  3. All other data members should only have get methods so they can be read outside the class but their values cannot be updated.
  4. The methods calculateMowerPrice, calculateDiscount, calculatePriceAfterDiscount, calculateGST, calculatePriceIncGST and calculateTotal should be private methods within the class.  A public method called calculate should call these methods in the correct order.  In the body of calculate all of the other methods are called only if the value of validCategory is true.  Again check the structure of the method calculate in Listing 6.3.
  5. Ensure that all processing is removed from the method main.  Instead it will create an object of the class Sale, call the set methods for category and amount sold and pass values to them, then call the calculate method.  Finally it will display the results by calling the get methods of the other values and printing the results of the values returned.  Again use Listing 6.4 as a model for how to do this.

Use both listings 6.3 and 6.4 as models for this exercise.