一、前言
對于內部類平時編碼時使用的場景不多,比較常用的地方應該就是綁定事件處理程序的時候了(從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 }

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 }
由于成員內部類依賴于外部類實例,因此創建內部類實例時要先創建外部類實例,然后通過下列兩種形式來創建內部類實例:
// 方式一 內部類 內部類實例 = 外部類實例.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 }

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 }
上述兩個類文件與成員內部類的幾乎一模一樣,那么就是說內部類作用范圍的限制其實是編譯器的限制,而不是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
文章列表