Java 反射机制 (一)

2014-11-24 10:51:00 · 作者: · 浏览: 2

一、什么是反射机制?为什么要用反射机制?

所谓Java反射机制是指,程序在运行状态时,可以加载一个运行时才得知名称的class,能够知道这个类的所有属性和方法,并生成其对象实体、或对其fields设值、或调用其方法;即利用反射技术,根据一个类名称,可以得到该类的构造方法、属性、方法等信息,并创建其对象。用一句话来概括,反射就是加载一个运行时才知道的类以及它的完整内部结构。

那我们为什么要用反射机制呢?

第一,反射的目的就是为了扩展未知的应用。比如,我们写好了一个软件,其中定义了一些接口,程序已经过编译并且发布了,当我们以后需要扩展功能时,不可能去修改已经安装在别人机器上的软件源码,此时我们只需要另写一个插件,让其实现某些接口即可,程序运行时,通过反射技术动态的创建和编译新写的类,并获知其内部细节,就可以调用其方法了;

第二,在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法以new的方式硬编码,而必须用到反射才能创建这个对象。


二、如何使用反射?

1. 反射中的类

Java反射机制的实现要借助于4个类:Class、Constructor、Field、Method,其中,

Class——类对象(Class 类的实例表示正在运行的 Java 应用程序中的类和接口 )

Constructor——类的构造器对象

Field——类的属性对象

Method——类的方法对象

可以看到一个类的各个组成部分(构造方法、属性、方法)都被封装成一个单独的类。

而java.lang.Class 提供了四种独立的反射调用,以不同的方式来获得以上信息。

获取构造方法的反射调用:

Constructor getConstructor(Class[] params) -- 匹配给定的参数类型来得到相应的公共构造函数  

Constructor[] getConstructors() -- 获得类的所有公共构造函数  

Constructor getDeclaredConstructor(Class[] params) -- 匹配给定的参数类型来得到相应 的构造函数 (从public、private、protect、默认中匹配选择)

Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数

获取字段属性的反射调用:

Field getField(String name) -- 获得匹配给定名称参数的公共字段  

Field[] getFields() -- 获得类的所有公共字段  

Field getDeclaredField(String name) -- 获得匹配给定名称参数的字段(从public、private、protect、默认中匹配选择)  

Field[] getDeclaredFields() -- 获得类声明的所有字段

获取方法的反射调用:

Method getMethod(String name, Class[] params) -- 匹配给定的方法参数名name和参数 类型param的公共方法  

Method[] getMethods() -- 获得类的所有公共方法  

Method getDeclaredMethod(String name, Class[] params) -- 匹配给定的方法参数名 name和参数类型param的方法(从public、private、protect、默认中匹配选择)

Method[] getDeclaredMethods() -- 获得类声明的所有方法

2. 反射的使用步骤

步骤一:获取类的Class对象和创建类的实例对象

· 获取类的Class对象:

① 通过Class.forName(String className) ——className表示完整的类路径

② 运用.class语法,如String.class

③ 调用Object的getClass()方法,如:

String str = "abc";

Class c = str.getClass();

④ 对于基本数据类型,可以使用其包装类中与定义好的TYPE字段,如

Class c = Integer.TYPE;

其中,以“.class”来创建Class对象的引用时,不会自动地初始化该Class对象,而使用Class.forName()立即就进行了初始化。例如,如果一个static final属性值是“编译期常量”(public static final int A = 2;),那么这个值不需要对类进行初始化就可以被读取。

注:获取Class的引用,其实就是在加载或寻找编译期编译出来的“.class”字节码文件。


附(

Class.forName() 和 ClassLoader.loadClass()的区别


Class.forName("xx")等同于 Class.forName("xx",true,CALLClass.class.getClassLoader()),第二个参数(bool)表示 装载类的时候是否初始化该类,即调用类的静态块的语句及初始化静态成员变量并为其分配空间。
ClassLoader loader = Thread.currentThread.getContextClassLoader(); //也可以用 (ClassLoader.getSystemClassLoader())

Class cls = loader.loadClass("xx"); //这句话没有执行初始化,其实与 Class.forName("xx.xx",false,loader)是一致的,只是loader.loadClass("xx")执行的 是更底层的操作。
只有执行cls.NewInstance()才能够初始化类,得到该类的一个实例

· 创建类的实例对象,如:

Class c = Class.forName("com.ReflectionTest.");

Object obj = c.newInstance();


步骤二:调用诸如getDeclaredMethods()的方法,已取得类的构造方法、属性或者方法

例如:

Class c = Class.forName("java.lang.String");

Method m[] = c.getDeclaredMethods();


步骤三:对于第二步骤取得的属性、方法、构造方法等采用Reflection API进行处理


下面引用一个例子:

/**
* 通过java的反射机制动态修改对象的属性
* @param o
*/
public void test2(Customer o) {
try {
Class c = o.getClass();
//getMethod方法第一个参数指定一个需要调用的方法名称,第二个参数是需要调用方法的参数类型列表,如无参数可以指定null,该方法返回一个方法对象 
Method sAge = c.getMethod("setAge", new Class[] { int.class });
Method gAge = c.getMethod("getAge", null);
Method sName = c.getMethod("setName", new Class[] { String.class });