Defining an instantiable class

 

Overview

You are already familiar with constructing and using an object of a packaged class (such as String and StringBuffer). In this lesson, you will learn how to define your own custom classes from which objects may be constructed.

Before defining a class, you must first identify and design it. A good candidate for an instantiable class is something about which data is to be maintained and upon which processing is to be performed (such as a customer). The design process will determine what instance variables and instance methods the class must contain.

 

Design considerations

For example, the design of a Customer class may include the following:

acctNo - The customer's account number. An int greater than zero.

name - The customer's name. A String that may not be empty.

creditLimit - The customer's credit limit. An int that must be 1000, 5000, or 10000.

balance - The customer's account balance. A double that may be any value.

In a reality, there would be many more instance variables (such as streetNumber, streetName, city, state, zip, phone, billingDate, etc.) but the above will serve to demonstrate what is involved in designing the class.

For example, the following may represent the interest rate to be used in determining a customer's monthly finance charge:

MONTHLY_RATE - The monthly interest rate. A double with the value .015 (meaning 1.5%).

For example, the Customer class may need one constructor that accepts no parameters (letting all instance variables default), a second that accepts initial values for the customer's account number and name (letting credit limit and balance default), and a third that accepts initial values for the customer's account number, name, and credit limit (letting balance default). The method headers might be

public Customer()

public Customer(int iAcctNo, String iName)

public Customer(int iAcctNo, String iName, int iCreditLimit)

To enhance data security and integrity, the instance variables of a class are normally hidden from the class "client" (a user of the class). To allow controlled access to instance variables, "set" and "get" methods are provided.

For example, the Customer class may provide "set" and "get" methods having the following headers:

public boolean setAcctNo(int nAcctNo)

public boolean setName(String nName)

public boolean setCreditLimit(int nCreditLimit)

public boolean setBalance(double nBalance)

public int getAcctNo()

public String getName()

public double getCreditLimit()

public double getBalance()

The "set" methods can enforce the edit criteria of their associated instance variable so that bad data will never be stored. The boolean value they return can indicate to the caller whether the data they passed was accepted and stored. The "get" methods are needed to retrieve the value of their associated hidden instance variable.

A variety of special-purpose methods can also be provided to support client processing.

For example, the Customer class may require a method to record a transaction (either a payment or a purchase) and return a boolean value that indicates if the transaction was accepted. Another method may be needed to calculate the customer's monthly finance charge. Their headers might be as follows:

public boolean recordTransaction(double transAmount)

public double financeCharge()

 

The class definition

class identifier {
  statements
;
}

where identifier is the name of the class and statements define the features of the class (its instance variables, constants, constructors, and methods).

Optionally, the public modifier may be coded in front of the class keyword. If specified, there may be only one public class in a .java source file and the file name must be the same as the class name. Coding public indicates that the class is publicly available to any other class.

When public is omitted, class access defaults to "package" access. The class will be available only to other classes within the same package (subdirectory).

For example, the Customer class definition may be as follows:

// This is a demonstration class that encapsulates certain data
// and processing of a customer. It is by no means complete.

class Customer {

  // Private instance variables.

  private int acctNo;
  private String name;
  private int creditLimit;
  private double balance;

  // Public class constant for the monthly interest rate.

  public static final double MONTHLY_RATE = .015;

  // This constructor accepts no parameters. It lets instance
  // variables retain their initial JVM construction values.

  public Customer() {
  }

  // This constructor accepts values for account number and name.

  public Customer(int iAcctNo, String iName) {
    setAcctNo(iAcctNo);
    setName(iName);
    setCreditLimit(1000);
    setBalance(0);
  }

  // This constructor accepts values for account number, name,
  // and credit limit.

  public Customer(int iAcctNo, String iName,int iCreditLimit) {
    setAcctNo(iAcctNo);
    setName(iName);
    setCreditLimit(iCreditLimit);
    setBalance(0);
  }

  // This method can be called to set the account number. If
  // successful, true is returned.

  public boolean setAcctNo(int nAcctNo) {
    boolean status;
    if (nAcctNo > 0) {
      acctNo = nAcctNo;
      status = true;
    }
    else
      status = false;
    return status;
  }

  // This method can be called to set the name. If successful,
  // true is returned.

  public boolean setName(String nName) {
    boolean status;
    if (nName.length() > 0) {
      name = nName;
      status = true;
    }
    else
      status = false;
    return status;
  }

  // This method can be called to set the credit limit. If successful,
  // true is returned.

  public boolean setCreditLimit(int nCreditLimit) {
    boolean status;
    switch (nCreditLimit) {
      case 1000:
      case 5000:
      case 10000:
        creditLimit = nCreditLimit;
        status = true;
        break;
      default:
        status = false;
        break;
    }
    return status;
  }

  // This method can be called to set the balance. If successful,
  // true is returned.

  public boolean setBalance(double nBalance) {
    balance = nBalance;
    return true;
  }

  // This method can be called to get the account number.

  public int getAcctNo() {
    return acctNo;
  }

  // This method can be called to get the name.

  public String getName() {
    return name;
  }

  // This method can be called to get the credit limit.

  public int getCreditLimit() {
    return creditLimit;
  }

  // This method can be called to get the balance.

  public double getBalance() {
    return balance;
  }

  // This method can be called to record a transaction (either a
  // purchase or a payment). It does not allow the customer to
  // exceed their credit limit.

  public boolean recordTransaction(double transAmount) {
    boolean status;
    double projectedBalance = balance + transAmount;
    if (projectedBalance <= creditLimit) {
      balance = projectedBalance;
      status = true;
    }
    else
      status = false;
    return status;
  }

  // This method can be called to get the monthly finance charge.

  public double financeCharge() {
    return balance * MONTHLY_RATE;
  }
}

Notes:

  1. The private access modifier is coded for each instance variable so they may only be accessed within this class. A class client can indirectly access the instance variables by calling the "set" and "get" methods.

  2. The MONTHLY_RATE constant is defined to be both public and static. It isn't necessary to hide a constant because it can never be modified. By coding static, it is associated with the class and not with an instance of the class (the implications of which will be covered in the next lesson).

  3. Constructor methods MUST have the same name as the class name and NEVER specify a value to be returned.

When an object is constructed by the JVM, all instance variables are initially set to default values (numeric variables are 0, boolean variables are false, char variables are '\u0000', and object references are null). Constructor methods may overlay these initial settings with other values.

To enforce editing, the above constructors that accept parameters call the appropriate "set" method for each instance variable. If the data passed to the constructor was invalid, the "set" methods will not store bad values. Instead, those instance variable are allowed to retain their default setting.

  1. As long as no other constructor is explicitly coded, the Java compiler generates a default constructor that accepts no parameters and lets all instance variables retain their default settings. In other words,

public Customer() {
}
 

would automatically exist if we hadn't defined another constructor. Because we required overloaded constructors, we were forced to explicitly define the constructor that accepts no parameters.

 

Using a custom class

A custom class is used in exactly the same way as a packaged Java class. The following sample program is admittedly large but well worth your study. It contains both the Customer class (as defined above) and an App class whose main() method permits the user to create and process a Customer object.

public class App {

  // This method is the starting point for the application.

  public static void main(String[] args) {

    // Method-wide variables.

    Customer current = new Customer();
    byte choice;

    // Loop to process one menu transaction.

    do {

      // Display the menu and read the user's menu selection.

      Utility.separator(70, '~');
      Utility.skip();
      System.out.println("CUSTOMER MENU");
      Utility.skip();
      System.out.println("1 - Create new customer");
      System.out.println("2 - Record customer transaction");
      System.out.println("3 - Display customer information");
      System.out.println("4 - Change customer credit limit");
      System.out.println("5 - Exit");
      Utility.skip(2);
      System.out.print("Enter your selection: ");
      choice = Keyboard.readByte();

      // Process the user's menu selection.

      switch (choice) {

        // This case constructs a new customer object with values
        // entered by the user.

        case 1: {
          Utility.separator(70, '~');
          System.out.print("Account number: ");
          int acctNo = Keyboard.readInt();
          System.out.print("Name: ");
          String name = Keyboard.readString();
          System.out.print("Credit limit (1000, 5000, or 10000): ");
          int creditLimit = Keyboard.readInt();
          current = new Customer(acctNo, name, creditLimit);
          break;
        }

        // This case records a customer transaction. Note that the
        // customer is not allowed to exceed their credit limit.

        case 2: {
          Utility.separator(70, '~');
          System.out.print("Transaction amount: ");
          double amount = Keyboard.readDouble();
          boolean status = current.recordTransaction(amount);
          Utility.skip();
          if (status == true) {
            System.out.println("\tAccepted");
          }
          else {
            System.out.println("\tRejected");
          }
          break;
        }

        // This case displays customer information. Note that their
        // monthly finance charge will only be calculated and displayed
        // when their balance is greater than zero.

        case 3:
          Utility.separator(70, '~');
          System.out.println("\tAccount number: " + current.getAcctNo());
          System.out.println("\t          Name: " + current.getName());
          System.out.println("\t  Credit limit: " +
            Utility.moneyFormat(current.getCreditLimit()));
          System.out.println("\t       Balance: " +
            Utility.moneyFormat(current.getBalance()));
          if (current.getBalance() > 0) {
            System.out.println("\tFinance charge: " +
              Utility.moneyFormat(current.financeCharge()));
          }
          break;

        // This case changes the customer's credit limit if a valid
        // credit limit is entered by the user.

        case 4: {
          Utility.separator(70, '~');
          System.out.print("Credit limit (1000, 5000, or 10000): ");
          int creditLimit = Keyboard.readInt();
          boolean status = current.setCreditLimit(creditLimit);
          Utility.skip();
          if (status == true) {
            System.out.println("\tAccepted");
          }
          else {
            System.out.println("\tRejected");
          }
          break;
        }

        // This is the exit case. It does absolutely nothing.

        case 5:
          break;

        // This default case handles an invalid menu selection.

        default:
          Utility.separator(70, '~');
          System.out.println("\tInvalid selection");
          break;
      }
    } while (choice != 5); // Loop unless the user wants to exit.
  }
}

// This is a demonstration class that encapsulates certain data
// and processing of a customer. It is by no means complete.

class Customer {

  // Private instance variables.

  private int acctNo;
  private String name;
  private int creditLimit;
  private double balance;

  // Public class constant for the monthly interest rate.

  public static final double MONTHLY_RATE = .015;

  // This constructor accepts no parameters. It lets instance
  // variables retain their initial JVM construction values.

  public Customer() {
  }

  // This constructor accepts values for account number and name.

  public Customer(int iAcctNo, String iName) {
    setAcctNo(iAcctNo);
    setName(iName);
    setCreditLimit(1000);
    setBalance(0);
  }

  // This constructor accepts values for account number, name,
  // and credit limit.

  public Customer(int iAcctNo, String iName,int iCreditLimit) {
    setAcctNo(iAcctNo);
    setName(iName);
    setCreditLimit(iCreditLimit);
    setBalance(0);
  }

  // This method can be called to set the account number. If
  // successful, true is returned.

  public boolean setAcctNo(int nAcctNo) {
    boolean status;
    if (nAcctNo > 0) {
      acctNo = nAcctNo;
      status = true;
    }
    else
      status = false;
    return status;
  }

  // This method can be called to set the name. If successful,
  // true is returned.

  public boolean setName(String nName) {
    boolean status;
    if (nName.length() > 0) {
      name = nName;
      status = true;
    }
    else
      status = false;
    return status;
  }

  // This method can be called to set the credit limit. If successful,
  // true is returned.

  public boolean setCreditLimit(int nCreditLimit) {
    boolean status;
    switch (nCreditLimit) {
      case 1000:
      case 5000:
      case 10000:
        creditLimit = nCreditLimit;
        status = true;
        break;
      default:
        status = false;
        break;
    }
    return status;
  }

  // This method can be called to set the balance. If successful,
  // true is returned.

  public boolean setBalance(double nBalance) {
    balance = nBalance;
    return true;
  }

  // This method can be called to get the account number.

  public int getAcctNo() {
    return acctNo;
  }

  // This method can be called to get the name.

  public String getName() {
    return name;
  }

  // This method can be called to get the credit limit.

  public int getCreditLimit() {
    return creditLimit;
  }

  // This method can be called to get the balance.

  public double getBalance() {
    return balance;
  }

  // This method can be called to record a transaction (either a
  // purchase or a payment). It does not allow the customer to
  // exceed their credit limit.

  public boolean recordTransaction(double transAmount) {
    boolean status;
    double projectedBalance = balance + transAmount;
    if (projectedBalance <= creditLimit) {
      balance = projectedBalance;
      status = true;
    }
    else
      status = false;
    return status;
  }

  // This method can be called to get the monthly finance charge.

  public double financeCharge() {
    return balance * MONTHLY_RATE;
  }
}

 

Lab exercise for Ferris students

E-mail your answers to this assignment no later than the due date listed in the class schedule.

 

Review questions

  1. A instance variable of a class should be declared

  1. public

  2. private

  3. static

  4. final

  1. What will result from attempting to compile and execute the following program? The line numbers are for reference purposes only.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class App {
  public static void main(String[] args) {
    Number x = new Number(3);
    System.out.println("Value is " + x.getNumber());
  }
}
class Number {
  private int number;
  public Number(int iNumber) {
    setNumber(iNumber);
  }
  public void setNumber(int nNumber) {
    if (nNumber >= 21)
    number = nNumber;
  }
  public int getNumber() {
    return number;
  }
}
  1. Compilation will fail at line 3

  2. Compilation will fail at line 4

  3. Compilation will fail at line 7

  4. Compiles and runs to display "Value is 3"

  5. Compiles and runs to display "Value is 0"

  1. What will result from attempting to compile and execute the following program? The line numbers are for reference purposes only.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class App {
  public static void main(String[] args) {
    Message x = new Message("Java is fun");
    System.out.println(x.getMessage());
  }
}
public class Message {
  private String message;
  public Message(String iMessage) {
    message = iMessage;
  }
  public String getMessage() {
    return message;
  }
}
  1. Compilation will fail at line 3

  2. Compilation will fail at line 4

  3. Compilation will fail at line 7

  4. Compiles and runs to display "Java is fun"

  5. Compiles and runs to display "null"

  1. What will result from attempting to compile and execute the following program? The line numbers are for reference purposes only.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class App {
  public static void main(String[] args) {
    Message x = new Message();
    System.out.println(x.getMessage());
  }
}
class Message {
  private String message;
  public Message(String iMessage) {
    message = iMessage;
  }
  public String getMessage() {
    return message;
  }
}
  1. Compilation will fail at line 3

  2. Compilation will fail at line 4

  3. Compilation will fail at line 7

  4. Compilation is successful but a runtime error will occur

  5. Compiles and runs to display "null"