471,318 Members | 1,960 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes and contribute your articles to a community of 471,318 developers and data experts.

Class Initializers

13,262 8TB
Inspiration

Inspired by a post by Jos in the Java forum, I have put together a little article on class initializers.
Initializers are indeed underutilized by most Java programmers. Once the Java programmer knows about the constructor he/she feels they have found a good place to put code that must be run before an object is created and sometimes don't try to look for more places to put pre-object creation code. The other places to put pre-object creation code are initializers.

There are two types of initializers that can be used in classes. Static initializers and instance initializers.


Static initializers.

Static initializers are the first bit of code that is run once when the class is being loaded. Since they are static, they are not tied to any particular instance but rather are shared among all objects of their class. An instance initializer block is thus executed only once (during class loading). After that, creation of objects in the program do not trigger execution of the static initializer block again. Another consequence of being static is that you can only use static initializers to initialize static variables. This makes sense because these are the only ones available during class loading. Enough chattering, let's dive into an example:

A scenario

The scenario is a Utility class that gives information about products in a database. The class works on a list of all the products in the database by loading them into an arraylist once when the class is loaded and then referring to that arraylist on all subsequent inquiries on products. The exists method for example, does not need to connect to the database but just queries the products ArrayList.

Disclaimer

Please note that this example is for illustration purposes only. This approach is only helpful in a read only database system because if a product is added into the database during this program's execution after the ProductUtils class has been loaded, then that product will not be available in the list of products created at class loading. There are workarounds of course but we digress ...

A static initializer block is simply denoted by a pair of opening and closing braces preceeded by the static keyword with the initialization code placed inside the braces
Expand|Select|Wrap|Line Numbers
  1.  static {
  2. //initialization code goes here.
Here it is in action
Expand|Select|Wrap|Line Numbers
  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.sql.ResultSet;
  4. import java.sql.Statement;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. class ProductUtils {
  8.     static List<Product> products = new ArrayList();
  9.     static {
  10.         try {
  11.             Class.forName("com.mysql.jdbc.Driver");
  12.             Connection con = DriverManager.getConnection("jdbc:mysql://localhost/test","r035198x ", "acomplexpassword");
  13.             Statement st = con.createStatement();
  14.             ResultSet rs = st.executeQuery("select * from Products");
  15.             while(rs.next()) {
  16.                 products.add(new Product(rs.getInt(1), rs.getString(2)));
  17.                 System.out.println("product added");
  18.             }
  19.         }
  20.         catch(Exception e) {
  21.             e.printStackTrace();
  22.         }
  23.  
  24.     }
  25.  
  26.     public static void main(String[] args) {
  27.  
  28.     }
  29.  
  30.     boolean exists (Product product) {
  31.         return products.contains(product);
  32.     }
  33.  
  34. }

Output with a blank main


Running this program with the proper configurations (database created and connector set in the class path correctly) prints product added for each product that is added to the ArrayList of products. Note that I have deliberately left the main method blank but the program connects to the database, loads objects into an arraylist and prints some stuff to the console. The static initializer block is responsible for all this. The main method is innocent.
This should make it clear that an instance of a class is not required for a static initializer block to be executed. Static initializers can be as sophisticated as you wish, just keep in mind that they are executed during class loading and therefore can only access other statics.

The more the merrier?

You can have as many static initializers as you wish in your class. They will be executed in the order that they appear in your code (from the top of the file to the bottom). To make your code easy to read, you should put different tasks in different initializer blocks so that each block performs one logical operation.

Use

Use static initializers to perform operations that must be done once before all objects of a class are created. Doing these operations in a static initializer ensures that they are done once and therefore can speed up your program because it doesn't have to do them again every time an object of that class is created. More specifically, use static initializers for operations that must be done during class loading of that class by the JVM. The example above also contains a common use of static initializers.

A common use

The specs for the Driver interface say
When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a driver by calling
Class.forName("foo.bah.Driver")
Database driver writers are being told here that when their drivers are loaded, an object must be created and registered with the driver manager.

The MySQL driver that was used to connect to the database in the example above had to obey this rule. The call to DriverManager.registerDriver() was done in a static initializer of the MySQL driver class which looks something like:
Expand|Select|Wrap|Line Numbers
  1.   static {
  2.         try {
  3.             java.sql.DriverManager.registerDriver(new Driver());
  4.         } catch (java.sql.SQLException E) {
  5.             throw new RuntimeException("Can't register driver!");
  6.         }
  7.  
  8.         if (DEBUG) {
  9.             Debug.trace("ALL");
  10.         }
  11.     }
written by Mark Matthews

This is helpful since loading a driver now just translates to loading the driver class for that driver.

Instance initializers

The difference between static initializers and instance initializers is in the time that they are executed. While static initializers are executed once when a class is loaded, instance initializers are run once before an object is created. This changes matters somewhat. Instance initializers now have access to non-static variables in a class including the this object.
Instance initializers in named classes should be used to initialize instance variables which cannot be initialized using one line of code but should be initialized the same way for all the constructors in that class. Otherwise if the variable should be initialized differently depending on the constructor that was invoked, then it should be initialized in the appropriate constructors.

Expand|Select|Wrap|Line Numbers
  1.  class Cat {
  2.     String name;
  3.     int numOfLegs;
  4.     int numOfEars;
  5.  
  6.     {
  7.         numOfLegs = 2;
  8.         numOfEars = 2;
  9.     }
  10.     Cat(String name) {
  11.         this.name = name;
  12.     }
  13.     public String toString() {
  14.         return name + " has " + numOfLegs + " legs and " + numOfEars + " ears"; 
  15.     }
  16.  
  17.     public static void main(String[] args) {
  18.         System.out.println(new Cat("Mary").toString());
  19.  
  20.     }
  21. }
Here, every cat is initialised with two legs and two ears.

Called before constructors

Instance initializers are always called before a constructor is called (note that the compiler actually generates <init> methods for every constructor in a class and these are the ones called when a constructor is invoked). As with static initializers, multiple initializers are executed in their textual order.

Instance initializers in anonymous classes

When used with anonymous classes, initializers can make cumbersome code more elegant. Generally you want to isolate code that does different operations. Specifically you don't want to mix code that builds a structure (e.g list, map or more exotic structures) with code that manipulates that structure. Suppose you want to build a mapping between Integers and Strings so that 1 maps to “Sunday”, 2 maps to “Monday” etc.
You could build the map as
Expand|Select|Wrap|Line Numbers
  1.  Map<Integer, String> days = new HashMap();
  2. days.put(1, “Sunday”);
  3. days.put(2, “Monday”);
  4. //etc
After this then you start manipulating the map.

If an anonymous class and initializer is used, this can be done as
Expand|Select|Wrap|Line Numbers
  1.  Map<Integer, String> days = new HashMap() {
  2.             {
  3.                          put(1, “Sunday”);
  4.                  put(2, “Monday”);
  5.                          //etc
  6.                }
  7. };
  8.  
The closing }; in the code above clearly marks the end of the creation of the map. Anyone looking over the code can easily switch between checking code that creates the structure and code that manipulates the structure. This can save a few seconds of debugging time depending on the complexity of the initialization. Remember that the HashMap is just an example of a structure that may require several lines of initialization. Other user defined structures can also benefit, perhaps even more, from the same approach depending on the actions that must be done to fully initialize the structure.


Initializers and exceptions

Static initializers cannot throw checked exceptions (it is a compilation error if they do).
An instance variable initializer in a named class can only throw checked exceptions if that exception or one of its supertypes is explicitly declared in the throws clause of each constructor of its class and the class has at least one explicitly defined constructor.(JLS Third Edition[8.3.2]).

Expand|Select|Wrap|Line Numbers
  1.  static {
  2.     if(true) {
  3.         throw new Exception();
  4.     }
  5. }
does not compile.
while
Expand|Select|Wrap|Line Numbers
  1.  public class Sorry {
  2.     String sorry;
  3.     public Sorry() throws Exception{}
  4.         {
  5.         if(true) {
  6.                           throw new Exception();
  7.                     }
  8.     }
  9. }
compiles fine.

Instance variable initializers in anonymous classes, however, are allowed to throw any checked exceptions.
Expand|Select|Wrap|Line Numbers
  1.  import java.io.IOException;
  2. public class Sorry {
  3.     String sorry;
  4.     public Sorry() {}
  5.     public static void main(String[] args) throws IOException{
  6.         System.out.print( new Sorry() {
  7.             {
  8.        if(true) {
  9.                               throw new IOException("Sorry");
  10.                       }
  11.             }
  12.         }.toString());
  13.  
  14.     }
  15.     public String toString() {
  16.         return sorry;
  17.     }
  18.  
  19. }
compiles fine.

In case you are wondering about the name of the class, a friend of mine wrote a Sorry class to apologize 5 000 times to his girlfriend through email and I just stole that class and changed it a bit to suit more serious purposes.

I hope you have now some understanding on initializers and know when to use which one to better your programs in both speed and maintanability.
Dec 3 '07 #1
1 6808
ajos
283 100+
Class initializers

In case you are wondering about the name of the class, a friend of mine wrote a Sorry class to apologies 5 000 times to his girlfriend through email and I just stole that class and changed it a bit to suit more serious purposes.
Hi r035198x,
I was just scanning around this article of yours. Though i didnt read all of the things completely....this one definitely was most funny :) . Funny because, the other day i had to face this very situation and had to say sorry some 200 times in my case(she's less demanding than your friend i guess :)). How the hell did you guys get this idea...coz it took me some time to type all the sorries :(. AnyWays great article. Kudos to you and TSDN.
regards,
ajos
Dec 6 '07 #2

Post your reply

Sign in to post your reply or Sign up for a free account.

Similar topics

4 posts views Thread by Doug | last post: by
106 posts views Thread by A | last post: by
6 posts views Thread by Neil Zanella | last post: by
4 posts views Thread by John Devereux | last post: by
8 posts views Thread by Pent | last post: by
10 posts views Thread by Ognen Duzlevski | last post: by
37 posts views Thread by JohnGoogle | last post: by
3 posts views Thread by dischdennis | last post: by
16 posts views Thread by shapper | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.