java

位置:IT落伍者 >> java >> 浏览文章

Java反射机制深入研究


发布日期:2018年10月07日
 
Java反射机制深入研究

Java 反射是Java语言的一个很重要的特征它使得Java具体了动态性

在Java运行时环境中对于任意一个类能否知道这个类有哪些属性和方法?对于任意一个对象能否调用它的任意一个方法?答案是肯定的这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制

Java 反射机制主要提供了以下功能

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时调用任意一个对象的方法

Reflection 是Java被视为动态(或准动态)语言的一个关键性质这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息包括其modifiers(诸如public static 等等)superclass(例如Object)实现之interfaces(例如Serializable)也包括fields和methods 的所有信息并可于运行时改变fields内容或调用methods

一般而言开发者社群说到动态语言大致认同的一个定义是程序运行时允许改变程序结构或变量类型这种语言称为动态语言从这个观点看PerlPythonRuby是动态语言C++JavaC#不是动态语言

尽管在这样的定义与分类下Java不是动态语言它却有着一个非常突出的动态相关机制Reflection这个字的意思是反射映象倒 影用在Java身上指的是我们可以于运行时加载探知使用编译期间完全未知的classes换句话说Java程序可以加载一个运行时才得知名称 的class获悉其完整构造(但不包括methods定义)并生成其对象实体或对其fields设值或唤起其methods这种看透 class的能力(the ability of the program to examine itself)被称为introspection(内省内观反省)Reflection和introspection是常被并提的两个术语

在JDK中主要由以下类来实现Java反射机制这些类都位于javalangreflect包中

Class类代表一个类

Field 类代表类的成员变量(成员变量也称为类的属性)

Method类代表类的方法

Constructor 类代表类的构造方法

Array类提供了动态创建数组以及访问数组的元素的静态方法

下面给出几个例子看看Reflection API的实际运用

通过Class类获取成员变量成员方法接口超类构造方法等

在javalangObject 类中定义了getClass()方法因此对于任意一个Java对象都可以通过此方法获得对象的类型Class类是Reflection API 中的核心类它有以下方法

getName()获得类的完整名字

getFields()获得类的public类型的属性

getDeclaredFields()获得类的所有属性

getMethods()获得类的public类型的方法

getDeclaredMethods()获得类的所有方法

getMethod(String name Class[] parameterTypes)获得类的特定方法name参数指定方法的名字parameterTypes 参数指定方法的参数类型

getConstructors()获得类的public类型的构造方法

getConstructor(Class[] parameterTypes)获得类的特定构造方法parameterTypes 参数指定构造方法的参数类型

newInstance()通过类的不带参数的构造方法创建这个类的一个对象

下面给出一个综合运用的例子

public class RefConstructor {

public static void main(String args[]) throws Exception {

RefConstructor ref = new RefConstructor();

refgetConstructor();

}

public void getConstructor() throws Exception {

Class c = null;

c = ClassforName(javalangLong);

Class cs[] = {javalangStringclass};

Systemoutprintln(\n\n);

Constructor cst = cgetConstructor(cs);

Systemoutprintln(通过参数获取指定Class对象的构造方法);

Systemoutprintln(csttoString());

Constructor cst = cgetDeclaredConstructor(cs);

Systemoutprintln(通过参数获取指定Class对象所表示的类或接口的构造方法);

Systemoutprintln(csttoString());

Constructor cst = cgetEnclosingConstructor();

Systemoutprintln(获取本地或匿名类Constructor 对象它表示基础类的立即封闭构造方法);

if (cst != null) Systemoutprintln(csttoString());

else Systemoutprintln( 没有获取到任何构造方法!);

Constructor[] csts = cgetConstructors();

Systemoutprintln(获取指定Class对象的所有构造方法);

for (int i = ; i < cstslength; i++) {

Systemoutprintln(csts[i]toString());

}

Systemoutprintln(\n\n);

Type types[] = cgetGenericInterfaces();

Systemoutprintln(返回直接实现的接口);

for (int i = ; i < typeslength; i++) {

Systemoutprintln(types[i]toString());

}

Type type = cgetGenericSuperclass();

Systemoutprintln(返回直接超类);

Systemoutprintln(typetoString());

Class[] cis = cgetClasses();

Systemoutprintln(返回超类和所有实现的接口);

for (int i = ; i < cislength; i++) {

Systemoutprintln(cis[i]toString());

}

Class cs[] = cgetInterfaces();

Systemoutprintln(实现的接口);

for (int i = ; i < cslength; i++) {

Systemoutprintln(cs[i]toString());

}

Systemoutprintln(\n\n);

Field fs[] = cgetFields();

Systemoutprintln(类或接口的所有可访问公共字段);

for (int i = ; i < fslength; i++) {

Systemoutprintln(fs[i]toString());

}

Field f = cgetField(MIN_VALUE);

Systemoutprintln(类或接口的指定已声明指定公共成员字段);

Systemoutprintln(ftoString());

Field fs[] = cgetDeclaredFields();

Systemoutprintln(类或接口所声明的所有字段);

for (int i = ; i < fslength; i++) {

Systemoutprintln(fs[i]toString());

}

Field f = cgetDeclaredField(serialVersionUID);

Systemoutprintln(类或接口的指定已声明指定字段);

Systemoutprintln(ftoString());

Systemoutprintln(\n\n);

Method m[] = cgetMethods();

Systemoutprintln(返回类所有的公共成员方法);

for (int i = ; i < mlength; i++) {

Systemoutprintln(m[i]toString());

}

Method m = cgetMethod(longValue new Class[]{});

Systemoutprintln(返回指定公共成员方法);

Systemoutprintln(mtoString());

}

}

输出结果输出结果很长这里不再给出

运行时复制对象

例程ReflectTester 类进一步演示了Reflection API的基本使用方法ReflectTester类有一个copy(Object object)方法这个方法能够创建一个和参数object 同样类型的对象然后把object对象中的所有属性拷贝到新建的对象中并将它返回

这个例子只能复制简单的JavaBean假定JavaBean 的每个属性都有public 类型的getXXX()和setXXX()方法

public class ReflectTester {

public Object copy(Object object) throws Exception {

// 获得对象的类型

Class<?> classType = objectgetClass();

Systemoutprintln(Class: + classTypegetName());

// 通过默认构造方法创建一个新的对象

Object objectCopy = classTypegetConstructor(new Class[]{})newInstance(new Object[]{});

// 获得对象的所有属性

Field fields[] = classTypegetDeclaredFields();

for (int i = ; i < fieldslength; i++) {

Field field = fields[i];

String fieldName = fieldgetName();

String firstLetter = fieldNamesubstring( )toUpperCase();

// 获得和属性对应的getXXX()方法的名字

String getMethodName = get + firstLetter + fieldNamesubstring();

// 获得和属性对应的setXXX()方法的名字

String setMethodName = set + firstLetter + fieldNamesubstring();

// 获得和属性对应的getXXX()方法

Method getMethod = classTypegetMethod(getMethodName new Class[]{});

// 获得和属性对应的setXXX()方法

Method setMethod = classTypegetMethod(setMethodName new Class[]{fieldgetType()});

// 调用原对象的getXXX()方法

Object value = getMethodinvoke(object new Object[]{});

Systemoutprintln(fieldName + : + value);

// 调用拷贝对象的setXXX()方法

setMethodinvoke(objectCopy new Object[]{value});

}

return objectCopy;

}

public static void main(String[] args) throws Exception {

Customer customer = new Customer(Tom );

customersetId(new Long());

Customer customerCopy = (Customer) new ReflectTester(py(customer);

Systemoutprintln(Copy information: + customerCopygetId() + + customerCopygetName() +

+ customerCopygetAge());

}

}

class Customer {

private Long id;

private String name;

private int age;

public Customer() {

}

public Customer(String name int age) {

thisname = name;

thisage = age;

}

public Long getId() {

return id;

}

public void setId(Long id) {

thisid = id;

}

public String getName() {

return name;

}

public void setName(String name) {

thisname = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

thisage = age;

}

}

输出结果

Class:comlangsinreflectionCustomer

id:

name:Tom

age:

Copy information: Tom

Process finished with exit code

解说

ReflectTester 类的copy(Object object)方法依次执行以下步骤

)获得对象的类型

Class classType=objectgetClass();

Systemoutprintln(Class:+classTypegetName());

)通过默认构造方法创建一个新对象

Object objectCopy=classTypegetConstructor(new Class[]{})newInstance(new Object[]{});

以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象它代表默认的构造方法然后调用Constructor对象的newInstance()方法构造一个实例

)获得对象的所有属性

Field fields[]=classTypegetDeclaredFields();

Class 类的getDeclaredFields()方法返回类的所有属性包括publicprotected默认和private访问级别的属性

)获得每个属性相应的getXXX()和setXXX()方法然后执行这些方法把原来对象的属性拷贝到新的对象中

用反射机制调用对象的方法

public class InvokeTester {

public int add(int param int param) {

return param + param;

}

public String echo(String msg) {

return echo: + msg;

}

public static void main(String[] args) throws Exception {

Class<?> classType = InvokeTesterclass;

Object invokeTester = classTypenewInstance();

// Object invokeTester = classTypegetConstructor(new

// Class[]{})newInstance(new Object[]{});

//获取InvokeTester类的add()方法

Method addMethod = classTypegetMethod(add new Class[]{intclass intclass});

//调用invokeTester对象上的add()方法

Object result = addMethodinvoke(invokeTester new Object[]{new Integer() new Integer()});

Systemoutprintln((Integer) result);

//获取InvokeTester类的echo()方法

Method echoMethod = classTypegetMethod(echo new Class[]{Stringclass});

//调用invokeTester对象的echo()方法

result = echoMethodinvoke(invokeTester new Object[]{Hello});

Systemoutprintln((String) result);

}

}

在例程InvokeTester类的main()方法中运用反射机制调用一个InvokeTester对象的add()和echo()方法

add()方法的两个参数为int 类型获得表示add()方法的Method对象的代码如下

Method addMethod=classTypegetMethod(addnew Class[]{intclassintclass});

Method类的invoke(Object objObject args[])方法接收的参数必须为对象如果参数为基本类型数据必须转换为相应的包装类型的对象invoke()方法的返回值总是对象如果实际被 调用的方法的返回类型是基本类型数据那么invoke()方法会把它转换为相应的包装类型的对象再将其返回

在本例中尽管InvokeTester 类的add()方法的两个参数以及返回值都是int类型调用add Method 对象的invoke()方法时只能传递Integer 类型的参数并且invoke()方法的返回类型也是Integer 类型Integer 类是int 基本类型的包装类

Object result=addMethodinvoke(invokeTester

new Object[]{new Integer()new Integer()});

Systemoutprintln((Integer)result); //result 为Integer类型

动态创建和访问数组

javalangArray 类提供了动态创建和访问数组元素的各种静态方法

例程ArrayTester 类的main()方法创建了一个长度为 的字符串数组接着把索引位置为 的元素设为hello然后再读取索引位置为 的元素的值

public class ArrayTester {

public static void main(String args[]) throws Exception {

Class<?> classType = ClassforName(javalangString);

// 创建一个长度为的字符串数组

Object array = ArraynewInstance(classType );

// 把索引位置为的元素设为hello

Arrayset(array hello);

// 获得索引位置为的元素的值

String s = (String) Arrayget(array );

Systemoutprintln(s);

}

}

例程ArrayTester 类的main()方法创建了一个 x x 的整型数组并把索引位置为[][][] 的元素的值为设

public class ArrayTester {

public static void main(String args[]) {

int[] dims = new int[]{ };

//创建一个具有指定的组件类型和维度的新数组

Object array = ArraynewInstance(IntegerTYPE dims);

Object arrayObj = Arrayget(array );

Class<?> cls = arrayObjgetClass()getComponentType();

Systemoutprintln(cls);

arrayObj = Arrayget(arrayObj );

ArraysetInt(arrayObj );

int arrayCast[][][] = (int[][][]) array;

Systemoutprintln(arrayCast[][][]);

}

}

深入认识Class类

众所周知Java有个Object类是所有Java类的继承根源其内声明了数个应该在所有Java类中被改写的方 法hashCode()equals()clone()toString()getClass()等其中getClass()返回一个 Class类的对象

Class类十分特殊它和一般classes一样继承自Object其实体用以表达Java程序运行时的classes和interfaces也用来表达enumarrayprimitive 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

Java允许我们从多种途径为一个class生成对应的Class对象参看本人的《 深入研究javalongClass类 <; 》一文

欲生成对象实体在Reflection 动态机制中有两种作法一个针对无自变量ctor一个针对带参数ctor如果欲调用的是带参数ctor就比较麻烦些不再调用Class 的newInstance()而是调用Constructor 的newInstance()首先准备一个Class[]做为ctor的参数类型(本例指定

为一个double和一个int)然后以此为自变量调用getConstructor()获得一个专属ctor接下来再准备一个Object[] 做为ctor实参值(本例指定调用上述专属ctor的newInstance()

动态生成Class object 所对应之class的对象实体无自变量

这个动作和上述调用带参数之ctor相当类似首先准备一个Class[]做为参数类型(本例指定其中一个是String另一个是 Hashtable)然后以此为自变量调用getMethod()获得特定的Method object接下来准备一个Object[]放置自变量然后调用上述所得之特定Method object的invoke()

为什么获得Method object时不需指定回返类型?

因为method overloading机制要求signature必须唯一而回返类型并非signature的一个成份换句话说只要指定了method名称和参数列就一定指出了一个独一无二的method

运行时变更field内容

与先前两个动作相比变更field内容轻松多了因为它不需要参数和自变量首先调用Class的getField()并指定field名称获得特定的Field object之后便可直接调用Field的get()和set()

public class RefFiled {

public double x;

public Double y;

public static void main(String args[]) throws NoSuchFieldException IllegalAccessException {

Class c = RefFiledclass;

Field xf = cgetField(x);

Field yf = cgetField(y);

RefFiled obj = new RefFiled();

Systemoutprintln(变更前x= + xfget(obj));

//变更成员x值

xfset(obj );

Systemoutprintln(变更后x= + xfget(obj));

Systemoutprintln(变更前y= + yfget(obj));

//变更成员y值

yfset(obj );

Systemoutprintln(变更后y= + yfget(obj));

}

}

运行结果

变更前x=

变更后x=

变更前y=null

变更后y=

Process finished with exit code

               

上一篇:JavaME程序 Run Anywhere-- 利用反射机制来动态加载声

下一篇:Eclipse中进行JVM内存设置