Friday, May 8, 2015

Local classes in non-static and static methods

Java code. 
The EnclosingClass has 2 local classes (classes that are defined within a code block). The ShoppingBag class is nested in a non-static method so it has access to all the non-static fields of the enclosing class; the Broom class is defined in a static method so it only has access to all the static fields of the enclosing class. 


Compiler compiles this java file into 3 class files:

EnclosingClass.class
EnclosingClass$1ShoppingBag.class
EnclosingClass$1Broom.class

JVM has no idea that which is the enclosing class and which are the inner classes. So how does it give accesses to the enclosing class' fields to the 2 local classes?

Answer: the compiler creates public static methods inside the EnclosingClass for you automatically:


static int access$0(EnclosingClass);
static java.lang.String access$1(EnclosingClass);
static int access$2();

The Enclosingclass instance is passed in to the local class inside non-static method as a parameter of the constructor and is stored as a field in the EnclosingClass$1ShoppingBag.class

 final EnclosingClass this$0;
    flags: ACC_FINAL, ACC_SYNTHETIC

So the local class inside non-static method accesses the enclosing class' fields by calling access$n methods and passing in the EnclosingClass instance as a parameter.

However, local class inside static method doesn't store the enclosing class instance as a field and notice that the access$2() method doesn't require an enclosing class instance as the parameter, because local class in a static method can only access the static fields of the enclosing class.

Notice that on bytecode level: static methods can access non-static fields.

Now let's look at the bytecode. 

---------------------EnclosingClass$1ShoppingBag.class---------------

class EnclosingClass$1ShoppingBag
  SourceFile: "EnclosingClass.java"
  EnclosingMethod: #20.#40                // EnclosingClass.doShopping
  InnerClasses:
       #43= #1; //ShoppingBag=class EnclosingClass$1ShoppingBag
  minor version: 0
  major version: 51
  flags: ACC_SUPER
Constant pool:
   #1 = Class              #2             //  EnclosingClass$1ShoppingBag
   #2 = Utf8               EnclosingClass$1ShoppingBag
   #3 = Class              #4             //  java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               numOfBags
   #6 = Utf8               I
   #7 = Utf8               msg
   #8 = Utf8               Ljava/lang/String;
   #9 = Utf8               this$0
  #10 = Utf8               LEnclosingClass;
  #11 = Utf8               <init>
  #12 = Utf8               (LEnclosingClass;)V
  #13 = Utf8               Code
  #14 = Fieldref           #1.#15         //  EnclosingClass$1ShoppingBag.this$0:LEnclosingClass;
  #15 = NameAndType        #9:#10         //  this$0:LEnclosingClass;
  #16 = Methodref          #3.#17         //  java/lang/Object."<init>":()V
  #17 = NameAndType        #11:#18        //  "<init>":()V
  #18 = Utf8               ()V
  #19 = Methodref          #20.#22        //  EnclosingClass.access$0:(LEnclosingClass;)I
  #20 = Class              #21            //  EnclosingClass
  #21 = Utf8               EnclosingClass
  #22 = NameAndType        #23:#24        //  access$0:(LEnclosingClass;)I
  #23 = Utf8               access$0
  #24 = Utf8               (LEnclosingClass;)I
  #25 = Fieldref           #1.#26         //  EnclosingClass$1ShoppingBag.numOfBags:I
  #26 = NameAndType        #5:#6          //  numOfBags:I
  #27 = Methodref          #20.#28        //  EnclosingClass.access$1:(LEnclosingClass;)Ljava/lang/String;
  #28 = NameAndType        #29:#30        //  access$1:(LEnclosingClass;)Ljava/lang/String;
  #29 = Utf8               access$1
  #30 = Utf8               (LEnclosingClass;)Ljava/lang/String;
  #31 = Fieldref           #1.#32         //  EnclosingClass$1ShoppingBag.msg:Ljava/lang/String;
  #32 = NameAndType        #7:#8          //  msg:Ljava/lang/String;
  #33 = Utf8               LineNumberTable
  #34 = Utf8               LocalVariableTable
  #35 = Utf8               this
  #36 = Utf8               LEnclosingClass$1ShoppingBag;
  #37 = Utf8               SourceFile
  #38 = Utf8               EnclosingClass.java
  #39 = Utf8               EnclosingMethod
  #40 = NameAndType        #41:#18        //  doShopping:()V
  #41 = Utf8               doShopping
  #42 = Utf8               InnerClasses
  #43 = Utf8               ShoppingBag
{
  int numOfBags;
    flags: 

  java.lang.String msg;
    flags: 

  final EnclosingClass this$0;
    flags: ACC_FINAL, ACC_SYNTHETIC

  EnclosingClass$1ShoppingBag(EnclosingClass);
    flags: 
    Code:
      stack=3, locals=2, args_size=2
         0: aload_0       
         1: aload_1       
         2: putfield      #14                 // Field this$0:LEnclosingClass;
         5: aload_0       
         6: invokespecial #16                 // Method java/lang/Object."<init>":()V
         9: aload_0       
        10: iconst_3      
        11: aload_1       
        12: invokestatic  #19                 // Method EnclosingClass.access$0:(LEnclosingClass;)I
        15: iadd          
        16: putfield      #25                 // Field numOfBags:I
        19: aload_0       
        20: aload_1       
        21: invokestatic  #27                 // Method EnclosingClass.access$1:(LEnclosingClass;)Ljava/lang/String;
        24: putfield      #31                 // Field msg:Ljava/lang/String;
        27: return        
      LineNumberTable:
        line 11: 0
        line 12: 9
        line 13: 19
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      28     0  this   LEnclosingClass$1ShoppingBag;
}

----------------------EnclosingClass$1Broom------------------------

class EnclosingClass$1Broom
  SourceFile: "EnclosingClass.java"
  EnclosingMethod: #20.#42                // EnclosingClass.cleanHouse
  InnerClasses:
       #45= #1; //Broom=class EnclosingClass$1Broom
  minor version: 0
  major version: 51
  flags: ACC_SUPER
Constant pool:
   #1 = Class              #2             //  EnclosingClass$1Broom
   #2 = Utf8               EnclosingClass$1Broom
   #3 = Class              #4             //  java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               msg
   #6 = Utf8               Ljava/lang/String;
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Methodref          #3.#11         //  java/lang/Object."<init>":()V
  #11 = NameAndType        #7:#8          //  "<init>":()V
  #12 = Class              #13            //  java/lang/StringBuilder
  #13 = Utf8               java/lang/StringBuilder
  #14 = String             #15            //  Take the broom and start cleaning.
  #15 = Utf8               Take the broom and start cleaning.
  #16 = Methodref          #12.#17        //  java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  #17 = NameAndType        #7:#18         //  "<init>":(Ljava/lang/String;)V
  #18 = Utf8               (Ljava/lang/String;)V
  #19 = Methodref          #20.#22        //  EnclosingClass.access$2:()I
  #20 = Class              #21            //  EnclosingClass
  #21 = Utf8               EnclosingClass
  #22 = NameAndType        #23:#24        //  access$2:()I
  #23 = Utf8               access$2
  #24 = Utf8               ()I
  #25 = Methodref          #12.#26        //  java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  #26 = NameAndType        #27:#28        //  append:(I)Ljava/lang/StringBuilder;
  #27 = Utf8               append
  #28 = Utf8               (I)Ljava/lang/StringBuilder;
  #29 = Methodref          #12.#30        //  java/lang/StringBuilder.toString:()Ljava/lang/String;
  #30 = NameAndType        #31:#32        //  toString:()Ljava/lang/String;
  #31 = Utf8               toString
  #32 = Utf8               ()Ljava/lang/String;
  #33 = Fieldref           #1.#34         //  EnclosingClass$1Broom.msg:Ljava/lang/String;
  #34 = NameAndType        #5:#6          //  msg:Ljava/lang/String;
  #35 = Utf8               LineNumberTable
  #36 = Utf8               LocalVariableTable
  #37 = Utf8               this
  #38 = Utf8               LEnclosingClass$1Broom;
  #39 = Utf8               SourceFile
  #40 = Utf8               EnclosingClass.java
  #41 = Utf8               EnclosingMethod
  #42 = NameAndType        #43:#8         //  cleanHouse:()V
  #43 = Utf8               cleanHouse
  #44 = Utf8               InnerClasses
  #45 = Utf8               Broom
{
  java.lang.String msg;
    flags: 

  EnclosingClass$1Broom();
    flags: 
    Code:
      stack=4, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #10                 // Method java/lang/Object."<init>":()V
         4: aload_0       
         5: new           #12                 // class java/lang/StringBuilder
         8: dup           
         9: ldc           #14                 // String Take the broom and start cleaning.
        11: invokespecial #16                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        14: invokestatic  #19                 // Method EnclosingClass.access$2:()I
        17: invokevirtual #25                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        20: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        23: putfield      #33                 // Field msg:Ljava/lang/String;
        26: return        
      LineNumberTable:
        line 22: 0
        line 23: 4
        line 22: 26
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      27     0  this   LEnclosingClass$1Broom;
}


----------------------EnclosingClass.class--------------------------------
public class EnclosingClass
  SourceFile: "EnclosingClass.java"
  InnerClasses:
       #45= #43; //Broom=class EnclosingClass$1Broom
       #48= #46; //ShoppingBag=class EnclosingClass$1ShoppingBag
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             //  EnclosingClass
   #2 = Utf8               EnclosingClass
   #3 = Class              #4             //  java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               numOfTree
   #6 = Utf8               I
   #7 = Utf8               count
   #8 = Utf8               good
   #9 = Utf8               Ljava/lang/String;
  #10 = Utf8               <clinit>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Fieldref           #1.#14         //  EnclosingClass.count:I
  #14 = NameAndType        #7:#6          //  count:I
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               <init>
  #18 = Methodref          #3.#19         //  java/lang/Object."<init>":()V
  #19 = NameAndType        #17:#11        //  "<init>":()V
  #20 = Fieldref           #1.#21         //  EnclosingClass.numOfTree:I
  #21 = NameAndType        #5:#6          //  numOfTree:I
  #22 = String             #23            //  Das ist gut
  #23 = Utf8               Das ist gut
  #24 = Fieldref           #1.#25         //  EnclosingClass.good:Ljava/lang/String;
  #25 = NameAndType        #8:#9          //  good:Ljava/lang/String;
  #26 = Utf8               this
  #27 = Utf8               LEnclosingClass;
  #28 = Utf8               doShopping
  #29 = Utf8               num
  #30 = Utf8               cleanHouse
  #31 = String             #32            //  Take the broom and start cleaning.
  #32 = Utf8               Take the broom and start cleaning.
  #33 = Utf8               message
  #34 = Utf8               access$0
  #35 = Utf8               (LEnclosingClass;)I
  #36 = Utf8               access$1
  #37 = Utf8               (LEnclosingClass;)Ljava/lang/String;
  #38 = Utf8               access$2
  #39 = Utf8               ()I
  #40 = Utf8               SourceFile
  #41 = Utf8               EnclosingClass.java
  #42 = Utf8               InnerClasses
  #43 = Class              #44            //  EnclosingClass$1Broom
  #44 = Utf8               EnclosingClass$1Broom
  #45 = Utf8               Broom
  #46 = Class              #47            //  EnclosingClass$1ShoppingBag
  #47 = Utf8               EnclosingClass$1ShoppingBag
  #48 = Utf8               ShoppingBag
{
  private int numOfTree;
    flags: ACC_PRIVATE

  private static int count;
    flags: ACC_PRIVATE, ACC_STATIC

  private java.lang.String good;
    flags: ACC_PRIVATE

  static {};
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        20
         2: putstatic     #13                 // Field count:I
         5: return        
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  public EnclosingClass();
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #18                 // Method java/lang/Object."<init>":()V
         4: aload_0       
         5: bipush        10
         7: putfield      #20                 // Field numOfTree:I
        10: aload_0       
        11: ldc           #22                 // String Das ist gut
        13: putfield      #24                 // Field good:Ljava/lang/String;
        16: return        
      LineNumberTable:
        line 2: 0
        line 4: 4
        line 6: 10
        line 2: 16
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      17     0  this   LEnclosingClass;

  public void doShopping();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: iconst_3      
         1: istore_1      
         2: return        
      LineNumberTable:
        line 9: 0
        line 15: 2
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       3     0  this   LEnclosingClass;
               2       1     1   num   I

  public static void cleanHouse();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=0
         0: ldc           #31                 // String Take the broom and start cleaning.
         2: astore_0      
         3: return        
      LineNumberTable:
        line 20: 0
        line 25: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               3       1     0 message   Ljava/lang/String;

  static int access$0(EnclosingClass);
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: getfield      #20                 // Field numOfTree:I
         4: ireturn       
      LineNumberTable:
        line 4: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  static java.lang.String access$1(EnclosingClass);
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: getfield      #24                 // Field good:Ljava/lang/String;
         4: areturn       
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  static int access$2();
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #13                 // Field count:I
         3: ireturn       
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

}

No comments:

Post a Comment