Tuesday, August 12, 2014

Java's static Keyword


Instance Variables vs Class Variables
When you declare a field without the static qualifier it is known as an instance variable.
public class Klass {
   public String instanceVariable;
}
Recall that default values are only assigned to class fields, not local variables.
Check out the post I did on Java's Primitive Data Types.

In order to access an instance variable you need to instantiate an instance of the class.
   public static void main(String[] args) {
      new Klass().instanceVariable = "Hello World";
   }
When you declare a field with the static qualifier it is known as a class variable.
public class Klass {
   public static String classVariable;
}
You don't need to instantiate an object of the class in order to access a class variable.
   public static void main(String[] args) {
      Klass.classVariable = "Hello";
   }
In fact, if this main method was in the Klass class, we could have just done this:
public class Klass {
   public static String classVariable;

   public static void main(String[] args) {
      classVariable = "Hello";
   }
}
Static Blocks
When you declare a static final field, you must specify an initial value for it:
public class Klass {
   public static final String good  = "good";
   public static final String bad; // Compiler Error: variable bad might not have been initialized
}
An alternative way to initialize static final fields in Java is to use what's known as a static initializer block.
public class Klass {
   public static final String good;
   
   static {
      good = "good";
   }
}
You are allowed to have multiple static blocks in your class.
public class Klass {
   private static final String first, second, third; 

   static {
      first = "first";
   }
   
   static {
      second = "second";
   }
   
   static {
      third = "third";
   }
}
Static blocks can only call static methods:
public class Klass {

   static {
      good();
      // bad(); // Non-static method 'bad()' cannot be referenced from a static content
   }

   private static void good() {
   }

   private void bad() {
   }
}
Note: This rule applies to static methods as well.  That is, static methods can only call other static methods.

The static block also cannot throw an exception:
static {
   throws new IllegalArgumentException(); // Compiler Error
}
Static Imports
Prior to Java 5, to use a static field or method you had to name the class that it came from.  For example, given the following:
package my.company.com;

public class Klass {
   public static String str = "Hello world";
}
I would need to do this:
package some.client;

import my.company.com.Klass;

public class OtherKlass {
   public static void main(String[] args) {
      System.out.println("str = " + Klass.str);
   }
}
Although it doesn't seem that bad in this example, imagine if the OtherKlass was just named Klass.  That would mean I would have to use the fully qualified name of the class:
package some.client;

public class Klass {
   public static void main(String[] args) {
      my.company.com.Klass.str = "Salut le monde.";
   }
}
On top of that, imagine how much of a pain it would be if that static field or method was used multiple times in the other class.

Static imports make static fields and methods appear as if they are part of the class, even though they are defined elsewhere.
package some.client;

import static my.company.com.Klass.str;

public class Klass {
   public static void main(String[] args) {
      System.out.println("str = " + str);
   }
}
Static Final Fields In Interfaces
You are allowed to define static final fields in interfaces:
public interface MyInterface {
   public static final String str = "Hello World";
}
This practice, however, is generally frowned upon. In his book "Effective Java", Joshua Bloch points out that doing so exposes a part of how the class was implemented. You're also stuck with implementing that interface even if the constant is no longer being used.

Note: You cannot use a static block inside of an interface.
Nested Classes vs Inner Classes
Java allows you to define a class inside of another class. Technically these are called nested classes.
public class Outer {

   public class Nested {
   }
}
There are actually two types of nested classes.  The one just shown is commonly referred to as an inner class.  Like the innards of your body, an inner class is considered to be a part of the enclosing class.
public class Outer {
   public Outer() {
      System.out.println("outer = " + this);
      new Inner();
   }

   public class Inner {
      public Inner() {
         System.out.println("Outer.this = " + Outer.this);
      }
   }

   public static void main(String[] args) {
      new Outer();
   }
}
Example Result
outer = Outer@66ec44cb
Outer.this = Outer@66ec44cb
Notice that the inner class contains a reference to the enclosing outer class.  The two cannot be separated.  In fact, you have to instantiate an instance of the outer class before you can instantiate an instance of the inner class.
   public static void main(String[] args) {
      Outer.Inner inner = new Outer().new Inner();
   }
Java calls the other type of nested classes static nested classes because the qualifier static is used.  I, however, refer to them as just plain nested classes.  Nested classes are different from inner classes in that they're not associated with the enclosing class. 
public class Outer {

   private String str = "Hello world";

   public class Inner {
      public Inner() {
         // Inner Classes can access all the members of their enclosing class.
         System.out.println("str = " + Outer.this.str);
      }
   }

   public static class Nested {
      public Nested() {
         // Nested Classes, however, cannot.
         // System.out.println("str = " + Outer.this.str); // Compiler Error
      }
   }
}
With nested classes, you don't need to instantiate the one before you can instantiate the other. 
public class Outer {
   public Outer() {
      System.out.println("Outer");
   }

   public static class Nested {
      public Nested() {
         System.out.println("Nested");
      }
   }

   public static void main(String[] args) {
      Outer outer = new Outer();
      Outer.Nested nested = new Outer.Nested();
   }
}
Where static Is Not Allowed
Inner classes cannot have static fields or methods
public class Klass {
   static String good = "GOOD";
   
   public class Inner {
      static String bad = "BAD"; // Compiler Error: Inner classes cannot have static declarations
   }
}
Local variables are not allowed to be static
public class Klass {
   public void fooBar() {
      static String str = "Hello World"; // Compiler Error: Modifier 'static' not allowed here.
   }
}

References

No comments:

Post a Comment