Tricks with Static Initializers
Don't be limited to simple expressions when initializing static values. Static initializers provide more complex processing for initializing classes
by Pete Ford

Posted April 15, 2003

The static initializer is a feature of Java that is very often not covered in-depth in books about Java. In fact, there are books about the language that don't even mention this feature of the language. For this reason, it's more than possible that even experienced Java developers reading this may not be aware of what static initializers are and how powerful they can be. When you declare static data in a class, you have the option of including an initial value, like this:

public static int fullCircle = 
   2.0 * Math.PI;

For simple initialization this is fine, but there are circumstances where this kind of initialization is not enough. For example, let's say that your application needs a preset table of sine values at one-degree intervals for all angles from 0 to 359 degrees. You could set the table up in a similar way:

public final static double[] 
   sine =
{
   0.0,
   0.01745240643728351,
   0.03489949670250097,
   // ...etc.
   -0.01745240643728445
};

As you can see, setting up for all 360 values would become rather cumbersome and error-prone. Another disadvantage is that the compiled class file includes all of the preset values so that they can be used in the initialization; as a result, a class file containing just this array is over 7,500 bytes long.

An alternative is to use a static initializer, which is a block of code—rather like a static method—that is executed once only, when the class is loaded. It can't be called under any other circumstances, so it doesn't have a name, arguments, or any access modifiers, and does not return a value. For these reasons, all we need to declare a static initializer is the keyword "static" and the code enclosed in parentheses. In our example case we can use a static initializer like this:

public final static double[] 
   sine;

static
{
   sine = new double[360];
   for (int angle = 0 ; 
      angle < sine.length ; 
      angle++)
   {
      sine[angle] = 
         Math.sin((angle * 
         Math.PI)/180.0);
   }
}

When the source for a class is compiled, the compiler generates code to initialize the class variables. This code is executed when the Java Virtual Machine (JVM) loads the class. A class can include one or more static initializers—for each of these the compiler generates the equivalent code and appends it to the class initialization code, and the initializers are executed in the order that they appear in the source. In this example, the compiler will allocate space for the array reference "sine." This code is followed by our initializer code from the static block, which will allocate space for 360 double values and then calculate and assign their values.

You may have noticed that the array "sine" is declared as final but is not initialized in its declaration; instead, the static initializer assigns a reference to the array when it is executed. This assignment is perfectly acceptable because the rules of Java are that any final static members must be initialized by the time the static initializers (if any) have been run. In other words, any final static values that do not have values assigned in their declarations must be assigned in a static initializer.

Worth It
A major difference is seen when you look at what the compiler creates in this case: the class file for this second example is only a little over 500 bytes long. There is a small penalty to pay—the additional execution time required to perform the floating-point calculations when the smaller class file is loaded—but I think you'll agree that the smaller disk space and memory requirements and the more easily read source file are well worth the price.

You can put pretty much any code you like into a static initializer. This means that you can perform more complicated and powerful operations at load time. JDBC includes a good example of this usage. If you have ever used JDBC then you'll know that the usual sequence of operations to initialize a database connection looks something like this:

Class.forName(
   "com.somedomain.jdbc.MyDriver");
Connection con =
   DriverManager.getConnection(
   "jdbc:xyz://10.135.188.21:3000/
   mydata", user, password);

The JDBC driver class file is loaded into the JVM in the Class.forName() call, which is particularly convenient because it means that the name of the driver does not need to be hard-coded in the source. It can be read from a file or supplied as a command-line argument, for example.

The DriverManager.getConnection() call is the mystery. An application can have several JDBC drivers loaded at any given time, and yet somehow this call is able to locate the correct driver and establish a connection to the correct database. It does so by examining the URL argument to the call, but how does this work? The key is that JDBC drivers (which all implement the interface java.sql.Driver) include static initializers that do two things: first, they instantiate an object of the driver class, and then they call the DriverManager class's static registerDriver() method, which adds a reference to the driver object to a static data structure.

Now, when the getConnection() method is called it scans the static data structure and calls the connect() method of each registered driver, passing the URL string as an argument. The connect() code in each driver parses the URL and decides whether or not it is the correct driver to handle that URL. If it is, it creates a Connection object and returns a reference to it; otherwise, it returns null. This procedure is how the DriverManager class locates the correct driver. When one of the drivers returns a Connection object, DriverManager knows it has found the correct driver for that URL.

Take a look at a much more simplified example of the DriverManager class (see Listing 1). The real code includes some exception handling and other items that were omitted for clarity. The static initializer in the Driver class is responsible for identifying the driver to the DriverManager:

public class MyDriver implements 
   java.sql.Driver
{
/**
   * Static initialization creates 
   * a Driver and registers it.
   */
   static
   {
      DriverManager.registerDriver(
         new MyDriver());
   }
   
/**
   * Parses the URL to see if this 
   * is the correct Driver to 
   * handle this URL. Returns a 
   * Connection if it is, null if
   * it is not.
   */
   public Connection connect(
      String url)
   {
      ...

The full details of JDBC drivers aren't important. What is important is to see how this technique can be implemented in general terms; all you need is a manager class that can maintain a collection of related objects, an interface that characterizes those objects, and a rule specifying that those objects must have a static initializer that creates an instance and registers it with the manager class when it is loaded by the JVM. The result is a general technique that allows you to load any class that implements the interface by name, and lets the manager class identify a particular instance at run time.

More important, and the point of this article, is that you are not limited to simple expressions when initializing static values. Static initializers give you the ability to use much more complex processing to initialize your classes at load time.

About the Author Pete Ford has been a software developer for more than 20 years, working mostly with embedded and turnkey systems. He lives and works in Dallas, TX. Reach Pete at .