嗨,你好,下面我们一起学习反射。
反射也叫 reflection,
简单地说就是我们程序里面能够认识自己,能够
知道自己有哪些类,然后每一个类有什么样的方法,有什么样的
字段,对于每一个对象还可以调用它的任意一个方法
和属性,所以我们简单地说反射就是程序自己知道自己。
当然自己知道自己这是很重要的,比如说一些框架性的应用程序,
比如说我们要编一个 plugin,那就是可以一些插件,
那么这个插件它用到我们的程序里面,我们这个框架那个程序自动就能识别出它来,
这就是我们说的这个反射。反射当然对我们现实世界也是很重要的,
反射我们知道又是照镜子的意思,自己认识自己,自己照自己。
所以我从小就不太敢照镜子,因为我怕看到镜子里的那个人。
直到有一次我看见阿波罗神庙上说有一句话叫做认识你自己,
那个时候我才觉得那个反射是多么的重要。
那对我们程序来说,它怎么认识自己呢? 那就是有一个特殊的一个对象叫做
class 对象,这个 class 对象呢, 我们可以取得这个 class 对象。
关于反射它都在 java.lang.reflect 这个包里头。
首先取得类的 class,任何一个类我们要取得它的 class
这个的信息, 得到 class 对象有三种方法,第一种就是一个类名.class,
比如说 String.class,这个写法相当特殊,
就是一个类名 点 class 就能得到它。第二个
就是如果有一个对象,我们用 getClass 方法可以
得到它,因为任何一个 object 都有一个 getClass 这个方法。
还有 Class.forName
它可以针对一个 类然后得到它的 class
信息,这个类的名字注意它是一个字符串,要写全称。
也就是比如说 string,java.lang.string,要写全称。
所以这三种方法,无论是类,还是对象,还是有个名字, 我们可以得到这个
class 信息,当然这个 名字得到的 class 信息对我们很多时候是很方便的。
那么有了这个类,我们下一步呢 就能得到这些字段以及它的方法的信息。
我们下面来看一个例子,就是有了这个 class 怎么得到它的
字段啊,方法啊等等这些信息。请看这里, 那么有一个
class 对象,我们可以得到它的,用这个对象得到 getName,
可以判断它是不是一个 interface, 等等,是不是一个基本类型等等。
这是一方面,另一方面我们可以得到它所在的 Package,它所在的包, 还可以得到它的
Modifiers,得到它的那个修饰符。下面我们还可以得到它的 DeclaredFields
就是这个类所声明的 这些字段,对每一个字段我们又可以遍历这所有的字段,
得到它的修饰符,得到它的名字,得到它的类型,等等。
同样呢我们还可以得到它的构造方法,
getDeclaredConstructors,还可以得到它的所声明的 Methods,
得到它的方法,也就是它的函数,那这些 方法我们也可以得到它的修饰符,得到它的名字,
当然还可以得到任何一个方法,它的参数。
总的说来我们有了这个 class 对象,我们就可以得到它里面的 所有的那些信息。
在程序里面我们用的更多是动态创建这个对象, 那么有了一个
class,我们就可以用那个 class 创建相应的实例, 调用相关的这些方法,也就是说
我们取得了这个方法以后呢,我们还可以利用这个方法 来调用,带上参数来调用
它。下面我们看一个应用示例, 这个示例就是加了一个反射功能的
HTTPServer, 是一个简单的 HTTP 服务器。
我们知道 HTTP 服务器就是当有客户端连接我们的时候呢,
我们取得信息,然后再响应给客户端。
所以它当然可以用一个 Socket 来 实现。我们先看看那个程序的应用效果。
请看这里,那这个程序我们运行起来以后我用浏览器去访问它, 用
aaa.htm 它就得到这样,把这个网页的信息显示出来了。
如果我用另一个地址,比如说用一个 EchoServlet,我可以用一个就是,
这个 Servlet 我们可以把它理解为一个小服务,这个 EchoServlet
就是 它能显示说你提交的信息是什么什么。我们这里提交的信息
一般来说都是这个样子的,get 一个网址,get 一个网站,然后
http/1.1, 这说明它网络的协议,然后后面还有一系列信息,
Host 等等,还有其他的一些信息。
这个就是客户端,也就是浏览器提交给服务端的,服务端把它显示出来。
我们还有一个就是 sqrt 平方根的 servlet,
然后后面带一个问号,带个参数,n 等于 34。
它的结果就是算出了 34 的平方根是 5.83, 等等。所以这样一个程序我们在后台服务它是怎么实现的呢?
我们看看,请看这里。那服务程序呢, 它的主要作用就是有一个
ServerSocket, 这个 ServerSocket
呢,它要接收服务,接收 客户端提出来的请求,我们利用
accept 方法, 那 accept 方法得到一个 socket 以后呢,
然后我们就针对这一个 socket 进行服务,
所以我们用了一个 service 方法,那这个服务呢它首先
从客户端那儿得到 客户发出来的请求,那这个请求呢当然我们要解析它,
解析的过程呢主要是要得到它的第一行,也就是 get
什么什么一个网址,所以我们把这个网址给取出来 uri, 这个网址,根据这个网址里面有两类,一类是
servlet, 就是一个小服务,这个小服务我们就调这个小服务的一个提供,
不同的小服务来提供的这个服务,否则 的话我们就是文件响应。那这里面就是
进行这个小服务是怎么做的呢?这个小服务我们根据它的 servlet 后面带的这个网址,得到这个
servletName, 得到这个网址以后,这里具体的
得到这个网址了,那得到这个网址呢就看到它的 servlet 的名字,
servlet 的名字呢,下一步就很关键了,这是我们的一个重点, 根据
servletName 然后从这个 Cache 里面 取得看已经是不是有这个
servlet,如果没有的话我们下面一步呢 就是我们用反射了。用这个
servletName 用 class.forName, class.forName
就得到这个 servlet 的类, 有了这个类我们可以用
newInstance, newInstance
创建这个对象的一个实例,这个 servlet 这么一个实例。
然后呢我们就可以呢有了这个实例以后呢,我们就可以调用它的初始化,
我们也可以调用它的服务,这个服务方法里头呢如果平方根, 那它就是调这个平方根的计算,如果是
Echo 的服务呢, 它就是把那个信息回写,就是这样一个过程。如果,当然如果是文件
这种网址,我们直接把文件读出来然后 发送到客户端就可以了。在这里面这个
servlet 我们是定义了一个接口,我们知道 Java 里面经常定义接口,
这个接口它里面有个初始化或提供服务,那具体在实现的时候, 比如说
Echo 这个 servlet 那我们就是把它已有的信息呢
把它请求的信息再把它 write 出去, 再把它写出去。那么平方根的服务呢我们就从它的
网址解析出等号后面的那个数字,用 params.
substring 得到它那个数字,然后这个数字 再用
sqrt 进行求平方,所以就实现了一个 我们有不同的这个
servlet 的名字,我们就可以创建这样的一个实例。
然后呢,从而呢提供了不同的服务,在一定意义上这就是一个框架的程序。
也就是说我们有一个不同的 servlet 它都能识别,能识别,然后呢
再调用它,创建它的实例, 然后提供服务。那么其他的框架程序呢,
也可以类似地来做。