In this post, I will mainly talk about the nested class in Java. static keyword and something of inheritance will be mentioned very shortly.

The Nested Class

According to Java Tutorial, we have

Terminology: Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes.

Static Class

Top level class can not be declared as static. Only nested classes can be declared as static.

A static class is a class declared as a static member of another class. It can access other static members of the class. The containing class is like a package in the view of namespace. Following is an example:

class O {
  static int n = 1;
  
  static class I {
    int b = n;
  }
}

Use the command javap -c -s -v -p <class>, we can decompile the class, where -c denotes printing out disassembled code, i.e., the instructions that comprise the Java bytecodes, for each of the methods in the class; -s denotes printing internal type signatures; -v denotes printing stack size, number of locals and args for methods; -p denotes showing all classes and members..

O is like:

{
  private static int n;
    descriptor: I
    flags: ACC_PRIVATE, ACC_STATIC

  O();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #2                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  static int access$000();
    descriptor: ()I
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field n:I
         3: ireturn
      LineNumberTable:
        line 1: 0

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: iconst_1
         1: putstatic     #1                  // Field n:I
         4: return
      LineNumberTable:
        line 2: 0
}

And I is like:

{
  int b;
    descriptor: I
    flags:

  O$I();
    descriptor: ()V
    flags:
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: invokestatic  #2                  // Method O.access$000:()I
         8: putfield      #3                  // Field b:I
        11: return
      LineNumberTable:
        line 4: 0
        line 5: 4
}

We can see that the static class I can easily access the private static member of O with the compiler auto-generated synthetic method access$000.

The nested classes are compiled as sperated classes with respect to their containing classes. In order to overcome the privilege restriction, some synthetic methods are generated.

The static class is very useful in many design patterns, such as singleton pattern, and builder pattern.

Top level classes and static class are semantically the same.

Inner Class

An inner class is a class declared as a non-static member of another class. Every instance of an inner class is tied to a particular instance of its containing class. Following is an example:

class O {
  int n = 1;

  class I {
    int b = n;
  }

  I getAnI(){
    return new I();
  }
}

class M {
  O o = new O();
  O.I i1 = o.getAnI();
  O.I i2 = o.new I();
}

Here I show two ways to create instances of an inner class in class M. Note the o.new I(), it gives the compiler a way to tie inner class instance to its containing class instance.

Decompiled for O:

{
  int n;
    descriptor: I
    flags:

  O();
    descriptor: ()V
    flags:
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_1
         6: putfield      #2                  // Field n:I
         9: return
      LineNumberTable:
        line 1: 0
        line 2: 4

  O$I getAnI();
    descriptor: ()LO$I;
    flags:
    Code:
      stack=3, locals=1, args_size=1
         0: new           #3                  // class O$I
         3: dup
         4: aload_0
         5: invokespecial #4                  // Method O$I."<init>":(LO;)V
         8: areturn
      LineNumberTable:
        line 9: 0
}

Decompiled for I:

{
  int b;
    descriptor: I
    flags:

  final O this$0;
    descriptor: LO;
    flags: ACC_FINAL, ACC_SYNTHETIC

  O$I(O);
    descriptor: (LO;)V
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LO;
         5: aload_0
         6: invokespecial #2                  // Method java/lang/Object."<init>":()V
         9: aload_0
        10: aload_0
        11: getfield      #1                  // Field this$0:LO;
        14: getfield      #3                  // Field O.n:I
        17: putfield      #4                  // Field b:I
        20: return
      LineNumberTable:
        line 4: 0
        line 5: 9
}

The synthetic field final O this$0; inside the inner class is the result of tying, via this field the inner class instance can access its containing class instance non-static members.

In the inner class this refers to itself, O.this refers to its containing class instance.

Inner classes in static contexts, i.e. an anonymous class used in static initilizer block, do not have lexically enclosing instances.

An inner class may not have static members.

The inner class includes: local inner class, anonymouse class and non-static member class. What I have already shown is the non-static member class.

Local Inner Class

A local inner class is declared inside of a block. The local inner class instance is tied to and can access the final local variables of its containing method. When the instance uses a final local of its containing method, the variable retains the value it held at the time of the instance’s creation, even if the variable has gone out of scope.

A local inner class is neither the member of a class or package, it is not declared whith an access level.

If a loal inner class is declared in an instance method, an instantiation of the inner class is tied to the instance held by the containing method’s this at the time of the instance’s creation.

class O {
  void m(int x) {
    final double z = 0.1;
    double z1 = 0.2;
    class I {
      int y = x;
      double u = z;
      double u1 = z1;
    }
    I i = new I();
  }
}

Decompiled O:

{
  O();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  void m(int);
    descriptor: (I)V
    flags:
    Code:
      stack=6, locals=7, args_size=2
         0: ldc2_w        #2                  // double 0.2d
         3: dstore        4
         5: new           #4                  // class O$1I
         8: dup
         9: aload_0
        10: iload_1
        11: dload         4
        13: invokespecial #5                  // Method O$1I."<init>":(LO;ID)V
        16: astore        6
        18: return
      LineNumberTable:
        line 4: 0
        line 10: 5
        line 11: 18
}

Decompiled I:

{
  int y;
    descriptor: I
    flags:

  double u;
    descriptor: D
    flags:

  double u1;
    descriptor: D
    flags:

  final int val$x;
    descriptor: I
    flags: ACC_FINAL, ACC_SYNTHETIC

  final double val$z1;
    descriptor: D
    flags: ACC_FINAL, ACC_SYNTHETIC

  final O this$0;
    descriptor: LO;
    flags: ACC_FINAL, ACC_SYNTHETIC

  O$1I();
    descriptor: (LO;ID)V
    flags:
    Code:
      stack=3, locals=5, args_size=4
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LO;
         5: aload_0
         6: iload_2
         7: putfield      #2                  // Field val$x:I
        10: aload_0
        11: dload_3
        12: putfield      #3                  // Field val$z1:D
        15: aload_0
        16: invokespecial #4                  // Method java/lang/Object."<init>":()V
        19: aload_0
        20: aload_0
        21: getfield      #2                  // Field val$x:I
        24: putfield      #5                  // Field y:I
        27: aload_0
        28: ldc2_w        #6                  // double 0.1d
        31: putfield      #8                  // Field u:D
        34: aload_0
        35: aload_0
        36: getfield      #3                  // Field val$z1:D
        39: putfield      #9                  // Field u1:D
        42: return
      LineNumberTable:
        line 5: 0
        line 6: 19
        line 7: 27
        line 8: 34
    Signature: #26                          // ()V
}

Now the I is generated as O$1I, also note the difference between the final z and non-final z1.

Anonymous Class

An anonymous class is an unnamed inncer class whose instance are created in expressions and statements. It is syntactically convenient way of writing a local inner class. It can be used to add new members or methods.

Let’s see an example:

class C {}

class O {
  int m() {
    return new C(){
      int m(){
        return 1;
      }
    }.m();
  }
}

Decompiled O:

{
  O();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0

  int m();
    descriptor: ()I
    flags:
    Code:
      stack=3, locals=1, args_size=1
         0: new           #2                  // class O$1
         3: dup
         4: aload_0
         5: invokespecial #3                  // Method O$1."<init>":(LO;)V
         8: invokevirtual #4                  // Method O$1.m:()I
        11: ireturn
      LineNumberTable:
        line 5: 0
        line 9: 8
}

Decompiled anonymous class:

{
  final O this$0;
    descriptor: LO;
    flags: ACC_FINAL, ACC_SYNTHETIC

  O$1(O);
    descriptor: (LO;)V
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LO;
         5: aload_0
         6: invokespecial #2                  // Method C."<init>":()V
         9: return
      LineNumberTable:
        line 5: 0

  int m();
    descriptor: ()I
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_1
         1: ireturn
      LineNumberTable:
        line 7: 0
}

Everything should be clear enough. There also exists a special way to create an anonymous class, called double brace initialization.

The first brace creates a new anonymous inner class. The second brace creates an instance initializers like static block in Class.

class C {
  int i = 1;
}

class O {
  int m() {
    C c = new C();
    return c.i;
  }
}

Decompiled for anonymous class:

{
  final O this$0;
    descriptor: LO;
    flags: ACC_FINAL, ACC_SYNTHETIC

  O$1(O);
    descriptor: (LO;)V
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LO;
         5: aload_0
         6: invokespecial #2                  // Method C."<init>":()V
         9: aload_0
        10: iconst_2
        11: putfield      #3                  // Field i:I
        14: return
      LineNumberTable:
        line 7: 0
}

static

Now let’s talk about the keyword static that is mensioned so many times in the above.

This keyword can be used to decorate fields and methods of a Class in Java. As we see above, it can also be used to decorate nested class. And there exists static block for initialization of class instances (static fields).

At the bytecode level, all of the static initializers (static fields that are non-final or initialized to a nonconstant expression) are compiled and concatenated into one method, called clinit.

clinit is called automatically by the JVM after a class is loaded.

The initialization-on-demand holder idiom is a lazy-loaded singleton.

class A {
  private A();
  private static class H {
    private static final A INSTANCE = new A();
  }
  public static A getInstance(){
    return H.INSTANCE;
  }
}

It works, because when class A is loaded by the JVM, the class goes through initialization. Since the class does not have any static variables to initialize, the initialization completes trivially.

The static class H within A is not initialized until the JVM determines that H must be executed. The static class H is only executed when the static method getInstance is invoked on the class A, and the first time this happens the JVM will load and initialize the H class. The initialization of H class results in static variable INSTANCE being initialized.

So it is lazy and concurrent safy, as the JLS guarantees the class initialization phase is serial.

Let’s first look at the following code:

class B {
  int n0 = -1;
  int n = 1;
  int myN(){
    return n;
  }
  static int sMyN(){
    return 1;
  }
}
class C extends B {
  int n = 2;
  int myN(){
    return n;
  }
  static int sMyN(){
    return 2;
  }
}
class I {
  void m(){
    C c = new C();
    B b = c;
    int bn0 = b.n0;
    int cn0 = c.n0;
    int bn = b.n;
    int cn = c.n;
    int bn1 = b.myN();
    int cn1 = c.myN();
    int bn2 = b.sMyN();
    int cn2 = c.sMyN();
  }
}

There is a base class B, extended by class C. Let’s see the decompiled I:

{
  I();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 20: 0

  void m();
    descriptor: ()V
    flags:
    Code:
      stack=2, locals=11, args_size=1
         0: new           #2                  // class C
         3: dup
         4: invokespecial #3                  // Method C."<init>":()V
         7: astore_1
         8: aload_1
         9: astore_2
        10: aload_2
        11: getfield      #4                  // Field B.n0:I
        14: istore_3
        15: aload_1
        16: getfield      #5                  // Field C.n0:I
        19: istore        4
        21: aload_2
        22: getfield      #6                  // Field B.n:I
        25: istore        5
        27: aload_1
        28: getfield      #7                  // Field C.n:I
        31: istore        6
        33: aload_2
        34: invokevirtual #8                  // Method B.myN:()I
        37: istore        7
        39: aload_1
        40: invokevirtual #9                  // Method C.myN:()I
        43: istore        8
        45: aload_2
        46: pop
        47: invokestatic  #10                 // Method B.sMyN:()I
        50: istore        9
        52: aload_1
        53: pop
        54: invokestatic  #11                 // Method C.sMyN:()I
        57: istore        10
        59: return
      LineNumberTable:
        line 22: 0
        line 23: 8
        line 24: 10
        line 25: 15
        line 26: 21
        line 27: 27
        line 28: 33
        line 29: 39
        line 30: 45
        line 31: 52
        line 32: 59
}

We can see that the non-static method myN is virtual, which means it is dynamic binding. C overrides myN.

C inherits n0 from B, it also hides n of B. The fields are different from the methods.

And the static method sMyN is not virtual. The static methods do not belong to the objects, they belong to the class. C hides sMyN.