Java为什么能够支持Reflection?答案是Java运行时仍然拥有类型信息,它包含了这个类一切:它有哪些字段、哪些方法,各是何种保护级别等等,还有这个类依赖于哪些类。在Java中,类信息以对象的形式存放,这些对象是一种元对象,它们的类型就是Class。拥有了这些信息,无论是动态创建对象还是调用某些方法都是轻而易举的。在C++中,通过RTTI(运行时类型识别),我们也可以知道类的一些信息,但为什么C++中却没有 Reflection,原因是类型信息不完整。RTTI这个名字本身就告诉我们,C++的类型信息是用来进行类型识别的,因此,它也不需要其它额外的信息。并不是C++无法做到这一点,而是C++不希望给用户增加额外的负担。有所得,必然有所失,因此,C++放弃了元对象。关于这一点,C++之父 Bjarne Stroustrup在他的《C++语言的设计与演化》的14.2.8节中进行了深入的讨论。
元对象是Java Reflection的物质基础,那它的精神基础又是什么呢?Java为什么要支持Reflection?经过上面的讨论,我们把这个问题再进一步,为什么Java要提供元对象?
讨论这个问题,我们还要拉回到十年前,那时Java刚刚来到正式登上历史的舞台。Java实际上诞生在这之前的数年,那时候还叫Oak,环境所限使得这一划时代的杰作甫一出炉便被束之高阁。当Netscape掀起了为网络大戏的序幕,Java得以凤凰涅槃,这其中很重要的一个原因就是Java是以网络为中心的。
仔细观察,我们会发现,Java的整个基础架构的设计都是为网络服务。首当其冲的便是Java中最著名的跨平台。其实,在Java之前的年代,人们也需要考虑平台之间的可移植性,但这种移植大多数集中在源码一级,这也就是C语言可以流行的原因之一,在单机环境下,平台的差异并不那么明显。网络的出现使平台之间差异凸现出来,因为网络可能会连接各种各样的计算机和设备。没错,还有设备,你也许知道Java最初的开发是和嵌入式设备相关的。一旦应用可以跨平台,程序开发和后期管理维护工作将得到极大的简化,可移植性也从源码级晋升到二进制级(Java字节码)。所以,跨平台实际上也是为了网络打基础。 Java中另一个重要的买点——安全性与网络之间的关系更为密切,谁都可以想出几条理由,把二者关联起来。
再来具体看看Java的基础架构如何对网络进行支持的。还记得Java最初是怎么吸引人的吗?没错,Applet。熟悉原理的朋友都知道,Applet的运行是把远程的类文件下载到本地来执行的。相对于本地硬盘,网络给我们的感觉就是一个字————慢。如果Java采用传统可执行文件组织方式,即一个完整的可执行文件,把整个Applet下载下来的运行,只怕等到花儿也谢了。Java采用的手法是把文件拆开,以类为单位进行组织,这就是我们今天见到的class文件。这样,执行的过程就变成第一个类下载之后就可以运行,大大节省了最初的等待时间。好的设计会把程序分成若干的模块,所以,绝大多数程序不可能写在一个类中。因此,类文件中必须包含它所用到类。对于引导部分,我们可以让它以特定的方式开始执行,比如把我们耳熟能详的main方法放在特定的字节,但对于没有定法的任意方法,是没有办法规定的,而一个类调用另一个类的方法就是这样随意,因此类文件中必须包含这个类方法的信息,进一步字段信息也会加进来,这样几乎一个完整类的信息就出来了,而这些信息对应的恰好是元对象。所以,元对象出现在Java基础架构中。
有了元对象,Reflection也成了一件顺其自然的事情。有了Reflection,Java也就拥有了动态扩展的能力,这样就可以极大的提高程序的灵活性。
关于Java基础结构对网络的支持还可以再说几句。class文件经过了精心的设计,本身相当紧凑,其目的就是为了方便在网络上传输,而JAR文件的出现,其目的也是为了方便网络传输,因为如果每次只传输一个类,大量的时间都被浪费在建立网络连接的过程中,JAR文件使得一次传输多个类成为可能,而且我们还知道JAR文件中的数据是经过压缩的,这样可以进一步减少下载时间。Java基础架构对网络的支持,《深入Java虚拟机》(第二版)的4.3节进行了很好阐述,有兴趣不妨看一下。
对Reflection思考让我有机会对Java本身的设计进行深入的思考。一个好的软件设计需要一个核心理念作为支撑,所有的一切都是围绕核心进行的,而对于Java,这个核心就是网络。