Site Navigation

Site Search

Chapter 6: A Second Look at Classes and Objects

Learning Objectives

After completion of this chapter, you should be able to

Explain the role of Instance fields and methods

Each instance of a class has its own copy of instance variables. For example:

Instance methods require that a new instance of a class be created in order to be used.

Instance methods typically interact with instance fields or calculate values based on those fields.

Explain the role of static class members

Static fields and static methods do not belong to a single instance of a class.

They are also known as class fields or class methods.

To invoke a static method or use a static field the class name, rather than the instance name, is used.

Declare and use static class fields

Class fields are declared using the static keyword between the access specifier and they field type.

private static int counter = 0;

The counter field is initialized to 0 only once, regardless of the number of times the class is instantiated.

Primitive static fields are initialized to 0 if no initialization is performed.

Example: Declare a static class variable

   public class Temperature
   {
   // Declare static class variable
      private static int numberOfTemperatures = 0;
   
   // Declare instance variable  
      private double degreesKelvin;
   
   // Constructor method
      public Temperature(double k)
      {
         degreesKelvin = k;        
         numberOfTemperatures++;
      }
   
   // Returns the number of Temperature objects instantiated
      public int getNumberOfTemperatures()
      {
         return numberOfTemperatures;
      }
   }

Example: Use the Temperature class

    public class Demo
   {
       public static void main(String [] args)
      {
         int count;

         Temperature t1 = new Temperature(250.5);
         Temperature t2 = new Temperature(300.8);
         Temperature t3 = new Temperature(299.6);
     
         count = t1.getNumberOfTemperatures();
         System.out.println(count + " Temperature objects created");
      }
   }

Define and use static class methods

Methods can be declared as static by placing the static keyword between the access modifier and the return type of the method.

   public static double kelvinToCelsius(double degreesKelvin)
   {
      return (degreesKelvin - 273.16);
   }

When a class contains a static method, it is not necessary to create an instance of the class in order to use the method. Instead, use the name of the class followed by a dot

 double celsius = Temperature.kelvinToCelsius(150.5);

Static methods are convenient because they may be called at the class level. They are typically used to create utility classes, such as the Math class in the Java Standard Library.

 double number = Math.sqrt(16.0);

However, static methods may not access instance fields, only static fields.

Explain what is meant by method overloading

Java allows methods to be overloaded. Overloading occurs when a class contains more than one method with the same name.

Overloaded methods must differ in the number or types of parameters.

Overloading is best used for methods that perform essentially the same operation. The advantage of overloading: Fewer method names to remember.

Example: method overloading, class Pay

   public class Pay
   {
       public static double getWeeklyPay(int hours, double payRate)
      {
         return hours * payRate;
      }
   
       public static double getWeeklyPay(double yearlySalary)
      {
         return yearlySalary / 52;
      }
   }

Example: using an overloaded method

   import java.util.Scanner;
   import java.text.DecimalFormat;

   public class WeeklyPay
   {
       public static void main(String[] args)
      {      
      // Create a Scanner object for keyboard input.
         Scanner keyboard = new Scanner(System.in);
     
      // Create a DecimalFormat object for output formatting.
         DecimalFormat formatter = new DecimalFormat("$#,##0.00");
     
      // Determine whether the employee is hourly or salaried.
         System.out.print("Are you hourly or salaried employee (H or S)? ");
         String userInput = keyboard.nextLine().trim();
         char selection = userInput.charAt(0);
     
      // Determine and display the weekly pay.
         switch(selection)
         {
            case 'H':
            case 'h':
               System.out.print("Enter hours worked and pay rate: ");
               int hours = keyboard.nextInt();
               double rate = keyboard.nextDouble();
               double weeklyPay = Pay.getWeeklyPay(hours, rate);
               System.out.println("Your pay is " + formatter.format(weeklyPay));
               break;
         
            case 'S':
            case 's':
               System.out.print("Enter annual salary? ");
               double salary = keyboard.nextDouble();
               System.out.println("Your weekly pay is " +
                                 formatter.format(Pay.getWeeklyPay(salary)));
               break;
         
            default:
               System.out.println("Invalid selection.");
         }
      }
   }

Define a method signature

Java uses the method signature (name, type of parameters and order of parameters) to determine which method to call.

This process is known as binding.

The return type of the method is not part of the method signature.

Overload the Constructor Method

Quite often we may wish to allow users of our class to use different constructor methods useful when instantiating a new object.

Since all constructor methods in a class must have the same name, that is the name of the class itself, we must overload this name if we want different methods.

With our Fraction class, for example, we may wish to allow users to use constructor methods to instantiate a default fraction, a whole number fraction, etc.

Code Example: Fraction class

// Constructor method: instantiate a regular fraction
   public Fraction(int n, int d)
   {
      numerator = n;
      denominator = d;
   }
   
// Constructor method: instantiate a "zero" fraction with no arguments
   public Fraction()
   {
      numerator = 0;
      denominator = 1;
   }
   
// Constructor method: instantiate a whole number fraction
   public Fraction(int wholeNumber)
   {
      numerator = wholeNumber;
      denominator = 1;
   }

We now have three ways to construct new Fraction object variables

   // Declare three object reference variables
      Fraction number1;
      Fraction number2;
      Fraction number3;
     
   // Instantiate a "zero" fraction using the no-args constructor
      number1 = new Fraction();
     
   // Instantiate a whole number fraction
      number2 = new Fraction(5);
     
   // Instantiate a regular fraction
      number3 = new Fraction(2, 6);

Use a default constructor

Java automatically provides a default no argument constructor for a class if a constructor is not explicitly written.

The default constructor provided by Java:

We, as programmers, can provide our own no-argument constructor to override the behavior of the default no-argument constructor.

If any parameter constructor is written, we should also write a no-argument constructor.

If we write a no-argument constructor, we should provide the initialization of all instance fields.

Explain the new keyword

The declaration of the object variable doesn't actually store the object's data. Instead, it will store a reference (pointer) to the object's data.

Using the new keyword causes the instance of the object to be created in memory.

When assigning objects to another, the object names are said to be aliases for the same object

// Declare two object reference variables
   Fraction number1;
   Fraction number2;
     
// Instantiate number1
   number1 = new Fraction(3, 8);
     
// Let number2 be an alias (another name) for number1
   number2 = number1;

To indicate that an object variable doesn't currently reference any object instance, the variable can be assigned the value null

 number1 = null;

Pass object reference variables as arguments

Object reference variables can be passed to methods as parameters.

Java passes all parameters by value, so when an object is passed as a parameter, the value of the reference variable is passed.

The value of the reference variable is a reference to the object in memory. A copy of the object is not passed, just a pointer to the object.

      Fraction number1 = new Fraction(3,8);
      Fraction number2 = new Fraction(4);
     
      Fraction number3 = null;
   
      number3 = number2.multiply(number1);

Return object references from methods

Methods are not limited to returning the primitive data types.

Methods can return references to objects as well.

Just as with passing parameters, a copy of the object is not returned, only its address.

Write equals and toString methods

The equals method is defined for all classes, but in many cases you will want to overload the equals method with your own version of equals.

   public boolean equals (Fraction f2)
   {
      return (numerator == f2.numerator) && (denominator == f2.denominator);
   }

One could then use the equals method in a main method

Fraction number1 = new Fraction(1,2);
Fraction number2 = new Fraction(2,3);

if (number1.equals(number2))
   // do something

Likewise, the toString method is also defined for all classes, but you will also want to write your own toString() method.

public string toString()
{
   String f = numerator + "/" + denominator;
   return f;
}

One could than use the toString method in a main method. Leaving off the .toString() inside a println statement will automatically invoke the toString method.

System.out.println("number 1 is " + number1);

This is equivalent to invoking it explicitly, but the latter is rarely done.

// toString() is shown but is not necessary
System.out.println("number 1 is " + number1.toString());

Write methods that return copies of objects

There are two ways to copy an object.

Write a Copy Constructor

A copy constructor is a constructor that takes an existing object of the same class as its argument and makes the new object an exact copy of the argument.

For example, the copy constructor for the Fraction class would be

   public Fraction(Fraction f)
   {
      numerator = f.numerator;
      denominator = f.denominator
   }

To use the copy constructor to instantiate a new Fraction object would look like

   Fraction number1 = new Fraction(3, 5);
   Fraction number2 = new Fraction(number1);
   
   // The copy constructor is very different from simply using assignment
   Fraction number3 = number2;

Use the this reference in your class definitions

The this reference is simply a name that an object can use to refer to itself. It is available for use in all non-static methods.

The this reference can be use to overcome shadowing and allow a parameter to be the same name as an instance variable.

   public class Height
   {
   // Declare instance variables
      private int feet;
      private int inches;
   
      public void setFeet(int feet)
      {
         this.feet = feet;
      }
   // Rest of class not shown
   }

The this reference can also be used to call a constructor method from another constructor method.

   // Constructor method: set Height's feet and inches
      public Height(int ft, int in)
      {
         feet = ft;
         inches = in;
      }
     
      // Overloaded constructor method: even feet, no inches
      public Height(int ft)
      {
         this(ft, 0);
      }
     
      // No-argument constructor method: set to 0 feet, 0 inches
      public Height()
      {
         this(0, 0);
      }

Use inner classes in your program

A class definition may have other classes defined within them. These inner classes have unique properties:

    public class Item
   {
   // Declare instance variables
      private String description;
      private int itemNumber;
      private CostData cost;
     
   // Item class constructor
       public Item(String desc, int itemNum, double wholesale, double retail)
      {
         description = desc;
         itemNumber = itemNum;
         cost = new CostData(wholesale, retail);
      }  
   // Regular class methods omitted
   
       // Begin inner class
       private class CostData
      {
      // Declare instance variables
         public double wholesaleCost;
         public double retailCost;    
               
      // Constructor for Cost Data
          public CostData(double w, double r)
         {
            wholesaleCost = w;
            retailCost = r;
         }
      } // end inner class
   } // end outer class

For more information on inner classes, see examples RetailItem.java and InnerClassDemo.java (Figures 6-26 and 6-27) in textbook.

Use enumerated types in your programs

New to Java 1.5 are enumerated data types. Known as an enum, you can use an enumerated data type to create variables that can hold only the values that belong to the enumerated data type.

Enumerated data types require a definition and then variable declarations just like a regular class

 // Define an enumerated type
    enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
   
 // Declare an enum variable
    Day workDay;
   
 // Assign it a value
    workDay = Day.WEDNESDAY;

An enum is a specialized class

Use Enumerated Types Methods

For more information on enum types and variables, see examples EnumDemo.java, CarType.java, CarColor.java, SportsCar.java, SportsCarDemo.java in your textbook.

Use enumerated types with switch statements

You can use enums as switch arguments in Java 1.5

public String getAbbreviation()
{
   switch (workDay)
   {
       case MONDAY:
         return "Mon.";
      case TUESDAY:
         return "Tues.";
      case WEDNESDAY:
         return "Wed.";
      case THURSDAY:
         return "Thurs.";
      default:
         return "Long Weekend";
   }
}

Explain what is meant by Aggregation

Creating an instance of one class as a reference in another class is called object aggregation.

Aggregation creates a "has a" relationship between objects. In the UML class diagram below, the course class has a Instructor and has a TextBook

UML Diagram

Explain class collaboration

Collaboration occurs when two classes interact with each other.

If an object is to collaborate with another object, it must know something about the second object, its methods, and how to call them

Telling one object about another object is most easily done when an object is instantiated.

The hard part of object-oriented design is determining the classes that will make up your program, their data and actions, and the interactions between the classes. A popular technique in the beginning stages of OO design is by creating CRC cards. CRC stands for class, responsibilities, and collaborations