Java An Object-Oriented Language Java An object-oriented language

 
Chapter 5

Copyright M.A.Smith 1999 may not be reproduced without written permission

5. The class

At the centre of Java programming is the class. The class is a description of all objects that share the same member variables and member methods. A class is used as a mould to create the objects used in a Java application or applet. In effect a class allows a user to create new data types.

5.1 Introduction to the class construct

In Java a class is used as a description or mould for the creation of objects. For example, a class Account would be used in a Java application to create specific instances of individual people's bank accounts. Each bank account would contain potentially a different balance but the actions performed on individual person's bank accounts would be the same.

This is illustrated in Figure 5.1 where Mike's account contains £100.00 and the minimum balance allowed is £0.00 and Corinna's account contains £200.00 with a minimum allowed balance of -£10.00. Each account is able to process the transactions of: deposit money into the account, return the balance of the account, withdraw money from the account and set the minimum balance that the account must hold.
Mike's account Corinna's account
Figure 5.1 Instances of the class Account. In the implementation of objects, the code for methods will not be duplicated for each object. Rather there will be a single copy of the code for each method that is used by all objects that belong to the same class. This optimization means that an object only contains its state information represented by instance variables. For example, the state information for an instance of an Account is contained in the instance variables min_balance and balance.

5.1.1 A class diagram showing a class

A class diagram for the class Account using the UML notation is illustrated in Figure 5.2.
Class Diagram Components
The class Account is composed of the instance variables: the_balance and the_min_balance and the methods: deposit, withdraw, account_balance and set_min_balance.
Figure 5.2 Class diagram for the class Account.

5.2 Class, objects, messages and methods

The objects mike and corinna are instances of the class Account. Messages that may be sent to these objects are:
  • Withdraw money from this account.
  • Deposit money into this account.
  • Return the account balance of this account.
  • Set the minimum balance for this account.
  • To deposit £100.00 into Mike's account the message deposit is sent with a parameter of 100.00 to the object mike. This invokes an internal hidden method to processes the transaction. In processing the transaction, the instance variable representing the current balance of the account is modified.

    In the above description of processing a deposit transaction on a bank account many object-oriented terms have been used, these are:
    class A collective name for all objects that have the same instance methods and variables. However, the contents of the instance variables may be different.
    instance variables The variables contained within the object that represent its internal state.
    message A request sent to an object to obey one of its hidden methods.
    method An action that manipulates or accesses the internal state of the object. The implementation of this action is however hidden from a client who sends messages to this object.
    object An item that has a hidden internal structure. The hidden structure is manipulated or accessed by messages sent to the object.

    5.2.1 Declaring an object in Java

    An object is declared in exactly the same way as variables are declared in Java. However, the declaration allocates no storage for the object. The storage for the object must be created either explicitly at a later stage, or as part of the declaration. Assuming that a class Account has already been created then the declaration and allocation of storage for an object to hold Mike's bank account is as follows:

    
      Account mike = new Account();
    

    This consists of two parts:
    1 The declaration of the object Account mike
    2 The allocation of storage for the object. = new Account()
    This process is explained in more detail in Section 5.3. The nature of an object in Java.

    5.2.2 Sending a message to an object

    The following notation is used to send the message deposit with a parameter of 100.00 to the object mike:

    
      mike.deposit(100.00);
    

    This is read as send the message deposit with a parameter of 100.00 to the object mike.
    The components of this Java statement are illustrated in Figure 5.3 opposite. Figure 5.3 Sending a message to an object.

    5.2.3 Using the class Account

    The class Account is used in the following application to create the objects mike and corinna. After these objects have been created a series of simple banking transactions are performed on the accounts.

    
    class Main
    {
      public static void main(String args[])
      {
        Account mike = new Account();
        Account corinna = new Account();
        double obtained;
        System.out.println( "Mike's Balance = " +
        mike.account_balance() );
        mike.deposit(100.00);
        System.out.println( "Mike's Balance = " +
        mike.account_balance() );
        obtained = mike.withdraw(20.00);
        System.out.println( "Mike has withdrawn : " + obtained );
        System.out.println( "Mike's Balance = " +
        mike.account_balance() );
        corinna.deposit(50.00);
        System.out.println( "Corinna's Balance = " +
        corinna.account_balance() );
      }
    }
    

    Which when compiled and run produces the following results.

    
    Mike's Balance = 0.0
    Mike's Balance = 100.0
    Mike has withdrawn : 20.0
    Mike's Balance = 80.0
    Corinna's Balance = 50.0
    

    5.2.4 Specification of the class Account

    The class Account described above contains the following methods and variables:

    Methods in the class

    Name Remarks
    account_balance Returns the current balance of the account.
    withdraw Withdraws money from the account. Naturally money can only be withdrawn if the account balance stays above or equal to the minimum balance allowed.
    deposit Deposits money into the account.
    set_min_balance Sets the minimum balance that the account must hold. A negative amount indicates an overdraft is allowed.

    Instance variables in the class

    Name Remarks
    the_balance Holds the current balance of the account.
    the_min_balance The minimum balance that the account is allowed to go to. If the account holder was allowed an overdraft of £200.00 then the_min_balance would contain -200.00.

    Note: To distinguish easily member variables from other data items the member variable name is prefixed with the_.

    5.2.5 The Java class Account

    The implementation of the class for Account is shown below:

    
    class Account
    {
    

    Contained within the class declaration is the declaration of the instance variables that hold the state information for each individual object. These declarations are prefixed with the reserved word private to indicate that they are not accessible outside of the class.

    
      private double the_balance = 0.0d;     //Balance of account
      private double the_min_balance = 0.0d; //Minimum bal (Overdraft)
    

    Following this the methods contained in the class are defined. The first method is special and is called automatically whenever an instance of the class is created. This special method is called a constructor and has the same name as the class. For the class Account the constructor sets the instance variables in the object to a zero value.

    
      public Account() {
        the_balance = the_min_balance = 0.00;
      }
    

    Note: As the instance variables are given initial values of zero there is no need to have a constructor. The constructor is shown here to illustrate the use of a constructor in a class.

    The reserved word public indicates that the constructor is visible to a user of the class. A constructor must be visible if an instance of the class is to be created.

    5.2.5.1 The method account_balance

    The next method returns the balance of the account contained in the instance variable the_balance.

    
      public double account_balance()
      {
        return the_balance;
      }
    

    Note: This method is necessary as the state of the object is hidden from the user. Figure 5.4 below illustrates the components of this method.

    Figure 5.4 Components of the method account_balance.

    5.2.5.2 The method withdraw

    The method withdraw has a single parameter money. In the implementation of the method a check is made to ensure that there are sufficient funds in this instance of Account to allow the transaction to be made. The result of executing the method is the amount of money that has been successfully withdrawn from the account. If no money can be withdrawn from the account then 0.00 is returned.

    
      public double withdraw( final double money)
      {
        if ( the_balance - money >= the_min_balance )
        {
          the_balance = the_balance - money;
          return money;
        } else {
         return 0.00;
        }
      }
    

    Note: The use of the reserved word final to indicate that the formal parameter money of type double cannot be written to.

    Parameters are more fully discussed in Chapter 6 on Anatomy of a method. Figure 5.5 below illustrates the components of this method.

    Figure 5.5 Components of the method withdraw.

    5.2.5.3 The method deposit

    The method deposit adds the contents of the parameter money to the instance variable the_balance. The reserved word void is used to indicate that this method returns no result.

    
      public void deposit( final double money )
      {
        the_balance = the_balance + money;
      }
    

    Figure 5.6 below illustrates the components of this method.

    Figure 5.6 Components of the method deposit.

    The method set_min_balance overwrites the instance variable the_min_balance with the new value supplied as a parameter.

    
      public void set_min_balance( final double money )
      {
        the_min_balance = money;
      }
    

    The class definition is terminated by a closing }.

    
      }
    

    5.3 The nature of objects in Java

    In Java an object is represented by a handle. The handle points to the physical storage of the object. For example, the storage for the object mike is shown in Figure 5.7.

    Figure 5.7 Storage for a Java object.

    Provided that a Java programmer never copies an object, this implementation detail can be ignored. However, if a programmer copies an object as in the following code, the semantics of the copy are that of a shallow copy. The concept of a shallow copy is more fully explored in Section 14.1.1.

    
      Account mike = new Account();
      Account shallow_copy_mike = mike;
    
      mike.deposit(100.00);
      System.out.println("Balance account mike = " +
                          mike.account_balance() );
      System.out.println("Balance account shallow copy mike = " +
                          shallow_copy_mike.account_balance() );
    

    Which when combined into a Java application and run produces the following results:

    
      Balance account mike = 100.0
      Balance account shallow copy mike = 100.0
    

    The reason for this is that only the handle for the object mike is copied to shallow_copy_mike. Hence though the handle has been copied the underlying storage has not. This is illustrated in Figure 5.8.

    Figure 5.8 Storage after a shallow copy of an object.

    Chapter 14 describes how to duplicate the storage for an object. This process is termed a deep copy of an object.

    5.3.1 The value null

    An objects handle may be assigned the value null to indicate that currently the handle does not point to any storage. When an object's handle is initially created it has an initial value of null. This null value can be tested for, so that a programmer can ascertain whether or not storage has been allocated for the object.

    
      Account mike = null;             // The default value
      if ( mike == null )
      {
        // No storage allocated for object
      }
    

    5.4 Garbage collection

    In Java an object consists of two components: the handle or reference to the object and the actual storage for the object. The storage for the handle for an object is returned back to the system when the method in which the handle was declared is exited. However, the storage for the object will not be returned to the system. This means that the Java run-time system has storage allocated for objects that can never be accessed. To prevent the eventual consumption of all available storage the Java run-time system periodically runs a garbage collector that searches for orphaned objects that cannot be accessed. When found by the garbage collector the storage for these orphaned objects is returned back to the systems pool of available storage.

    However, this process takes a finite time to run. The consequence of this is that there may be times when a Java application appears less responsive than might be expected, due to the resources consumed by the running garbage collector.

    5.4.1 An example

    The following method declares an instance of the class Account, the storage for which is orphaned once the method is exited.

    
      public void process()
      {
        Account mike = new Account(); // Processing on the object
      }
    

    Note: The storage for the object mike is allocated in two distinct phases.

    Allocation of the handle Account mike
    Allocation of the objects storage = new Account();

    The storage for the object will be returned back to the pool of available storage when the garbage collector is called.


    5.4.2 Explicitly calling the garbage collector

    The garbage collector can be explicitly called by evoking the following static method:

    
      System.gc();
    

    5.5 A personal account manager

    A personal account manager, implemented on a small electronic notebook with an LCD display, presents the user with a menu on the first few lines of the display, in the form:

    
      [a] Deposit
      [b] Withdraw
      [c] Balance
      Input selection:
    

    The bottom lines of the display would enable the user to provide any input values required and also provide room to display any results.

    In the example screens below, the user input is shown in bold type. For example, if option a were selected, then after the user had input £100, the bottom of the display would look like:

    
      Input selection: a
      Amount to deposit = 100
    

    Then if option c where selected the bottom of the screen would display

    
      Input selection : c
      Balance = #100.0
    

    5.5.1 The class TUI

    To simplify the implementation of this application an instance of a TUI (Text User Interface) class is used to implement the interactions between the end user and the application.

    The responsibilities of the class TUI are:

    Method Responsibility
    menu Sets up the menu that will be displayed to the user. Each menu item is described by a string.
    chose_option Returns the menu item selected by a user of the TUI.
    message(s) Display the message s to a user.
    dialogue(s) Displays the message prompt s to a user returning the string they type as a response.
    dialogue_int(s) Displays the message prompt s to a user returning the number they type as an int.
    dialogue_double(s) Displays the message prompt s to a user returning the number they type as a double value.

    In implementing this class in Java a class that just contains static fields is created to represent an enumeration for the menu items selected.

    
      class Menu_item
      {
        public static final int M_1 = 1;
        public static final int M_2 = 2;
        public static final int M_3 = 3;
        public static final int M_4 = 4;
        public static final int M_5 = 5;
        public static final int NONE = 0;
      }
    

    Note: An instance of the class Menu_item does not need to be declared to use the static fields representing the enumeration's.

    The use of static fields are more fully discussed in Chapter 6 on the Anatomy of a method.

    The class TUI contains 5 string variables to hold the names of the 5 possible menu choices that may be displayed.

    
      class TUI
      {
        private String men_1 = ""; //Name of 1st menu item
        private String men_2 = ""; //Name of 2nd menu item
        private String men_3 = ""; //Name of 3rd menu item
        private String men_4 = ""; //Name of 4th menu item
        private String men_5 = ""; //Name of 5th menu item
        private static final int EOF = -1;
      }
    

    The method menu stores the 5 possible menu items that a user may select from. To allow some flexibility if a menu item is the empty string then it will not be displayed.

    
      public void menu( final String m1, final String m2,
                        final String m3, final String m4,
                        final String m5 )
      {
        men_1 = m1; men_2 = m2; men_3 = m3;
        men_4 = m4; men_5 = m5;                     //Store names
    

    A menu choice is presented to the user by the method chose_option. This method displays the individual menu items onto the user's screen. The private method display_menu_item will only write an individual menu item if it is not an empty string.

    
      public int chose_option()
      {
        int choice = Menu_item.NONE;
    

    The code decoding a users response is repeated until a valid response is given.

    
      while ( choice == Menu_item.NONE )    //While no valid reply
      {
        char selection = ' '; System.out.println("");
        display_menu_item( "[a] ", men_1 ); //First menu item
        display_menu_item( "[b] ", men_2 ); //Second ..
        display_menu_item( "[c] ", men_3 ); //Third
        display_menu_item( "[d] ", men_4 ); //Fourth
        display_menu_item( "[e] ", men_5 ); //Fifth
    

    The user's response to the list of menu items is obtained by using the method dialogue that returns the user's response as a string. If it exists then the first non-white space character from the string is taken as the response typed by the user.

    
        String res = dialogue("Input selection"); //Response
        res = res.toLowerCase().trim();           //Lower case
        if ( res.length() >= 1 )
          selection = res.charAt(0);              //First char
    

    Note: Individual characters in a string are accessed by using the method charAt. The first character in a string is at position 0, the second character at position 1 etc.

    The method length() returns the number of characters in a string. A check is made to determine if the response given is a valid menu item and also if it can be selected. For example, a menu item that is the empty or null string cannot be selected.

    
        switch ( selection )
        {
          case 'a' :
            if ( ! men_1.equals( "" ) ) choice = Menu_item.M_1;
            break;
          case 'b' :
            if ( ! men_2.equals( "" ) ) choice = Menu_item.M_2;
            break;
          case 'c' :
            if ( ! men_3.equals( "" ) ) choice = Menu_item.M_3;
            break;
          case 'd' :
            if ( ! men_4.equals( "" ) ) choice = Menu_item.M_4;
            break;
          case 'e' :
            if ( ! men_5.equals( "" ) ) choice = Menu_item.M_5;
            break;
        }
        if ( choice == Menu_item.NONE )
          message( "Invalid response" );
        return choice; //User selection
      }
    

    Note: The use of the method equals( "" ) to test for any empty string.

    The method display_menu_itemwill only display menu items that are not the null or empty string.

    
      private void display_menu_item( String prompt, String name )
      {
        if ( ! name.equals( "" ) ) //Not null String so print
        {
          System.out.println( prompt + name );
          System.out.println( "" );
        }
      }
    

    A response is sent to the users screen by using the method message. This simply writes the supplied string onto the users terminal.

    
        public void message( final String mes )
        {
          System.out.println( mes );
        }
    

    Information is obtained from a user by requesting a string response to a dialogue message. The input line is read character by character and converted into a string. To simplify processing, the end of file character causes an immediate termination of the application.

    
      public String dialogue( final String mes )
      {
        String line = "";                           //Line read
        int ch;
        System.out.print( mes + " : " );            //Prompt
        try
        {
          ch = System.in.read();                    //Read ch
          while ( ch != '\n' && ch != EOF )         //While !EOL
          {
            line += (char) ch;                      // append ch
            ch = System.in.read();                  // next ch
            if ( ch == '\r' ) ch = System.in.read();// Skip ''
          }
          if ( line.equals("") && ch == EOF )
          System.exit(-1);                          //Exit **
          return line;                              //return line
        }
        catch( IOException exp )                    //Problem
        {
          System.exit(-1);                          //Exit **
        }
        return "";                                  //Blank line
      }
    

    To read a floating point number from the user the method dialogue_double is used. This method calls the method dialogue to return the user's response as a string. Then the wrapper class Double is used to convert this string into an instance of a double.

    
        public double dialogue_double( final String mes)
        {
          String res = dialogue( mes );            //Read line
          double value = 0.0;                      //
          try
          {
            value = Double.parseDouble( res );     //Convert
          }
          catch ( NumberFormatException ex )       //Problem
          { // ignore
          }
          return value;                            //return
        }
    

    Note: The use of wrapper classes is more fully explained in Chapter 7 on Wrapper classes.

    In a similar way the method dialogue_int returns an instance of an int as the number input by a user.

    
      public int dialogue_int( final String mes )
      {
        String res = dialogue( mes );      //Read line
        int value = 0;                     //
        try
        {
          value = Integer.parseInt( res ); //Convert
        }
        catch ( NumberFormatException ex ) //Problem
        {                                  // ignore
        }
        return value;                      //return
      }
    }
    

    5.5.2 The main application

    Having created a class TUI and re-using the existing class Account the implementation of the personal account manager is achieved in only a few lines of code.

    In essence the application is an endless loop that requests a menu choice from the end user.

    
    class Main
    {
      public static void main( String args[] )
      {
        Account mine = new Account();      // My Account
        TUI screen = new TUI();            // Interaction screen
        double amount;                     // money
        screen.menu("Deposit", "Withdraw", "Balance", "", "" );
        while ( true )
        {
          switch ( screen.chose_option() )
          {
    

    When deposit is selected the user is interrogated for the amount that they wish to deposit. If this is a valid amount then the money is deposited into the user's account.

    
            case Menu_item.M_1 :
              amount = screen.dialogue_double("Amount to deposit");
              if ( amount >= 0.0 )
              {
                mine.deposit( amount );
              } else {
               screen.message("Amount must be positive");
              }
              break;
    

    Likewise for withdraw after a validation on the amount to be withdraw the transaction is performed. If the transaction fails due to insufficient funds the user's is informed of the situation.

    
             case Menu_item.M_2 :
             amount = screen.dialogue_double("Amount to withdraw");
             if ( amount > 0.0 )
             {
               double get = mine.withdraw( amount );
               if ( get <= 0.0 )
               screen.message("Sorry not enough funds");
             } else {
              screen.message("Amount not valid");
             }
             break;
    

    The code for balance converts the result from applying the transaction account_balance into a string so that it can be displayed using the method message.

    
            case Menu_item.M_3 :
            {
              String mes = "Balance = #" +
              String.valueOf(mine.account_balance());
              screen.message( mes ); //"Bal ... "
            }
            break;
          }
        }
      }
    }
    

    5.5.3 Putting it all together

    When compiled and run a typical series of interactions with the running application are shown below:


    Deposit an initial 100.00 into the account.
    [a] Deposit
    [b] Withdraw
    [c] Balance
    Input selection : a
    Amount to deposit : 100.00
    
    Try and take 200.00 from the account.
    [a] Deposit
    [b] Withdraw
    [c] Balance
    Input selection : b
    Amount to withdraw : 200.00
    Sorry not enough funds
    
    Show the balance
    [a] Deposit
    [b] Withdraw
    [c] Balance
    Input selection : c
    Balance = #100.0

    5.6 Interfaces

    An interface defines a set of methods that a class must implement. When a class implements an interface a user of the class knows that all the methods in the interface have a concrete implementation. If all the methods in the interface were not implemented then a compile time error message would be generated when an instance of the class was compiled.

    For example, a designer of a banking system might create the following interface:

    
    interface Account_protocol
    {
      public double account_balance();
      public void deposit( final double money);
      public double withdraw( final double money );
    }
    

    Note: An interface is like a class except that there is only a signature for the methods in the interface.

    Implementors of classes representing bank accounts would be required to implement this interface. By insisting that each implementor of a class implements the interface Account_protocol guarantees that each instance of an account must be able to: deposit money into, withdraw money from and return the balance of the account. For example, a simple bank account that implements the Account_protocol is defined as follows:

    
    class Simple_Account implements Account_protocol
    {
      private double the_balance = 0.0d; //Balance of account
    
      public double account_balance()
      {
        return the_balance;
      }
    
      public double withdraw( final double money )
      {
        if ( the_balance - money >= 0.00 )
        {
          the_balance = the_balance - money;
          return money;
        } else {
          return 0.00;
        }
      }
    
      public void deposit( final double money )
      {
        the_balance = the_balance + money;
      }
    }
    

    The method transfer may be passed as its first parameter any type of bank account that implements the Account_protocol. This is a great advantage as it means that in the future if a new type of bank account is created that implements the interface Account_protocol then an instance of this new account may be passed to the method transfer.

    
      public double transfer( Account_protocol other, final double money )
      {
        if ( money > 0.00 )
        {
          double obtain = other.withdraw( money );
          if ( obtain != 0.00 )
          {
            deposit( money );
            return money;
          }
        }
        return 0.00;
        }
      }
    

    Note: A class may implement many protocols. The method transfer may be passed any object that implements the Account_protocol.

    5.6.1 Putting it all together

    The following class will test the class Simple_Account.

    
    class Main
    {
      public static void main( String args[] )
      {
        Simple_Account mike = new Simple_Account();
        Simple_Account corinna = new Simple_Account();
        double obtained;
    
        mike.deposit(100.00);
        System.out.println( "Mike's Balance = " +
                            mike.account_balance() );
        corinna.deposit(150.00);
        System.out.println( "Corinna's Balance = " +
                            corinna.account_balance() );
        corinna.transfer( mike, 80.00 );
        System.out.println( "Mike's Balance = " +
        mike.account_balance() );
        System.out.println( "Corinna's Balance = " +
                            corinna.account_balance() );
      }
    }
    

    Which when compiled and run will produce the following output:

    
    Mike's Balance = 100.0
    Corinna's Balance = 150.0
    Mike's Balance = 20.0
    Corinna's Balance = 230.0
    

    5.7 Nested classes

    A class declared inside another class is termed a nested class. The nested class can access all of the members of its enclosing class. There are two types of nested class:

    A static nested class This may only access the enclosing classes class variables or methods.
    A non static nested class also called an inner class. This may access all the enclosing classes member variables or methods.

    For example, the following class Car contains an inner class Engine.

    
    class Car
    {
      class Engine                               //Inner class
      {
        private double the_capacity;             //Engine Capacity
    
        public Engine( double engine_capacity )  //Constructor
        {
          the_capacity = engine_capacity;
        }
    
        public double engine_size()              //Return Engine size
        {
          return the_capacity;
        }
      }
    
      Engine the_engine = new Engine( 1.3 );     //Instance of engine
    
      public double capacity()                   //Return capacity
      {
        return the_engine.engine_size();
      }
    }
    

    5.8 Self-assessment

    5.9 Exercises

    Construct the following classes:

    A class Performance, an instance of which represents the seats at a particular showing of a film, has the following methods:

    Method Responsibility
    book_seats Books n seats at the performance.
    cancel Un-books n seats.
    seats_free Returns the number of seats that are still unsold.
    seats_booked Returns the number of seats sold for this performance.

    Thus on an instance of Performance the following actions can be performed:

    Remember to make sure that the transactions that you process are valid. For example, booking -5 seats will result in the number of booked seats going down.

  • Library book
  • A class to represent a book in a library, such that the following operations can be processed:

    Construct the following application:

    An application to deal with the single day's administration of bookings for a cinema. Each day there is a single performance at 8pm.

    The application should display a menu of the possible options that may be selected. There should be checking for invalid data items such as booking -5 seats.

    Hint: Use the class TUI to provide a simple menu system. Use a single instance of the class Performance to represent the 8pm performance
    © M.A.Smith University of Brighton. Created March 1999 last modified October 2000.
    Comments, suggestions, etc. M.A.Smith at brighton dot ac dot uk * [Home page]