Java内部类与匿名类

来源:互联网 发布:plm软件价格 编辑:程序博客网 时间:2024/06/05 18:16

一、内部类的基本语法

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

c) 在局部内部类中定义的内部类也不能被publicprotectedprivate这些访问控制修饰符修饰。

d) 局部内部类和实例内部类一样,可以访问外部类的所有成员,此外,局部内部类还可以访问所在方法中的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(String[] args){A a = new A(){ //匿名类void method(){System.out.println("from anonymous");}};a.method();}}//以上”new A(){…}”定义了一个继承类A的匿名类,大括号内是类A的类体,”new A(){…}”返回//匿名类的一个实例的引用。//打印结果为:default constructorfrom anonymous
匿名类本身没有构造方法,但是会调用父类的构造方法。示例代码如下:匿名类有以下特点:

public static void main(){int v = 1;A a = new A(v){ //匿名类void method(){System.out.println("from anonymous");}};a.method();}//以下代码的打印结果为:another constructorfrom 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();}

匿名类尽管没有构造方法,但是可以在匿名类中提供一段实例初始化代码,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 constructorinitialize instancefrom anonymous
除了可以在外部类的方法内定义匿名类以外,还可以在声明一个成员变量时定义一个匿名类。示例代码如下:类A有一个实例变量a,它引用一个继承类A的匿名类的实例。注意:匿名类的实例只能有一种初始化方式。
abstract class A{A a = new A(){void method(){System.out.println("inner");}};abstract void method();}
匿名类和局部内部类一样,可以访问外部类的所有成员,如果匿名类位于一个方法中,还能访问所在方法的final类型的变量和参数。匿名类除了可以继承类以外,还可以实现接口,示例代码如下:
public class Sample {public static void main(){Thread t = new Thread(new Runnable(){public void run(){System.out.println("hi");}});t.start();}}

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

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

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

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

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

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

五、内部类的类文件

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

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

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

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

六、内部类的特殊用法

从一个内部类的实例拿到所在外部类的实例可以使用this$0默认字段,this$0就是内部类自动保留的一个指向所在外部类的引用。

package com.dailey.example.java.innerclass;import java.lang.reflect.Field;public class Outer {//this$0public class FirstInner {//this$1public class SecondInner {//this$2public class ThirdInner {}}}public static void main(String[] args) throws Exception,NoSuchFieldException {Outer test = new Outer();FirstInner first = test.new FirstInner();FirstInner.SecondInner second = first.new SecondInner();FirstInner.SecondInner.ThirdInner third=second.new ThirdInner();//OuterField outerfield = first.getClass().getDeclaredField("this$0");outerfield.setAccessible(true);Object object = outerfield.get(first);System.out.println(object instanceof Outer);//FirstInnerField firstInnerfied = second.getClass().getDeclaredField("this$1");firstInnerfied.setAccessible(true);object = firstInnerfied.get(second);System.out.println(object instanceof FirstInner);//SecondInnerField secondInnerfield = third.getClass().getDeclaredField("this$2");secondInnerfield.setAccessible(true);object = secondInnerfield.get(third);System.out.println(object instanceof FirstInner.SecondInner);}}
 Java 反编译工具生成以上内部类代码可以看出:每个内部类都有一个指向所在最近的外部类的一个引用,只不过该引用不需要手动添加,编译器会负责
public class Outer$FirstInner {// FirstInner内部类有一个带外部类参数的构造子public Outer$FirstInner(Outer paramOuter) {}// ....}public class Outer$FirstInner$SecondInner {public Outer$FirstInner$SecondInner(Outer.FirstInner paramFirstInner) {}// ....}public class Outer$FirstInner$SecondInner$ThirdInner {public Outer$FirstInner$SecondInner$ThirdInner(Outer.FirstInner.SecondInner paramSecondInner) {}}