标签:
在类内部不但可以定义成员变量和方法,还可以定义另一个类。如果在Outer的内部再定义一个类Inner,此时称Inner为内部类,Outer为外部类。广泛意义上的内部类包括成员内部类、局部内部类、匿名内部类和静态内部类这四种,下面分别介绍这四种内部类及其相关的知识。
一、成员内部类
成员内部类是最普通的内部类,它与外部类的成员变量和成员方法处在同一级别上。形如下面代码所示:
class Outer{ ????private double num=0; ????public static int count=1; ????public Outer(double num) { ????????// TODO Auto-generated constructor stub ????????this.num=num; ????} ????class Inner{ ????????public void print(){ ????????????System.out.println(num); ????????????System.out.println(count); ????????} ????} } |
?
成员内部类可以无条件地访问外部类的所有成员变量和方法,包括private和static成员。要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即在内部类中默认情况下访问的是内部类的成员,如果要访问外部类的同名成员,需要采用下面的方式:
外部类.this.成员变量
外部类.this.成员方法
虽然成员内部类可以无条件地访问外部类的成员,但如果在外部类中想要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过对象的引用去访问。
使用成员内部类时要注意以下三点:
第一:成员内部类看起来像是外部类的一个成员,所以可以像外部类的成员一样,用多种访问控制符来修饰,如果用public修饰,则可以在任何地方使用内部类,如果限制为private,则只能在外部类中使用;
第二:成员内部类是依附外部类而存在的。因为外部类的成员(非static成员)依赖于具体的对象,而成员内部类又可以无条件地访问外部类的成员,所以如果想要在外部类之外创建成员内部类的对象(内部类也要声明为public),必须先存在一个外部类对象。可以按以下的方式在外部类之使用外内部类:
Outer outer =new Outer();
Outer.Inner inner = outer.new Inner();
?
第三:成员内部类中不能有任何static变量和方法(其实非静态内部类中都不能有static变量和方法)。成员内部类是一种特殊的类,对它的理解不能束缚在单独类的概念上,我们可以把它当成是外部类的一个非静态方法,这样成员内部类中不能有static变量和方法就和普通方法中不能定义static变量是一个道理。
?
成员内部类可以无条件访问外部类成员的原因:在编译之后,会出现两个字节码文件,Outer$Inner.class和Inner.class,即编译器会将成员内部类单独编译成一个class文件,再使用javap反编译Outer$Inner.class文件,会包含以下两条重要信息:
final packagename.Outer this$0;
public packagename.Outer$Inner(packagename.Outer);
第一行是一个外部类对象的引用,第二行是成员内部类的构造函数。可以看出,编译器默认会给成员内部类添加一个指向外部类的引用,而且通过成员内部类的构造函数为该引用赋值。所以可以在成员内部类中随意访问外部类的成员。这也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对Outter this&0引用进行初始化赋值,也就无法创建成员内部类的对象了。
?
二、局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
class Outer{ ????private double num=0; ????public static int count=1; ????public Outer(double num) { ????????// TODO Auto-generated constructor stub ????????this.num=num; ????} ????public void test(final int a){ ????????int b=1; ????????class Inner{ ????????????public void print(){ ????????????????System.out.println(num);//ok ????????????????System.out.println(count);//ok ????????????????System.out.println(a);//ok ????????????????System.out.println(b);//error ????????????} ????????} ????} } |
?
test方法中的局部内部类Inner只能访问test中的final局部变量。
?
三、匿名内部类
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
class Outer{ ????private double num=0; ????public static int count=1; ????public Outer(double num) { ????????// TODO Auto-generated constructor stub ????????this.num=num; ????} ????public void test(final int a){ ????????int b=1; ????????new Thread(){ public void run() { ????System.out.println(num);//ok ????System.out.println(count);//ok System.out.println(a);//ok System.out.println(b); //error }; }.start(); ????} } |
?
匿名内部类和局部内部类一样,只能访问方法中的final局部变量。
局部内部类和匿名内部类只能访问方法中的final局部变量原因:通过反编译class文件,我们可以得到:对于局部变量,如果其值在编译期间就可以确定,则直接在内部类中创建一个拷贝。如果其值不能在编译期间确定,则通过构造器传参的方式对拷贝初始化赋值。这样,通过在内部类中创建局部变量的拷贝,就可以解决生命周期不一致的问题,再使用final限制局部变量,就可以维持变量和其拷贝的数据一致性。
?
四、静态内部类
如果在成员内部类的前面加上static关键字,就变成了静态内部类。静态内部类实质上是外部类,所以不依赖它外层的外部类。静态内部类不能访问外部类的非static成员变量或方法,因为它们必须依附于外部类的对象,而在没有外部类对象存在的情况下,就可以创建静态内部类的对象。
class Outer{ ???? int a=5; ???? static int b=10; ???? ? ???? static class Inner{ ????????void display(){ ????????????System.out.println(a);//error ????????????System.out.println(b);//ok ????????} ????} } |
?
五、内部类的好处和使用场景
1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整;
2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏;
3.方便编写事件驱动程序;
4.方便编写线程代码。
标签:
原文地址:http://www.cnblogs.com/damon-ly/p/4441306.html