内部类
我们在前面的学习过程中,在一个文件中定义两个类,并且其中任何一个类都不在另一个类的内部。如果在类中再定义一个类,则将在类中再定义的那个类称为内部类。成员内部类和匿名类是最常见的内部类,本节将对这两种内部类进行讲解。
成员内部类
成员内部类简介
在一个类中使用内部类,可以在内部类中直接存取其所在类的私有成员变量。成员内部类的语法如下:
class OuterClass { // 外部类
class InnerClass { // 内部类
}
}
在成员内部类中可以随意使用外部类的成员方法及成员变量,尽管这些类成员被修饰为 private。图8.3充分说明了内部类的使用,尽管成员变量 i 以及成员方法 g() 都在外部类中被修饰为 private,但在成员内部类中可以直接使用。

内部类的实例一定要被绑定在外部类的实例上,如果从外部类中初始化一个内部类对象,那么内部类对象就会被绑定在外部类对象上。内部类初始化方式与其他类的初始化方式相同,都是使用 new 关键字。下面来看一个实例。
【例8.3】使用成员内部类模拟发动机点火(实例位置:资源包\TM\sl\8\3)
首先创建 Car 类,Car 类中有私有属性 brand 和 start() 方法,然后在 Car 类的内部创建 Engine 类,Engine 类中有私有属性 model 和 ignite() 方法,最后输出 “启动大众朗行,发动机EA211点火”。
public class Car { // 创建汽车类
private String brand; // 汽车品牌
public Car(String brand) { // 汽车类的构造方法,参数为汽车品牌
this.brand = brand; // 给汽车品牌赋值
}
class Engine { // 发动机类(内部类)
String model; // 发动机型号
public Engine(String model) { // 发动机类的构造方法,参数为发动机型号
this.model = model; // 给发动机型号赋值
}
public void ignite() { // (发动机)点火方法
System.out.println("发动机" + this.model + "点火");
}
}
public void start() { // 启动(汽车)方法
System.out.println("启动" + this.brand);
}
public static void main(String[] args) {
Car car = new Car("大众朗行"); // 创建汽车类对象,并为汽车品牌赋值
car.start(); // 汽车类对象调用启动(汽车)方法
// 创建发动机类(内部类)对象,并为发动机型号赋值
Car.Engine engine = car.new Engine("EA211");
engine.ignite(); // 发动机类对象调用(发动机)点火方法
}
}
运行结果如下:
启动大众朗行
发动机EA211点火
成员内部类不止可以在外部类中使用,在其他类中也可以使用。在其他类中创建内部类对象的语法非常特殊,语法如下:
外部类 outer = new 外部类();
外部类.内部类 inner = outer.new 内部类();
|
使用this关键字获取内部类与外部类的引用
如果在外部类中定义的成员变量与内部类的成员变量名称相同,可以使用 this 关键字。
【例8.4】在内部类中调用外部类对象(实例位置:资源包\TM\sl\8\4)
在项目中创建 TheSameName 类,在类中定义成员变量 x,再定义一个内部类 Inner,在内部类中也创建x变量,并在内部类的 doit() 方法中定义一个局部变量 x。
public class TheSameName {
private int x = 7; // 外部类的x
private class Inner {
private int x = 9;// 内部类的x
public void doit() {
int x = 11; // 局部变量x
this.x++; // 调用内部类的x
TheSameName.this.x++; // 调用外部类的x
}
}
}
在类中,如果遇到内部类与外部类的成员变量重名的情况,则可以使用 this 关键字进行处理。例如,在内部类中使用 this.x 语句可以调用内部类的成员变量 x,而使用 TheSameName.this.x 语句可以调用外部类的成员变量 x,即使用外部类名称后跟一个点操作符和 this 关键字便可获取外部类的一个引用。
匿名内部类
匿名类是只在创建对象时才会编写类体的一种写法。匿名类的特点是 “现用现写”,其语法如下:
new 父类/父接口() {
子类实现的内容
};
最后一个大括号之后有分号。 |
【例8.5】使用匿名内部类创建一个抽象狗类的对象(实例位置:资源包\TM\sl\8\5)
创建一个抽象的狗类,类中有一个颜色属性和两个抽象方法,在测试类的主方法中创建抽象类对象,并用匿名内部类实现该对象的抽象方法。
abstract class Dog {
String Color;
public abstract void move();
public abstract void call();
}
public class Demo {
public static void main(String args[]) {
Dog maomao = new Dog() {
public void move() {
System.out.println("四腿狂奔");
}
public void call() {
System.out.println("嗷呜~");
}
};
maomao.Color = "灰色";
maomao.move();
maomao.call();
}
}
运行结果如下:
四腿狂奔
嗷呜~
从这个结果中可以看出,本来无法创建对象的抽象类竟然也可以出现在 new 关键字的右侧。为何叫匿名内部类?就是因为实例中 Dog 抽象类的实现类没有名称。创建出的对象 maomao 既不是金毛犬,也不是哈士奇犬,在程序中 maomao 只能被解读为 “一只具体的无名之狗”。使用匿名类时应该遵循以下原则:
-
匿名类不能写构造方法。
-
匿名类不能定义静态的成员。
-
如果匿名类创建的对象没有赋值给任何引用变量,会导致该对象用完一次就会被 Java 虚拟机销毁。
匿名内部类编译以后,会产生以 “外部类名$序号” 为名称的 |
编程训练(答案位置:资源包\TM\sl\8\编程训练)
【训练3】让心脏成为内部类 创建一个人类,人类中包含一个内部类—心脏类。当人类执行走路方法时,心脏方法也会同时执行。
【训练4】猫吃鱼,狗吃肉 参照下面的代码,创建Animal类的匿名子类对象,重写eat()方法,执行该方法后会在控制台上输出“猫吃鱼,狗吃肉”字样。
class Animal {
void eat() {
}
}