匿名内部类的一般好处是:是代码更加简洁,紧凑,但带来的是易读性下降。
它一般用在GUI编程中实现事件处理等等。
因为匿名内部类没有名字这个特殊性质,所以我们无从给它指定构造方法,构造方法必须和类名同名,类名都没有,构造方法就无从谈起了。但是匿名内部类可以通过直接调用父类的构造方法实现初始化,当然要求父类构造方法对它父类中定义的成员变量进行初始化。这里用一个例子看创建匿名内部类的时候父类的构造方法到底是如何调用的。
编译得到4个class文件,这里只需要关注InnerTest.class 和 InnerTest$1.class。这里InnerTest$1.class是匿名内部类的class文件,InnerTest.class是InnerTest类的class文件。我们先看InnerTest$1.class的内容:
javap -c InnerTest$1 > InnerTest$1.txt
得到代码如下 :
很明显,我们的匿名内部类有了名字 InnerTest$1 ,而且是继承自 Test
class InnerTest$1 extends Test
这个类中有一个成员final InnerTest this$0;我想这应该是该内部类所在的外部类InnerTest的引用
这个匿名内部类的构造方法是:
InnerTest$1(InnerTest, int);
一个是InnerTest类型,也就是该类外部类的引用,调用的时候应该是把外部类对象的this指针传给它,这样就内部类就可以直接访问外部类的成员了。
另一个就是int类型的,应该是对i进行初始化用的。
看到下面这行:
7: invokespecial #2; //Method Test."<init>":(I)V
现在应该清楚了,这是调用了父类Test的构造方法。
下面再看InnerTest是如何实现的:
InnerTest的get方法是关键。
这里首先new InnerTest$1,后面调用了InnerTest$1的构造方法,并把两个参数传了进去。
这样一切都清楚了。
结论:其实匿名内部类也没有什么特别的地方,编译之后它有了名字,有了构造方法,就是一个正常的类了。
因为匿名内部类没有名字这个特殊性质,所以我们无从给它指定构造方法,构造方法必须和类名同名,类名都没有,构造方法就无从谈起了。但是匿名内部类可以通过直接调用父类的构造方法实现初始化,当然要求父类构造方法对它父类中定义的成员变量进行初始化。这里用一个例子看创建匿名内部类的时候父类的构造方法到底是如何调用的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public
class
Main {
public
static
void
main(String[] args) {
InnerTest inner =
new
InnerTest();
Test t = inner.get(
3
);
System.out.println(t.getI());
}
}
class
Test {
//超类
private
int
i;
public
Test(
int
i) {
this
.i = i;
}
public
int
getI() {
return
i;
}
}
class
InnerTest {
//用于内部类的测试
public
Test get(
int
x) {
return
new
Test(x) {
//创建匿名内部类,调用父类的构造方法
@Override
public
int
getI() {
return
super
.getI() *
10
;
}
};
}
}
|
编译得到4个class文件,这里只需要关注InnerTest.class 和 InnerTest$1.class。这里InnerTest$1.class是匿名内部类的class文件,InnerTest.class是InnerTest类的class文件。我们先看InnerTest$1.class的内容:
javap -c InnerTest$1 > InnerTest$1.txt
得到代码如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
Compiled from
"Main.java"
class
InnerTest$
1
extends
Test{
final
InnerTest
this
$
0
;
InnerTest$
1
(InnerTest,
int
);
Code:
0
: aload_0
1
: aload_1
2
: putfield #
1
;
//Field this$0:LInnerTest;
5
: aload_0
6
: iload_2
7
: invokespecial #
2
;
//Method Test."<init>":(I)V
10
:
return
public
int
getI();
Code:
0
: aload_0
1
: invokespecial #
3
;
//Method Test.getI:()I
4
: bipush
10
6
: imul
7
: ireturn
}
|
很明显,我们的匿名内部类有了名字 InnerTest$1 ,而且是继承自 Test
class InnerTest$1 extends Test
这个类中有一个成员final InnerTest this$0;我想这应该是该内部类所在的外部类InnerTest的引用
这个匿名内部类的构造方法是:
InnerTest$1(InnerTest, int);
一个是InnerTest类型,也就是该类外部类的引用,调用的时候应该是把外部类对象的this指针传给它,这样就内部类就可以直接访问外部类的成员了。
另一个就是int类型的,应该是对i进行初始化用的。
看到下面这行:
7: invokespecial #2; //Method Test."<init>":(I)V
现在应该清楚了,这是调用了父类Test的构造方法。
下面再看InnerTest是如何实现的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
Compiled from
"Main.java"
class
InnerTest
extends
java.lang.Object{
InnerTest();
Code:
0
: aload_0
1
: invokespecial #
1
;
//Method java/lang/Object."<init>":()V
4
:
return
public
Test get(
int
);
Code:
0
:
new
#
2
;
//class InnerTest$1
3
: dup
4
: aload_0
5
: iload_1
6
: invokespecial #
3
;
//Method InnerTest$1."<init>":(LInnerTest;I)V
9
: areturn
}
|
InnerTest的get方法是关键。
这里首先new InnerTest$1,后面调用了InnerTest$1的构造方法,并把两个参数传了进去。
这样一切都清楚了。
结论:其实匿名内部类也没有什么特别的地方,编译之后它有了名字,有了构造方法,就是一个正常的类了。
定义在类中的内部类分为实例内部类和静态内部类,实例内部类自动持有外部类的实例的引用,即可以访问外部类的所有变量;静态内部类可以直接访问外部类的静态成员
定义在方法中的内部类叫局部内部类,该类只能访问被final修饰的成员变量和参数