When you look at something like the tutorial, you may find a sentence like
When a number of objects are created from the same class blueprint, they each have their own distinct copies of instance variables.
Note that it say variables, not members. But even that is an oversimplification. It actually means, objects can have different values for the instance variables. The definitions of the variables, including annotations and their values, are invariant and copying them to the objects would lead to pointless identical copies.
Likewise, the declarations of methods, including their code, are invariant, hence, there would be no point in copying them around. But the effective behavior of methods operating on instances may be different due to the fact that they are potentially operating on different values.
The same applies to the inner classes. Their definitions are invariant and there’s no need to copy them. But a non-static inner class instance is associated with an outer instance which may have different field values, potentially leading to different behavior of the inner class’ methods.
In your example, there are no instance fields in the outer class and no methods in the inner class depending on the outer object’s state, so it makes no difference which outer instance you use to instantiate the inner class object (except for some minor impact on garbage collection).
Things change when you add state to the outer instance and dependent behavior to the inner:
public class Outer {
boolean even;
Outer(boolean even) {
this.even = even;
}
class Inner {
@Override
public String toString() {
return even? "Even": "Odd";
}
}
public static void main(String[] args) {
Inner i1 = new Outer(true).new Inner();
Inner i2 = new Outer(false).new Inner();
System.out.println(i1);
System.out.println(i2);
}
}
Here, the two Inner
instances exhibit different behavior due to the outer object used for their creation. Still, it doesn’t mean that the class definition is copied.
Starting with JDK 16, you will be able to add static members to inner classes. At this point, it becomes important that inner classes are not copied per outer instance, in other words, the static members of inner classes exist exactly one time in the runtime, just like static members of top level or nested static classes.
E.g. we will be able to change the example to
public class Outer {
boolean even;
Outer(boolean even) {
this.even = even;
}
class Inner {
static {
System.out.println("Inner initialized");
}
@Override
public String toString() {
return even? "Even": "Odd";
}
}
public static void main(String[] args) {
Inner i1 = new Outer(true).new Inner();
Inner i2 = new Outer(false).new Inner();
System.out.println(i1);
System.out.println(i2);
}
}
This will work with JDK 16 and newer and print “Inner initialized” exactly once, not twice.