嵌套类
浏览 类和对象 主题: ) |
在 Java 中,你可以在一个类中定义另一个类。一个类可以嵌套在另一个类中,或者嵌套在一个方法中。一个没有嵌套的类被称为顶级类,而定义嵌套类的类则被称为外部类。
当一个类在另一个类中声明时,嵌套类的访问修饰符可以是 public
、private
、protected
或 package(默认)
。
代码清单 4.10:OuterClass.java
public class OuterClass {
private String outerInstanceVar;
public class InnerClass {
public void printVars() {
System.out.println("Print Outer Class Instance Var.:" + outerInstanceVar);
}
}
}
|
内部类可以访问封闭类实例的变量和方法,即使是私有的变量和方法,如上所示。这使得它与 C++ 中的嵌套类非常不同,C++ 中的嵌套类等效于“静态”内部类,见下文。
内部对象有一个对外部对象的引用。换句话说,所有内部对象都与外部对象绑定。内部对象只能通过对“外部”对象的引用来创建。见下文。
代码段 4.20:外部类调用。
public void testInner() {
...
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
...
}
|
请注意,内部对象,因为它们与外部对象绑定,不能包含静态变量或方法。
当在外部类的非静态方法中时,你可以直接使用 new InnerClass()
,因为类实例隐含地为 this
。
你可以使用语法 OuterClass.this
从内部类中直接访问对外部对象的引用;尽管这通常是不必要的,因为你已经可以访问它的字段和方法。
内部类编译成单独的“.class”字节码文件,文件名是封闭类的名称,后跟一个“$”,然后是内部类的名称。例如,上面的内部类将编译成一个名为“OuterClass$InnerClass.class”的文件。
嵌套类可以声明为静态。这些类不绑定到外部定义类的实例。静态嵌套类没有封闭实例,因此不能访问外部类的实例变量和方法。在创建静态内部类时,你无需指定实例。这等效于 C++ 中的内部类。
这些内部类,也称为局部类,不能有访问修饰符,就像局部变量一样,因为该类对方法是“私有”的。内部类只能是 抽象
或 最终
。
代码清单 4.11:OuterClass.java
public class OuterClass {
public void method() {
class InnerClass {
}
}
}
|
除了封闭类的实例变量外,局部类还可以访问封闭方法的局部变量,但仅限于声明为final
的变量。这是因为局部类实例可能比方法的调用持续时间更长,因此需要它自己的变量副本。为了避免在同一作用域中具有两个名称相同但可变的变量副本的问题,要求该变量必须是final
的,因此不能更改。
在Java中,类的定义及其实例化可以组合成一个步骤。通过这样做,类不需要名称。这些类称为匿名类。匿名类可以在可以使用引用的上下文中定义和实例化,并且它是现有类的嵌套类。匿名类是局部于方法的类的特例;因此,它们也可以访问封闭方法的final局部变量。
匿名类最常用于创建接口或适配器类的实例,而无需创建全新的类。
代码清单 4.12:ActionListener.java
public interface ActionListener {
public void actionPerformed();
}
|
代码段 4.21:匿名类。
ActionListener listener = new ActionListener() {
public void actionPerformed() {
// Implementation of the action event
...
return;
}
};
|
在上面的示例中,实现ActionListener
的类是匿名的。该类在其被实例化的位置定义。
以上代码比显式定义类更难阅读,那么为什么要使用它呢?如果需要接口的许多实现,这些类仅在一个特定位置使用,并且很难为它们想出名称,使用匿名内部类是有意义的。
以下示例使用匿名内部类来实现动作监听器。
代码清单 4.13:MyApp.java
import java.awt.Button;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class MyApp {
Button aButton = new Button();
MyApp() {
aButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello There");
}
}
);
}
}
|
以下示例执行相同的操作,但它为实现动作监听器的类命名。
代码清单 4.14:MyApp.java
import java.awt.Button;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class MyApp {
Button aButton = new Button();
// Nested class to implement the action listener
class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello There");
}
}
MyApp() {
aButton.addActionListener(new MyActionListener());
}
}
|
当您打算使用许多不同的类,并且每个类都实现相同的接口时,使用匿名类尤其合适。