Java 11 Developer Certification - Static Keyword

December 20, 2020

static keyword

By declaring something static, we are associating the element with the Class and not with any instance of that Class. Such elements exist and can be used even if we never create a single instance of that Class.

Static keyword can be applied to the following elements of the Class

  • fields
  • initializer
  • method
  • nested class
  • nested interface
  • nested enum

static modifier cannot be used on an outer class itself, or a local variable as it is only meaningful to use static when it describes the relationship to a class and its elements.

From the Oracle Documentation Link, we derive the following scenarios of when a class itself gets initialised. A class or interface type T will be initialised immediately before the first occurrence of any of the following:

  • T is a class and an instance of T is created
  • A static method declared by T is invoked
  • A static field declared by T is assigned
  • A static field declared by T is used and the field is not a constant variable.

Now these rules are nuanced and we will go through each of them via examples.

We can access a static element with the exception of an initializer by calling it with the dot operator on the class name itself.

A constructor cannot be static because a constructor is used in creating an instance and to anything that’s modified, the static keyword will not be associated with the instance, although it is available for use by the instance.

We can access the static members of the class using the class name qualifier or the instance reference variable, even if the variable itself is NULL.

The best example of a static method is something we have been using day in and day out, the main method. The main method is static and callable without executing an instance of the class itself.

We have also seen static initializers and static fields briefly in the previous videos. Lets have a look at the code to understand static usage properly.

package maxCode.online.Methods;

//This class demonstrates static fields, a static initializer, and
//a static method
class StaticStuff {
	static final String appName = "A Good App";

	// static initializer
	static {
		System.out.println("Initializing StaticStuff class");
		StaticStuff.counter++;
	}

	static void printAppName() {
		System.out.println("Application Name:  " + appName + " : counter  = " + counter);
	}

	static int counter;
}

public class StaticElementClass {
	public static void main(String[] args) {
		// Add some 'work' first to prove StaticStuff not initialized
		// on start-up
		for (int i = 0; i < 3; i++) {
			System.out.println("Printing " + (i));
		}

		System.out.println(StaticStuff.appName);
		System.out.println(StaticStuff.appName + " " + StaticStuff.counter);
		//StaticStuff.printAppName();
		// ** Create an object of type StaticStuff.
		// StaticStuff s = null;

		// // We access static member of the StaticStuff class
		// System.out.println("Application Name: " + s.appName);
		//
		// int myCounter = StaticStuff.counter;
		// System.out.println("myCounter = " + myCounter);

		// Execute static method on StaticStuff
		// s.printAppName();

		StaticStuff s = new StaticStuff();
		System.out.println("Created first instance of StaticStuff");
		s.printAppName();

		System.out.println("Created second instance of StaticStuff");
		StaticStuff s0 = new StaticStuff();
		s0.printAppName();

		System.out.println("Created third instance of StaticStuff");
		StaticStuff s1 = new StaticStuff();
		s1.printAppName();
		
		System.out.println("Null instance of StaticStuff");
		StaticStuff ss = null;
		ss.printAppName();
	}
}

Output

Printing 0
Printing 1
Printing 2
A Good App
Initializing StaticStuff class
A Good App 1
Created first instance of StaticStuff
Application Name:  A Good App : counter  = 1
Created second instance of StaticStuff
Application Name:  A Good App : counter  = 1
Created third instance of StaticStuff
Application Name:  A Good App : counter  = 1
Null instance of StaticStuff
Application Name:  A Good App : counter  = 1

So we have a class StaticStuff with two static variables, a static initializer to let us know when class gets initialized and a static method which prints a statement.

Then we have the main class, which first prints some values, then we experiment with the static class objects.

The first thing we realize watching the output is that the static stuff is not initialized until we try to access the static field. Only when we try to access the static counter, the static block gets initialized. This is because the static variable is also declared final, and when we try to access the final variable, the static initializer does not get executed.

Similarly if we had instantiated the class before, the static stuff would have been been called there itself and the static initializer will get executed.

The counter, as we see in the output, remains constant. The reason for that is the fact that it is incremented in a static initializer and as we know very well, the static initializer will get called only once. We can also see that eventhough we have created multiple instance of the StaticStuff class, the static initializer is executed only once.

Also, calling StaticStuff.appName or s0.appName doesnt make any difference, the output will remain the same. Although, we do get a warning stating “The static method printAppName() from the type StaticStuff should be accessed in a static way.”

Another interesting thing here is the last output. We can see in the code that the object is initialized to null, still we can use it to call the static method and it does print the output (without throwing any NullPointerException). Also it shows that just declaring an object of the StaticStuff and setting it to null doesn’t trigger the class’s initialization.

Lets look at another piece of code to have a detailed view of the static nested class (do remember static class can only be a nested class). Note that anytime static class is defined at outer level, the code will throw a compiler error.

package maxCode.online.Methods;

//The UtilityClass defines a static abstract class,
//a static field with the type of the abstract class
//and a method that is a pass thru to the abstract method on
//the abstract class
class UtilityClass {
	// nested static abstract class
	static abstract class Logger {
		abstract void log(String logMessage);
	}

	// static field typed to the nested static abstract class
	static Logger logger;

	// static method (pass through method to the method on
	// the abstract class
	static void log(String logMessage) {
		logger.log(logMessage);
	}
}

// This class extends the UtilityClass and implements the log method
class CustomLogger extends UtilityClass.Logger {
	// overrides and implements the abstract method from
	// the abstract class
	void log(String logMessage) {
		logMessage = doSomethingCustomBeforeLogging(logMessage);
		System.out.println("I want to log " + logMessage + " my own great way");
	}

	// A custom private method used in log method
	private String doSomethingCustomBeforeLogging(String logMessage) {
		// code might search, replace, persist, whatever...
		logMessage = "'" + logMessage + "' (" + logMessage.split(" ").length + " words)";
		return logMessage;
	}
}

// This code tests the code from above.
public class StaticClassExample {
	public static void main(String[] args) {
		UtilityClass.logger = new CustomLogger();
		UtilityClass.log("An important message");
	}
}

Output

I want to log 'An important message' (3 words) my own great way

The above code has all three types of static usage, the static class, a static variable and a static method. Logger is a static abstract class, which is nested in this utility class. This class will allow the consumer of the code to create a custom logger class by implementing their own class which would extend this abstract class, and then set it universally. logger is a static variable which will be populated by concrete logger which we will create. And last one log is a static method.

The new class CustomLogger extends the Logger utility class and implements custom log method implementation.

The main class creates an instance of the CustomLogger and sets to the static variable logger.

Thats all for the static keyword, we will discuss Encapsulation in the next section!