In Java, the static modifier means something is directly related to a class: if a field is static, then it belongs to the class; if a method is static, then it belongs to the class. As a result, you can use the class's name to call a static method or reference a static field. For example, if the count field is static in the Counter class, it means you can reference the variable with the following expression: Counter.count. Of course, access modifiers must be considered. For example, private fields are available only within the class in which they are declared. And protected fields are available to all classes within a package, as well as to all their subclasses outside the package. Suppose the Counter class has a static increment() method whose job is to increment the count field. To call this method, you can use Counter.increment(). There is no need to create an instance of the Counter class to access a static field or method. This is the fundamental difference between static (class) variables and methods and NON-static (instance) variables and methods. An important note. Don't forget that the static members of the class directly belong to the class, not any instance of the class. That is, the value of the static count variable will be the same for all Counter objects. In this article, we'll look at the fundamental aspects of using the static modifier in Java, as well as some features that will help you understand key programming concepts.

What every programmer should know about the static modifier in Java.

In this section, we look at the main aspects of using static methods, fields, and classes. Let's start with the variables.
  1. You CANNOT access non-static members of a class within a static context, such as a static method or block. Compiling the code below will result in an error:

    public class Counter {
    private int count;
    public static void main(String args []) {
       System.out.println(count); //  Compile time error
    }
    }

    This is one of the most common mistakes made by Java programmers, especially newbies. Since the main method is static and the count variable is not, using the println method inside the main method will produce a "compile time error".

  2. Unlike local variables, static fields and methods are NOT thread safe in Java. In practice, this is one of the most frequent causes of security problems in multi-threaded programming. Considering that each instance of a class references the same copy of a static variable, such a variable needs to be protected or "locked" by the class. Therefore, when using static variables, be sure that they are properly synchronized to avoid problems such as race conditions.

  3. Static methods have a practical advantage in that there is no need to create a new object each time you want to call them. A static method can be called using the name of the class that declares it. That's why these methods are perfect for factory methods and utility methods. The java.lang.Math class is a wonderful example: almost all its methods are static. Java's utility classes are marked final for the same reason.

  4. Another important point is that you can't override (@Override) static methods. If you declare such a method in a subclass, i.e. a method with the same name and signature, you just "hide" the method of the superclass instead of overriding it. This phenomenon is known as method hiding. This means that if a static method is declared in both parent and child classes, the method called will always be based on the variable type at compile time. Unlike with method overriding, such methods will not be executed when the program runs. Let's consider an example:

    class Vehicle {
         public static void kmToMiles(int km) {
              System.out.println("Inside the parent class / static method");
         }
    }
    
    class Car extends Vehicle {
         public static void kmToMiles(int km) {
              System.out.println("Inside the child class / static method");
         }
    }
    
    public class Demo {
       public static void main(String args []) {
          Vehicle v = new Car();
           v.kmToMiles(10);
      }
    }

    Console output:

    Inside the parent class / static method

    The code clearly demonstrates that despite the fact that the object is a Car, the static method in the Vehicle class is called, since the method was called at compile time. And note that there were no compilation errors!

  5. What's more, other than top-level classes, you can declare classes static. Such classes are known as nested static classes. They are useful for providing better cohesion. A striking example of a nested static class is HashMap.Entry, which is a data structure inside HashMap. It is worth noting that, like inner classes, static nested classes are declared in a separate .class file. Thus, if you declare five nested classes in your main class, you will have 6 files with the .class extension. Another example is the declaration of our own Comparator, such as an age comparator (AgeComparator) in the Employee class.

  6. The static modifier can also be specified in a static block, better known as a "static initialization block", which is executed when the class is loaded. If you don't declare such a block, Java collects all static fields into a single list and initializes them when the class is loaded. A static block CANNOT throw checked exceptions, but it can throw unchecked ones. In this case, an ExceptionInInitializerError will occur. In practice, any exception that occurrs during initialization of static fields will be wrapped in this error by Java. This is also the most common cause of the NoClassDefFoundError, because the class won't be in memory when it is referenced.

  7. It's useful to know that static methods are linked at compile time, unlike the linking of virtual or non-static methods, which are linked at run time when called on a real object. Accordingly, static methods cannot be overridden in Java, since polymorphism does not apply to them at run time. This is an important limitation to consider when declaring a method static. Doing so makes sense only when there is no ability or need to override the method in a subclass. Factory methods and utility methods are good examples of proper use of the static modifier. Joshua Bloch points out several advantages that static factory methods have over constuctors in his book Effective Java, which is mandatory reading for every Java programmer.

  8. Initialization is an important aspect of a static block. Static fields or variables are initialized after the class is loaded into memory. The order of initialization is from top to bottom, in the same order in which they are declared in the source file of the Java class. Since static fields are initialized in a thread-safe manner, this process is also used to implement the Singleton pattern. If you are not using an Enum as a Singleton for some reason, then you have a good alternative. But in this case, you must take into account that this is not a "lazy" initialization. This means that the static field will be initialized even BEFORE someone "asks" for it. If an object is resource-heavy or rarely used, then initializing it in a static block won't work in your favor.

  9. During serialization, static fields, like transient variables, are not serialized. Indeed, if you save any data in a static field, it will contain its initial (default) value after deserialization. For example, if a static field is an int, its value will be zero after deserialization. If its type is float, the value will be 0.0. If the field is an Object, the value will be null. To be honest, this is one of the most frequently asked questions about serialization in interviews for Java positions. Don't store essential object data in a static field!

  10. Finally, let's talk about static import. This modifier has a lot in common with the standard import statement, but it is different in that it lets you mport one or all static class members. Once static methods are imported, they can be accessed as if they were declared in the same class. Similarly, by importing static fields, we can access them without specifying the class name. This feature appeared in Java 1.5 and improves code readability when used properly. This construct is found most often in JUnit tests, since almost all test developers use static import for assert methods, e.g. assertEquals() and their overloaded variants.

  11. That's all for now. Every Java programmer needs to know all of the aspects of the static modifier mentioned above. This article reviewed basic information about static variables, fields, methods, initialization blocks, and imports. It also touched on some important properties that are essential to know to write and understand Java programs. I hope that every developer will perfect their skillful use of static members, because it's very important for serious software development."