你好!在今天的课程中,我们将继续研究嵌套类的主题。现在是最后一组的时候了:匿名内部类。让我们回到我们的图表: 就像我们上节课讲到的局部类一样,匿名类是内部类的一种……它们也有几个相同点和不同点。但首先,让我们深入探讨:为什么他们被称为“匿名”?要回答这个问题,请考虑一个简单的例子。想象一下,我们有一个不断运行并做某事的基本程序。我们想为这个程序创建一个监控系统,由几个模块组成。一个模块将跟踪性能的一般指标并维护日志。第二个将在错误日志中注册和记录错误。第三个将跟踪可疑活动:例如,未经授权的访问尝试和其他与安全相关的事情。因为从本质上讲,所有三个模块都应该简单地从程序的开头开始并在后台运行,
public interface MonitoringSystem {
public void startMonitoring();
}
3个具体类将实现它:
public class GeneralIndicatorMonitoringModule implements MonitoringSystem {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor general indicators!");
}
}
public class ErrorMonitoringModule implements MonitoringSystem {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor errors!");
}
}
public class SecurityModule implements MonitoringSystem {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor security!");
}
}
似乎一切都井井有条。我们有一个由几个模块组成的非常连贯的系统。他们每个人都有自己的行为。如果我们需要新模块,我们可以添加它们,因为我们有一个很容易实现的接口。但是让我们考虑一下我们的监控系统将如何工作。 基本上,我们只需要创建 3 个对象 — GeneralIndicatorMonitoringModule
, ErrorMonitoringModule
, SecurityModule
— 并startMonitoring()
在每个对象上调用方法。也就是说,我们需要做的就是创建 3 个对象并调用它们的 1 个方法。
public class Main {
public static void main(String[] args) {
GeneralIndicatorMonitoringModule generalModule = new GeneralIndicatorMonitoringModule();
ErrorMonitoringModule errorModule = new ErrorMonitoringModule();
SecurityModule securityModule = new SecurityModule();
generalModule.startMonitoring();
errorModule.startMonitoring();
securityModule.startMonitoring();
}
}
控制台输出:
Starting to monitor general indicators!
Starting to monitor errors!
Starting to monitor security!
通过如此少的工作,我们已经编写了整个系统:3 个类和一个接口!而这一切只需要6行代码就可以实现。另一方面,我们有什么选择?好吧,我们编写这些“一次性”课程并不是很酷。但是我们怎样才能解决这个问题呢?在这里匿名内部类来拯救我们!在我们的案例中,它们是这样的:
public class Main {
public static void main(String[] args) {
MonitoringSystem generalModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor general indicators!");
}
};
MonitoringSystem errorModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor errors!");
}
};
MonitoringSystem securityModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor security!");
}
};
generalModule.startMonitoring();
errorModule.startMonitoring();
securityModule.startMonitoring();
}
}
让我们弄清楚发生了什么!看起来我们正在创建一个接口对象:
MonitoringSystem generalModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor general indicators!");
}
};
但是我们早就知道不能创建界面对象了!所以它是——这是不可能的。事实上,这不是我们正在做的。当我们写:
MonitoringSystem generalModule = new MonitoringSystem() {
};
Java 机器内部发生以下情况:
- 创建一个未命名的 Java 类来实现该
MonitoringSystem
接口。 - 当编译器看到这样的类时,它要求你实现接口的所有方法
MonitoringSystem
(我们做了 3 次)。 - 创建了此类的一个对象。注意代码:
MonitoringSystem generalModule = new MonitoringSystem() {
};
末尾有一个分号!它在那里是有原因的。我们同时声明类(使用大括号)并创建它的实例(使用();
)。我们的三个对象中的每一个都startMonitoring()
以自己的方式重写该方法。最后,我们简单地对它们中的每一个调用这个方法:
generalModule.startMonitoring();
errorModule.startMonitoring();
securityModule.startMonitoring();
控制台输出:
Starting to monitor general indicators!
Starting to monitor errors!
Starting to monitor security!
就是这样!我们实现了我们的目标:我们创建了三个MonitoringSystem
对象,以三种不同的方式覆盖了一个方法,并调用了它三次。所有三个模块都已成功调用并正在运行。同时,我们程序的结构也变得简单多了!毕竟,现在可以从程序中完全删除GeneralIndicatorMonitoringModule
、ErrorMonitoringModule
和类!SecurityModule
我们根本不需要它们——没有它们我们也做得很好。 如果我们的每个匿名类都需要一些不同的行为,例如它自己的其他人没有的特定方法,我们可以轻松地添加它们:
MonitoringSystem generalModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor general indicators!");
}
public void someSpecificMethod() {
System.out.println("Specific method only for the first module");
}
};
Oracle 文档提供了一个很好的建议:“如果您只需要使用本地类一次,请使用 [匿名类]。” 匿名类是成熟的内部类。相应地,它可以访问外部类的变量,包括静态变量和私有变量:
public class Main {
private static int currentErrorCount = 23;
public static void main(String[] args) {
MonitoringSystem errorModule = new MonitoringSystem() {
@Override
public void startMonitoring() {
System.out.println("Starting to monitor errors!");
}
public int getCurrentErrorCount() {
return currentErrorCount;
}
};
}
}
它们与本地类有一些共同之处:它们仅在声明它们的方法内可见。errorModule
在上面的示例中,任何在方法之外访问对象的尝试都main()
将失败。匿名类从它们的“祖先”(内部类)继承还有一个更重要的限制:匿名类不能包含静态变量和方法。在上面的示例中,如果我们尝试将getCurrentErrorCount()
方法设为静态,编译器将生成错误:
// Error! Inner classes cannot have static declarations
public static int getCurrentErrorCount() {
return currentErrorCount;
}
如果我们尝试声明一个静态变量,我们会得到相同的结果:
MonitoringSystem errorModule = new MonitoringSystem() {
// Error! Inner classes cannot have static declarations!
static int staticInt = 10;
@Override
public void startMonitoring() {
System.out.println("Starting to monitor errors!");
}
};
而我们今天的课程也结束了!但是即使我们调查了最后一组嵌套类,我们还没有完成这个话题。关于嵌套类,我们还会学到什么?你一定会很快发现!:)
GO TO FULL VERSION