Java: the static keyword

May 9, 2014

In my experience, is there is no more misunderstood feature of Java by beginners than the static keyword. This is a big problem: the function of static is not trivial, and you won’t be able to avoid it if you end up reading and writing a lot of Java code. If you’re new to Java or don’t fully understand static, read on!

Java is an object-oriented programming language. Furthermore, its object-oriented features are not optional — from your first program to your last, Java always enforces an object-oriented style (contrary to, say, Python, which has object-oriented features, but does not require you to use them). Therefore, whenever you write code, you write it in a class. Classes might be instantiated into objects (through the use of the new operator with a class name). When a new object is formed from the blueprint of its class, it’s called an instance.

When you use the new operator, a new area of memory is allocated for the storage of the new instance. Then the instance gets its own copy of the variables and methods defined in the class. But instances will only get their own copies of non-static variables and methods. If a variable or method is declared as static, the memory for it is allocated only once — and all instances of a class share it!

Say I have the following Person class, which specifies that every Person object has a string called name:

public class Person {
    public String name;
}

In some main method somewhere (say, in another class in the same directory as Person.java), you might write the following code:

Person p1 = new Person();
Person p2 = new Person();

p1.name = "Arthur";
p2.name = "Margaret";

System.out.println("person #1's name: " + p1.name);
System.out.println("person #2's name: " + p2.name);

The output of this little program is simple. You will believe me when I say it looks like this (if you don’t believe me, try it out):

person #1's name: Arthur
person #2's name: Margaret

If I decided to make the name field static, like the following:

public class Person {
    public static String name;
}

The output of the same main method code is the following:

person #1's name: Margaret
person #2's name: Margaret

But why? By making the field static, the memory for that field is not allocated per instance, it is allocated per class. So each and every Person object shares the same area of memory to store its name field. That is why, if we change the name field of person #1, we are also changing the name field for person #2!

When to use static

We usually declare variables as static when

When writing a method, we declare it as static when

When a method is declared as static, we refer to its body as being “in a static context.” This means that the code in the body of the static method runs without access to the internal state of any one object (i.e., the use of this is not allowed — it would be ambiguous). However, it does have access to the per class state; in other words, anything declared as static2.

For example, say I decided to alter the Person class to add some encapsulation3 features:

public class Person {
    private String name;

    public void setName(String s) {
        name = s;
    }

    public String getName() {
        return name;
    }
}

Notice how the setter method setName is non-static. Why? Since it accesses state specific to a Person object (the name field), it necessarily cannot be static. In fact, if we tried to declare setName as static, the compiler would halt with the following error:

Person.java:5: error: non-static variable name cannot be
referenced from a static context

We made the setName method static. It accesses a non-static variable of an object. Why is this a problem? Since the method is static, it runs in a static context, which means it does not have access to the state of a specific instance. (This is true even if we do something like p1.setName("George") — so long as setName is static, it doesn’t matter that we call the method on p1setName still cannot access the internal state of p1.)

Static setters and getters

As in our example above, we might have multiple Person objects in a given program. If we wanted to change a specific Person object’s name field, we should be calling a method stored within that Person object.

If we really wanted the setName method to be static, we could do this:

public static void setName(Person p, String s) {
    p.name = s;
}

Notice how this version of setName needs a Person as a parameter. In static methods, this is how you access non-static fields of an object — you have to pass the reference in explicitly. In most cases, though, an object-oriented program will not have many static methods like these.

Keep in mind that the access modifiers of class variables and methods still apply here. If the static version of the setName method were written in a class somewhere outside of the Person class, and the name field were private, this code would not compile due to name being inaccessible! This is another reason why we might prefer non-static methods for encapsulation.

  1. In a non-static method, the this keyword is a special variable containing a reference to the object on which the method was called. For example, if I have a reference to a String object stored in a variable str, doing str.substring(1, 3) calls the substring method on str: in the body of the substring method, any use of the this keyword pertains to the str object itself. 

  2. Note that non-static methods can access both non-static and static state, but static methods may only access static state. 

  3. If you’ve never heard of encapsulation, it refers to the practice of keeping an object’s fields from general access, and writing methods to manipulate a field instead. Methods that are designed to change a field from outside the object are called setters (or mutators), and methods designed to return the value of a field are called getters (or accessors). Encapsulation has many benefits, but it’s most often used defensively, to make sure that no other code can set a field to an improper value (e.g., writing a setter to prevent some code from changing the age field of a Person object to a negative value — you can’t have a negative age!).