ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Understanding the Interplay Between Utility Classes and Static Initialization Understanding the Interplay Between Utility Classes and Static Initialization

by Satya Komatineni
09/15/2004

Java is an OO language, which means much of the functionality of a Java application is encapsulated into cohesive classes that can be instantiated and acted upon. Nevertheless, once in a while you end up with some functions that are applicable to more than one class. These functions don't really belong to any particular class, but to a sub-system or a package. Although one can express this grouping as a class by itself (represented by interfaces), it is just simpler to collect them as static functions in a class, when one doesn't need the sophistication of service-centric approach for these methods. For example, I have a class called ServletUtils (see Example 1 below).

One characteristic of static functions is that they are stateless, although for efficiency they may depend on some static variables. When these static variables are introduced or utilized, you need to know the class initialization rules laid out by the language. Although these rules are fairly intuitive, it is beneficial to know them in some depth, as static initialization involves side effects. This article is about what these side effects are and how to minimize them through a pattern called Static Resource Holder. With this in mind, I want to first review the basics of static initialization rules. For a complete treatment of the rules, refer to the Java language specification links at the end of the article.

Example 1. ServletUtils

public class ServletUtils
{
   static public void 
   writeOutAsJavaScript(PrintWriter out,
                        StringBuffer javaScript);
   
   static public String 
   convertToHtmlLines(String inString);
   
   static public Map 
   parseQueryString(String httpQueryString);
   
   static public Map 
   getParameters(HttpServletRequest request);
   
   static public String
   getSusbstitutedURL(String encodedString,
                      Map arguments);
   
   //.. so on and so forth
}

Simplified Rules of Static Initialization

  1. A class is initialized before any of its members are accessed. The accessed members can include class variables and static functions.

  2. Before a class is initialized, its superclass is initialized first.

  3. You can use static blocks to initialize static variables. Java not only allows static variables, but also blocks of code that are declared just like variables. These are nameless blocks of code that appear alongside the variables.

  4. Static variables and static blocks are executed in the order they appear in source code (so-called textual order).

  5. A class is initialized before any of its instances can exist.

Rules of Static Blocks

  1. Static blocks cannot return. They are simply nameless blocks of code that execute in the order they are specified.

  2. Static blocks cannot throw checked exceptions. This is somewhat expected as well, since they're called by the internals of the JVM. With all of the exceptions possible in arbitrary blocks of code, the JVM could not possibly meaningfully declare them all and warn about them through checked exceptions.

  3. Nevertheless, static blocks can throw runtime exceptions. This also should be expected, because any piece of code in Java could result in a runtime exception, such as NullPointerException, ArrayIndexOutOfBoundsException, etc.

  4. Being static in nature, these static blocks of code cannot refer to this or super because at the time the static block is executed, there is not yet an instance of these classes.

Static Initialization Surprise

While reviewing the static initialization behavior, I ran into an interesting piece of code involving name resolution and a static variable. Under this scenario, accessing a static variable of a class through one of its subclass's namespaces will not initialize the subclass (as one might rightly or wrongly expect), but instead will only initialize the superclass.

Consider the following superclass with a static variable defined in it. There is no magic in this class: a static variable and a static block, perhaps completely unrelated to each other.

class superClass
{
   public static int i=10;
   static 
   {
      System.out.println("Super class initialized");
   }
}

In addition to demonstrating the oddity that will be revealed shortly, this code also demonstrates the basic syntax of a static block of code. Notice how bare it is in its structure, only demarcated by the static keyword. Also notice that there are no returns in the code block, nor any exceptions. Nevertheless, it is perfectly acceptable to throw runtime exceptions.

Now let us try to access that static variable i and see what happens. To test this out, let me define a simple Test class with a main in it that makes use of the variable i.

public class Test
{
    public static void main(String[] args)
    {
    	System.out.println("Accessing static i:" + superClass.i);
    }
}

When executed, the Test class will produce the following output:

Super class initialized
Accessing static i:10

See how the intended output ("Accessing static i:10") shows up after the Super class initialized print statement? This is a typical behavior of the static initialization--Side Effects 101 stuff. Now let me introduce a subclass and access the same static variable through the scope of the subclass, and see the nature of the print statements.

class subClass extends superClass
{
   static
   {
      System.out.println("sub class initialized");
   }
}

public class Test
{
    public static void main(String[] args)
    {
    	System.out.println("Accessing static i:" + subClass.i);
    }
}

Notice how in the main method I am accessing the static variable i via the subclass and not the superclass (to which it actually belongs). This code, when executed, produces the following lines.

Super class initialized
Accessing static i:10

As you can see, this test code does not initialize the child, although the variable is invoked through its namespace.

Pages: 1, 2

Next Pagearrow