Java反射机制
摘要
Reflection
是
Java
被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过
Reflection APIs
取得任何一个已知名称的
class
的内部信息,包括其
modifiers
(诸如
public, static
等等)、
superclass
(例如
Object
)、实现之
interfaces
(例如
Cloneable
),也包括
fields
和
methods
的所有信息,并可于运行时改变
fields
内容或唤起
methods
。本文借由实例,大面积示范
Reflection APIs
。
关于本文:
读者基础:具备
Java
语言基础。
本文适用工具:
JDK1.5
关键词:
Introspection
(内省、内观)
Reflection
(反射)
有时候我们说某个语言具有很强的动态性,有时候我们会区分动态和静态的不同技术与作法。我们朗朗上口动态绑定(
dynamic binding
)、动态链接(
dynamic linking
)、动态加载(
dynamic loading
)等。然而
“
动态
”
一词其实没有绝对而普遍适用的严格定义,有时候甚至像对象导向当初被导入编程领域一样,一人一把号,各吹各的调。
一般而言,开发者社群说到动态语言,大致认同的一个定义是:
“
程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言
”
。从这个观点看,
Perl
,
Python
,
Ruby
是动态语言,
C++
,
Java
,
C#
不是动态语言。
尽管在这样的定义与分类下
Java
不是动态语言,它却有着一个非常突出的动态相关机制:
Reflection
。这个字的意思是
“
反射、映象、倒影
”
,用在
Java
身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的
classes
。换句话说,
Java
程序可以加载一个运行时才得知名称的
class
,获悉其完整构造(但不包括
methods
定义),并生成其对象实体、或对其
fields
设值、或唤起其
methods
1
。这种
“
看透
class
”
的能力(
the ability of the program to examine itself
)被称为
introspection
(
内省、内观、反省
)。
Reflection
和
introspection
是常被并提的两个术语。
Java
如何能够做出上述的动态特性呢?这是一个深远话题,本文对此只简单介绍一些概念。整个篇幅最主要还是介绍
Reflection APIs
,也就是让读者知道如何探索
class
的结构、如何对某个
“
运行时才获知名称的
class
”
生成一份实体、为其
fields
设值、调用其
methods
。本文将谈到
java.lang.Class
,以及
java.lang.reflect
中的
Method
、
Field
、
Constructor
等等
classes
。
“
Class
”
class
众所周知
Java
有个
Object class
,是所有
Java classes
的继承根源,其内声明了数个应该在所有
Java class
中被改写的
methods
:
hashCode()
、
equals()
、
clone()
、
toString()
、
getClass()
等。其中
getClass()
返回一个
Class object
。
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
标准库源码的改动办法。
Class
是
Reflection
故事起源。针对任何您想探勘的
class
,唯有先为它产生一个
Class object
,接下来才能经由后者唤起为数十多个的
Reflection APIs
。这些
APIs
将在稍后的探险活动中一一亮相。
#001 public final
#002 class
Class
<T>
implements java.io.Serializable,
#003 java.lang.reflect.GenericDeclaration,
#004 java.lang.reflect.Type,
#005 java.lang.reflect.AnnotatedElement {
#006
private
Class() {}
#007 public String
toString
() {
#008 return ( isInterface() ? "interface " :
#009 (isPrimitive() ? "" : "class "))
#010 + getName();
#011 }
...
图
1
:
Class
class
片段。注意它的
private empty ctor
,意指不允许任何人经由编程方式产生
Class
object
。是的,其
object
只能由
JVM
产生。
“
Class
”
object
的取得途径
Java
允许我们从多种管道为一个
class
生成对应的
Class
object
。
图
2
是一份整理。
Class object
诞生管道
|
示例
|
运用
getClass()
注:每个
class
都有此函数
|
String str = "abc";
Class c1 = str.getClass();
|
运用
Class.getSuperclass()
2
|
Button b = new Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();
|
运用
static method
Class.forName()
(最常被使用)
|
Class c1 = Class.forName ("java.lang.String");
Class c2 = Class.forName ("java.awt.Button");
Class c3 = Class.forName ("java.util.LinkedList$Entry");
Class c4 = Class.forName ("I");
Class c5 = Class.forName ("[I");
|
运用
.class
语法
|
Class c1 = String.class;
Class c2 = java.awt.Button.class;
Class c3 = Main.InnerClass.class;
Class c4 = int.class;
Class c5 = int[].class;
|
运用
primitive wrapper classes
的
TYPE
语法
|
Class c1 = Boolean.TYPE;
Class c2 = Byte.TYPE;
Class c3 = Character.TYPE;
Class c4 = Short.TYPE;
Class c5 = Integer.TYPE;
Class c6 = Long.TYPE;
Class c7 = Float.TYPE;
Class c8 = Double.TYPE;
Class c9 = Void.TYPE;
|
图
2
:
Java
允许多种管道生成
Class object
。