Reflection学习总结

● 什么是反射
    反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。
    在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
    反射是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性和方法。Java 的这一能力在实际应用中用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。

● 反射能干什么
    反射是 Java 被视为动态(或准动态)语言的关键,允许程序于执行期通过 Reflection API 取得任何已知名称的class 的內部信息,包括 package、superclass、implemented interfaces、inner classes, outer class, fields、constructors、methods、modifiers,並可于执行期生成instances、变更 fields 內容或唤起 methods。
    这里将示例通过反射获得class的fields,methods,constructors。其他class的信息通过Reflection API也可以反射获得。

● 如何使用反射
1. 反射的路口
    使用反射的起点是 java.lang.Class 实例。如果您希望与预先定义的类协作,那么Java语言提供一种直接获得 Class 实例的简便快捷方式:
Class clas = MyClass.class;

    当您使用这一项技术时,装入类涉及的所有工作在幕后进行。但是,如果您需要在运行时从某些外部源读取类名,这种方法并不适合。实际上,您需要使用一个类装入器来查找类信息。以下介绍一种方法:
// "name" is the class name to load
Class clas = null;
try {
  clas = Class.forName(name);
} catch (ClassNotFoundException ex) {
  // handle exception case
}
// use the loaded class

2. 反射构造函数
    Class 对象为您提供接入类元数据的反射的所有基本hook。这类元数据包括类自身的信息,如包,类的父类,以及该类实施的接口。它还包括该类定义的构造函数、字段和方法的详细信息。
    对于构造函数、字段和方法三类组件中的任何一类来说java.lang.Class 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用:
Constructor getConstructor(Class[] params)-- 获得使用特殊的参数类型的公共构造函数,
Constructor[] getConstructors()-- 获得类的所有公共构造函数
Constructor getDeclaredConstructor(Class[] params)-- 获得使用特定参数类型的构造函数
Constructor[] getDeclaredConstructors()-- 获得类的所有构造函数(与接入级别无关)
    每类这些调用都返回一个或多个java.lang.reflect.Constructor 函数。这种Constructor 类定义 newInstance 方法,它采用一组对象作为其唯一的参数,然后返回新创建的原始类实例。该组对象是用于构造函数调用的参数值。为了解释这一工作流程,假设您有一个 MyString 类和MyString 类中有一个使用一对String s的构造函数,如清单①所示。
清单①:定义MyString类,其中有使用两个String的构造函数。
public class MyString {
	private String str1;
	private String str2;
	public MyString(String str1, String str2) {
		this.str1 = str1;
		this.str2 = str2;
	}
	public void print() {
		System.out.println(str1);
		System.out.println(str2);
	}
}

清单②:使用反射获得构造函数,并调用获得的构造函数。
public class ReflectionTest {
	public static void main(String[] args) {
		Class myStringCla = null;
		Class[] types = new Class[]{String.class, String.class};
		Constructor<Object> cons = null; 
		MyString myString = null;
		try {
		     myStringCla  =
                   Class.forName("com.fujitsu.reflectioon.MyString");
			 cons = myStringCla.getConstructor(types);
			 Object[] objs = new Object[]{"haha", "reflect success"};
			 myString = (MyString) cons.newInstance(objs);
			 myString.print();
		} catch (ClassNotFoundException e) {
......

    控制台输出"haha reflect success",说明使用Reflection API成功反射获得MyString的构造函数,通过获得的构造函数成功创建了MyString的对象。

3. 反射字段信息
    获得字段信息的Class反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用的是字段名:
Field getField(String name)-- 获得命名的公共字段
Field[] getFields()-- 获得类的所有公共字段
Field getDeclaredField(String name)-- 获得类声明的命名的字段
Field[] getDeclaredFields()-- 获得类声明的所有字段
    尽管与构造函数调用类似,但是在字段方面仍存在一个重要的区别点:前两个变量返回可以通过类接入的公共字段的信息,即使它们来自于祖先类。后两个变量返回类直接声明的字段的信息,与字段的接入类型无关。
    调用返回的java.lang.reflect.Field实例定义所有基本类型的 getXXX 和 setXXX 方法,以及与引用类型协作的通用get和set 法。您可以根据实际的字段类型自行选择一种适当的方法,而 getXXX 方法将自动处理扩展转换(如使用 getInt 方法来检索一个字节值)。
清单③显示使用字段反射获得MyString的str1,并修改str1字段的信息:
Field field = myStringCla.getDeclaredField("str1");
			 field.setAccessible(true);
			 Object str =  field.get(myString);
			 str = "reflect filed success";
			 field.set(myString, str);
			 System.out.println(myString.getStr1());

    控制台输出"reflect filed success",说明使用Reflection API成功反射获得MyString的字段信息,并成功修改了MyString的字段。
4. 反射方法
    获得方法信息的Class反射调用与用于构造函数和字段的调用非常类似:
Method getMethod(String name, Class[] params)-- 使用特定的参数类型,获得命名的公共方法
Method[] getMethods()-- 获得类的所有公共方法
Method getDeclaredMethod(String name, Class[] params)-- 使用特写的参数类型,获得类声明的命名的方法
Method[] getDeclaredMethods()-- 获得类声明的所有方法
    与字段调用一样,前两个变量返回可以通过类接入的公共方法的信息,即使它们来自于祖先类。后两个变量返回类声明的方法的信息,与方法的接入类型无关。
    调用返回的java.lang.reflect.Method实例定义了一种 invoke 方法,可以用invoke 来调用被反射的Method。这种invoke方法有两个参数,为调用提供类实例和参数值数组。
清单④进一步显示反射获得MyString实例的print方法。并使用Method的invoke方法调用print方法,清单④示例如下。
Method method = myStringCla.getDeclaredMethod("print");
		method.invoke(myString, null);

    控制台输出" haha reflect Method success",说明使用Reflection API成功反射获得MyString的print方法,并使用Method的invoke方法调用MyString的print方法。
    以上介绍了如何使用反射获得实例的构造函数,字段,方法。当然我们还可以通过反射获得实例的更多信息,这里就不一一介绍。

你可能感兴趣的:(java,reflection)