1. Overview

In this tutorial, we’ll explore the static keyword of the Java language in detail.

We’ll find out how we can apply the static keyword to variables, methods, blocks, and nested classes, and what difference it makes.

2. The Anatomy of the static Keyword

In the Java programming language, the keyword static means that the particular member belongs to a type itself, rather than to an instance of that type.

This means we’ll create only one instance of that static member that’s shared across all instances of the class.

Static variables shared in Java

We can apply the keyword to variables, methods, blocks, and nested classes.

3. The static Fields (Or Class Variables)

In Java, when we declare a field static, exactly a single copy of that field is created and shared among all instances of that class.

It doesn’t matter how many times we instantiate a class. There will always be only one copy of static field belonging to it. The value of this static field is shared across all objects of either the same class.

From the memory perspective, static variables are stored in the heap memory.

3.1. Example of the static Field

Let’s say we have a Car class with several attributes (instance variables).

Whenever we instantiate new objects from this Car blueprint, each new object will have its distinct copy of these instance variables.

However, suppose we want a variable that holds the count of the number of instantiated Car objects and is shared across all instances so they can access it and increment it upon their initialization.

That’s where static variables come in:

public class Car {
    private String name;
    private String engine;
    
    public static int numberOfCars;
    
    public Car(String name, String engine) {
        this.name = name;
        this.engine = engine;
        numberOfCars++;
    }

    // getters and setters
}

Now for every object of this class that we instantiate, the same copy of the numberOfCars variable is incremented.

So, for this case, these will be true:

@Test
public void whenNumberOfCarObjectsInitialized_thenStaticCounterIncreases() {
    new Car("Jaguar", "V8");
    new Car("Bugatti", "W16");
 
    assertEquals(2, Car.numberOfCars);
}

3.2. Compelling Reasons to Use static Fields

Here are some reasons for when we’d want to use static fields:

  • when the value of the variable is independent of objects
  • when the value is supposed to be shared across all objects

3.3. Key Points to Remember

Since static variables belong to a class, we can access them directly using the class name. So, we don’t need any object reference.

We can only declare static variables at the class level.

We can access static fields without object initialization.

Finally, we can access static fields using an object reference (such as ford.numberOfCars++). But we should avoid this because it becomes difficult to figure out if it’s an instance variable or a class variable. Instead, **we should always refer to static variables using the class name (Car.numberOfCars++)**.

4. The static Methods (Or Class Methods)

Similar to static fields, static methods also belong to a class instead of an object. So, we can call them without creating the object of the class in which they reside.

4.1. Example of static Method

We generally use static methods to perform an operation that’s not dependent upon instance creation.

In order to share code across all instances of that class, we write it in a static method:

static void setNumberOfCars(int numberOfCars) {
    Car.numberOfCars = numberOfCars;
}

We also commonly use static methods to create utility or helper classes so that we can get them without creating a new object of these classes.

As examples, we can take a look at Collections or Math utility classes from JDK, StringUtils from Apache, or CollectionUtils from Spring framework and notice that all their utility methods are static.

4.2. Compelling Reasons to Use static Methods

Let’s look at a few reasons why we’d want to use static methods:

  • to access/manipulate static variables and other static methods that don’t depend upon objects.
  • static methods are widely used in utility and helper classes.

4.3. Key Points to Remember

static methods in Java are resolved at compile time. Since method overriding is part of Runtime Polymorphism, static methods can’t be overridden.

Abstract methods can’t be static.

static methods can’t use this or super keywords.

The following combinations of the instance, class methods, and variables are valid:

  1. instance methods can directly access both instance methods and instance variables
  2. instance methods can also access static variables and static methods directly
  3. static methods can access all static variables and other static methods
  4. static methods can’t access instance variables and instance methods directly. They need some object reference to do so.

4.4. Calling Non-static Method in Static Method in Java

In order to call a non-static method in a static method, we must use an instance of the class containing the non-static method. This is a common use case when calling a non-static method in the main() static method for example.

Let’s consider an example of the Car class that we introduced earlier in this article, and which defines the following methods:

public String getName() {
    return name;
}

public String getEngine() {
    return engine;
}

public static String getCarsInformation(Car car) {
    return car.getName() + "-" + car.getEngine();
}

As we can see, we are calling getName() and getEngine() methods, which are non-static methods, in the getCarsInformation() static method. This is only possible because we use an instance of the Car object to access these methods. Otherwise, we’d get this error message “Non-static method ‘getName()’ cannot be referenced from a static context“.

5. A static Block

We use a static block to initialize static variables. Although we can initialize static variables directly during declaration, there are situations when we need to do multiline processing. In such cases, static blocks come in handy.

If static variables require additional, multi-statement logic during initialization, we can use a static block.

5.1. The static Block Example

For instance, let’s suppose we want to initialize a List object with some predefined values.

This becomes easy with static blocks:

public class StaticBlockDemo {
    public static List<String> ranks = new LinkedList<>();

    static {
        ranks.add("Lieutenant");
        ranks.add("Captain");
        ranks.add("Major");
    }
    
    static {
        ranks.add("Colonel");
        ranks.add("General");
    }
}

It wouldn’t be possible to initialize a List object with all the initial values along with declaration. So, this is why we’ve utilized the static block here.

5.2. Compelling Reasons to Use static Blocks

Below are a few reasons for for using static blocks:

  • if the initialization of static variables needs some additional logic apart from the assignment
  • if the initialization of static variables is error-prone and needs exception handling

5.3. Key Points to Remember

A class can have multiple static blocks.

static fields and static blocks are resolved and run in the same order as they are present in the class.

6. A static Class

Java allows us to create a class within a class. It provides a way of grouping elements that we’ll only use in one place. This helps to keep our code more organized and readable.

In general, the nested class architecture is divided into two types:

  • nested classes that we declare static are called static nested classes
  • nested classes that are non-static are called inner classes

The main difference between these two is that the inner classes have access to all members of the enclosing class (including private ones), whereas the static nested classes only have access to static members of the outer class.

In fact, static nested classes behave exactly like any other top-level class, but are enclosed in the only class that will access it, to provide better packaging convenience.

6.1. Example of static Class

The most widely used approach to create singleton objects is through a static nested class:

public class Singleton  {
    private Singleton() {}

    private static class SingletonHolder {
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

We use this method because it doesn’t require any synchronization and is easy to learn and implement.

Another nested static class example, where visibility between parent and nested members is displayed, and vice versa:

public class Pizza {

    private static String cookedCount;
    private boolean isThinCrust;

    public static class PizzaSalesCounter {

        private static String orderedCount;
        public static String deliveredCount;

        PizzaSalesCounter() {
            System.out.println("Static field of enclosing class is "
              + Pizza.cookedCount);
            System.out.println("Non-static field of enclosing class is "
              + new Pizza().isThinCrust);
        }
    }

    Pizza() {
        System.out.println("Non private static field of static class is "
          + PizzaSalesCounter.deliveredCount);
        System.out.println("Private static field of static class is "
          + PizzaSalesCounter.orderedCount);
    }

    public static void main(String[] a) {
           new Pizza.PizzaSalesCounter();
    }
}

The result when we run the main method is:

Static field of enclosing class is null
Non private static field of static class is null
Private static field of static class is null
Non-static field of enclosing class is false

6.2. Compelling Reasons to Use a static Inner Class

Let’s take a look at a few reasons for using static inner classes in our code:

  • grouping classes that will be used only in one place increases encapsulation
  • we bring the code closer to the only place that will use it. This increases readability, and the code is more maintainable.
  • if a nested class doesn’t require any access to its enclosing class instance members, it’s better to declare it as static. This way, it won’t be coupled to the outer class and is therefore more optimal, as they won’t require any heap or stack memory.

6.3. Key Points to Remember

Basically, a static nested class doesn’t have access to any instance members of the enclosing outer class. It can only access them through an object’s reference.

static nested classes can access all static members of the enclosing class, including private ones.

Java programming specification doesn’t allow us to declare the top-level class as static. Only classes within the classes (nested classes) can be made as static.

7. Understanding the Error “Non-static variable cannot be referenced from a static context”

Typically, this error occurs when we use a non-static variable inside a static context.

As we saw earlier, static variables belong to the class and are loaded at class load time. On the other hand, we need to create an object in order to refer to non-static variables.

So, the Java compiler complains because there’s a need for an object to call or use non-static variables.

Now that we know what causes the error, let’s illustrate it using an example:

public class MyClass { 
    int instanceVariable = 0; 
    
    public static void staticMethod() { 
        System.out.println(instanceVariable); 
    } 
    
    public static void main(String[] args) {
        MyClass.staticMethod();
    }
} 

As we can see, we used instanceVariable, which is a non-static variable, inside the static method staticMethod.

As a result, we will get the error Non-static variable cannot be referenced from a static context.

8. Conclusion

In this article, we saw the static keyword in action.

We also discussed the reasons and advantages of using static fields, static methods, static blocks, and static inner classes.

Finally, we learned what causes the compiler to fail with the error “Non-static variable cannot be referenced from a static context”.

As always, we can find the complete code over on GitHub.