慢慢来比较快,虚心学技术
前言:在Java中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。
反射
反射的概念(是什么):
java中的反射------相当于对象的镜子,对象在照镜子时可以获取自身的信息,比如:属性,方法和构造器,实现接口等,而且这面镜子还可以直接调用执行java对象的方法
反射的用处(有什么用):
1.当接收到别人传过来的类时,可以进行解析
2.当只有类名时,可以获取到对象的完整信息,从而执行相应处理
多用于可配置和动态代理
java反射涉及的几个类:
java.lang. Class:反射的核心类,可以获取类的属性,方法等信息
java.lang.ClassLoader:类的加载器,用来把类(class)装载进 JVM
java.lang.reflect.Field :类的成员变量,用来获取和设置类的属性
java.lang.reflect.Method:类的方法,用来获取类中方法的信息或调用类的方法
java.lang.reflect.Modifier:类的修饰符工具,可以用来判断类中属性或者方法的修饰级别
为方便后续学习,事先创建一个基本类
public class BaseEntity {
private String name;
private Integer age;
public BaseEntity(){
super();
}
public BaseEntity(String name,Integer age){
super();
this.name = name;
this.age = age;
}
private void addAge(String name,Integer age){
if(this.name.equals(name)){
this.age+=age;
}else{
System.out.println("名称不符,请核验");
}
}
}
基本类BaseEntity
Class类
class是一个用来描述类的类,封装了当前对象所对应的类信息,我们可以通过该类获取目标对象的属性,方法等信息,还可以根据class类创建目标对象实例
获取Class类
1.通过类名获取 : 类名.class
Class clazz = BaseEntity.class;
2. 通过类全类名获取:Class.forName(全类名)--这种方式一般在开发中用的比较多,多为配置项获取实例,如最简单的JDBC获取驱动方式
Class clazz =Class.forName("com.java.entity.BaseEntity");
3.通过对象实例获取
BaseEntity baseEntity = new BaseEntity("test",50);
Class clazz = baseEntity.getClass();
获取父类Class
Class clazz = Class.forName("com.java.entity.BaseEntity");
Class personClazz = sonClazz.getSuperclass();
创建目标对象实例
Object newObject = clazz.newInstance();
类的字段Field
通过class类获取目标对象的字段对象,获取到字段对象后可以对具体对象的属性值进行读取和设置操作
获取字段列表:
//获取基本公共字段(无法获取保护字段和父类字段)
Field[] fields = clazz.getFields(); //结果是null,因为BaseEntity没有public字段
//获取所有字段(无法获取父类字段)
fields = clazz.getDeclaredFields(); //结果是name和age的对应字段对象
获取指定字段
一般使用getDeclaredField方法获取,两种方法找不到对应字段时都会抛NoSuchFieldException异常
方法结构如下:
Field getField(字段名);
Field getDeclaredField(字段名) ;
//getField()---只能获取到公共字段
Field field = clazz.getField("name");
//getDeclaredField()----可以获取到指定字段,不管是否保护字段
Field field = clazz.getDeclaredField("name");
设置对象对应字段的值field.set(Object obj,Object value)
方法结构如下:
void set(实例对象,字段值)
如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作
//如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作
field.setAccessible(true);
//设置属性值
field.set(baseEntity,"base");
获取对象对应字段的值field.get(Object obj)
//如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作
field.setAccessible(true);
//获取实例baseEntity对应属性的属性值(此处获取的是name的值)
field.get(baseEntity); //-----------得到结果为base
类的方法Method
通过class类获取目标对象的方法对象,获取到方法对象后可以对具体对象的方法进行调用和方法
获取方法列表
两种方法获取都不能够获取到父类的方法,只能通过获取父类Class来简介获取父类方法
//获取公共方法列表,无法获取保护方法
Method[] methods = clazz.getMethods();
//获取全部方法列表
Method[] methods = clazz.getDeclaredMethods();
获取指定方法
两种方法的区别和获取指定对象的区别一致,方法内的参数结构如下:
Method getMethod("方法名",方法参数类型列表)
Method getDeclaredMethod("方法名",方法参数类型列表)
//获取BaseEntity的addAge方法对象
Method method = clazz.getMethod("addAge",String.class,Integer.class);
Method method = clazz.getDeclaredMethod("addAge",String.class,Integer.class);
执行指定方法 invoke(Object obj, Object... args)
反射执行指定方法是框架中用的比较普遍的用法,方法结构如下:
Object invoke(具体实例对象, 参数值列表)
如果是保护方法,执行方法前都需要先setAccessible(true)方可操作
method.setAccessible(true);
//执行调用baseEntity的addAge方法,传入参数name和age的值
method.invoke(baseEntity, "base", 12);
//之后再获取baseEntity的age值看一下结果:
Field field = clazz.getDeclaredField("age");
field.setAccessible(true);
field.get(baseEntity,"age");//---------------得到结果:50+12= 62,方法执行正确
类的修饰符工具Modifier
如上所述,方法和属性字段都有保护和公共之分,那么如何判断一个方法或者属性是否公共呢?java反射提供了Modifier工具类,该工具类对类和成员访问修饰符进行解码,通过判断类和成员修饰符的整数编码,判定类和成员是否保护以及修饰符类型
Modifier类将修饰符作为整数编码代号,具体部分常量如下:
public static final int PUBLIC = 0x00000001;
public static final int PRIVATE = 0x00000002;
public static final int PROTECTED = 0x00000004;
public static final int STATIC = 0x00000008;
public static final int FINAL = 0x00000010;
public static final int SYNCHRONIZED = 0x00000020;
Modifier工具类提供如下静态方法判断类和成员的修饰符:
Modifier.isPublic(int mod)
Modifier.isPrivate(int mod)
Modifier.isProtected(int mod)
Modifier.isStatic(int mod)
Modifier.isFinal(int mod)
Modifier.isSynchronized(int mod)
所以,我们可以通过调用Modifier工具类的方法来判断我们的方法或者属性字段是否公共字段/方法,其中,可以使用field.getModifiers(),method.getModifiers()获取字段或方法修饰符的对应编码值
//判断field是否公共字段
if(!Modifier.isPublic(field.getModifiers())){
field.setAccessible(true);
}
field.set(baseEntity,50);
综合使用-动态执行方法(通过传入全类名,通过传入具体对象)
①依旧使用BaseEntity作为基本类操作
②创建ClassUtil类,作为工具类
public class ClassUtil {
/**
* 执行方法
*
* @param className 目标class名称
* @param methodName 目标方法名称
* @param args 执行目标方法所需要的参数值
* @return java.lang.Object 执行目标方法后的返回值
*
* @author *** 2019/2/15
* @version 1.0
**/
public static Object invoke(String className,String methodName,Object ...args){
Class clazz = null;
Class[] parementerType = new Class[args.length];
try {
//通过传入的class路径反射class
clazz = Class.forName(className);
for(int i=0;i<args.length;i++){
parementerType[i] = args[i].getClass();
}
//获取要执行的目标方法对象
Method method = clazz.getDeclaredMethod(methodName,parementerType);
System.out.println("执行方法:"+method.getName());
//开启权限,执行方法
method.setAccessible(true);
return method.invoke(clazz.newInstance(),args);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* 执行方法
*
* @param obj 要执行方法的对象
* @param methodName 要执行的目标方法
* @param args 要执行目标方法需要的参数
*
* @return java.lang.Object 执行目标方法需要的参数值
*
* @author *** 2019/2/15
* @version 1.0
**/
public static Object invoke(Object obj,String methodName,Object ...args){
Class clazz = null;
Class[] parementerType = new Class[args.length];
try {
//通过传入的class路径反射class
clazz = obj.getClass();
for (int i = 0; i < args.length; i++) {
parementerType[i] = args[i].getClass();
}
//获取要执行的目标方法对象
Method method = clazz.getDeclaredMethod(methodName, parementerType);
System.out.println("执行方法:" + method.getName());
//开启权限,执行方法
method.setAccessible(true);
return method.invoke(obj, args);
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
③综合测试
//传入全类名调用方法
ClassUtil.invoke("com.java.entity.BaseEntity","setAge",123);
//传入具体对象调用方法
BaseEntity baseEntity = new BaseEntity();
ClassUtil.invoke(baseEntity,"setAge",123);
System.out.println(baseEntity.getAge());
总结
1.java反射-对象在照镜子时可以获取自身的信息,比如:属性,方法和构造器,实现接口等,而且这面镜子还可以直接调用执行java对象的方法
2.java反射的主要类为Class,ClassLoader,Field,Method,Modifier
3.Class类是一个用来描述类的类,封装了当前对象所对应的类信息,我们可以通过该类获取目标对象的属性,方法等信息,还可以根据class类创建目标对象实例
4.Field类,通过class类获取目标对象的字段对象,获取到字段对象后可以对具体对象的属性值进行读取和设置操作。对于保护的字段的读取和更改,需要先调用setAccessible(true)方法。
5.Method类,通过class类获取目标对象的方法对象,获取到方法对象后可以对具体对象的方法进行调用和方法。对于保护的方法执行,需要先调用setAccessible(true)方法。
6.java反射提供了一个专门封装了类和类的成员修饰符的工具类Modifier,用于判断类与类的成员的修饰符。其底层使用的是修饰符整数编码,本人认为与日志级别的设计思想一致,值得学习
参考文档
【1】java编程思想
网友评论