Java学习总结之第十章 内部类

时间:2023-02-17 15:59:08
一、内部类的基本语法

1、顶层类只能处于public和默认访问级别,而成员内部类可以处于public、protected、默认和private四种访问级别。

2、实例内部类有以下特点:

l 在创建实例内部类的实例时,外部类的实例必须已经存在。

l 实例内部类的实例自动持有外部类的实例的引用。在内部类中,可以直接访问外部类的所有成员,包括成员变量和成员方法。并且在多重嵌套中,内部类可以访问所有外部类的成员。

l 外部类实例与内部类实例之间是一对多的关系,一个内部类实例只会引用一个外部类实例,而一个外部类实例对应零个或多个内部类实例。在外部类中不能直接访问内部类实例,必须通过内部类的实例去访问。

l 在实例内部类中不能定义静态成员,而只能定义实例成员。

l 如果实例内部类B与外部类A包含同名的成员(比如成员变量v),那么在类B中,this.v表示类B的成员,A.this.v表示类A的成员。

3、静态内部类有以下特点:

l 静态内部类的实例不会自动持有外部类的特定实例的引用,在创建内部类的实例时,不必创建外部类的实例。

l 静态内部类可以直接访问外部类的静态成员,如果访问外部类的实例成员,就必须通过外部类的实例去访问。

l 在静态内部类中可以定义静态成员和实例成员。

l 客户类可以通过完整的类名直接访问静态内部类的静态成员。

4、局部内部类是一个在方法中定义的内部类,它的可见范围是当前方法,和局部变量一样,局部内部类不能用访问控制修饰符及static修改符来修饰。局部内部类具有以下特点:

l 局部内部类只能在当前方法中使用。

l 局部内部类和实例内部类一样,不能包含静态成员。

l 在局部内部类中定义的内部类也不能被public、protected和private这些访问控制修饰符修饰。

l 局部内部类和实例内部类一样,可以访问外部类的所有成员,此外,局部内部类还可以访问所在方法中的final类型的参数和变量。不可以访问没有被final修饰的局部变量。

二、内部类的继承

在一个类A继承某个外部类B的内部类C时,要求类A的构造方法必须通过参数传递一个类B的实例的引用,然后在构造方法中调用super语句来建立类A实例与类B实例的关联关系。示例代码如下:

class B{

public B(){}

class C{

public C(){}

}

}

public class A extends B.C{

public A(B b){

b.super();

}

public static void main(String[] args){

B b = new B();

A a = new A(b);

}

}

三、匿名类

public class A {

A(int v){

System.out.println("another constructor");

}

A(){

System.out.println("default constructor");

}

void method(){

System.out.println("from A");

}

public static void main(){

A a = new A(){ //匿名类

void method(){

System.out.println("from anonymous");

}

};

a.method();

}

}

//以上”new A(){…}”定义了一个继承类A的匿名类,大括号内是类A的类体,”new A(){…}”返回

//匿名类的一个实例的引用。

//打印结果为:

default constructor

from A

default constructor

from anonymous

1、匿名类有以下特点:

l 匿名类本身没有构造方法,但是会调用父类的构造方法。示例代码如下:

public static void main(){

int v = 1;

A a = new A(v){ //匿名类

void method(){

System.out.println("from anonymous");

}

};

a.method();

}

//以下代码的打印结果为:

another constructor

from anonymous

在以上“new A(v){…}”中,如果参数v是局部变量,并且在匿名类的类体中会使用它,那么v必须是final类型,否则会导致编译错误。

public static void main(){

int v = 1; //编译错误,v必须定义为final类型

A a = new A(v){ //匿名类

void method(){

System.out.println("from anonymous"+v); //使用局部变量v

}

};

a.method();

}

l 匿名类尽管没有构造方法,但是可以在匿名类中提供一段实例初始化代码,Java虚拟机会在调用了父类的构造方法后,执行这段代码。示例如下:

public static void main(){

int v = 1;

A a = new A(v){ //匿名类

{ //实例初始化代码---开始

System.out.println("initialize instance");

} //实例初始化代码---结束

void method(){

System.out.println("from anonymous");

}

};

a.method();

}

//打印结果如下:

another constructor

initialize instance

from anonymous

注意:匿名类的实例只能有一种初始化方式。

l 除了可以在外部类的方法内定义匿名类以外,还可以在声明一个成员变量时定义一个匿名类。示例代码如下:类A有一个实例变量a,它引用一个继承类A的匿名类的实例。

abstract class A{

A a = new A(){

void method(){

System.out.println("inner");

}

};

abstract void method();

}

l 匿名类除了可以继承类以外,还可以实现接口,示例代码如下:

public class Sample {

public static void main(){

Thread t = new Thread(new Runnable(){

public void run(){

System.out.println("hi");

}

});

t.start();

}

}

l 匿名类和局部内部类一样,可以访问外部类的所有成员,如果匿名类位于一个方法中,还能访问所在方法的final类型的变量和参数。

l 局部内部类的名字在方法外是不可见的,因此与匿名类一样,能够起到封装类型名字的作用,局部内部类与匿名类有以下区别:

Ø 匿名类和程序代码比较简短。

Ø 一个局部内部类可以有多个重载构造方法,并且客户类可以多次创建局部内部类的实例。而匿名类没有重载构造方法,并且只能创建一次实例。

四、内部接口及接口中的内部类

l 在一个类中也可以定义内部接口。

l 在接口中可以定义静态内部类,此时静态内部类位于接口的命名空间中。

五、内部类的类文件

对于每个内部类来说,Java编译器会生成独立的.class文件。这此类文件的命名规则如下:

l 成员内部类:外部类的名字$内部类的名字

l 局部内部类:外部类的名字$数字$内部类的名字

l 匿名类:外部类的名字$数字