内部类
# 内部类
内部类(Inner Class),也叫嵌套类,是一个定义在另外一个类范围中的类。
class OuterClass {
class InnerClass {
// 定义了一个Inner Class
}
}
2
3
4
5
一个内部类可以如常规类一样使用。通常,在一个类只被它的外部类所使用的时候,才将它定义为内部类。一个内部类具有以下特征:
— 个内部类被编译成一个名为
OuterClass$InnerClass
的类一个内部类可以引用定义在它所在的外部类中的数据和方法,即使是 private 的也可以。这很好理解,因为内部类就在 OuterClass 内部。所以,没必要将外部类对象的引用传递给内部类的构造方法。基于这个原因,内部类可以使得程序更加精简。
一个内部类可以使用可见性修饰符所定义,和应用于一个类中成员的可见性规则一样。
内部类对象通常在外部类中所创建并使用。但是也可以从另外一个类中来创建一个内部类的对象。如果内部类是非静态的,你必须先创建一个外部类的实例。换句话说,Inner Class 的实例不能单独存在,必须依附于一个 Outer Class 的实例。
可以使用以下语法来创建一个内部类的对象:
Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();
1
2
使用内部类的好处:
- 一个简单的内部类的用途,是将相互依赖的类结合到一个主类中。这样做减少了源文件的数量。这样也使得类文件容易组织,因为它们都将主类名作为前缀。
- 另外一个内部类的实际用途是避免类名的冲突。
- 如果一个类不会被其他应用使用,仅仅会被某个类使用,将它定义在主类里面作为一个内部类使用是恰如其分的。例如为一个按钮设置处理器对象,这个处理器对象不会被其他应用所共享,仅仅用来处理这个按钮。内部类不被其他应用所共享,所以将它定义在主类里面作为一个内部类使用是恰如其分的。
演示:我们定义一个内部类
class Outer{
private String name;
Outer(String name ){
this.name = name;
}
class Inner{
void helloInner(){
System.out.println("Hello " + Outer.this.name);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
然后在 main 方法里创建内部类对象:
public class LearnInnerClass {
public static void main(String[] args) {
Outer outer = new Outer("PeterJXL");
Outer.Inner inner = outer.new Inner();
inner.helloInner();
}
}
2
3
4
5
6
7
运行结果:
Hello PeterJXL
# 静态内部类(Static Nested Class)
一个内部类可以被定义为 static。一个 static 的内部类可以使用外部类的名字所访问。一个 static 的内部类不能访问外部类中非静态的成员。
使用以下语法来创建一个内部类对象:
OuterClass.InnerClass innerObject = new OuterClass.InnerClass();
# 匿名内部类(Anonymous Class)
一个匿名内部类是一个没有名字的内部类。它将一步实现定义一个内部类以及创建一个内部类的实例,简化代码
匿名内部类的语法如下所示:
new SuperClassName/InterfaceName() {
// Implement or override methods in superclass or interface
// Other methods if necessary
}
2
3
4
由于匿名内部类是一种特殊类型的内部类,它被当作一个内部类对待,但同时具有以下特征:
- — 个匿名内部类必须总是从一个父类继承或者实现一个接口,但是它不能有显式的 extends 或者 implements 子句
- 一个匿名内部类必须实现父类或者接口中的所有抽象方法。
- — 个匿名内部类总是使用它父类的无参构造方法来创建一个实例。如果一个匿名内部类实现一个接口,构造方法是
Object()
。 - 一个匿名内部类被编译成一个名为 OuterClassName$n.class 的类,n 是数字,从 1 开始命名并递增。如果有多个匿名类,Java 编译器会将每个匿名类依次命名为
Outer$1
、Outer$2
、Outer$3
……
使用匿名内部类,可以更加简化代码。
例如,下面定义了一个内部类:
public class LearnInnerClass2 {
public static void main(String[] args) {
new Outer("PeterJXL").sayHello();
}
}
class Outer{
private String name;
public Outer(String name){
this.name = name;
}
public void sayHello(){
new inner().run();
}
class inner implements Runnable{
@Override
public void run(){
System.out.println("Hello, " + Outer.this.name);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
可以被一个匿名内部类所替代,如下所示:
public class LearnInnerClass3 {
public static void main(String[] args) {
Outer outer = new Outer("peterjxl");
outer.sayHello();
}
}
class Outer{
private String name;
Outer(String name){
this.name = name;
}
void sayHello(){
Runnable r = new Runnable() {
@Override
public void run(){
System.out.println("Hello, " + Outer.this.name);
}
};
new Thread(r).start();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
观察 sayHello()
方法,我们在方法内部实例化了一个 Runnable
。Runnable
本身是接口,接口是不能实例化的,所以这里实际上是定义了一个实现了 Runnable
接口的匿名类,并且通过 new
实例化该匿名类,然后转型为 Runnable
。
匿名类和 Inner Class 一样,可以访问 Outer Class 的 private
字段和方法。之所以我们要定义匿名类,是因为在这里我们通常不关心类名,比直接定义 Inner Class 可以少写很多代码。
# 小结
Java 的内部类可分为 Inner Class、Anonymous Class 和 Static Nested Class 三种:
- Inner Class 和 Anonymous Class 本质上是相同的,都必须依附于 Outer Class 的实例,即隐含地持有
Outer.this
实例,并拥有 Outer Class 的private
访问权限; - Static Nested Class 是独立类,但拥有 Outer Class 的
private
访问权限。
# 参考
《Java 语言程序设计 基础篇 原书第 10 版 ,梁勇著》 15.4 和 15.5 小节