Session 8

More on Growing Java Programs

CS 2530
Intermediate Computing

New Java: Naming Constants

Over the last two sessions, we have grown and refined an ArrayMemoDatabase class that meets our project requirements and is reasonably easy to understand. But there is still one bit of code that bothers me:

    pair = new MemoAssociation[10];

It's that 10, in the constructor. It's the only place the number appears in my program, and it hides an important idea. It is the maximum number of memos that my database can hold.

In your intro courses, you surely learned the risk of using "magic numbers" in your code. For one thing, they tend to appear multiple times. Duplication makes it harder to modify code, because now the programmer has to remember to change every occurrence.

But this is only a symptom of a deeper problem. If 10 does appear twice in a program, how do I know that they mean the same thing? The meaning of a constant is inherent in its context. This means people who read the code must slow down every time they encounter a constant to figure out what it means in this position, and if that meaning is the same as another occurrence elsewhere.

As we saw last time when we factored common behavior out into a method, we did more than eliminate duplication. We gave the common behavior a name. Using the name makes it easier to understand the code because it says what it means. Giving a name to a constant has the same deep effect.

In Java, we could treat the maximum number of memos like other information the memo database must know: create an instance variable to hold the constant:

    private int maximumNumberOfMemos;
and initialize it in the constructor:
      maximumNumberOfMemos = 10;

      pair = new MemoAssociation[maximumNumberOfMemos];
      counter = 0;

But this isn't quite right, in two ways:

The code doesn't says what it means.

Java gives us a way to say what we mean, using keywords that modify our variable declaration.

Class Features

maximumNumberOfMemos is a feature of the ArrayMemoDatabase class, so we declare it to be a static variable. static is a lifetime modifier. It tells the Java compiler that this object exists throughout the program, independent of any instance of the class. It is created at the time the class is loaded, before any instances are created.

This is what makes static useful for representing a feature of the class. There will be one copy of the variable, and it will be shared by all the instances. This means, of course, that it isn't part of any instance, so Java does not have to waste space storing a copy in every instance.

Because a static variable is a class feature and not an instance feature, we usually name it with a capital letter. So our variable will be named MaximumNumberOfMemos.

We have seen static once before, in the declaration of our programs' main methods. It means the same thing there. The main method is a feature of the class that contains it, not a feature of any instance of the class. It must exist before we create any instances. Indeed, it is the mechanism by which the first object(s) in a running program are created.

(Perhaps now you can appreciate more fully why I referred to our first main method back in Session 3 as The Big Bang of the memo pad universe. It's the ultimate source of all the objects.)


When an object's value cannot be changed, we say that the object is immutable. We would like to initialize MaximumNumberOfMemos with its value and never change it again. So we declare it to be a final variable. final is a mutability modifier. It tells the Java compiler that this is the last time that the object's value is changed.

Because we declare MaximumNumberOfMemos as both static and final, we must initialize it immediately, and not do so in the constructor. This makes sense. The object is a feature of the class, not an instance. The purpose of a constructor is to initialize a new instance when it is created. This variable exists and has its value prior to the creation of any object.

So, we are ready to create our named constant:

    private static final int MaximumNumberOfMemos = 10;
and use it in our constructor:
      pair = new MemoAssociation[MaximumNumberOfMemos];
      counter = 0;

After making a change like this -- even a small one! -- don't forget to run your tests. They help you to be confident that you haven't broken anything. Even a small change can introduce an expected bug or side effect. Running the tests after each change lets you program with greater confidence.

That was a long discussion to fix two little characters, but we learned a lot in the process, some about OO programming and some about Java.

Exercise: Static Cling

Suppose we have this class:

    public class StaticVariable
      private static int mystery;
      public StaticVariable()
      { mystery++; }
      public int mysteryValue()
      { return mystery; }

What is the output of this piece of code?

    StaticVariable uno = new StaticVariable();
    System.out.println( "Mystery value for uno : " + uno.mysteryValue() );

    StaticVariable dos = new StaticVariable();
    System.out.println( "Mystery value for uno : " + uno.mysteryValue() );
    System.out.println( "Mystery value for dos : " + dos.mysteryValue() );

    StaticVariable tres = new StaticVariable();
    System.out.println( "Mystery value for uno : " + uno.mysteryValue() );
    System.out.println( "Mystery value for dos : " + dos.mysteryValue() );
    System.out.println( "Mystery value for tres: " + tres.mysteryValue()) ;

What happens if we make mystery final, too? What happens if we take static off mystery?

Don't worry... [We won't have to deal with all this complexity very often. We will occasionally create named constants, making them static final. We will rarely see either modifier in another setting.]

Default Constructors

Suppose we have this class:

    public class SimpleClass
      private int multiplier = 2;
      private int sum        = 0;
      public SimpleClass( int m )
        multiplier = m;
      public void apply( int term )
        sum = sum + multiplier * term;
      public int value()
        return sum;

What is the output of this piece of code?

    SimpleClass sc = new SimpleClass();
    sc.apply( 2 );
    sc.apply( 3 );
    sc.apply( 4 );

18. Good. What happens if we add a constructor?

    public SimpleClass( int m )
      multiplier = m;


    > SimpleClass sc = new SimpleClass( 3 );
    > sc.value()

Fine. How about our original test?

    > SimpleClass sc = new SimpleClass();
    Static Error: No constructor in SimpleClass matches this invocation
        Arguments: ()
        Expected return type: SimpleClass
        Candidate signatures: SimpleClass(int)

Huh? [Adding code broke existing code, despite....] Why? [If a class has no constructors, then Java provides a default constructor. If it has even one, Java does not.]

[Don't worry... This is one reason we have a rule. There is a more important reason for the rule in a course for beginning object-oriented programmers. Each time you create a new class, I want you to think about two things: What must the object know to do its job? And: What is its initial state? These are the instance variable declarations and the constructor(s), repsectively. I want you to write code that makes these two ideas explicit. That way I know you have thought about them. And you do, too!]

Multiple Constructors for ArrayMemoDatabase

... What if we want a larger database?

... Pass desired size as an argument.

... Now, 10 is the default arrray size, not a requirement.

... Make the default a named class constant.

... Write a second constructor, which takes the desired size as an argument.

... (There is duplication. Factor out the common behavior into a helper method.)

... We can use from either constructor from within our program, including main. What about our GUI? What about sending the argument to main, say, from the command line? (Soon.)

Wrap Up

Eugene Wallingford ..... ..... September 18, 2012