| Home | Resources | Papers | Essays | Short Fiction | About |

Static Fields & Methods

Table of Contents

Introduction

In a previous chapter we investigated instance fields and methods and how they related to class definitions and objects. We learned that every instance of an object has its own corresponding fields tied to that instance which is distinct from another instance. Java provides the direct opposite of instance fields with static fields.

What Does static Mean?

static is a reserved keyword which can be prepended to either a variable or a method in a class definition. Per the Java Language Specification for static fields:

If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created. A static field, sometimes called a class variable, is incarnated when the class is initialized.

This means that static fields are tied to an entire class. They are not associated with a specific instance of a class (i.e. an object). The same goes for static methods:

A method that is declared static is called a class method.

A class method is always invoked without reference to a particular object.

Both static methods and static fields belong to the class itself and not a given instance of that class.

We can modify the BankAccount class from the instance chapter to include a static field which tracks the unique ID of each bank account created:

 1: public class BankAccount {
 2:     private static int UID = 0;
 3:     private String owner;
 4:     private double balance;
 5:     private int uid;
 6: 
 7:     BankAccount(String owner, double initialBalance){
 8:         this.owner   = owner;
 9:         this.balance = initialBalance;
10:         this.uid = BankAccount.UID;
11:         BankAccount.UID += 1;
12:     }
13: 
14:     void deposit(double amount){
15:         balance = balance + amount;
16:     }
17: 
18:     void withdraw(double amount){
19:         balance = balance - amount;
20:     }
21: 
22:     void printBalance(){
23:         System.out.printf("(%d) %s's balance: $%.2f%n", uid, owner, balance);
24:     }
25: 
26:     static int getNumAccountsCreated(){
27:         return BankAccount.UID;
28:     }
29: }

We can then use the above class definition like so:

 1: public class UsingStaticAccount{
 2:     public static void main(String[] args){
 3:         String[] names = {"Alan", "Barbara", "Clive", "Denise", "Eddie", "Fran"};
 4:         BankAccount[] accs = new BankAccount[names.length];
 5:         for(String n : names){
 6:             int i = BankAccount.getNumAccountsCreated();
 7:             accs[i] = new BankAccount(n, 1000);     
 8:         }
 9:         for(BankAccount acc : accs){
10:             acc.printBalance();
11:         }
12:         System.out.printf("We created %d accounts!%n", BankAccount.getNumAccountsCreated());
13:     }
14: }

After compiling and running the code we get:

josephraskind@stargazer:/tmp/static$ javac UsingStaticAccount.java; java UsingStaticAccount
(0) Alan's balance: $1000.00
(1) Barbara's balance: $1000.00
(2) Clive's balance: $1000.00
(3) Denise's balance: $1000.00
(4) Eddie's balance: $1000.00
(5) Fran's balance: $1000.00
We created 6 accounts!

That's a lot to process!

Let's go through this step by step. First, we modified BankAccount to include a new static field UID, a new instance field uid, and a new static method getNumAccountsCreated. Next, the constructor was modified to set the uid of the new instance wiht the value of UID. Because UID is private it can only be referenced in the context of the BankAccount definition. Furthermore, because UID is static, the value is "shared" between all instances (at the class level). This is why each constructor invocation is able to use the value of UID to set the value of their internal uid​s. We can also see that the printBalance method was modified to include a reference to the uid. Each printout of uid in UsingStaticAccount is different because uid is an instance field. Finally, getNumAccountsCreated returns the value of the UID static field—it is allowed to do so because it is defined as static in its method declaration.

UID was referenced using the class name paired with the . notation. BankAccount.UID retrieved the static UID value. Again, static members are not tied to an instance, but rather the class, therefore I need the class name when referencing a static member.

Since static methods are not tied to a particular instance they cannot reference instance members. For example, if I created a new, static function called printStaticBalance:

1: static void printStaticBalance{
2:     System.out.printf("(%d) %s's balance: $%.2f%n", uid, owner, balance);
3: } 

it would not compile! uid, owner, and balance only make semantic sense when referring to a particular instance of BankAccount. However, it is possible for instance methods to reference static members.

Many beginner Java programmers do not recognize this when they start programming. Oftentimes they will make the mistake of calling instance methods from a static context and get frustrated with the compiler when it points out that is illegal in the language. Always double check that you are in the proper method context when invocation errors crop up.

Exercises

  1. Compile and run Listing 2. Then create a second program that instantiates three BankAccount objects and prints the result of BankAccount.getNumAccountsCreated() once after all three have been created. What value does it return? Now call getNumAccountsCreated before creating any accounts and print that too. What does this confirm about when the static field is initialized and how it accumulates across all instantiations?
  2. The chapter states that static members are accessed via the class name rather than an instance variable. Attempt to access getNumAccountsCreated through an instance variable instead:

    BankAccount acc = new BankAccount("Alice", 1000.00);
    System.out.println(acc.getNumAccountsCreated());
    

    Does it compile? Does it run? What warning or note does the compiler give you and why does it discourage this style even if it technically works?

  3. It turns out that public static void main must be static because the JVM calls it before any objects exist. Try removing the static keyword from a main method and attempt to run it. What error do you get? In your own words explain why the JVM cannot call a non-static main method at program startup.
  4. Add a static final double INTEREST_RATE = 0.05 field to BankAccount representing a fixed annual interest rate shared by all accounts. Then add an instance method applyInterest that multiplies balance by (1 + INTEREST_RATE). Test it on two accounts and verify that changing INTEREST_RATE to 0.10 updates the behavior for all accounts without modifying any instance. Why is final an appropriate qualifier for this field?
  5. The chapter states that instance methods can reference static members but static methods cannot reference instance members. Write a small class that demonstrates both directions: one instance method that reads a static field, and one static method that attempts to read an instance field. Compile both and record what happens. Explain in your own words why the restriction only applies in one direction.
  6. The chapter shows UID is declared private static. Change private to public and recompile. Now attempt to reset the counter from UsingStaticAccount with BankAccount.UID = 0 after creating a few accounts. What happens to subsequent uid values? What does this demonstrate about why UID should remain private?
  7. Write a class Counter with a single private static int count = 0 field, a constructor that increments count, and a static method getCount that returns it. Instantiate Counter objects in a loop and verify getCount reflects the total number of instances created. Then write a second class in the same program that also instantiates Counter objects. Does getCount reflect instantiations from both classes? Explain why.
  8. The chapter shows that BankAccount.UID persists across all instances created during a program run. Consider what happens if you wanted to reset the UID counter — for example when writing tests. Add a static void resetUID method to BankAccount that resets UID to 0. Write a program that creates three accounts, resets the counter, then creates three more. Print all six accounts' uid values. What do you observe and what does this suggest about the risks of mutable static state?
Contact: [email protected] | rss feed | Compiled with org-mode | Licensed under CC BY-NC-SA 4.0