将一个类的定义放在另一个类的定义内部,这就是内部类。如下所示
1 | class OuterClass { |
从总体上来讲,Java中的内部类可以分为:
- 静态内部类(static inner class):声明为static的内部类
- 非静态内部类(non-static inner class)
1 | class OuterClass { |
其中,非静态内部类又可细分为以下三种:
- 成员内部类:作为外部类的成员定义
- 局部内部类(local inner class):在外部类的方法中定义
- 匿名内部类(anonymous inner class):没有名字的内部类
内部类是一种编译器现象,与虚拟机无关。它可以访问外部类中定义的所有方法和域,即使这些方法和域声明为private。此外,内部类可以使用访问说明符public、protected和private修饰。
非静态内部类
非静态内部类中不能声明static方法,声明的所有静态域都必须是final。
内部类的特殊语法
要想实例化一个非静态内部类,需要使用一种特殊的语法.new
,就像下面这样:
1 | public class OuterClass { |
在外部类的作用域之外,可以这样引用内部类:
1 | OuterClass.InnerClass |
下面是示例展示了如何在外部类的作用域之外,实例化它的内部类。
1 | public class Main { |
在内部类中,如果需要使用外部类对象的引用,可以使用外部类的名字后紧跟圆点和this,就像这样OuterClass.this
,下面是一个简单的示例:
1 | public class OuterClass { |
成员内部类
成员内部类是最常见的内部类,也称为普通内部类。成员内部类作为外部类的成员来定义,如下所示:
1 | public class OuterClass { |
局部内部类
在外围类的方法中声明的类,称为局部内部类。下面是一个简单的例子
1 | public class Outer { |
局部内部类不能用public、protected或private访问说明符声明。它的优势是对外部世界可以完全地隐藏起来,除了start方法之外,Outer的其他地方均不能访问它。
匿名内部类
匿名内部类,顾名思义,就是没有命名的内部类。语法格式如下
1 | new SuperType(construction parameters) { |
下面是匿名内部类的一个示例。
1 | interface Counter { |
在上面的程序中,由于getCounter方法中的name这个变量不是外围类中定义的属性,所以必须声明为final类型,才能在匿名内部类中使用(对于局部内部类,情况也是一样的),否则,将产生如下的语法错误:
1 | Cannot refer to the non-final local variable name defined in an enclosing scope |
由于构造器的名字必须与类名相同,而匿名类没有类名,所以匿名类不能有构造器。因此,将参数传递给超类(父类)构造器。在匿名内部类实现接口的时候,不能有任何构造参数。
静态内部类
静态内部类,就是声明为static的内部类。实例化静态内部类,不需要其外围类对象。下面是一个静态内部类的示例:
1 | public class Outer { |
值得一提的是,静态内部类不能访问外围类中的非静态的域和方法。以上面的程序为例,StaticInnerClass类可以访问到Outer类的className属性,因为className声明为static,但StaticInnerClass类不能访问count。
什么时候使用静态内部类?在内部类不需要访问外围类对象的时候,应该使用静态内部类。
内部类标识符
对于成员内部类和静态内部类,编译器将会把它编译成文件名为外围类名$内部类名.class
的class文件。
对于匿名内部类,编译器会将它编译名为外围类名$n.class
的class文件;对于局部内部类,则被编译为外围类名$n内部类名
的class文件。(n是由编译器简单生成的一个数字)
下面通过一个简单的例子来展示这四种内部类的字节码文件的命名规则
1 | interface Counter { |
Outer.java编译之后,生成了如下几个class文件:
1 | Counter.class |