Home CPSC 240

Static Variables and Methods

The static keyword can be confusing at first, but is important for understanding object-oriented systems and is especially key to some of the design patterns we'll look at later in the class.


 

Static Variables

Imagine we have a simple class for representing cars and various properties of them:


public class Car {
    private String make;
    private String model;
    private int year;
    private int mileage;

    public Car(String make, String model, int year, int mileage) {
        this.make = make;
        this.model = model;
        this.year = year;
        this.mileage = mileage;
    }

    // ...
}

This class could also have other constructors and methods too, but we'll just talk about the instance variables for now. Every time we make a Car object, it will get its own copies of make, model, year, and mileage. Let's say we make a couple Car objects:


Car c1 = new Car("Honda", "Accord", 2006, 78000);
Car c2 = new Car("Ford", "Focus", 2014, 27500);

Both of these cars have different values for each of the four variables in the class. They are instance variables meaning they are associated with each instance, or object, of the class.

If we make a variable in a class static, then it doesn't work this way. Instead static variables are associated with the class itself, and only one variable is shared amongst all instances.

This is not often useful, and the great majority of variables in a class will not be static. One example where a static variable makes sense is if we want to keep track of how many objects of a class have been created. Let's say we want to know how many Car objects we have made in our program thus far. We can do that with a static variable:


public class Car {
    private String make;
    private String model;
    private int year;
    private int mileage;

    // the static variable is shared amongst all instances
    private static int numCars = 0;

    public Car(String make, String model, int year, int mileage) {
        this.make = make;
        this.model = model;
        this.year = year;
        this.mileage = mileage;
        
        // increment the count of all cars
        numCars++;
    }

    // ...
}

Here we have added the static variable numCars. There is only one copy of this variable that all Car objects share. If it is changed anywhere, it affects all cars.

It is initialized in the class body itself as opposed to in the constructor. The reason is that the constructor is called for every object and so if we set numCars to 0 there, then it would be reset each time a car is made. By initializing it in the class statement, it will only be set to 0 once, when the program starts.

Then the value is incremented in the constructor of the class. That way, every time we make a new Car object, we will add one to this value.


 

Static Methods

Now that we are keeping track of how many cars have been made, we can create a method to access this information. It makes sense to do this as a static method. Static methods, like static variables, are associated with the class itself rather than any particular object. Getting the number of cars that exist doesn't have to do with a specific Ford Focus or Honda Accord, but rather the Car class in general.

We could write this method simply like this:


    public static int getNumCars() {
        return numCars;
    }

Now calling this method will work differently than other methods of the class. Most methods are called on a particular object of a class. For example:


c1.getMake();
c2.addToMileage(100);

If these are both regular, non-static methods, they are called on particular Car objects like this. However, our static method is not. It's called on the class itself:


Car.getNumCars();

You have probably seen other examples of static methods that come with Java. For example, this code sorts an ArrayList:


Collections.sort(myList);

Here, the sort method is from a class called Collections and is called on the class itself. The Math class is a class that groups together static methods for computing various things. It doesn't make sense to make a "Math object". We just call these methods statically on the class itself.

Of course the main method is another example of a static class. The Java VM calls this method for us when it starts our programs.

One important thing to keep in mind when writing static methods is that they can not reference any specific object. For example, when we call the getNumCars method, we do it on the Car class overall, not on any specific car. That means it doesn't make sense to refer to any attribute of a specific car like the make, model, or year.

The compiler enforces this by stopping you from referencing any non-static variables or methods inside of a static method. If we try something like this:


    public static int getNumCars() {
        System.out.println("Make = " + make);
        return numCars;
    }

Then we will get an error like this:

java: non-static variable make cannot be referenced from a static context

If you think about it for a second, this should make sense. We call the getNumCars method on the class Car in general, not on any specific car object. So how could it possibly know which make variable we are talking about? In fact getNumCars can be called before we ever even make a car.

The rule to keep in mind then is this: static methods can only call other static methods, and can only reference static variables.

Copyright © 2024 Ian Finlayson | Licensed under a Attribution-NonCommercial 4.0 International License.