首页 > 代码库 > 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_SUPERConstant 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_SUPERConstant 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_SUPERConstant 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_SUPERConstant 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

Java魔法堂:内部类详解