一、Java 语言的反射机制
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?
答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
Java 反射机制主要提供了以下功能:
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法
Reflection 允许程序在运行时透过Reflection APIs取得任何一个 已知名称 的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods。
那么反射这么厉害,java是不是动态语言呢?
来看一下动态语言百科上的定义:
动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。
从这个角度看很明显,发现java不是动态语言
尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或调用其methods。
二、Java Reflection API 简介
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
- Class类:代表一个类。
- Field 类:代表类的成员变量(成员变量也称为类的属性)。
- Method类:代表类的方法。
- Constructor 类:代表类的构造方法。
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法
具体如下: - 常用方法用法:
1、获得对象的类型
Class classType=object.getClass();
在java.lang.Object 类中定义了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():通过类的 不带参数 的构造方法创建这个类的一个对象。
2、通过默认构造方法创建一个新对象
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表 默认的构造方法 ,然后调用Constructor对象的newInstance()方法构造一个实例。
3、获得对象的所有属性
Field fields[]=classType.getDeclaredFields();
Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性。
获得每个属性相应的getXXX()和setXXX()方法,然后执行这些方法,把原来对象的属性拷贝到新的对象中
4、动态创建和访问数组
java.lang.Array 类提供了动态创建和访问数组元素的各种 静态 方法。
三、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。
1、“Class” object的取得途径。
Java允许我们从多种途径为一个class生成对应的Class object
四、应用
代码操作的实体类:
1、构造函数无参
/**
* Created by jiankunking on 2016/9/16.
*/
public class entityClassWithoutParameter
public int getAddResult(int a, int b)
{
return a + b;
}
private int getMultiplyResult(int a, int b)
{
return a * b;
}
public String getField1()
{
return field1;
}
public void setField1(String field1)
{
this.field1 = field1;
}
public String getField2()
{
return field2;
}
public void setField2(String field2)
{
this.field2 = field2;
}
public String field1;
protected String field2;
private String field3 = "我是私有的";
/**
* 测试私有函数是否可见
* @return
private String testPrivate(String string)
{
System.out.println("testPrivate:"+string);
return
2、构造函数有参
/**
* Created by jiankunking on 2016/9/16.
*/
public class entityClassWithParameter
public entityClassWithParameter(String string, int num)
{
System.out.println("我是entityClassWithParameter:"
1、运行时生成instances
在Reflection 动态机制中有两种作法:一个针对“无参ctor”,一个针对“带参数ctor”。如果欲调用的是“带参数ctor“就比较麻烦些,不再调用Class的newInstance(),而是调用Constructor 的newInstance()。
无参:
//获取 指定类
Class classType = Class.forName("entityClassWithoutParameter");
//创建 无参 对象
Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
有参:
//创建 有参 对象
Class cls = Class.forName("entityClassWithParameter");
Class[] paramTypes = {String.class, int.class};
// 方法传入的参数
Object[] params = {"我是string参数", 23};
Constructor con = cls.getConstructor(paramTypes);
//主要就是这句了
entityClassWithParameter base = (entityClassWithParameter) con.newInstance(params);
2、运行时调用methods
首先准备一个Class[]做为参数类型(本例指定其中一个是String,另一个是int),然后以此为自变量调用getMethod(),获得特定的Method object。接下来准备一个Object[]放置参数变量,然后调用上述所得之特定Method object的invoke()。
//获取 指定类
Class classType = Class.forName("entityClassWithoutParameter");
//获取 指定方法
Method addMethod = classType.getMethod("getAddResult", new Class[]{int.class, int.class});
//调用函数 并接收结果
Object result = addMethod.invoke(classType.newInstance(), new Object[]{new Integer(100), new Integer(200)});
为什么获得Method object时不需指定回返类型?
因为method overloading机制要求signature必须唯一,而回返类型并非signature的一个成份。换句话说,只要指定了method名称和参数列,就一定指出了一个独一无二的method。
3、运行时变更fields内容
首先调用Class的getField()并指定field名称。
获得特定的Field object之后便可直接调用Field的get()和set(),
//获取private字段
Field f = classType.getDeclaredField("field3");
//修改protected、private字段的可见性 public字段不需要这句
f.setAccessible(true);
//创建 无参 对象
Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
System.out.println("获取私有字段值:" + f.get(objectCopy));
f.set(objectCopy,"我要修改自己");
System.out.println("获取私有字段值:" + f.get(objectCopy));
效果如下:
4、对类的私有变量和私有方法的访问
//获取private字段
Field f = classType.getDeclaredField("field3");
//修改protected、private字段的可见性 public字段不需要这句
f.setAccessible(true);
//创建 无参 对象
Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
System.out.println("获取私有字段值:" + f.get(objectCopy));
f.set(objectCopy, "我要修改自己");
System.out.println("获取私有字段值:" + f.get(objectCopy));
//获得私有方法
Method method = classType.getDeclaredMethod("testPrivate", new Class[]{String.class});
//设置私有方法可以被访问
method.setAccessible(true);
//调用私有方法
String str = (String) method.invoke(objectCopy, new Object[]{"测试私有方法是否可以通过反射调用"});
System.out.println("我是私有方法返回值:"+str);//输出 测试私有方法是否可以通过反射调用
完整测试代码
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by jiankunking on 2016/7/31.
*/
public class test
public static void main(final String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException
{
//获取 指定类
Class classType = Class.forName("entityClassWithoutParameter");
//获取 指定方法
Method addMethod = classType.getMethod("getAddResult", new Class[]{int.class, int.class});
//调用函数 并接收结果
Object result = addMethod.invoke(classType.newInstance(), new Object[]{new Integer(100), new Integer(200)});
//输出 结果
System.out.println("加法结果:" + (Integer) result);
//获取 所有方法
for (Method item : classType.getDeclaredMethods())
{
System.out.println("函数名:" + item.getName());
}
//获取 所有public字段
for (Field item : classType.getFields())
{
System.out.println("字段名:" + item.getName());
}
//获取private字段
Field f = classType.getDeclaredField("field3");
//修改protected、private字段的可见性 public字段不需要这句
f.setAccessible(true);
//创建 无参 对象
Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
System.out.println("获取私有字段值:" + f.get(objectCopy));
f.set(objectCopy, "我要修改自己");
System.out.println("获取私有字段值:" + f.get(objectCopy));
//获得私有方法
Method method = classType.getDeclaredMethod("testPrivate", new Class[]{String.class});
//设置私有方法可以被访问
method.setAccessible(true);
//调用私有方法
String str = (String) method.invoke(objectCopy, new Object[]{"测试私有方法是否可以通过反射调用"});
System.out.println("我是私有方法返回值:"+str);//输出 测试私有方法是否可以通过反射调用
//创建 有参 对象
Class cls = Class.forName("entityClassWithParameter");
Class[] paramTypes = {String.class, int.class};
// 方法传入的参数
Object[] params = {"我是string参数", 23};
Constructor con = cls.getConstructor(paramTypes);
//主要就是这句了
结果如下:
本文部分参考百度百科及网络资料:JAVA反射机制
作者:jiankunking