Activity #3: Objects and Components

by Steve Hirst
12 Sept 97

 
There is quite a bit of content in this activity and its things I see a lot of questions on in internet news groups.  So, bear with me.

Objects constructors, finalizers and reference

First, the object model.  Just like C++, Java has a notion of class, object, class and object fields, and object references. Classes are programmer defined types; the values that correspond to the types are objects. Unlike C++, however, when I declare a class MyClass and then a variable typed by the classname (i.e. MyClass oneObject; ) an object of type MyClass is not created; instead, the variable oneObject is initializialized to a null reference.  Strictly speaking all object variables are reference variables and all objects must be dynamically created with the new operator. Look at the following code snippet:
 
Cloning Class
// Cloning: demonstrate reference and clone
// Steve Hirst
// $Id: Cloning.java 1.1 1997/09/12 03:29:19 Steve Exp Steve $
package activity;

public class Cloning implements Cloneable {
  private int value;
  public int getValue() { return value; }
  public void setValue(int newValue ) {
        value = newValue;
        }
  public Object clone() {
    Cloning copy = new Cloning();
        copy.setValue(getValue());
    return copy;
        }…

The class Cloning has three public methods defined: getValue(), setValue(int) and clone(). The methods setValue and getValue allow for the setting and getting of the private integer field.  As in C++, a default constructor is generated by the system. Consider the following code:
 
What happens here:?
        Cloning one,two,three,four;
        one = new Cloning();
        one.setValue(42);
        two = one;
        two.setValue(47);
        three = new Cloning();
        three.setValue(24);
        four = (Cloning) three.clone();
        four.setValue(74);
        System.out.print("One = ");
        System.out.println(one.getValue());
        System.out.print("Two = ");
        System.out.println(two.getValue());     
        System.out.print("Three = ");
        System.out.println(three.getValue());   
        System.out.print("Four = ");
        System.out.println(four.getValue());
        System.out.println("======================");
 

Activity: Figure out the values for one,two, three and four.

Write up a quick java program to test your answer.  If you haven't noticed before, java does not have global variables or functions.  You will need to define a main proc. attached to some class; you can add the above code as a static main method in the class clone.
 

Passing reference parameters by value.

Unlike C++, java passes all parameters by value. That means that assignments to parameter variables do not effect the actual arguments.  However, call a method on an object may change that object and change the behavior in all the places that object is referred to (i.e. the calling environment). Try the following method to see this in action:
 
 
shuffle
        static void shuffle(Cloning arg1, Cloning arg2,
                          int arg3, int arg4) {
                arg1 = arg2; // arg1 passed by value; no change to actual1
                arg1.setValue(99); // arg1 and arg2 both refer to same
                                   //     object as actual2
                arg3 = arg4; // arg3 passed by value no change to intActual1
                arg4 = -2; //  ditto
                }
 

Class vs. Object members

If you put the keyword static in front of a member declaration, it is a class member.  There is always exactly one instance of a class member independent of how many (including zero) objects exist of that type.  So, if you declare a class:
 
Util
class Util {
   static int screenWidth;
   public static void setScreenWidth(int width ) { screenWidth=width; };
   public static int getScreenWidth() { return screenWidth; };
   int cursorPosition=0;
   public void moveLeft()
     { ++cursorPosition;
           if (cursorPosition>=screenWidth) cursorPosition=0;
         }
   public int getCursorPostion() {return cursorPosition; };
   
  }

The members screenWidth, setScreenWidth, and getScreenWidth are all class members.  They can  be referred to as Util.screenWidth, Util.setScreenWidth(23) and Util.getScreenWidth(); however referencing an object member would be illegal (for example, Util.moveLeft() ).  Also if we have the following code:
 
Using Util
          Util one = new Util();
          one.setScreenWidth( 80 );
          System.out.println(util.getScreenWidth());
will output 80.  Also the following is illegal:
 
Illegal reference to non-static member
   public static void setScreenWidth(int width ) { 
      screenWidth=width;
      if (cursorPosition>=screenWidth) cursorPosition = 0;
      };
 

Garbage collection and finalization

Because all objects are dynamically created in java, they must all be destroyed.  However, java does not have a delete operator or the concept of a destructor. Instead, the java runtime uses garbage collection to recover unreference memory locations.  You can define a finalize method.  The following defines a class Thing which keeps track of how many objects of type Thing have been created and exits when the 20th object created is finalized:
 
Thing
class Thing {
   private static int counter=0;
   private int index;
   public Thing() {
      index = ++counter;
          if (index==100) 
              System.out.println("Hundredth thing served.");
          }
   public static int numberCreated()
      { return counter; }
   public void finalize() {
      System.out.print("Finalizing thing #");
          System.out.println(index);
          if (index==20) {
              System.out.print( counter );
                  System.out.println(" things created.");
                  System.exit(1);
                  }
        }
}

The following code creates a lot of garbage:
 
Garbage
                Thing aThing;
                int loopCount = 0;
                while (true) { 
                   aThing = new Thing();
                   switch (++loopCount) {
                   case 4:
                   case 12:{
                       System.out.print("Calling runFinalization after creating ");
                           System.out.print( Thing.numberCreated() );
                           System.out.println(" things.");
                           System.runFinalization();
                           System.out.println("---- end of finalization ---");
                           break;
                           }
                   case 8: {
                       System.out.print("Calling gc after creating ");
                           System.out.print( Thing.numberCreated() );
                           System.out.println(" things.");
                           System.gc();
                           System.out.println("---- end of finalization ---");
               break;
                           }
                        case 16: {
                           System.out.print("Calling gc before runFinalization after creating ");
                           System.out.print( Thing.numberCreated() );
                           System.out.println(" things.");
                           System.gc();
                           System.runFinalization();
                           System.out.println("---- end of finalization ---");
                           break;
                           }
                   }
               } 
 

What the hell...

does this code do? First the working part  the loop is basically: while(true)  { aThing = new Thing(); ... }. Everytime through the loop the reference to the previously created Thing is replaced by a new one; this makes the previous object into garbage.  If you write a loop that does nothing more than this, it will eventually fill up memory and evoke garbage collection and run the finalization members of the garbage Things until #20 is finalized at which time the program exits.
The reason for the funny switch is to demonstrate the two system calls: System.gc() and System.runFinalization().  The code evokes runFinalization() by itself on the 4th and 12th pass through the loop.  It evokes gc() by itself on the 8th pass through the loop.
And, it runs gc() followed by runFinalization() after the 16th pass.  Try it and examine the output.  As a clue, the garbage collector identifies all unreferenced objects and marks them available for finalization; finalization takes the objects marked by the garbage collector, runs their finalization procedure and frees them memory.
 

Next time:

I will get to components I promise