一、前言
对于内部类平时编码时使用的场景不多,比较常用的地方应该就是绑定事件处理程序的时候了(从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 外部类.静态内部类() 。