static, final keyword and main() method

static

Static variable

We all know Java is an object oriented programming language. So, most of the time, first we create an object and then call a method using that object.

For example, let’s consider a Human class where we have an instance variable called name. We will initialize that variable during object creation to ensure that every Human should have a name.

public class Human {
    private String name;

    public Human(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

This name will be different for different objects. For example, we can create one Human object with the name Aditi and another object with the name Rahul.

Human person1 = new Human("Aditi");
Human person2 = new Human("Rahul");

So we can say, the value of the instance variable name belongs to individual objects and each object (person1 & person2) will have its distinct copy of this variable.

To get the value of the variable name, we can call the getName() method against individual objects.

System.out.println(person1.getName());
System.out.println(person2.getName());

Output:

Aditi
Rahul

But there can be some scenario where you might want to share a single copy of a variable across all objects. For example, let’s assume you want to keep track of the number of human objects created. Here we need a shared variable (which doesn’t belong to any particular instance), and that variable will be incremented whenever we will create a new instance of Human class. We can achieve that by using a special keyword called static. A static variable is shared among all instances of a class and as a result the value is the same for all instances.

We can rewrite our human class as below –

public class Human {
    private String name;
    private static int humanCount = 0;

    public Human(String name) {
        this.name = name;
        humanCount++;
    }


    public String getName() {
        return name;
    }
}
Static method

As we know, the outcome of our regular (non-static) method depends on the value of the instance variable. For example, the outcome of the getName() method depends on the instance variable name and this value will vary from object to object.

But there might be some method that doesn’t depend on any instance variable. For example, you might want to print the number of legs human beings have. It doesn’t depend on any instance variable. It will give you exactly the same output for all the human objects.

For this scenario, we can use a static method. As a static method doesn’t depend on any instance variable, we can call a static method directly by class name and without using any instance of that class. For this reason, we say, static members belong to class, not to any instance of that class.

public class Human {
    public static int getLegCount() {
        return 2;
    }
}
System.out.println(Human.getLegCount());

Output: 2

Also, in our earlier example, we had one static field called humanCount. Value of that field doesn’t depend on any particular object of the Human class. So, here also we can use a static method to get the value of human count.

public class Human {
    private String name;
    private static int humanCount = 0;

    public Human(String name) {
        this.name = name;
        humanCount++;
    }

    public String getName() {
        return name;
    }

    public static int getCount() {
        return humanCount;
    }
}
Human person1 = new Human("Aditi");
Human person2 = new Human("Rahul");

System.out.println(Human.getCount());

Output:  2

It is also allowed to call a static method by object reference as shown below and it will give you the correct result. But that is just not right and misleading. So never do that.

System.out.println(person1.getCount());

Output: 2

So please remember –

  1. Call a static method using a class name.
  2. Call a non-static method using an object reference.
Can a static method use instance variables or a non-static method?

No. To test it, let’s make the getName() method static.

public class Human {
    private String name;

    public Human(String name) {
        this.name = name;
    }

    public static String getName() {
        return name;
    }
}

You will get following compilation error at line 9: Cannot make a static reference to the non-static field name

The reason is pretty obvious. If we call the Human.getName() method and there are multiple instances of the Human class, how will the JVM decide which name to use? And in fact, the static method doesn’t know or even care about any instance of a class. Remember what we said earlier, static method belongs to class, not to any particular instance of that class.

Applying the same logic, as non-static methods belong to the instance of a class and depend on instance variables, we cannot call a non-static method from a static method. But the reverse is allowed. You can call a static method from a non-static method.

You may argue, what if a non-static method is not using any instance variable? Why is it not allowed to call that method from a static method? This may be fine for now, but what will happen if you change the implementation of the non-static method in future such that the method will use some instance variable? That will fail right. So, the compiler doesn’t allow this.

Initialization of static variable

All static variables are initialized when the JVM loads the class for the first time. As a result, static variables are initialized before any object of that class is created. Also, static variables are initialized before we call any static method.

If you don’t explicitly initialize a static variable, it will be initialized with the default value just like instance variables. For example, if the static variable is int, it will be initialized with 0.

Static initializer

A static initializer or static block is a block of code that can be used to initialize static variables. This block is executed as soon as the JVM loads the class – before any static method is called or static variables are used.

public class Human {
    private static int legCount;
    
    static {
        legCount = 2;
    }

    public static int getLegCount() {
        return legCount;
    }
}
System.out.println(Human.getLegCount());

Output: 

final

final is another important keyword in java. We can use final along with variables, methods and classes.

  1. A final variable means you can’t change its value.
  2. A final method means, you cannot override the method.
  3. A final class means you cannot extend the class.
Final variable

As I said, a final variable means its value cannot be modified. So essentially, final variables are nothing but constants.

public class Human {
    private final int legCount = 2;
}

If you try to modify the value, it will throw a compilation error.

public class Human {
    private final int legCount = 2;
    private void setLegCount(int count) {
        legCount = count;
    }
}

You will get a compilation error at line 4 stating: The final field Human.legCount cannot be assigned.

 Also, a final variable must be initialized. Otherwise it will throw a compilation error –

public class Human {
    private final int legCount;
}

Second line will throw compilation error: The blank final field legCount may not have been initialized.

To fix this, we have 2 options –

  1. Initialize the variable during declaration: private final int legCount = 2;
  2. Initialize the variable inside the constructor as shown below.
public class Human {
    private final int legCount;

    public Human(int legCount) {
        this.legCount = legCount;
    }
}

If the variable is static, the rule is almost the same. Once initialized, the value cannot be changed. A static variable can be initialized –

  1. During declaration: public static final int LEG_COUNT = 2;
  2. Inside the static block as shown below.
public class Human {
    public static final int LEG_COUNT;
    
    static {
        LEG_COUNT = 2;
    }
}

Probably you have noticed, we have named the static final variable all in caps with underscore separating the word. This is the naming convention for static final variables (constants) so that we can identify them just by looking at the name.

Can we initialize the static final variable inside the constructor?

No. The reason is, static variables are initialized when the class is loaded and the constructor may be called much later when we want an object of that class. Also, if a class contains only static methods, there is no need to call the constructor. We can directly call the methods using the class name. So if we initialize the static final variable inside the constructor, there is a possibility that it won’t be initialized at all.

Also as static variables are shared across all instances of a class, if we call the constructor multiple times to create new instances, the static final variable will be re-assigned with the value – which is not allowed as the final variable can be assigned with a value only once. 

As the static block is executed only once during class load, we are allowed to initialize the static final variables inside the static block.

final method

If a method is declared as final in Java, then it cannot be overridden by any subclass of that class. To illustrate this, create a final method and try to override it in child class.

public class Parent {
    public final void finalMethod() {
        System.out.println("This is a final method");
    }
}
public class child extends Parent {
    public final void finalMethod() {
        System.out.println("Overriding is not allowed");
    }
}

You will get a compilation error at line 2 stating: Cannot override the final method from Parent.

final class

If a class is declared as final in Java, then it cannot be extended. To illustrate it, create a final class and try to extend it.

public final class Parent {

}
public class child extends Parent {

}

You will get a compilation error at line 1 stating: The type child cannot subclass the final class Parent.

final reference variable

Just like final variables, in case of final reference variables, once a value is assigned, nothing else can be re-assigned. As we discussed earlier, a reference variable holds an object reference. To test this, first create a Human class that will contain a final reference variable to hold the address. Then try to re-assign the variable with a new address.

public class Address {
    String country;
    
    public Address(String country) {
        this.country = country;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }
}
public final class Human {
    private final Address address = new Address("India");
    
    public void setNewAddress(Address newAddress) {
        address = newAddress;
    }
}

You will get a compilation error at line 5 stating: The final field Human.address cannot be assigned.

The reason is as the variable address is final, once a value is assigned, we are not allowed to re-assign anything else. But the address object itself can be changed as that doesn’t change the reference or re-assign a new reference to the final variable. So, below code will work just fine.

public final class Human {
    private final Address address = new Address("India");
    
    public void updateAddress(Address newAddress) {
        address.setCountry("Australia");
    }
}

The main() method

The main() method is the entry point of a java program. When you try to execute a java code, java.exe is called and it finds the main method and starts the execution from there.

If you pass any argument in the command line, those are passed to the main method as String array. To understand this, create a class called TestMain.

public final class TestMain {
    public static void main(String[] args) {
        System.out.println(Arrays.toString(args));
    }
}

First compile the java file from command line: javac TestMain.java. Then execute the generated class file from the command line: java TestMain.

If you pass something as an argument, that will be converted to an array and passed to the main method.

Now the question is, why is the main method public, static and void? Let me explain that one by one.

public: If the access modifier is public for a method, that indicates that the method can be accessed from outside the class. As the main method is called by JVM from outside the class, it should be public.

static: The main method is static because it can be invoked by the JVM without having to instantiate an object of that class. You can ask, why not just create an instance of the class and call the main() method using that instance? The answer is, it may create ambiguity. Consider you have overloaded constructors. How will the JVM decide which constructor should be called? So to keep it simple, the main() method is static.

void: The return type void means the method will not return anything. If you think about it, when JVM starts the main method, that program gets started and as soon as the main() method completes, the java program terminates too. So, it doesn’t make any sense to return anything from the main() method as JVM can’t do anything with the return value.

That’s it for now. Hope the concept of static and final is clear now. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.