zoukankan      html  css  js  c++  java
  • Java魔法堂:内部类详解

    一、前言                              

      对于内部类平时编码时使用的场景不多,比较常用的地方应该就是绑定事件处理程序的时候了(从C#、JS转向Java阵营的孩子总不不习惯用匿名内部类来做事件订阅:()。本文将结合Bytecode对四种内部类作介绍,当作一次梳理以便日后查阅。

      首先要明确的是内部类是编译器提供的特性,编译器会将含内部类的java文件编译成外部类和内部类的N个文件(N>=2) ,然后JVM就按普通类的方式运行。就如下面的源码会被编译为Outer.class和和Outer$Inner.class文件。

    class Outer{
      class Inner{}
    }

    三、成员内部类                        

      定义在一个类的内部。相对外部类仅有默认和public两种访问修饰符而言,成员内部类可有默认、private、proteced和public四种访问修饰符,效果与成员字段和方法的一样。

      示例:

    import java.io.*;
    // Main.java文件
    class Main{
        public static void main(String[] args) throws IOException{
            MemberCls outer = new MemberCls();
            Inner inner1 = outer.new Inner();
            Inner inner2 = outer.getInner();
    
            System.out.println(inner1.getVal());
            System.out.println(inner2.getVal());
            inner1.setVal(2);
            System.out.println(inner1.getVal());
            System.out.println(inner2.getVal());
            inner2.setVal(3);
            System.out.println(inner1.getVal());
            System.out.println(inner2.getVal());
    
            System.in.read();
        }
    }
    // MemberCls.java文件
    class MemberCls{
        private int val = 1;
    
        class Inner{
            void setVal(int val){
                MemberCls.this.val = val;
            }    
            int getVal(){
                return val;
            }
        }
    
        Inner getInner(){
            return new Inner();
        }
    // 运行结果
    // 1
    // 1
    // 2
    // 2
    // 3
    // 3

      并生成MemberCls.class和MemberCls$Inner.class两个类文件。

    Classfile /F:/skyDrive/repos/self/jottings/java/sample/01/MemberCls.class
      Last modified 2015-2-3; size 1117 bytes
      MD5 checksum aea71084f78ab319a339717e4d0e1e79
      Compiled from "MemberCls.java"
    class MemberCls
      SourceFile: "MemberCls.java"
      InnerClasses:
           #16= #3 of #5; //Inner=class MemberCls$Inner of class MemberCls
      minor version: 0
      major version: 51
      flags: ACC_SUPER
    
    Constant pool:
       #1 = Fieldref           #5.#36         //  MemberCls.val:I
       #2 = Methodref          #15.#37        //  java/lang/Object."<init>":()V
       #3 = Class              #38            //  MemberCls$Inner
       #4 = Methodref          #3.#39         //  MemberCls$Inner."<init>":(LMemberCls;)V
       #5 = Class              #40            //  MemberCls
       #6 = Methodref          #5.#37         //  MemberCls."<init>":()V
       #7 = Methodref          #15.#41        //  java/lang/Object.getClass:()Ljava/lang/Class;
       #8 = Methodref          #5.#42         //  MemberCls.getInner:()LMemberCls$Inner;
       #9 = Fieldref           #43.#44        //  java/lang/System.out:Ljava/io/PrintStream;
      #10 = Methodref          #3.#45         //  MemberCls$Inner.getVal:()I
      #11 = Methodref          #46.#47        //  java/io/PrintStream.println:(I)V
      #12 = Methodref          #3.#48         //  MemberCls$Inner.setVal:(I)V
      #13 = Fieldref           #43.#49        //  java/lang/System.in:Ljava/io/InputStream;
      #14 = Methodref          #50.#51        //  java/io/InputStream.read:()I
      #15 = Class              #52            //  java/lang/Object
      #16 = Utf8               Inner
      #17 = Utf8               InnerClasses
      #18 = Utf8               val
      #19 = Utf8               I
      #20 = Utf8               <init>
      #21 = Utf8               ()V
      #22 = Utf8               Code
      #23 = Utf8               LineNumberTable
      #24 = Utf8               getInner
      #25 = Utf8               ()LMemberCls$Inner;
      #26 = Utf8               main
      #27 = Utf8               ([Ljava/lang/String;)V
      #28 = Utf8               Exceptions
      #29 = Class              #53            //  java/io/IOException
      #30 = Utf8               access$002
      #31 = Utf8               (LMemberCls;I)I
      #32 = Utf8               access$000
      #33 = Utf8               (LMemberCls;)I
      #34 = Utf8               SourceFile
      #35 = Utf8               MemberCls.java
      #36 = NameAndType        #18:#19        //  val:I
      #37 = NameAndType        #20:#21        //  "<init>":()V
      #38 = Utf8               MemberCls$Inner
      #39 = NameAndType        #20:#54        //  "<init>":(LMemberCls;)V
      #40 = Utf8               MemberCls
      #41 = NameAndType        #55:#56        //  getClass:()Ljava/lang/Class;
      #42 = NameAndType        #24:#25        //  getInner:()LMemberCls$Inner;
      #43 = Class              #57            //  java/lang/System
      #44 = NameAndType        #58:#59        //  out:Ljava/io/PrintStream;
      #45 = NameAndType        #60:#61        //  getVal:()I
      #46 = Class              #62            //  java/io/PrintStream
      #47 = NameAndType        #63:#64        //  println:(I)V
      #48 = NameAndType        #65:#64        //  setVal:(I)V
      #49 = NameAndType        #66:#67        //  in:Ljava/io/InputStream;
      #50 = Class              #68            //  java/io/InputStream
      #51 = NameAndType        #69:#61        //  read:()I
      #52 = Utf8               java/lang/Object
      #53 = Utf8               java/io/IOException
      #54 = Utf8               (LMemberCls;)V
      #55 = Utf8               getClass
      #56 = Utf8               ()Ljava/lang/Class;
      #57 = Utf8               java/lang/System
      #58 = Utf8               out
      #59 = Utf8               Ljava/io/PrintStream;
      #60 = Utf8               getVal
      #61 = Utf8               ()I
      #62 = Utf8               java/io/PrintStream
      #63 = Utf8               println
      #64 = Utf8               (I)V
      #65 = Utf8               setVal
      #66 = Utf8               in
      #67 = Utf8               Ljava/io/InputStream;
      #68 = Utf8               java/io/InputStream
      #69 = Utf8               read
    {
      MemberCls();
        flags: 
    
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0       
             1: invokespecial #2                  // Method java/lang/Object."<init>":()V
             4: aload_0       
             5: iconst_1      
             6: putfield      #1                  // Field val:I
             9: return        
          LineNumberTable:
            line 2: 0
            line 3: 4
            line 5: 9
    
      MemberCls$Inner getInner();
        flags: 
    
        Code:
          stack=3, locals=1, args_size=1
             0: new           #3                  // class MemberCls$Inner
             3: dup           
             4: aload_0       
             5: invokespecial #4                  // Method MemberCls$Inner."<init>":(LMemberCls;)V
             8: areturn       
          LineNumberTable:
            line 15: 0
    
      public static void main(java.lang.String[]) throws java.io.IOException;
        flags: ACC_PUBLIC, ACC_STATIC
    
        Code:
          stack=4, locals=4, args_size=1
             0: new           #5                  // class MemberCls
             3: dup           
             4: invokespecial #6                  // Method "<init>":()V
             7: astore_1      
             8: new           #3                  // class MemberCls$Inner
            11: dup           
            12: aload_1       
            13: dup           
            14: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
            17: pop           
            18: invokespecial #4                  // Method MemberCls$Inner."<init>":(LMemberCls;)V
            21: astore_2      
            22: aload_1       
            23: invokevirtual #8                  // Method getInner:()LMemberCls$Inner;
            26: astore_3      
            27: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
            30: aload_2       
            31: invokevirtual #10                 // Method MemberCls$Inner.getVal:()I
            34: invokevirtual #11                 // Method java/io/PrintStream.println:(I)V
            37: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
            40: aload_3       
            41: invokevirtual #10                 // Method MemberCls$Inner.getVal:()I
            44: invokevirtual #11                 // Method java/io/PrintStream.println:(I)V
            47: aload_2       
            48: iconst_2      
            49: invokevirtual #12                 // Method MemberCls$Inner.setVal:(I)V
            52: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
            55: aload_2       
            56: invokevirtual #10                 // Method MemberCls$Inner.getVal:()I
            59: invokevirtual #11                 // Method java/io/PrintStream.println:(I)V
            62: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
            65: aload_3       
            66: invokevirtual #10                 // Method MemberCls$Inner.getVal:()I
            69: invokevirtual #11                 // Method java/io/PrintStream.println:(I)V
            72: aload_3       
            73: iconst_3      
            74: invokevirtual #12                 // Method MemberCls$Inner.setVal:(I)V
            77: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
            80: aload_2       
            81: invokevirtual #10                 // Method MemberCls$Inner.getVal:()I
            84: invokevirtual #11                 // Method java/io/PrintStream.println:(I)V
            87: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
            90: aload_3       
            91: invokevirtual #10                 // Method MemberCls$Inner.getVal:()I
            94: invokevirtual #11                 // Method java/io/PrintStream.println:(I)V
            97: getstatic     #13                 // Field java/lang/System.in:Ljava/io/InputStream;
           100: invokevirtual #14                 // Method java/io/InputStream.read:()I
           103: pop           
           104: return        
          LineNumberTable:
            line 19: 0
            line 20: 8
            line 21: 22
            line 23: 27
            line 24: 37
            line 25: 47
            line 26: 52
            line 27: 62
            line 28: 72
            line 29: 77
            line 30: 87
            line 32: 97
            line 33: 104
        Exceptions:
          throws java.io.IOException
    
      static int access$002(MemberCls, int);
        flags: ACC_STATIC, ACC_SYNTHETIC
    
        Code:
          stack=3, locals=2, args_size=2
             0: aload_0       
             1: iload_1       
             2: dup_x1        
             3: putfield      #1                  // Field val:I
             6: ireturn       
          LineNumberTable:
            line 2: 0
    
      static int access$000(MemberCls);
        flags: ACC_STATIC, ACC_SYNTHETIC
    
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0       
             1: getfield      #1                  // Field val:I
             4: ireturn       
          LineNumberTable:
            line 2: 0
    }
    MemberCls.class
    Classfile /F:/skyDrive/repos/self/jottings/java/sample/01/MemberCls$Inner.class
      Last modified 2015-2-3; size 525 bytes
      MD5 checksum b092ffe3c5b358c786d99d98c104dc40
      Compiled from "MemberCls.java"
    class MemberCls$Inner
      SourceFile: "MemberCls.java"
      InnerClasses:
           #25= #5 of #21; //Inner=class MemberCls$Inner of class MemberCls
      minor version: 0
      major version: 51
      flags: ACC_SUPER
    
    Constant pool:
       #1 = Fieldref           #5.#19         //  MemberCls$Inner.this$0:LMemberCls;
       #2 = Methodref          #6.#20         //  java/lang/Object."<init>":()V
       #3 = Methodref          #21.#22        //  MemberCls.access$002:(LMemberCls;I)I
       #4 = Methodref          #21.#23        //  MemberCls.access$000:(LMemberCls;)I
       #5 = Class              #24            //  MemberCls$Inner
       #6 = Class              #27            //  java/lang/Object
       #7 = Utf8               this$0
       #8 = Utf8               LMemberCls;
       #9 = Utf8               <init>
      #10 = Utf8               (LMemberCls;)V
      #11 = Utf8               Code
      #12 = Utf8               LineNumberTable
      #13 = Utf8               setVal
      #14 = Utf8               (I)V
      #15 = Utf8               getVal
      #16 = Utf8               ()I
      #17 = Utf8               SourceFile
      #18 = Utf8               MemberCls.java
      #19 = NameAndType        #7:#8          //  this$0:LMemberCls;
      #20 = NameAndType        #9:#28         //  "<init>":()V
      #21 = Class              #29            //  MemberCls
      #22 = NameAndType        #30:#31        //  access$002:(LMemberCls;I)I
      #23 = NameAndType        #32:#33        //  access$000:(LMemberCls;)I
      #24 = Utf8               MemberCls$Inner
      #25 = Utf8               Inner
      #26 = Utf8               InnerClasses
      #27 = Utf8               java/lang/Object
      #28 = Utf8               ()V
      #29 = Utf8               MemberCls
      #30 = Utf8               access$002
      #31 = Utf8               (LMemberCls;I)I
      #32 = Utf8               access$000
      #33 = Utf8               (LMemberCls;)I
    {
      final MemberCls this$0;
        flags: ACC_FINAL, ACC_SYNTHETIC
    
    
    
      MemberCls$Inner(MemberCls);
        flags: 
    
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0       
             1: aload_1       
             2: putfield      #1                  // Field this$0:LMemberCls;
             5: aload_0       
             6: invokespecial #2                  // Method java/lang/Object."<init>":()V
             9: return        
          LineNumberTable:
            line 5: 0
    
      void setVal(int);
        flags: 
    
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0       
             1: getfield      #1                  // Field this$0:LMemberCls;
             4: iload_1       
             5: invokestatic  #3                  // Method MemberCls.access$002:(LMemberCls;I)I
             8: pop           
             9: return        
          LineNumberTable:
            line 7: 0
            line 8: 9
    
      int getVal();
        flags: 
    
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0       
             1: getfield      #1                  // Field this$0:LMemberCls;
             4: invokestatic  #4                  // Method MemberCls.access$000:(LMemberCls;)I
             7: ireturn       
          LineNumberTable:
            line 10: 0
    }
    MemberCls$Inner.class

      由于成员内部类依赖于外部类实例,因此创建内部类实例时要先创建外部类实例,然后通过下列两种形式来创建内部类实例:

    // 方式一
    内部类 内部类实例 = 外部类实例.new 内部类();
    
    // 方式二
    外部类{
      内部类{}
      内部类 get内部类(){
        return new 内部类();
      }
    }
    内部类 内部类实例 = 外部类实例.get内部类();

      注意:
        1. 当成员内部类拥有与外部类同名的成员变量或方法时,默认是使用成员内部类的成员。若要访问外部类的同名成员,则需要进行如下操作:

    外部类.this.成员变量;
    外部类.this.成员方法;

        2. 对于同一个外部类实例创建的内部类实例,这些内部类实例均操作同一个外部实例。像上述例子那样,均操作同一个val字段。
            看Bytecodes可知,编译器自动为MemberCls创建创建两个静态方法access$002和access$000,而MemberCls$Inner实例则通过这两个静态方法访问私有私有字段val的。

    // MemberCls.class文件
    
    /** 等价于
     * static int setVal(MemberCls outer, int val){
     *     outer.val = val;
     *      return val;
     * }
     */
    static int access$002(MemberCls, int);
        flags: ACC_STATIC, ACC_SYNTHETIC
        Code:
          stack=3, locals=2, args_size=2
             0: aload_0       
             1: iload_1       
             2: dup_x1        
             3: putfield      #1                  // Field val:I
             6: ireturn       
          LineNumberTable:
            line 2: 0
    
    /** 等价于
     * static int getVal(MemberCls outer){
     *      return outer.val;
     * }
     */
      static int access$000(MemberCls);
        flags: ACC_STATIC, ACC_SYNTHETIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0       
             1: getfield      #1                  // Field val:I
             4: ireturn       
          LineNumberTable:
            line 2: 
    // MemberCls$Inner.class文件
    
    /** 等价于
     * void setVal(int val){
     *     MemberCls实例.setVal(val);
     * }
     */
    void setVal(int);
        flags:
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0       
             1: getfield      #1                  // Field this$0:LMemberCls;
             4: iload_1       
             5: invokestatic  #3                  // Method MemberCls.access$002:(LMemberCls;I)I
             8: pop           
             9: return        
          LineNumberTable:
            line 7: 0
            line 8: 9
    
    /** 等价于
     * int getVal(int val){
     *     MemberCls实例.getVal();
     * }
     */
      int getVal();
        flags:
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0       
             1: getfield      #1                  // Field this$0:LMemberCls;
             4: invokestatic  #4                  // Method MemberCls.access$000:(LMemberCls;)I
             7: ireturn       
          LineNumberTable:
            line 10: 0

      因此内部类可以访问外部类的所有类型的字段和方法(包括private)。

    四、局部内部类                           

      局部内部类定义在方法或某个作用域里面,并且仅限于方法和该作用域内访问。

      示例:

    import java.io.*;
    
    class Main{
        public static void main(String[] args) throws IOException{
            LocalCls outer = new LocalCls();
            outer.print();
    
            System.in.read();
        }
    }
    
    class LocalCls{
        private int val = 1;
    
        void print(){
            final String name = "fsjohnhuang";
            class Inner{
                int getVal(){
                    return val;
                }    
                void setVal(int val){
                    LocalCls.this.val = val;
                }
                String getName(){
                    return name;
                }
            }
    
            Inner inner = new Inner();
            System.out.println(inner.getVal());
            inner.setVal(2);
            System.out.println(inner.getVal());
            System.out.println(inner.getName());
        }
    }
    // 结果:
    // 1
    // 2
    // fsjohnhuang

      生成LocalCls.class和LocalCls$1Inner.class两个类文件。

    Classfile /F:/skyDrive/repos/self/jottings/java/sample/02/LocalCls.class
      Last modified 2015-2-3; size 1052 bytes
      MD5 checksum a636f470da37d8c1cb9370dde083d6d8
      Compiled from "LocalCls.java"
    class LocalCls
      SourceFile: "LocalCls.java"
      InnerClasses:
           #17= #8; //Inner=class LocalCls$1Inner
      minor version: 0
      major version: 51
      flags: ACC_SUPER
    
    Constant pool:
       #1 = Fieldref           #3.#36         //  LocalCls.val:I
       #2 = Methodref          #16.#37        //  java/lang/Object."<init>":()V
       #3 = Class              #38            //  LocalCls
       #4 = Methodref          #3.#37         //  LocalCls."<init>":()V
       #5 = Methodref          #3.#39         //  LocalCls.print:()V
       #6 = Fieldref           #40.#41        //  java/lang/System.in:Ljava/io/InputStream;
       #7 = Methodref          #42.#43        //  java/io/InputStream.read:()I
       #8 = Class              #44            //  LocalCls$1Inner
       #9 = Methodref          #8.#45         //  LocalCls$1Inner."<init>":(LLocalCls;)V
      #10 = Fieldref           #40.#46        //  java/lang/System.out:Ljava/io/PrintStream;
      #11 = Methodref          #8.#47         //  LocalCls$1Inner.getVal:()I
      #12 = Methodref          #48.#49        //  java/io/PrintStream.println:(I)V
      #13 = Methodref          #8.#50         //  LocalCls$1Inner.setVal:(I)V
      #14 = Methodref          #8.#51         //  LocalCls$1Inner.getName:()Ljava/lang/String;
      #15 = Methodref          #48.#52        //  java/io/PrintStream.println:(Ljava/lang/String;)V
      #16 = Class              #53            //  java/lang/Object
      #17 = Utf8               Inner
      #18 = Utf8               InnerClasses
      #19 = Utf8               val
      #20 = Utf8               I
      #21 = Utf8               <init>
      #22 = Utf8               ()V
      #23 = Utf8               Code
      #24 = Utf8               LineNumberTable
      #25 = Utf8               main
      #26 = Utf8               ([Ljava/lang/String;)V
      #27 = Utf8               Exceptions
      #28 = Class              #54            //  java/io/IOException
      #29 = Utf8               print
      #30 = Utf8               access$000
      #31 = Utf8               (LLocalCls;)I
      #32 = Utf8               access$002
      #33 = Utf8               (LLocalCls;I)I
      #34 = Utf8               SourceFile
      #35 = Utf8               LocalCls.java
      #36 = NameAndType        #19:#20        //  val:I
      #37 = NameAndType        #21:#22        //  "<init>":()V
      #38 = Utf8               LocalCls
      #39 = NameAndType        #29:#22        //  print:()V
      #40 = Class              #55            //  java/lang/System
      #41 = NameAndType        #56:#57        //  in:Ljava/io/InputStream;
      #42 = Class              #58            //  java/io/InputStream
      #43 = NameAndType        #59:#60        //  read:()I
      #44 = Utf8               LocalCls$1Inner
      #45 = NameAndType        #21:#61        //  "<init>":(LLocalCls;)V
      #46 = NameAndType        #62:#63        //  out:Ljava/io/PrintStream;
      #47 = NameAndType        #64:#60        //  getVal:()I
      #48 = Class              #65            //  java/io/PrintStream
      #49 = NameAndType        #66:#67        //  println:(I)V
      #50 = NameAndType        #68:#67        //  setVal:(I)V
      #51 = NameAndType        #69:#70        //  getName:()Ljava/lang/String;
      #52 = NameAndType        #66:#71        //  println:(Ljava/lang/String;)V
      #53 = Utf8               java/lang/Object
      #54 = Utf8               java/io/IOException
      #55 = Utf8               java/lang/System
      #56 = Utf8               in
      #57 = Utf8               Ljava/io/InputStream;
      #58 = Utf8               java/io/InputStream
      #59 = Utf8               read
      #60 = Utf8               ()I
      #61 = Utf8               (LLocalCls;)V
      #62 = Utf8               out
      #63 = Utf8               Ljava/io/PrintStream;
      #64 = Utf8               getVal
      #65 = Utf8               java/io/PrintStream
      #66 = Utf8               println
      #67 = Utf8               (I)V
      #68 = Utf8               setVal
      #69 = Utf8               getName
      #70 = Utf8               ()Ljava/lang/String;
      #71 = Utf8               (Ljava/lang/String;)V
    {
      LocalCls();
        flags: 
    
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0       
             1: invokespecial #2                  // Method java/lang/Object."<init>":()V
             4: aload_0       
             5: iconst_1      
             6: putfield      #1                  // Field val:I
             9: return        
          LineNumberTable:
            line 3: 0
            line 11: 4
    
      public static void main(java.lang.String[]) throws java.io.IOException;
        flags: ACC_PUBLIC, ACC_STATIC
    
        Code:
          stack=2, locals=2, args_size=1
             0: new           #3                  // class LocalCls
             3: dup           
             4: invokespecial #4                  // Method "<init>":()V
             7: astore_1      
             8: aload_1       
             9: invokevirtual #5                  // Method print:()V
            12: getstatic     #6                  // Field java/lang/System.in:Ljava/io/InputStream;
            15: invokevirtual #7                  // Method java/io/InputStream.read:()I
            18: pop           
            19: return        
          LineNumberTable:
            line 5: 0
            line 6: 8
            line 8: 12
            line 9: 19
        Exceptions:
          throws java.io.IOException
    
      void print();
        flags: 
    
        Code:
          stack=3, locals=3, args_size=1
             0: new           #8                  // class LocalCls$1Inner
             3: dup           
             4: aload_0       
             5: invokespecial #9                  // Method LocalCls$1Inner."<init>":(LLocalCls;)V
             8: astore_2      
             9: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
            12: aload_2       
            13: invokevirtual #11                 // Method LocalCls$1Inner.getVal:()I
            16: invokevirtual #12                 // Method java/io/PrintStream.println:(I)V
            19: aload_2       
            20: iconst_2      
            21: invokevirtual #13                 // Method LocalCls$1Inner.setVal:(I)V
            24: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
            27: aload_2       
            28: invokevirtual #11                 // Method LocalCls$1Inner.getVal:()I
            31: invokevirtual #12                 // Method java/io/PrintStream.println:(I)V
            34: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
            37: aload_2       
            38: invokevirtual #14                 // Method LocalCls$1Inner.getName:()Ljava/lang/String;
            41: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            44: return        
          LineNumberTable:
            line 26: 0
            line 27: 9
            line 28: 19
            line 29: 24
            line 30: 34
            line 31: 44
    
      static int access$000(LocalCls);
        flags: ACC_STATIC, ACC_SYNTHETIC
    
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0       
             1: getfield      #1                  // Field val:I
             4: ireturn       
          LineNumberTable:
            line 3: 0
    
      static int access$002(LocalCls, int);
        flags: ACC_STATIC, ACC_SYNTHETIC
    
        Code:
          stack=3, locals=2, args_size=2
             0: aload_0       
             1: iload_1       
             2: dup_x1        
             3: putfield      #1                  // Field val:I
             6: ireturn       
          LineNumberTable:
            line 3: 0
    }
    LocalCls.class
    Classfile /F:/skyDrive/repos/self/jottings/java/sample/02/LocalCls$1Inner.class
      Last modified 2015-2-3; size 651 bytes
      MD5 checksum a4bf7c12f15f22b2ebb3f79438a555ab
      Compiled from "LocalCls.java"
    class LocalCls$1Inner
      SourceFile: "LocalCls.java"
      EnclosingMethod: #23.#24                // LocalCls.print
      InnerClasses:
           #31= #6; //Inner=class LocalCls$1Inner
      minor version: 0
      major version: 51
      flags: ACC_SUPER
    
    Constant pool:
       #1 = Fieldref           #6.#25         //  LocalCls$1Inner.this$0:LLocalCls;
       #2 = Methodref          #7.#26         //  java/lang/Object."<init>":()V
       #3 = Methodref          #23.#27        //  LocalCls.access$000:(LLocalCls;)I
       #4 = Methodref          #23.#28        //  LocalCls.access$002:(LLocalCls;I)I
       #5 = String             #29            //  fsjohnhuang
       #6 = Class              #30            //  LocalCls$1Inner
       #7 = Class              #33            //  java/lang/Object
       #8 = Utf8               this$0
       #9 = Utf8               LLocalCls;
      #10 = Utf8               <init>
      #11 = Utf8               (LLocalCls;)V
      #12 = Utf8               Code
      #13 = Utf8               LineNumberTable
      #14 = Utf8               getVal
      #15 = Utf8               ()I
      #16 = Utf8               setVal
      #17 = Utf8               (I)V
      #18 = Utf8               getName
      #19 = Utf8               ()Ljava/lang/String;
      #20 = Utf8               SourceFile
      #21 = Utf8               LocalCls.java
      #22 = Utf8               EnclosingMethod
      #23 = Class              #34            //  LocalCls
      #24 = NameAndType        #35:#36        //  print:()V
      #25 = NameAndType        #8:#9          //  this$0:LLocalCls;
      #26 = NameAndType        #10:#36        //  "<init>":()V
      #27 = NameAndType        #37:#38        //  access$000:(LLocalCls;)I
      #28 = NameAndType        #39:#40        //  access$002:(LLocalCls;I)I
      #29 = Utf8               fsjohnhuang
      #30 = Utf8               LocalCls$1Inner
      #31 = Utf8               Inner
      #32 = Utf8               InnerClasses
      #33 = Utf8               java/lang/Object
      #34 = Utf8               LocalCls
      #35 = Utf8               print
      #36 = Utf8               ()V
      #37 = Utf8               access$000
      #38 = Utf8               (LLocalCls;)I
      #39 = Utf8               access$002
      #40 = Utf8               (LLocalCls;I)I
    {
      final LocalCls this$0;
        flags: ACC_FINAL, ACC_SYNTHETIC
    
    
    
      LocalCls$1Inner(LocalCls);
        flags: 
    
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0       
             1: aload_1       
             2: putfield      #1                  // Field this$0:LLocalCls;
             5: aload_0       
             6: invokespecial #2                  // Method java/lang/Object."<init>":()V
             9: return        
          LineNumberTable:
            line 15: 0
    
      int getVal();
        flags: 
    
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0       
             1: getfield      #1                  // Field this$0:LLocalCls;
             4: invokestatic  #3                  // Method LocalCls.access$000:(LLocalCls;)I
             7: ireturn       
          LineNumberTable:
            line 17: 0
    
      void setVal(int);
        flags: 
    
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0       
             1: getfield      #1                  // Field this$0:LLocalCls;
             4: iload_1       
             5: invokestatic  #4                  // Method LocalCls.access$002:(LLocalCls;I)I
             8: pop           
             9: return        
          LineNumberTable:
            line 20: 0
            line 21: 9
    
      java.lang.String getName();
        flags: 
    
        Code:
          stack=1, locals=1, args_size=1
             0: ldc           #5                  // String fsjohnhuang
             2: areturn       
          LineNumberTable:
            line 23: 0
    }
    LocalCls$1Inner.class

      上述两个类文件与成员内部类的几乎一模一样,那么就是说内部类作用范围的限制其实是编译器的限制,而不是JVM的限制了。

      注意:

        1. 不能有public、protected、private和static作修饰;

        2. 局部内部类中仅能访问方法或作用域内的常量,若访问的是变量则编译时会出错。

           Q:为什么不能访问局部变量呢?

           A:假设可以访问局部变量,那么要考虑的是如何引用到局部变量。

                首先局部变量是存放在JVM栈帧中的局部变量表中,并且当方法执行完栈帧也随之弹出,也就是说局部变量所占的内存空间是短暂的(不稳定)。

                假如局部变量A是基本类型的话,那么数据直接就存放在局部变量表中相应的Slots中,方法执行完就没了。那局部内部类中所访问的局部变量A到底是什么就无从得知了!             假如局部变量A是String类型或其他类类型,那么局部内部类中访问的局部变量A时就有两种方式了,第一种是访问String常量池中该字符串的地址,第二种是指向局部变量A的地址,然后通过变量A去访问String常量池中该字符串。

                但上述这些均是在运行时才能决定,而编译时是无法正确地被描述出来。并且由于内部类将被编译成独立的类文件,访问其他类方法的局部变量的操作无法在类文件中描述。而常量则可以在内部类文件的常量池部分中被正确地描述,而JVM中处理时也十分简单高效。类文件的常量池条目将合并到运行时常量池中,因此外部和内部量访问的是同一个常量。

          下面的Bytecodes表示内部类中直接将常量池中的常量压栈后作为返回值返回。

    java.lang.String getName();
        flags:
        Code:
          stack=1, locals=1, args_size=1
             0: ldc           #5                  // String fsjohnhuang
             2: areturn       
          LineNumberTable:
            line 23: 

      

    五、匿名内部类                          

      匿名内部类其实是局部内部类的特殊形式。一般用来绑定事件监听处理程序上。Android示例:

    class Outer{
      public void subs(){ 
          scan_bt.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                // TODO Auto-generated method stub
                 
                }
            });
      }
    }

      上述代码生成了一个继承OnClickListener类的匿名内部类,然后实例化匿名类的一个实例,然后以该实例作为参数调用setOnClickListener方法。
      并生成一个Outer.class和Outer$1.class类文件。

      注意事项与局部内部一样。

    六、静态内部类                          

      静态内部类定义在类下,只不过多了个关键字static。静态内部类只能访问外部类的静态字段和静态方法。
      而实例化静态内部类时只需 new 外部类.静态内部类() 。

    七、总结                            

      尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4270044.html  ^_^肥仔John

    八、参考                            

    http://www.cnblogs.com/dolphin0520/p/3811445.html

  • 相关阅读:
    [Compose] 12. Two rules about Funtors
    [Compose] 11. Use Task for Asynchronous Actions
    [React Native] Animate Styles of a React Native View with Animated.timing
    [Compose] 10. Capture Side Effects in a Task
    [Angular Router] Lazy loading Module with Auxiliary router
    注重实效的程序员——途径篇
    UVa 674: Coin Change
    apache POI 导出excel相关方法
    Java调用R(二)_JRI
    drp用户管理完成后,asp.net与java的一个简单比较
  • 原文地址:https://www.cnblogs.com/fsjohnhuang/p/4270044.html
Copyright © 2011-2022 走看看