最近由于工作上需要,对reflection做了一番了解,以下是学习总结,有不少内容是借鉴的,但已无法找到源文出处,还请原文作者见谅。
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class
的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields
和methods的所有信息,并可于运行时改变fields内容或唤起methods。
Java为什么能够支持Reflection?答案是Java运行时仍然拥有类型信息,它包含了这个类一切:它有哪些字段、哪些方法,各是何种保护级别等等,还有这个类依赖于哪些类。在Java中,类信息以对象的形式存放,这些对象是一种元对象,它们的类型就是Class。拥有了这些信息,无论是动态创建对象还是调用某些方法都是轻而易举的。在C 中,通过RTTI(运行时类型识别),我们也可以知道类的一些信息,但为什么C 中却没有Reflection,原因是类型信息不完整。RTTI这个名字本身就告诉我们,C 的类型信息是用来进行类型识别的,因此,它也不需要其它额外的信息。并不是C 无法做到这一点,而是C 不希望给用户增加额外的负担。有所得,必然有所失,因此,C 放弃了元对象。关于这一点,C 之父Bjarne Stroustrup在他的《C 语言的设计与演化》的14.2.8节中进行了深入的讨论。
“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。
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()
Button b = new Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();
运用static methodClass.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;
Class.forName(xxx.xx.xx) 返回的是一个类, .newInstance() 后才创建一个对象 Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段
Class aClass = Class.forName(xxx.xx.xx);
Object anInstance = aClass.newInstance();
Class.forName("").newInstance()返回的是object
但是创建实例时有个限制,你的class构造函数不能包括参数,而且你需要自己来手工cast实例
用时:实例:
protected final static String CLASS_TEST = "Test";
protected final static String CLASS_PROBLEM = "Problem";
protected final static String CLASS_PARAMETER = "Parameter";
getProblem(vx, vy){
Object result;
result = Class.forName(CLASS_PROBLEM).newInstance();
........
}
getParameters(){
Object result;
result = Class.forName(CLASS_PARAMETER).newInstance();
........
}
buildTest(){
String error_msg = (String) invokeMethod(
Class.forName(CLASS_TEST).newInstance(),
"add",
new Class[]{
Class.forName(CLASS_PROBLEM),
Class.forName(CLASS_PARAMETER)},
new Object[]{
getProblem(vx, vy),
getParameters()});
}
protected Object invokeMethod(Object o, String name, Class[] paramClasses, Object[] paramValues) {
Method m;
Object result;
result = null;
try {
m = o.getClass().getMethod(name, paramClasses);
result = m.invoke(o, paramValues);
}
catch (Exception e) {
e.printStackTrace();
result = null;
}
return result;
}
class Test{
public static model train_model(problem prob, parameter param)
}
class Problem{
...........
}
Parameter{
...............
}
附:
大家或许有些疑问,jdbc连接数据库时,有的写法是Class.forName(xxx.xx.xx);而有一些:Class.forName(xxx.xx.xx).newInstance(),为什么会有这两种写法呢?
Class.forName(xxx.xx.xx) 返回的是一个类,
.newInstance() 后才创建一个对象
Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段
在JDBC规范中明确要求这个Driver类必须向DriverManager注册自己,即任何一个JDBC Driver的Driver类的代码都必须类似如下:
public class MyJDBCDriver implements Driver {
static {
DriverManager.registerDriver(new MyJDBCDriver());
}
}
所以我们在使用JDBC时只需要Class.forName(XXX.XXX);就可以了