Espresso: A Concentrated Introduction to Java


Static Fields

Summary: We consider Java's static fields, a mechanism for sharing information between static methods within the same class. We also consider how to write constant values in java.

Prerequisites: Java basics. Input and Output. Numbers. Static Methods.

Contents:

Background: Sharing Information Between Methods

At this point, you've learned enough Java to solve most problems using what is traditionally called imperative programming: You can define variables, sequence operations, do input and output, call subroutines, and control the order of operations with conditionals and loops. Do you need to know anything else? No. However, you will certainly benefit from other aspects of Java.

One particularly important programming technique involves sharing information between methods. You know one way to share information: You can pass that information as a parameter and, in some cases, modify it before it is returned to the caller.

However, it may be inconvenient to pass information. For example, if two methods in the same class need to access the same piece of data, it may be difficult to require that the client who calls those methods passes the same information to both methods. In addition, as you've already determined, Java sometimes does not pass changes to a parameter back to the caller.

As an alternative, Java provides a number of mechanisms to share information, including constants and static fields. (Java also provides other mechanisms that we will study later.) Constants are what they sound like: constant and therefore unchanging. We use constants to name constant values, like PI, that we might want to use in a variety of places. You have already used some constants, such as Integer.MAX_VALUE. Static fields are also sharable between methods, but their value can change.

Static Fields

To declare a static field, you write something that looks much like a combined variable declaration and initialization:

protection static type name = expression;

This field declaration goes within the body of a class, but outside the body of any method within that class.

Contrast the static field declaration with a variable declaration. Variable declarations lack the protection and the keyword static. Variable declarations must also be inside the body of a method.

Once you have declared a static field, you can use it much like a variable. That is, you can assign to it, change it, and, if it's an object, call its methods.

Protection Levels

The field declaration above includes one part that we have not yet considered in any depth: a protection level. The protection level is one mechanism Java includes for encapsulation; higher levels of protection limit what other classes can access your field.

There is a fourth type of protection, protected, which we will discuss in a subsequent reading.

An Application: Counting Recursive Calls

There are, of course, a wide variety of reasons to use static fields. Here's a simple one. As you may recall, one important thing that computer scientists do is evaluate the running time of algorithms. They do so in a variety of ways, including through theoretical methods and through experimental methods.

Consider two mechanisms for computing xn. One is given by the following recursive definition:

The other is given by a more complex recursive definition:

We can encode those in Java as follows:

public class MyMath
{
  public static BigInteger expt1(BigInteger x, int n)
  {
    if (n == 0) {
      return BigInteger.ONE;
    }
    else {
      return x.multiply(expt1(x, n-1));
    } 
  } // expt1(BigInteger, int)

  public static BigInteger expt2(BigInteger x, int k)
    throws Exception
  {
    if (0 == k) {
      return BigInteger.ONE;
    } // k is 0
    else if ((k % 2) == 0) { // k is even
      BigInteger tmp = expt2(x, k/2);
      return tmp.multiply(tmp);
    } // k is even
    else { // k is odd
      return x.multiply(expt2(x, k-1));
    } // k is odd
  } // expt2(BigInteger, int)
} // class MyMath

Which one makes fewer recursive calls? The latter, although more complicated, is significantly more efficient. How do we know? The first subtracts one from the exponent each time, the second cuts the exponent in half every other time, which takes us to 0 quite quickly. If you are not convinced by that informal argument, you might want us to run some experiments. To run the experiment, we'll add a static field, calls, that counts the recursive calls.

private static long calls = 0;

We'll extend each method to increment that count:

  public static BigInteger expt1(BigInteger x, int n)
  {
    ++calls;
    ...
  }
  public static BigInteger expt2(BigInteger x, int n)
  {
    ++calls;
    ...
  }

Finally, we'll add methods that let a client access the count.

  public static long getCalls()
  {
    return calls;
  } // getCalls()
  public static void resetCalls()
  {
    calls = 0;
  } // resetCalls()

We can then run an experiment:

  BigInteger x = new IO.readBigInteger(pen, eyes, "Enter x: ");
  int n = new IO.readBigInteger(pen. eyes, "Enter the exponent: ");
  MyMath.resetCalls();
  BigInteger result1 = MyMath.expt1(x, n);
  long calls1 = MyMath.getCalls();
  MyMath.resetCalls();
  BigInteger result2 = MyMath.expt2(x, n);
  long calls2 = MyMath.getCalls();
  pen.println("Strategy one returned: " + result1);
  pen.println("Strategy two returned: " + result2);
  pen.println("Strategy one used " + calls1 + " recursive calls.");
  pen.println("Strategy two used " + calls2 + " recursive calls.");

Constants

You declare constants much like you declare static fields, except that you include the keyword final.

protection static final type NAME = expression;

Custom is that you name constants with all capital letters.

You refer to a constant within the declaring class with just the name of the constant. You refer to a constant in another class by prefacing the name of the constant with name of the class. You've already used constants, such as Integer.MAX_VALUE and Math.PI.

An Application: Standard Input and Output

As some of you have noted, it become irritating to write the same code over and over again to declare the PrintWriter that prints to the screen and the BufferedReader that reads from the keyboard. We can put their declarations as constants within a utility class and then refer to them from that class.

Here are the declarations:

public class IO
{
  public static PrintWriter SCREEN = 
    new PrintWriter(System.out, true);
  public static BufferedReader KEYBOARD = 
    new BufferedReader(new InputStreamReader(System.in, true));
  ...
} // class IO

In our main method, presumably in another class, you can then write

  public static void main(String[] args)
    throws Exception
  {
    PrintWriter pen = IO.SCREEN;
    BufferedReader eyes = IO.KEYBOARD;
    ...
  }

History

Wednesday, 15 February 2006 [Samuel A. Rebelsky]

Sunday, 19 February 2006 [Samuel A. Rebelsky]


This page was generated by Siteweaver on Thu Mar 30 15:24:31 2006.
The source to the page was last modified on Sun Feb 19 21:17:03 2006.
This page may be found at http://www.cs.grinnell.edu/~rebelsky/Espresso/Readings/static-fields.html.

You may wish to validate this page's HTML ; Valid CSS! ; Check with Bobby

Samuel A. Rebelsky
rebelsky@grinnell.edu