2007年5月16日
Java looking at Java
Java的一个很不寻常的能力,它允许一个程序检查自己
你可以确定一个对象所属的类型
你可以查看一个类的全部内容,它们的访问修饰符、父类、字段、构造函数、方法
你可以查看一个接口的内容
即使在编写代码的时候你不知道事物的名字你也可以做到:
创建一个类的实例
取得和设置一个实例的变量
调用一个对象的一个方法
创建和操作数组
我猜它之所以被称为“反射”是因为它允许一个Java应用程序像“在镜子里”看到自己一样。
What is reflection for?
在“平常”的程序中你可能用不到反射
如果你是在使用应用程序来操作应用程序,那你就需要使用反射了。
典型的应用:
一个类型查看器
一个调试程序
一个GUI创建器
一个IDE,比喻说BlueJ或Eclipse
IDE
有需要的IDE本身就是用Java编写的—它们可以做什么?
编译一个程序(简单—只是一个系统调用)
编译后加载进你的系统
弄清楚你都有些什么类,它们的构造函数和方法
执行你的main方法
替你创建对象,即使现在没有运行main方法
为对象传递信息,并显示结果
这些所有的功能,除了编译都是使用反射做到的
Class 类
要发现一个具体的类的信息,首先要取得它的Class 对象
如果你有一个 obj 对象,你可以通过下面的方式来取得它的class对象
Class c = obj.getClass();
你可以通过一个class的实例c 取得它的父类
Class sup = c.getSuperclass();
如果你已经知道一个类在编译时候的名字 (比喻说, Button) ,你可以通过下面这样的简单方法取得class对象
Class c = Button.class;
如果你知道一个类在运行期的名字(比喻说,一个String类型变量str),你可以通过下面的方法取得类对象
Class c = Class.forName(str);
取得类的名字
如果你已经有了一个class对象c,你可以通过下面的方法取得类的名字 c.getName()
getName 取得类的全路径名字; 比喻说,
Class c = Button.class;
String s = c.getName();
System.out.println(s);
会输出
java.awt.Button
类 Class 和它的方法都是在 java.lang中,所以不需要使用import使其可用。
取得所有的父类
getSuperclass() 返回一个 Class 对象 (或者 null 如果你在 Object上使用的话,它是没有父类的 )
下面的代码来自Sun的入门指南:
static void printSuperclasses(Object o) {
Class subclass = o.getClass();
Class superclass = subclass.getSuperclass();
while (superclass != null) {
String className = superclass.getName();
System.out.println(className);
subclass = superclass;
superclass = subclass.getSuperclass();
}
}
取得类的修饰符I
Class对象有一个实例方法 getModifiers() ,它返回一个int型的值
为了“解释” int 型的结果,我们要用到Modifier类的一些方法,它们位于 java.lang.reflect, 所以:
import java.lang.reflect.*;
现在我们就可以做下面的事情了:
if (Modifier.isPublic(m)) {
System.out.println("public");
}
取得类的修饰符II
Modifier 包含下面的一些方法 (只列出部分):
public static boolean isAbstract(int)
public static boolean isFinal(int)
public static boolean isInterface(int)
public static boolean isPrivate(int)
public static boolean isProtected(int)
public static boolean isPublic(int)
public static String toString(int)
这会返回一个如下所示的字符串
"public final synchronized strictfp"
取得接口
一个类可以实现0个或多个接口
getInterfaces() 返回一个 Class 对象的数组
这些是类实现的一些接口
下面的代码来自Sun的入门指南:
static void printInterfaceNames(Object o) {
Class c = o.getClass();
Class[ ] theInterfaces = c.getInterfaces();
for (int i = 0; i < theInterfaces.length; i++) {
String interfaceName = theInterfaces[i].getName();
System.out.println(interfaceName);
}
}
注意:零长度数组在Java中是合法的
判断类和接口
Class 类既能代表类也能代表接口
要确定一个给定的 Class 对象 c 是否是一个是接口,可以使用 c.isInterface()
要研究class对象的更多内容的话,你可以使用下面这些方法:
getModifiers()
getFields() // "fields" == "instance variables"
getConstructors()
getMethods()
isArray()
取得字段
public Field[] getFields() throws SecurityException
返回一个public的字段数组 (变量)
数组的长度允许是0
字段的组织顺序没有任何的规则
本身定义的和通过继承的来的变量都被返回,但是不包含 static 变量
public Field getField(String name)
throws NoSuchFieldException, SecurityException
返回指定名称的 public 字段
如果没有直接的字段被发现,那父类或者接口被递归的查询
使用字段 I
如果 f 是 Field 对象, 那么
f.getName() 返回字段的简单的名字
f.getType() 返回字段的类型 (Class)
f.getModifiers() 返回字段的Modifier
f.toString() 返回一个包含了访问修饰符,类型,和全路径字段名字的字符串
例如: public java.lang.String Person.name
f.getDeclaringClass() 返回定义字段的类的 Class 对象
使用字段 II
obj 对象的字段的值可以使用下面的方式进行操作:
boolean f.getBoolean(obj),
int f.getInt(obj),
double f.getDouble(obj),
等等,返回字段的值,假设它是那个类型或者可以扩展成那个型
Object f.get(obj) 返回一个字段的值,假设它是一个对象
void f.set(obj, value),
void f.setBoolean(obj, bool),
void f.setInt(obj, i),
void f.getDouble(obj, d),等等,设置一个字段的值
构造函数
如果 c 是一个构造函数,那么
c.getName() 返回构造函数的名字,与类的名字完全一致
c.getDeclaringClass() 返回构造函数被声明的 Class 对象
c.getModifiers() 返回构造函数的 Modifier
c.getParameterTypes() 返回一个Class 对象数组, 以声明的顺序
c.newInstance(Object[] initargs) 创建被返回一个c类的实例对象
需要使用简单类型的地方会被自动的转换
方法
public Method[] getMethods()
throws SecurityException
返回一个 Method 对象数组
返回的是类或接口的 public 成员方法,同时也包含继承的来的方法
返回的方法没有任何的顺序
public Method getMethod(String name,
Class[] parameterTypes)
throws NoSuchMethodException, SecurityException
Method 中的方法 I
getDeclaringClass()
返回一个Class 对象,它代表了这个Method 对象所被声明的类或接口
getName()
返回代表这个Method 对象的名字,以字符串的方式
getModifiers()
返回代表这个Method 对象的修饰符,以整型的方式
getParameterTypes()
返回这个Method 对象所需要的参数的 Class 对象数组,以声明的顺序出现
Method 中的方法 II
getReturnType()
返回代表这个Method 对象的返回值的一个 Class对象名字
toString()
返回一个代表这个Method 对象的字符串,通常情况下会很长
public Object invoke(Object obj, Object[] args)
在特定的对象上使用特定的参数,执行这个Method对象所代表的方法
个别的参数会被自动转换来实现一些对基本数据类型的需要
数组 I
可以采用下面的方法来判断一个obj 对象是不是一个数组
Class c = obj.getClass();
c.isArray()
要取得这个数组的元素的类型可以使用,
c.getComponentType()
如果c不是一个数组的话会返回一个 null
在java.lang.reflect 包中包含了一个 Array 类,它提供了一些 static 方法来操作数组
数组 II
要创建一个数组,
Array.newInstance(Class componentType, int size)
返回一个新创建的数组Object对象,
你可以把它造型成你想要的类型
componentType 本身就可以是一个数组
这会产生一个多维数组
维数的最大限制一般在255
Array.newInstance(Class componentType, int[] size)
返回一个新创建的多维数组Object对象 (使用 size.length 维度)
数组 III
得到数组元素的值
Array.get(Object array, int index) 返回一个 Object
Array.getBoolean(Object array, int index) 返回一个 boolean
Array.getByte(Object array, int index) 返回一个 byte
等等.
往数组中保存值,
Array.set(Object array, int index, Object value)
Array.setBoolean(Object array, int index, boolean z)
Array.setByte(Object array, int index, byte b)
等等.
结束语
很多方法都有自己的异常,我在这里没有描述
要查看详情,你参照Java API
反射在“平常”的程序中用不到,可是你真的需要它的时候却是不可替代的。
一个相关的话题-类的加载器(class loaders)—Java把需要的类读进内存
你可以编写你自己的加载器
这是别的大多数语言不包含的一种功能
posted @
2007-05-16 19:20 鱿鱼 阅读(593) |
评论 (0) |
编辑 收藏