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:
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:
And I is like:
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.
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:
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:
Decompiled for I:
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.
Decompiled O:
Decompiled I:
Now the I is generated as O$1I, also note the difference between the finalz and
non-finalz1.
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:
Decompiled O:
Decompiled anonymous class:
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.
Decompiled for anonymous class:
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.
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.
Something related to Inheritance
Let’s first look at the following code:
There is a base class B, extended by class C. Let’s see the decompiled I:
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.