Java 反射机制 <o:p> </o:p>
<o:p> </o:p>
摘要<o:p></o:p>
Reflection 是 Java 被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过 Reflection APIs 取得任何一个已知名称的 class 的内部信息,包括其 modifiers (诸如 public, static 等等)、 superclass (例如 Object )、实现之 interfaces (例如 Cloneable ),也包括 fields 和 methods 的所有信息,并可于运行时改变 fields 内容或唤起 methods 。本文借由实例,大面积示范 Reflection APIs 。 <o:p> </o:p>
<o:p> </o:p>
关于本文:<o:p></o:p>
读者基础:具备 Java 语言基础。 <o:p> </o:p>
本文适用工具: JDK1.5<o:p></o:p>
<o:p> </o:p>
关键词:<o:p></o:p>
Introspection (内省、内观) <o:p> </o:p>
Reflection (反射) <o:p> </o:p>
<o:p> </o:p>
<o:p> </o:p>
有时候我们说某个语言具有很强的动态性,有时候我们会区分动态和静态的不同技术与作法。我们朗朗上口动态绑定( dynamic binding )、动态链接( dynamic linking )、动态加载( dynamic loading )等。然而 “ 动态 ” 一词其实没有绝对而普遍适用的严格定义,有时候甚至像对象导向当初被导入编程领域一样,一人一把号,各吹各的调。 <o:p> </o:p>
<o:p> </o:p>
一般而言,开发者社群说到动态语言,大致认同的一个定义是: “ 程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言 ” 。从这个观点看, Perl , Python , Ruby 是动态语言, C++ , Java , C# 不是动态语言。 <o:p> </o:p>
<o:p> </o:p>
尽管在这样的定义与分类下 Java 不是动态语言,它却有着一个非常突出的动态相关机制: Reflection 。这个字的意思是 “ 反射、映象、倒影 ” ,用在 Java 身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的 classes 。换句话说, Java 程序可以加载一个运行时才得知名称的 class ,获悉其完整构造(但不包括 methods 定义),并生成其对象实体、或对其 fields 设值、或唤起其 methods 1 。这种 “ 看透 class ” 的能力( the ability of the program to examine itself )被称为 introspection ( 内省、内观、反省 )。 Reflection 和 introspection 是常被并提的两个术语。 <o:p> </o:p>
<o:p> </o:p>
Java 如何能够做出上述的动态特性呢?这是一个深远话题,本文对此只简单介绍一些概念。整个篇幅最主要还是介绍 Reflection APIs ,也就是让读者知道如何探索 class 的结构、如何对某个 “ 运行时才获知名称的 class ” 生成一份实体、为其 fields 设值、调用其 methods 。本文将谈到 java.lang.Class ,以及 java.lang.reflect 中的 Method 、 Field 、 Constructor 等等 classes 。 <o:p> </o:p>
<o:p> </o:p>
“ Class ” class<o:p></o:p>
众所周知 Java 有个 Object class ,是所有 Java classes 的继承根源,其内声明了数个应该在所有 Java class 中被改写的 methods : hashCode() 、 equals() 、 clone() 、 toString() 、 getClass() 等。其中 getClass() 返回一个 Class object 。 <o:p> </o:p>
<o:p> </o:p>
Class class 十分特殊。它和一般 classes 一样继承自 Object ,其实体用以表达 Java 程序运行时的 classes 和 interfaces ,也用来表达 enum 、 array 、 primitive Java types ( boolean, byte, char, short, int, long, float, double )以及关键词 void 。当一个 class 被加载,或当加载器( class loader )的 defineClass() 被 JVM 调用, JVM 便自动产生一个 Class object 。如果您想借由 “ 修改 Java 标准库源码 ” 来观察 Class object 的实际生成时机(例如在 Class 的 constructor 内添加一个 println() ),不能够!因为 Class 并没有 public constructor (见 图 1 )。本文最后我会拨一小块篇幅顺带谈谈 Java 标准库源码的改动办法。 <o:p> </o:p>
<o:p> </o:p>
Class 是 Reflection 故事起源。针对任何您想探勘的 class ,唯有先为它产生一个 Class object ,接下来才能经由后者唤起为数十多个的 Reflection APIs 。这些 APIs 将在稍后的探险活动中一一亮相。 <o:p> </o:p>
<o:p> </o:p>
#001 public final<o:p></o:p>
#002 class Class <T> implements java.io.Serializable,<o:p></o:p>
#003 java.lang.reflect.GenericDeclaration,<o:p></o:p>
#004 java.lang.reflect.Type,<o:p></o:p>
#005 java.lang.reflect.AnnotatedElement {<o:p></o:p>
#006 private