Java反射之获取类的信息
0. 前言
反射技术是Java的一大特色,它可以让Java程序运行过程中,获得一个类的所有字段和方法,也可以创建一个类的实例并调用任意一个方法。简而言之,就是解剖一个类,获得它的心肝脾肺肾,再进行操作。反射技术主要应用在框架的编写上,可能以后我们要做一个框架或者工具就会使用到反射技术。反射技术经常有用到的操作便是获取一个类的构造方法、字段、方法还有一些其它信息,并初始化一个类的对象并调用其方法。下面我就介绍一下反射技术的基本操作。
1. 获取Class对象
要想解剖一个类,得先获得它,一个类是用类Class描述的,所以一个类就是一个Class对象,下面有三种方法可以获取一个类的Class对象。此处获取Person类的Class对象,假设Person的全限定类名为edu.jyu.reflect.Person。
- Class类的forName()静态方法,Class clazz = Class.forName(“edu.jyu.reflect.Person”);
- 调用某个类的class属性,Class clazz = Person.class;
- 调用每个类的实例对象的getClass()方法,Class clazz = new Person().getClass();
2. 获取构造函数
类的构造方法,是用java.lang.reflect包中的类Constructor表示的,获取一个类的构造函数,有以下几个方法
- Constructor[] getDeclaredConstructors();返回已加载类声明的所有的构造方法(包括private修饰)的Constructor 对象数组.
- Constructor getDeclaredConstructor(Class[] paramTypes);返回已加载类声明的指定构造方法的Constructor 对象,paramTypes指定了参数类型.
- Constructor[] getConstructors();返回已加载类声明的所有的 public类型的构
造方法的Constructor 对象数组. - Constructor getConstructor(Class[] paramTypes);返回已加载类声明的指定
的public类型的构造方法的Constructor对象,paramTypes指定了参数类型
这四个方法可以分成两类,一类(1、2方法)是可以获得private修饰的构造方法,一类(3、4方法)不可以,只能获取public的。而这两类中又可以分成两类,一类(方法名最后带s的,1、3方法)是获得所有可以获得的构造方法,一类(2、4)是获得指定参数列表对应的构造方法。说到这里可能会有点晕,通过下面这些代码大家可以看的更清晰。
先写一个Person类作为小白鼠,提供3个构造方法,其中有两个是public一个是private的,又有两个是带参数。
package edu.jyu.reflect;
class Person{
public Person(){}
public Person(Integer age){}
private Person(String name){}
}
下面就是测试类,大家要对应着上面的Person类观察一下,这样比较清晰地看出各个方法之间的区别。
package edu.jyu.reflect;
import java.lang.reflect.Constructor;
class ReflectConstructorDemo{
public static void main(String[] args) throws Exception{
//获取Person类的Class对象
Class pClazz = Class.forName("edu.jyu.reflect.Person");
//=======可以获得private修饰的构造方法=========
Constructor[] arrA = pClazz.getDeclaredConstructors();
Constructor conA1 = pClazz.getDeclaredConstructor(Integer.class);
Constructor conA2 = pClazz.getDeclaredConstructor(String.class);
System.out.println("结果一:arrA length:"+arrA.length);
System.out.println("结果二:"+conA1);
System.out.println("结果三:"+conA2);
//=======不能获得private修饰的构造方法=========
Constructor[] arrB = pClazz.getConstructors();
Constructor conB1 = pClazz.getConstructor(Integer.class);
//Constructor conB2 = pClazz.getConstructor(String.class);
System.out.println("结果四:arrB length:"+arrB.length);
for (Constructor constructor : arrB) {
System.out.println(constructor);
}
System.out.println("结果五:"+conB1);
}
}
上面代码输出的结果
结果一:arrA length:3
结果二:public edu.jyu.reflect.Person(java.lang.Integer)
结果三:private edu.jyu.reflect.Person(java.lang.String)
结果四:arrB length:2
public edu.jyu.reflect.Person()
public edu.jyu.reflect.Person(java.lang.Integer)
结果五:public edu.jyu.reflect.Person(java.lang.Integer)
结果分析:
- 第一个结果可以看出arrA的长度为3,可以看出getDeclaredConstructors方法可以获取一个类所有的构造方法。
- 第二个结果是Person类的带有Integer参数的公有构造方法,说明getDeclaredConstructor方法可以获取一个类指定参数列表的公有构造方法。
- 第三个结果是Person类的带有String参数的私有构造方法,说明getDeclaredConstructor方法可以获取一个类指定参数列表的私有构造方法。
- 第四个结果说明getConstructors方法只能获取一个类所有的public修饰的构造函数。
- 第五个结果说明getConstructor方法可以获取一个类指定参数列表的公有构造方法。那么问题来了,怎么知道这个方法不能获取私有构造方法呢?把上面的Constructor conB2 = pClazz.getConstructor(String.class);这行代码的注释去掉,程序就会抛一个NoSuchMethodException异常,说明getConstructor根本就不能获得私有的构造方法。
3. 获取字段
类的字段,是用java.lang.reflect包中的类Field表示的,获取一个类的字段,有以下几个方法
- Field[] getDeclaredFields():返回已加载类声明的所有字段的Field对象数
组,不包括从父类继承的字段. - Field getDeclaredField(String name):返回已加载类声明的所有字段的
Field对象,不包括从父类继承的字段,参数name指定字段的名称. - Field[] getFields():返回已加载类声明的所有public型的字段的Field对象
数组,包括从父类继承的字段 - Field getField(String name):返回已加载类声明的所有字段的 Field对象,
包括从父类继承的字段,参数name指定字段的名称
这四个方法可以分成两类,一类(1、2方法)是可以获得private修饰的字段但是不包括从父类继承的字段,一类(3、4方法)不可以获得private修饰的字段但是包括从父类继承的字段。而这两类中又可以分成两类,一类(方法名最后带s的,1、3方法)是获得所有可以获得的字段,一类(2、4)是获得指定字段名称的字段。通过下面这些代码测试一下。
先写两个类,一个是Employee,一个是Manager,其中Manager继承Employee,这两个类都有一个公有字段和一个私有字段。
Employee类
package edu.jyu.reflect;
public class Employee {
private String name;
public Double salary;
}
Manager类
package edu.jyu.reflect;
public class Manager extends Employee{
private Double bonus;
public String Department;
}
下面是测试类
package edu.jyu.reflect;
import java.lang.reflect.Field;
public class ReflectFieldDemo {
public static void main(String[] args) throws Exception{
Class mClazz = Class.forName("edu.jyu.reflect.Manager");
System.out.println("====getDeclaredFields结果====");
Field[] arrA = mClazz.getDeclaredFields();
for (Field field : arrA) {
System.out.println(field);
}
System.out.println("====getDeclaredFields结果====");
Field field = mClazz.getDeclaredField("bonus");
System.out.println(field);
field = mClazz.getDeclaredField("Department");
System.out.println(field);
//field = mClazz.getDeclaredField("name");
System.out.println("====getFields结果====");
Field[] arrB = mClazz.getFields();
for (Field field2 : arrB) {
System.out.println(field2);
}
System.out.println("====getFields结果====");
field = mClazz.getField("Department");
System.out.println(field);
field = mClazz.getField("salary");
System.out.println(field);
//field = mClazz.getField("bonus");
}
}
输出结果
====getDeclaredFields结果====
private java.lang.Double edu.jyu.reflect.Manager.bonus
public java.lang.String edu.jyu.reflect.Manager.Department
====getDeclaredFields结果====
private java.lang.Double edu.jyu.reflect.Manager.bonus
public java.lang.String edu.jyu.reflect.Manager.Department
====getFields结果====
public java.lang.String edu.jyu.reflect.Manager.Department
public java.lang.Double edu.jyu.reflect.Employee.salary
====getFields结果====
public java.lang.String edu.jyu.reflect.Manager.Department
public java.lang.Double edu.jyu.reflect.Employee.salary
其中那两句被注释的代码如果去掉注释,将会抛出NoSuchFieldException异常。
4. 获取方法
类的方法,是用java.lang.reflect包中的类Method表示的,获取一个类的方法,有以下几个方法
- Method[] getDeclaredMethods():返回已加载类声明的所有方法的 Method 对象数组,不包括从父类继承的方法.
- Method getDeclaredMethod(String name,Class[] paramTypes):返回已加载类声明的所有方法的 Method对象,不包括从父类继承的方法,参数name指定方
法的名称,参数 paramTypes指定方法的参数类型. - Method[] getMethods():返回已加载类声明的所有方法的Method对象数组,包
括从父类继承的方法. - Method getMethod(String name,Class[] paramTypes):返回已加载类声明的
所有方法的Method对象,包括从父类继承的方法,参数name指定方法的名称,参
数paramTypes指定方法的参数类型
大家可以观察到,获取类方法的这几个方法与获取类的字段十分相像,所以在此便不再多解释了。
5. 获取其它信息
当你得到一个类的Class对象的时候,基本上你就可以获取它的全部信息了,除了构造方法,字段,方法外还有一些类的包名、父类等等信息,如下面这几种。
- int getModifiers():返回已加载类的修饰符的整形标识值.
- Package getPackage():返回已加载类的包名
- Class getSuperclass():返回已加载类的父类的 Class实例.
- Class [] getInterfaces():返回已加载类实现的接口的 Class对象数组.
- boolean isInterface():返回已加载类是否是接口
还有更多的反射方法大家可以去查看API
如果上面的内容有错误的地方或者讲的不好的地方,还请大家指点一下,我好及时修改。
作者:腹黑大壁花
来源链接:https://blog.csdn.net/TimHeath/article/details/53305528