你好!在今天的课程中,我们将继续研究嵌套类的主题。现在是最后一组的时候了:匿名内部类。让我们回到我们的图表: 匿名类 - 2就像我们上节课讲到的局部类一样,匿名类是内部类的一种……它们也有几个相同点和不同点。但首先,让我们深入探讨:为什么他们被称为“匿名”?要回答这个问题,请考虑一个简单的例子。想象一下,我们有一个不断运行并做某事的基本程序。我们想为这个程序创建一个监控系统,由几个模块组成。一个模块将跟踪性能的一般指标并维护日志。第二个将在错误日志中注册和记录错误。第三个将跟踪可疑活动:例如,未经授权的访问尝试和其他与安全相关的事情。因为从本质上讲,所有三个模块都应该简单地从程序的开头开始并在后台运行,

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基本上,我们只需要创建 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 机器内部发生以下情况:
  1. 创建一个未命名的 Java 类来实现该MonitoringSystem接口。
  2. 当编译器看到这样的类时,它要求你实现接口的所有方法MonitoringSystem(我们做了 3 次)。
  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对象,以三种不同的方式覆盖了一个方法,并调用了它三次。所有三个模块都已成功调用并正在运行。同时,我们程序的结构也变得简单多了!毕竟,现在可以从程序中完全删除GeneralIndicatorMonitoringModuleErrorMonitoringModule和类!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!");
   }

};
而我们今天的课程也结束了!但是即使我们调查了最后一组嵌套类,我们还没有完成这个话题。关于嵌套类,我们还会学到什么?你一定会很快发现!:)