Java学习总结之第十章 内部类
一、内部类的基本语法
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 匿名类:外部类的名字$数字