介绍

内部类(Inner Class)或内部接口(Inner Interface),是定义在一个类中的成员。相对的,这个内部类外的类被称为外部类;和外部类并列的类被称为“其它类”。这里的内部类,其官方名称为嵌套类(Nested Class),包含静态嵌套类(又称“静态内部类”)和非静态嵌套类(又称“内部类”)两种。

  • 内部类和外部类能够更好地表现这两个类间的逻辑性。
  • 内部类和普通类同样,在编译后产生同样的.class字节码文件,但在命名上有所不同,为“外部类名$内部类名.class”。

    成员内部类

    1. class Outer {
    2. class Inner {
    3. }
    4. }

    成员内部类是外部类的一个成员。和成员变量一样,使用成员内部类需要先实例化一个对象才能使用。成员内部类有如下特点:

  • 成员内部类不能定义静态成员和静态方法。

  • 成员内部类和其它类差不多,可以正常地继承、实现,并且也可以具有内部类和内部接口。
  • 内部类和外部类自己可以相互使用对方的成员和方法,无论是不是私有的。在内部类中使用外部类成员和方法时,需要使用this先实例化一个外部类对象。
  • 其它类若要使用一个类的成员内部类时,需要先导包,并实例化外部类对象后才能使用这个内部类。

    静态内部类

    1. class Outer {
    2. static class Inner {
    3. static int A;
    4. }
    5. }

    静态内部类是使用static修饰的内部类。静态内部类有以下特点:

  • 静态内部类是属于类的。和其它静态代码一样,可以使用类名直接访问静态内部类;也可以通过对象间接访问。

  • 和其它内部类不同,静态内部类可以定义静态成员和静态方法。
  • 静态内部类中,无法访问外部类的非静态属性和方法。
  • 其它类若要使用一个类的静态内部类时,需要先导包,然后即可直接使用类名创建静态内部类对象。且只能使用类名访问,不能使用外部类对象访问。

    局部内部类

    1. class Outer {
    2. void A() {
    3. class Inner {
    4. }
    5. }
    6. }

    局部内部类是在外部类的方法中定义的类,只能被方法中的代码访问和调用。局部内部类有以下特点:

  • 局部内部类中的方法不能使用外部方法的同名参数。局部内部类不能定义静态成员和静态方法。

  • 局部内部类访问外部类的成员变量需为一个final常量(或仅被赋值一次的变量)。
  • 局部内部类只能在当前方法中被使用,外部类和其他类只能通过调用方法间接使用。
  • 其.class文件的命名为“外部类名$1内部类名.class”,其中1为序号,以识别外部类名和内部类名都相同的类。

    匿名内部类

    1. interface If {
    2. void p();
    3. }
    4. class Test {
    5. void main() {
    6. If if = new If() { // 同时在这里也是一个局部内部类,只能使用final外部类成员变量
    7. void p() {
    8. ;
    9. }
    10. };
    11. if.p();
    12. }
    13. }
    匿名内部类是对接口的实现的一个简便方式。同时,也可以对父类(一般是个抽象类)的方法(一般是个抽象方法)进行重写:
    1. class Father {
    2. void p();
    3. }
    4. class Test {
    5. void main() {
    6. Father f = new Father() {
    7. void p() {
    8. ;
    9. }
    10. };
    11. f.p();
    12. }
    13. }
    其方法是用一个接口(或抽象类)的引用指向其“无参构造器”(但并没有创建接口的对象),同时在后面用一个大括号括起一个类的内容。因为匿名内部类没有类名,故只能通过这种方式创建仅一次对象,且匿名内部类中无法定义构造器。其.class文件的命名为“外部类名$1.class”,其中1为序号。

内部类的对比

  • 所有的内部类都具有独立的.class字节码文件。
  • 成员内部类和静态内部类与外部类之间可以相互访问(包括私有);局部内部类和匿名内部类可以访问外部类,但外部类只能在当前方法中访问这两种类。
  • 其它类要访问成员内部类和静态内部类需要导包(外部类全类名.内部类),前者通过对象调用,后者通过类名调用。所有私有代码均无法访问。
内部类 定义 静态代码 非静态代码 .class字节码文件
包含/访问外部 被调用 访问外部 被调用
成员内部类 定义在类中的成员,和成员变量概念相当 不能包含,访问:
类名.变量
N/A 自己类:this.成员
外部类:外部类.this.成员
其他类:其他类对象引用.成员
自己类:this.成员
外部类:new 内部类对象.成员
其他类:导包,new 外部类对象.new 内部类对象.成员
.Father$Son
静态内部类 被static修饰的成员内部类 可以包含,访问:
自己类:变量
外部类和其他类:类名.变量
自己类:变量
外部类:类名.变量 或 对象.变量
外部类:导包,类名.变量 或 对象.变量
自己类:this.成员
外部类和其他类:N/A
同成员内部类 .Father$Son
局部内部类 定义在方法中的类,和局部变量相当 同成员内部类 N/A 自己类:this.成员
外部类:
外部类.this.成员
当前方法:final修饰的常量
其他类:其他类对象引用.成员
自己类:this.成员
外部类:只在本方法中,new 内部类对象.成员
其他类: N/A
.Father$1Son
匿名内部类 没有类名的局部内部类 同成员内部类 N/A 同局部内部类 同局部内部类 .Father$1

内部类的选择

假设现在已经确定了要使用内部类,那么一般情况下,该如何选择?
1. 考虑这个内部类,如果需要反复的进行多次使用(必须有名字)

  • 在这个内部类中,如果需要定义静态的属性和方法,选择使用静态内部类
  • 在这个内部类中,如果需要访问外部类的非静态属性和方法,选择使用成员内部类
  1. 考虑这个内部类,如果只需要使用一次(可以没有名字)
  • 选择使用匿名内部类
  1. 局部内部类,几乎不会使用

以下是一个代码补充题(我觉得这题出得很好):

  1. interface Inter{
  2. public abstract void method();
  3. }
  4. class InnerClassDemo2 {
  5. public static void main(String[] args){
  6. Test.function().method(); //分析此语句的实现功能。
  7. //1. 该语句通过类名Test直接调用,故function应为一个static方法
  8. //2. 方法调用后还能够调用方法,故该方法应有具有返回值,且是一个对象
  9. //3. 该对象具有一个方法method(),并具有一个匿名内部类来重写接口的方法
  10. //故整句的功能即:运行Test类的静态function方法以创建一个Test对象,并调用method方法中的匿名内部类重写接口Inter的method方法
  11. }
  12. }
  13. class Test {
  14. //题目:通过匿名内部类补足该类代码
  15. static Test function() {
  16. retern new Test();
  17. }
  18. void method() {
  19. new Inter() {
  20. public void method() {}
  21. }.method();
  22. }
  23. //补充完毕
  24. }