反射作用于分析类
要想分析一个类,必须有这个类的元数据,java中描述类的元数据的类是Class类,元数据就是类自己的数据
要想得到Class类对象,可以有多种方法
1、通过Class.forName加载类
2、通过对象.getClass
1、基础类
下面的所有的探寻反射的方法都是基于下面这个类修改来的
package com.test;
import java.io.Serializable;
public class TestPojo implements Serializable,Cloneable{
public String a;
private Test t;
public TestPojo() {
}
public TestPojo(String s) {
}
public void f1() {
System.out.println("没有参数的f1");
}
public void f1(String s) {
System.out.println("有一个String参数的f1");
}
public void f1(int s) {
System.out.println("有一个int参数的f1");
}
public void f1(String s,int i) {
System.out.println("有两个参数的分别为String和int的f1");
}
}
2、Class方法
Class clazz = Class.forName("com.test.TestPojo");//得到类对象
(1)得到类的修饰符
clazz.getModifiers()
我就不分开了,下面的表格是类,方法,成员变量的修饰符
修饰符 | 值 |
---|---|
不写 | 0 |
public | 1 |
final | 16 |
protected | 4 |
private | 2 |
static | 8 |
volatile | 64 |
transient | 128 |
synchronized | 32 |
abstract | 1024 |
native | 256 |
注意:
你可能会遇上值为17或者不是上面这些值的,尤其是在下面的操作中,这是因为类的修饰符为 public final修饰的,是16+1=17;如果得出其他的值请不要慌张或者我靠,试试上面的某些值相加是否得出你的值
(2)获得类的接口
Class[] interfaces = clazz.getInterfaces();
for(Class i : interfaces) {
//获得Class的简单名字,如果直接打印i,会打印出接口的包名.类名
System.out.println(i.getSimpleName());
}
(3)获得对象的成员变量
获得对象可见变量
Field [] fd = clazz.getFields();//得到可见的成员变量
for(Field f : fd) {
System.out.println(f.getName()); //变量的别名
System.out.println(f.getType()); //修饰符
System.out.println(f.getModifiers()); //成员变量的修饰符值
}
获得对象所有变量(不管可见不可见),俗称暴力破解
Field [] fd = clazz.getDeclaredFields(); //获得全部变量
for(Field f : fd) {
System.out.println(f.getName()); //变量的别名
System.out.println(f.getType()); //修饰符
System.out.println(f.getModifiers()); //成员变量的修饰符值
}
这是成员变量的暴力破解,要和下面方法的暴力破解做出区分
3、方法Method
(1)Method方法获得方法的信息
获得公开的方法
获得Class里f1,且无参的方法赋给m
Method m = clazz.getDeclaredMethod("f1", null);
Method m = clazz.getMethod("f1", null);
把隐藏的方法公开,暴力破解方法
f1的修饰符换成private
Method m = clazz.getDeclaredMethod("f1", null); //不能用getMethod,要不会报错
m.setAccessible(true); //true 可以显示隐藏的
如果有参数null 换成 参数类型.class
m.getModifiers(); //也有获得方法的修饰符的值
m.getName(); //获得方法的名字,也就是上面的f1
m.getReturnType(); //返回类型,比如 void
(2)执行方法
Object obj = clazz.newInstance(); //首先实例化class
Method m = clazz.getDeclaredMethod("f1", int.class); //找出class的方法
m.invoke(obj, 5); //执行方法,参数:类的对象,方法的参数
提醒:
如果是数组类型的怎么写呢?
答:那就以int数组为例,上面的获得class里执行方法里的第二个参数换成int[ ].class
例:clazz.getDeclaredMethod("f1", int[].class)
4、实例化class方法
有两种:
- 类对象直接实例化
- 用构造方法实例化对象
区别:第一种不能带参数,第二种可以带参数
第一种方式
Object obj = clazz.newInstance();
第二种方式
Constructor c1 = clazz.getDeclaredConstructor(String.class); //参数为一个String
Object obj = c1.newInstance("55"); //写生你构造器的参数
5、获得注解
(1)方法上的注解
把基础类的一个方法加上注解
@Deprecated
@SuppressWarnings("unused")
public void f1(int s) {
System.out.println("有一个int参数的f1");
}
执行
Method m = clazz.getDeclaredMethod("f1", int.class);
Annotation[] anns = m.getDeclaredAnnotations();
for(Annotation ann : anns) {
System.out.println(ann);
}
只有一个,因为注解 @SuppressWarnings(“unused”) 不是运行时注解,所以执行时就不会找到
(2)类上的注解
Annotation[] anns = clazz.getDeclaredAnnotations();
6、获得类上的泛型
目前本人只知道这一种方法,不是说只有这一种
通过继承父类的方式来反射泛型
父类
public class TestPojo <T>{
private Class genericityClass;//泛型
public Class getGenericityClass() {
if(genericityClass == null) {
genericityClass = Until.getGenericityClass(this.getClass()); //获得泛型
}
return genericityClass;
}
}
子类(空的)
public class TestPojoExte extends TestPojo<String> {
}
工具类
public class Until {
public static Class getGenericityClass(Class clazz) {
return getGenericityClass(clazz, 0); //调下面的方法
}
private static Class getGenericityClass(Class clazz,int index) {
Type type = clazz.getGenericSuperclass();
if(type instanceof ParameterizedType) {
Type[] params = ((ParameterizedType) type).getActualTypeArguments();
if(params != null && (params.length >= (index - 1))) {
return (Class) params[index];
}
}
return clazz;
}
}
执行
public static void main(String[] args) throws Exception{
TestPojo<String> t = new TestPojoExte();
System.out.println(t.getGenericityClass());
}