slanted W3C logo

Day 08 — Objects

In today's lecture we look at what happens when you create an object.

Fractions

Java's built-in primitive types lets the client represent integer and real numbers, but there is no primitive type that represents fractions.

You could as the client keep track of all of the fraction numerators and denominators, but this quickly becomes cumbersome:

import java.io.PrintStream;

public class YuckFractions
{
   public static void main(String[] args)
   {
       PrintStream output = System.out;
       
       // computes Example 4.3 from the textbook manually
       int num1 = 5;
       int den1 = 3;
       
       int num2 = 7;
       int den2 = 6;
       
       int num3 = 31;
       int den3 = 45;
       
       int num4 = 3;
       int den4 = 4;
       
       // compute (num1 / den1) x (num2 / den2)
       int num5 = num1 * num2;
       int den5 = den1 * den2;
       
       // compute (num5 / den5) / (num3 / den3)
       int num6 = num5 * den3;
       int den6 = den5 * num3;
       
       // compute (num6 / den6) + (num4 / den4)
       int num7 = num6 * den4 + num4 * den6;
       int den7 = den6 * den4;
       
       // aargh, even printing out the fraction is a pain
       output.printf("%d/%d%n", num7, den7);
   }
}

Delegating to a Fraction Utility

« utility »
FractionUtility
numerator: double
denominator: double
add(double, double): void
divide(double, double): void
multiply(double, double): void
      // set the numerator and denominator
      FractionUtility.numerator = 5;
      FractionUtility.denominator = 3;

      // do some arithmetic
      FractionUtility.multiply(7, 6);
      FractionUtility.divide(31, 45);
      FractionUtility.add(3, 4);

      output.printf("%d/%d%n",
                    FractionUtility.numerator,
                    FractionUtility.denominator);

There Can Be Only One

The utility looks like it made using fractions much easier, but what if you want more than one fraction? For example, suppose that you want to read in two fractions from the user:

      Scanner scanner = new Scanner(System.in);

      // read the first fraction
      Fraction.numerator = scanner.nextInt();
      Fraction.denominator = scanner.nextInt();
      
      // where do I store the second fraction???

A key feature of a utility is that there can only ever be one copy of the attributes (and methods) in memory, and those copies belong to the utility. This is what the keyword static means.

type.lib.Fraction

The type.lib package provides a Fraction class that encapsulates the idea of a fraction. It provides methods for fraction arithmetic, getting and setting the values of the numerator and denominator, and many other actions.

import java.io.PrintStream;
import type.lib.Fraction;

public class YippeeFractions
{
   public static void main(String[] args)
   {
       PrintStream output = System.out;
       
       Fraction f = new Fraction(5, 3);
       
       f.multiply(new Fraction(7, 6));
       f.divide(new Fraction(31, 45));
       f.add(new Fraction(3, 4));
       output.println(f);
   }
}

Unlike a utility class, the Fraction class allows the client to create separate and distinct "copies" called objects, or instances.

Objects

In the real world, an object is a physical thing (the person next to you, your shoe, your computer, etc.). Objects have state (properties) and behavior (actions they can perform).

"Lola" the Cat
State: name, color, weight, asleep, awake, crazy, ...
Behavior: sleep, sleep, sleep, eat, purr, ...

In Java, an object also has state and behavior. An object stores its state in attributes and performs its behavior using methods. Often, the methods will cause the state of the object to change (by modifying its attributes). You can think of attributes as being variables that are owned by the object.

Classes

In the real world, there are many individual house cats; however, cats in general share enough common features that when someone says "my cat ..." everyone knows what you are talking about.

In Java, a class is a blueprint for constructing objects. A class defines what attributes an object will have (its state) and it defines what behavior an object can perform (its methods). Notice that a class groups related attributes and methods in one place.

A class defines a type; for example, if we had a Cat class, it would be possible to create variables of type Cat.

Using a class, the client can create many different instances of the class; for example, if we had a Cat class, we could create many objects, each one representing a different cat.

What Does Fraction f; Mean?

The statement Fraction f; is similar to the the statement int x;. It causes a block of memory to be reserved that is large enough to hold what is called a reference to a Fraction object and labels the block with the identifier f.

0
 
  ¦
  main
 
f ⇒ 100
 
 
  ¦

We say that f is a reference variable that holds a value that refers to a Fraction object (or a Fraction instance). In this example, the actual Fraction object has not yet been created, so no value is set for the memory block starting at 100.

Loading the Fraction Class

Recall that a class serves as a blueprint for creating objects; the Fraction class serves as a blueprint for creating Fraction objects.

The first time that the running program uses the Fraction class, the compiled class is loaded somewhere into memory. In our memory diagram model, the class is shown as a block of memory that contains the attributes, methods, and constructors of the class.

0
 
  ¦
  main
 
f ⇒ 100
 
  ¦
  ¦
500 Fraction class
numerator ⇒ 504
denominator ⇒ 520
 
  ¦

Not all of the features need to shown in the memory diagram; in the example below we only show the attributes that represent the numerator and denominator:

What Does new Fraction(5, 3) Mean

The expression new Fraction(5, 3) creates a new Fraction object with a numerator of 5 and a denominator of 3. In our memory diagram model, you can think of the expression as leading to the following sequence of events:

  1. allocate a block of memory large enough to hold a Fraction object
  2. copy the Fraction class into the allocated block of memory
  3. set the value of the numerator attribute to 5 and the denominator attribute to 3.
0
 
  ¦
  main
 
f ⇒ 100
 
  ¦
500 Fraction class
numerator ⇒ 504
denominator ⇒ 520
 
  ¦
600 Fraction object
numerator ⇒ 604 5
denominator ⇒ 620 3
 
  ¦

Notice that the class Fraction is not used to store the values of the numerator or denominator; the class (in this case) is only used as a template to create the Fraction object. The object is responsible for storing the values of the numerator and denominator.

What Does f = new Fraction(5, 3) Mean

The statement f = new Fraction(5, 3) causes a value to be stored in the memory reserved for the variable f. But what value is stored there?

Recall that f is a reference variable; thus it must hold some value that refers to an object in memory. We shall use for the value the memory address of the block of memory holding the newly created Fraction object.

0
 
  ¦
  main
 
f ⇒ 100 600
 
  ¦
500 Fraction class
numerator ⇒ 504
denominator ⇒ 520
 
  ¦
600 Fraction object
numerator ⇒ 604 5
denominator ⇒ 620 3
 
  ¦

Notice that the Fraction object looks like a copy of the Fraction class; the object also has:

What is a Reference?

reference
a value that refers to an object

Whenever you declare a variable whose type is some sort of object (i.e. not a primitive like int or double) you are declaring a reference variable.

A reference variable holds a reference. A reference is some value that ultimately refers to an object in memory. In the preceding examples, we assumed that the reference was the memory address of an object.

The statement:

Fraction f = new Fraction(5, 3);

means f is a variable whose value refers to a Fraction object.

the value of f IS NOT a Fraction object.
the value of f IS NOT a Fraction object.
the value of f IS NOT a Fraction object.

one more time...

the value of f IS NOT a Fraction object.

Multiple References to an Object

You can have multiple reference variables that all refer to the same object.

import java.io.PrintStream;
import type.lib.Fraction;

public class YippeeFractions2
{
   public static void main(String[] args)
   {
       PrintStream output = System.out;
       
       Fraction f = new Fraction(5, 3);
       Fraction g = f;
       
       f.multiply(new Fraction(7, 6));
       f.divide(new Fraction(31, 45));
       f.add(new Fraction(3, 4));
       output.println(f);
       output.println(g);

       Fraction h = g;
       output.println(h);
   }
}

In the above example, f, g, and h all refer to the same Fraction object which results in the fraction 443/124 being printed 3 times.

What Does Fraction g = f; Mean

The statement Fraction g = f; does not create a new Fraction object. Instead, a block of memory is reserved to hold a reference to a Fraction object and the block is labelled with the name g. Then the value of f is copied into the memory block named g.

0
 
  ¦
  main
 
f ⇒ 100 600
g ⇒ 104 600
 
  ¦
500 Fraction class
numerator ⇒ 504
denominator ⇒ 520
 
  ¦
600 Fraction object
numerator ⇒ 604 5
denominator ⇒ 620 3
 
  ¦

Creating Objects

A class that allows a client to create objects will usually provide one or more constructors that allows the client to create an object with the desired state.

A constructor looks similar to a method but there are several important differences. First, the name of a constructor is the same as the name of the class. The statement

Fraction f = new Fraction(5, 3);

uses the constructor from the Fraction class to create a Fraction object with a numerator of 5 and a denominator of 3.

The constructor is responsible for initializing the state of the object based on the arguments provided by the client.

Constructors Never Return a Value

Second, a constructor never returns a value (not even void). If you look in the API you will see that the constructor declarations look like:

public Fraction()
public Fraction(long numerator, long denominator)

The operator new is responsible for returning a reference to the newly created object.

Constructors

Third, as a client, you always use new with a constructor, and you never include the class name or an object reference in front of the constructor.

// good
Fraction f = new Fraction(5, 3);

// good
f.multiply(new Fraction(7, 6));

The Default Constructor

Some classes define a constructor with no parameters; such a constructor is called a default constructor. The purpose of a default constructor is to allow a client to create an object without having to specify its initial state; instead, an object will be created with a default state (as defined by the implementer of the class).

The statement

Fraction f = new Fraction();

creates a Fraction object with a numerator of 0 and a denominator of 1 (look in the API), and assigns the value of the memory address of the object to the reference f.