内部类(Inner Class)及匿名类(Anonymous Class)。简单地说,内部类是定义在
其他类中的类,内部类的主要作用是将逻辑上相关联的类放到一起;而匿名类是一种特殊
的内部类,它没有类名,在定义类的同时,就生成该对象的一个实例,由于不会在其他地
方用到该类,所以不用取名字。
5.5.1 内部类
在 Java 中,可将一个类定义置入另一个类定义中,这就叫做“内部类”。利用内部类,
可对那些逻辑上相互联系的类进行分组,并可控制一个类在另一个类里的“可见性”。
1.内部类的定义和使用
定义内部类是简单的,将类的定义置入一个用于封装它的类内部即可。
注意:内部类不能与外部类同名(那样的话,编译器无法区分内部类与外部类),如果
内部类还有内部类,内部类的内部类不能与它的任何一层外部类同名。
在封装它的类的内部使用内部类,与普通类的使用方式相同,在其他地方使用内部类
时,类名前要冠以其外部类的名字才能使用,在用new 创建内部类时,也要在new 前面冠
以对象变量。如例5-11 所示。
例 5-11 TestInnerUse.java 使用内部类。
class TestInnerUse{
public static void main( String[] args ){
Parcel p = new Parcel();
Parcel.Contents c = p.new Contents(33);
Parcel.Destination d = p.new Destination( "Hawii" );
p.setValue( c, d );
p.ship();
}
}
class Parcel {
private Contents c;
private Destination d;
class Contents {
private int i;
Contents( int i ){ this.i = i; }
int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {label = whereTo;}
String readLabel() { return label; }
}
void setValue( Contents c, Destination d ){
this.c =c; this.d = d;
}
void ship(){
System.out.println( "运输"+ c.value() +"到"+ d.readLabel() );
}
public void testShip() {
c = new Contents(22);
d = new Destination("Tanzania");
ship();
}
}
该例中,在 Parcel 类中定义内部类Contents 及Destination,在Parcel 类使用Contents
及Destinationship()类与其他类没有区别(见ship()方法及testShip()方法)。而在其他类中(见
main()方法),在用类名和new 运算符前分别要冠以外部类的名字及外部对象名。
2.在内部类中使用外部类的成员
内部类与类中的域、方法一样是外部类的成员,所以在内部类中可以直接访问外部类
的其他域及方法,即使它们是private 的。这也是使用内部类的一个好处。
如果内部类中与外部类有同名的域或方法,可以使用冠以外部类名.this 来访问外部类
中的同名成员。
例 5-12 TestInnerThis.java 在内部类中使用this。
public class TestInnerThis
{
public static void main(String args[]){
A a = new A();
A.B b = a.new B();
b.mb(333);
}
}
class A
{
private int s = 111;
public class B {
private int s = 222;
public void mb(int s) {
System.out.println(s); // 局部变量s
System.out.println(this.s); // 内部类对象的属性s
System.out.println(A.this.s); // 外层类对象属性s
}
}
}
在该例中,分别访问了局部变量、内部类对象的属性、外层类对象属性。程序的结果
为333,222,111。
3.内部类的修饰符
内部类与类中的域、方法一样是外部类的成员,它的前面也可以访问控制符及其他修
饰符。内部类可用的修饰符比外部类的修饰符更多。例如,外部类不能使用protected,
private,static 等修饰,而内部类可以。
访问控制符包括 public, protected, 默认及private,其含义与域前面的访问控制符一样。
内部类前面用 final 修饰,表明该内部类不能被继承;内部类前面用abstract 修饰,表
明该内部类不能被实例化。
内部类前面用 static 修饰,则表明该内部类实际是一种外部类,因为它们的存在不依
赖于外部类的一个具体实例。Static 内部类与普遍的内部类有较大的不同:由于static 的内
部类使用在static 环境,static 环境在使用时要遵循以下规则:
(1)实例化static 内部类时,在new 前面不需要用对象变量;
(2)static 内部类中不能访问其外部类的非static 域及方法,即只能访问static 成员。
(3)static 方法中不能访问非static 的域及方法,也不能不带前缀地new 一个非static
的内部类。
根据以上规则,不难看出在例 5-13 中的被注释起来的错误行。
例5-13 TestInnerStatic.java 静态内部类。
class TestInnerStatic
{
public static void main(String[] args)
{
A.B a_b = new A().new B(); // ok
A a = new A();
A.B ab = a.new B();
Outer.Inner oi = new Outer.Inner();
//Outer.Inner oi2 = Outer.new Inner(); //!!!error
//Outer.Inner oi3 = new Outer().new Inner(); //!!! error
}
}
class A
{
private int x;
void m(){
new B();
}
static void sm(){
//new B(); // error!!!!
}
class B
{
B(){ x=5; }
}
}
class Outer
{
static class Inner
{
}
}
5.5.2 方法中的内部类及匿名类
1.方法中的内部类
在一个方法中,也可以定义类,这种类称为方法中的内部类。
例 5-14 TestInnerInMethod.java 方法中的内部类。
class TestInnerInMethod
{
public static void main(String[] args)
{
Object obj = new Outer().makeTheInner(47);
System.out.println("Hello World!" + obj.toString() );
}
}
class Outer
{
private int size = 5;
public Object makeTheInner( int localVar )
{
final int finalLocalVar = 99;
class Inner {
public String toString() {
return ( " InnerSize: " + size +
// " localVar: " + localVar + // Error!
" finalLocalVar: " + finalLocalVar
);
}
}
return new Inner();
}
}
在方法中定义内部类时,要注意以下几点。
注意:(1)同局部变量一样,方法中的内部类前面不能用public,private,protectd 修
饰,也不能用static 修饰,但可以被final 或abstract 修饰。
(2)方法中的内部类,可以访问其外部类的成员;若是static 方法中的内部类,
可以访问外部类的static 成员。
(3)方法中的内部类中,不能访问该方法的局部变量,除非是final 的局部变量。
(4)方法中定义的类,在其他地方使用时,没有类的名字,正像上面的例子中
一样,只能用其父类(例中是用Object)来引用这样的变量。
2.匿名类
在类及其方法中,可以定义一种匿名类,匿名类有以下几个特点。
(1)这种类不取名字,而直接用其父类的名字或者它所实现的接口的名字。
(2)类的定义与创建该类的一个实例同时进行,即类的定义前面有一个new。不使用
关键词class,同时带上()表示创建对象。也就是说,匿名类的定义方法是:
new 类名或接口名() { ……… }
(3)类名前面不能有修饰符。
(4)类中不能定义构造方法,因为它没有名字。也正是这个原因,在构造对象时,也
不能带参数,因为默认构造方法不能带参数。
上面的例子用匿名类可以改写成如例 5-15 的形式。
例 5-15 TestInnerAnonymous.java 匿名类。
class TestInnerAnonymous
{
public static void main(String[] args)
{
Object obj = new Outer().makeTheInner(47);
System.out.println("Hello World!" + obj.toString() );
}
}
class Outer
{
private int size = 5;
public Object makeTheInner( int localVar )
{
final int finalLocalVar = 99;
return new Object() {
public String toString() {
return ( " InnerSize: " + size +
" finalLocalVar: " + finalLocalVar
);
}
};
}
}
通过这个例子也可以看出,匿名类可以简化程序的书写。匿名类主要使用在那些需要
扩展某个类或实现某个接口做参数的地方,在后面的章节中讲解图形化界面与事件监听时,
会大量地用到匿名类。