Understanding static and final in Java

Java provides various modifiers which when used with variables, methods or classes, provides them with special properties. Static and final are two such modifiers which are used frequently by Java developers. In this article we will take a look at these two modifiers and understand the various ways how they can be used.


Final and Static with variables

Whenever an object of a class is created a memory is allocated for it. All instance variables resides in this memory location.

Final variables are also instance variables, with a limitation that their value cannot be changed. Final variables should be initialized when they are declared, or in the constructor.

Static variables on the other hand are class variables, which means a separate memory location will be created for them while classloading and all objects of the class will share the same memory location. Hence any changes made to a static variable will reflect in all the objects of the class, since all the objects are accessing the same memory location.

Static variable can also be made final. A final static variable should be initialized when declared or in a static block (discussed later).

The example below demonstrates the use of final and static in variables:

public class VariableTest {
    
    int instanceVariable;    
    static int staticVariable;
    final int finalVariable;
    static final int finalStaticVariable = 10;
    
    public VariableTest() {
        instanceVariable = 1;
        staticVariable = 2;
        finalVariable = 3;
    }
    
    public VariableTest(int instanceVar, int staticVar, int finalVar){
        instanceVariable = instanceVar;
        staticVariable = staticVar;
        finalVariable = finalVar;
    }
    
    public static void main(String[] args) {
        VariableTest obj1 = new VariableTest();
        System.out.println("Object 1");
        System.out.println("Instance Variable: "+ obj1.instanceVariable +
                    ", Static Variable: "+ VariableTest.staticVariable +
                    ", Final Variable: "+ obj1.finalVariable +
                    ", Final Static Variable: "+ VariableTest.finalStaticVariable );
        
        VariableTest obj2 = new VariableTest(11, 12, 13);
        
        System.out.println();
        System.out.println("Object 1");
        System.out.println("Instance Variable: "+ obj1.instanceVariable +
                ", Static Variable: "+ obj1.staticVariable +
                ", Final Variable: "+ obj1.finalVariable +
                ", Final Static Variable: "+ obj1.finalStaticVariable );
        
        System.out.println();
        System.out.println("Object 2");
        System.out.println("Instance Variable: "+ obj2.instanceVariable +
                ", Static Variable: "+ obj2.staticVariable +
                ", Final Variable: "+ obj2.finalVariable +
                ", Final Static Variable: "+ obj2.finalStaticVariable );
        
        obj1.instanceVariable = 101;
        obj2.instanceVariable = 102;
        
        obj1.staticVariable = 201;
        obj2.staticVariable = 202;
        
        /** This will result in compilation error because
         *  assigning a final variable is not allowed.  
         * 
        obj1.finalVariable = 301;
        obj2.finalVariable = 302;
        
        obj1.finalStaticVariable = 401;
        obj2.finalStaticVariable = 402;
         */
        
        System.out.println();
        System.out.println("Object 1");
        System.out.println("Instance Variable: "+ obj1.instanceVariable +
                ", Static Variable: "+ obj1.staticVariable +
                ", Final Variable: "+ obj1.finalVariable +
                ", Final Static Variable: "+ obj1.finalStaticVariable );
        
        System.out.println();
        System.out.println("Object 2");
        System.out.println("Instance Variable: "+ obj2.instanceVariable +
                ", Static Variable: "+ obj2.staticVariable +
                ", Final Variable: "+ obj2.finalVariable +
                ", Final Static Variable: "+ obj2.finalStaticVariable );
    }
}

Below is the output on executing the above class:

Object 1
Instance Variable: 1, Static Variable: 2, Final Variable: 3, Final Static Variable: 10
----------------------  Object 2 created ---------------------------------------------
Object 1
Instance Variable: 1, Static Variable: 12, Final Variable: 3, Final Static Variable: 10

Object 2
Instance Variable: 11, Static Variable: 12, Final Variable: 13, Final Static Variable: 10
------------------------- Object Values Reassigned --------------------------------------
Object 1
Instance Variable: 101, Static Variable: 202, Final Variable: 3, Final Static Variable: 10

Object 2
Instance Variable: 102, Static Variable: 202, Final Variable: 13, Final Static Variable: 10

We have declared 4 variables, an instance variable, a static variable, a final variable and a final static variable. Once we create our 1st object we can see the values of the variables are as set by the default constructor.

Notice that after we create the 2nd object, the variable values of the 1st object remains as is, other than the static variable which is updated to the value set by 2nd object. Similar behaviour can also be seen after we reassigned the values of the objects. This is because separate copies of static variable are not maintained by each object, rather they access a common memory location for static variables.


Final and Static with methods

When final is used with a method, it prevents the method from being overridden. When any method in a parent class is declared as final, its child classes cannot override the method.

public  class MyClass(){ 
    public final void myFinalMethod(){ ... }
}

Static method doesn't require an instance of a class to be called. To call a static method we can use the class name as shown below:

public class MyClass{
    public static void myStaticMethod(){ ... }
}

MyClass.myStaticMethod();

Final and Static with classes

When final is used with a class, it prevent the class from being extended i.e. a final class cannot have child classes.

public final class MyClass(){ ... }

Java doesn't allows creating top-level static class, only an inner class (nested class) can be declared as static. Static nested class can only access static members of the outer class.

public final class MyClass{
    ...
    static class NestedStaticClass{ ... }
}

Static Block

A static block is a simple block of code enclosed within braces ({}) and preceded by static keyword as shown below.

static {
    ...
}

A static block is executed only once when the constructor of the class is called or a static member of the class is accessed. Also when a constructor is called the static block get executed even before the constructor.

Consider the example code below:

public class StaticBlockTest {
	
    static{
	System.out.println("In static block");
    }
	
    public StaticBlockTest(){
	System.out.println("In constructor");
    }
	
    public static void main(String[] args) {
    	StaticBlockTest obj = new StaticBlockTest();
    }
}

Below is the output:

In static block
In constructor
RELATED ARTICLES

2 ways to Create a Thread in Java

Threads in Java can be created in 2 different ways, extending the Thread class and implementing the Runnable interface. This article explains the two ways with suitable examples.

View Article

4 Different Ways of Creating an Object in Java

Most of us have created object using new operator in Java. Did you know the other ways to create objects in Java. This article talks about 4 different ways of creating objects in Java.

View Article

Using AutoCloseable with 'try-with-resources'

Java 7 added the java.lang.AutoCloseable interface that developers can implement in their custom classes for using them with try-with-resources.

View Article

Chained Exception

Chained exception is a feature introduced with JDK 1.4, which allow developers to associate an exception with another exception.

View Article

Shallow Cloning and Deep Cloning

Cloning an object can happen in two ways Deep and Shallow Cloning. In shallow cloning only object references are copied while in deep copy actual objects are copied to the newly cloned object.

View Article

Exception Handling in Inheritance

There are certain rules that must be followed while throwing exceptions in inheritance and overriding methods. This article discuss about these rules.

View Article

Garbage Collection in Java

Java Garbage Collector is a boon to Java developers; it allows developers to program without worrying about memory management. This article describes the working of Java Garbage Collector in details.

View Article

Understanding Object Cloning

Java provides the clone() method which can be used to create a copy an object. It creates a new memory location and copies the content of the object being cloned into the new location.

View Article

Try-with-resources Statement in Java 7

Java 7 introduces the new 'try-with-resources' statement which help developers to overcome the tedious task of closing the resources that have been used in the program.

View Article

Varargs: Variable Arguments to Methods

Java 5 introduced a new feature called varargs, which allows methods to accept variable number of arguments. They are useful in cases where the number of arguments to be passed to a method is unknown.

View Article