在学习java中的反射机制之前,我们必须先了解两个问题(小伙伴们现在不能完全明白没关系,后面我会一个一个详细的和大家分享我的理解).
1.什么是反射?
Reflection,翻译为映像,反射。JAVA反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2.为什么需要反射?
程序在运行时,允许改变程序的结构或变量的类型,具有这种特性的语言被称为动态语言,如Python,Ruby等,正是由于java中反射机制的存在,使java被视为准动态语言,极大扩展了java的功能与操作,具有反射机制的java具有以下几种特殊的功能。
a.只要给定类的全名,即可通过反射获取类的所有信息。
b.反射可以在程序运行时获取任意一个对象所属的类对象。
c.在运行时可以获取到类中的所有属性,并对其进行操作。
d.在运行时可以获取到类中,父类中的所有方法,并调用。
鉴于拥有反射机制的java如此强大,目前主流的框架如Struts2,Hibernate,Spring等框架的核心全部离不开java反射的影子。
在我们有了这两个概念之后,我们就可以进行真正的学习了!
1.Class类
java语言中,除了静态的成员,普通数据类型不是对象以外,其它的都是对象,既然如此,那么类又是谁的对象呢?没错,正如我们的标题,java中的类都是java.lang.Class类的对象。
1.1 类类型的三种表示方式
当我们实例化一个普通对象时,我们通常这样做:
T a = new T();
我们是否可以类比普通对象的实例化来实例Class对象呢,答案是不能,看了Class的源代码就很容易理解这个原因(构造函数是私有的):
/*
* Private constructor. Only the Java Virtual Machine creates Class objects.
* This constructor is not used and prevents the default constructor being
* generated.
*/
private Class(ClassLoader loader) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
}
那么我们该怎么办呢,不用担心,这里至少有三种表示方式:
Class c1 = T.class;
这种方式我们也能看出,任何一个类都有一个隐含的class成员变量。
第二种是已知一个类的实例化对象,通过这个对象的getClass()方法.
Class c2 = a.getClass();
第三种是通过类的全路径来获取。
Class c3 = Class.forName("com.example.T");
说明:
c1/c2/c3是T的类型,T是a的类型。c1/c2/c3也称作为T的class type.
一个T只能有一个类型,既c1 == c2为true.
同时,也可以通过class type来获取相应类的实例(为了使逻辑清晰,异常处理我都全部省略了),但它的前提是必须要有无参数的构造方法。
T b = (T)c1.newInstance();
1.2 基本数据的类类型(type class)
同理,基本数据类型也具有相应的type class,如:
Class f1 = int.class;
Class f2 = String.class;
Class f3 = double.class;
Class f4 = Double.class;
注意,
这里的f3和f4不同,一个是基本类型的type class,一个是包装器类型的type class.
2.Method类
java.lang.reflect.Method类是用于表示类中,接口中方法对象的类,通过它可以操作类中私有,以及公有的全部方法。它的几个重要方法如下:
package com.example.xw;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class TestClass {
/**
* 打印类的信息,包括类的成员函数,成员变量
* @param ob该对象所属类的信息
*/
public static void show(Object ob){
//要获取类的信息,首先要获取类的class type.
//传递的是哪一个子类的对象,c1就是该子类的class type。
Class c1 = ob.getClass();
//获取类的名称
System.out.println("类的名称 : " + c1.getName());
/**
* Method类,方法对象
* 一个成员方法就是一个Method对象
* getMethods()方法获取的是所有的public函数,包括从父类继承来的
* getDeclaredMethods()获取的是所有该类自己声明的方法,所有访问权限的都能获得到
*/
Method[] me = c1.getDeclaredMethods();
for(int i = 0; i < me.length ; i ++){
//得到方法的修饰符
System.out.print(Modifier.toString(me[i].getModifiers()) + " ");
//得到方法的返回值类型的class type
Class returnType = me[i].getReturnType();
//得到方法的返回值类型
System.out.print(returnType.getName() + " ");
//得到方法的名称
System.out.print(me[i].getName() + "(");
//获取参数类型
Class[] paramTypes = me[i].getParameterTypes();
for(Class class1 : paramTypes){
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
在演示一下获取方法后并调用,
//该方法用于使用传递过来的实体对象获取其中的方法并调用
public static void showUse(Object ob){
Class c1 = ob.getClass();
try{ //getMethod()的第一个参数是调用的哪一个函数,第二个参数是那个函数的
//参数的class type.
Method me1 = c1.getMethod("getName",null);
//利用ob对象调用该方法,第二个参数为传递给函数的参数
me1.invoke(ob,new Object[]{});
Method me2 = c1.getMethod("setName",String.class);
me2.invoke(ob,"西游记");
//多参数调用
Class[] clll = {String.class , int.class};
Method me3 = c1.getMethod("show",clll);
//Method me3 = c1.getMethod("show",new Class[]{String.class,int.class});
Object[] ooo = {"a",2};
me3.invoke(ob,ooo);
}catch(Exception e){
e.printStackTrace();
}
}
3.Field类
java.lang.reflect.Field类,是用于表示类中、接口中属性对象的类,可以操作类中私有的,以及公有的全部属性。
该方法用于使用传递过来的Class对象获取类中的属性
public void show(Class c1){
//可以获取到私有的属性
Field[] fi = c1.getDeclaredFields();
//只可以获取到公有的属性
//Field[] fi = c1.getFields();
for(Field ff : fi){
System.out.println(ff.getName());
System.out.println(ff.getType());
}
}
该方法用于使用传递过来的实体类对象获取属性以及属性的值,注意如果想要获取私有属性的值,必须先声明权限,既使用setAccessible(true)方法。
<span style="font-size:12px;">public void show(Object ob) throws IllegalArgumentException, IllegalAccessException{
Class c1 = ob.getClass();
Field[] fi = c1.getDeclaredFields();
for(Field ff : fi){
//必须设置访问权限,否则不能用ff.get(ob)取私有属性
ff.setAccessible(true);
System.out.println(ff.getName() + "值" + ff.get(ob));
ff.set(ob,"1");
}
}</span>
4.打印构造函数的信息
因为打印构造函数较为特殊,故把它单独放在一节。
public static void printCon(Object ob){
Class c1 = ob.getClass();
Constructor[] con = c1.getDeclaredConstructors();
for(Constructor cc : con){
System.out.print(cc.getName() + "(");
Class[] paramTypes = cc.getParameterTypes();
for(Class cla : paramTypes){
System.out.print(cla.getName() + ",");
}
System.out.println(")");
}
}
参考资料:
极客学院JAVA反射课程
慕课网JAVA反射课程
JAVA反射百度百科