第一章 基础

本章内容:

  • 反射基础
  • Class 基础
  • 通过反射调用方法

我们常常遇到一些可以通过反射简单、优雅解决的问题。如果没有反射,解决方法变得凌乱、笨重、脆弱。考虑如下场景:

  • 你的项目经理正在致力实现一个可插拔框架,该系统即使在构建、部署完成之后依然可以添加新组建。你创建了一些接口,准备了一个给 JAR 包打补丁的机制,但是你知道这并不能完全满足可插拔的需求。
  • 在开发一个客户端应用几个月后,市场告诉你如果采用一个不同的远程机制将有助于销售,虽然更换方案是有利于商业的决定,但是你必须重新实现所有的远程接口。
  • 你的模块的公用方法需要仅仅接受特定包内类的调用,以防止外部调用者滥用你的模块。你在所有 API 上添加一个参数,用来持有调用类的包名,但现在所有合法调用者必须修改他们的代码,而不受欢迎的代码可以通过伪造包名进行访问。

这些场景依次说明模块化、远程调用和安全的问题,他们似乎并无相似之处。但他们确实有相同的地方:他们都存在需求的变化,必须根据程序的结构修改代码才能满足需求。

重新实现接口、给 JAR 打补丁、修改方法调用都是无聊、机械的任务。他们如此机械,以至于可以通过算法来描述操作步骤:

  1. 检查程序的结构或者数据。
  2. 根据检查结果做出决定。
  3. 根据决定,修改程序的行为、结构或者数据。

虽然上述步骤在程序员看来非常熟悉,但是你无法想象程序能够做到。结果,修改代码的任务必须由一个坐在键盘前的程序员,而非运行在电脑上的程序来完成。学习反射可以让你超越前面的假设,让程序代替你修改代码。考虑下面简单的例子:

public class HelloWorld {
    public void printName() {
        System.out.println(this.getClass().getName());
    }
}

下面这行代码

(new HelloWorld()).printName();

在标准输出打印 HelloWorld 字符串。现在假设 xHelloWorld 类或者它的任意子类的实例,下行的代码

x.printName();

将把该类名字的字符串发送到标准输出。

这个简单的例子比它表面上更激动人心--它包含前面提到的所有步骤。首先,printName 方法检查调用对象的类型。检查结束之后,打印什么内容的决定代理给 x 对象的类(this.getClass())。最后该方法根据代理的返回结果打印不同的内容。如果没有被覆盖,printName 方法在 HelloWorld 的子类上表现与在 HelloWorld 类上不同。printName 方法非常灵活,它可以适应不同类型,从而表现出不同行为。在本书的例子中,我们会展现更多通过反射保持灵活性的方法。

results matching ""

    No results matching ""