JAVA的反射
定义
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
用途
在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的。
反射机制的相关类
与Java反射相关的类如下:
反射Class类
lass Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
Class是反射的源头,一旦加载类,就会存在一个当前类的Class对象,Class只有一个,存着这个类的所有内容,如果一旦获取到一个类的Class对象,就能够对这个进行任何操作。
如何获取反射的源头,一个类的Class对象:
- 类名.class
- 对象.getClass()
- Class.forName(“报名+类名”)
- 获得类相关的方法
示例
public class ReflectDemo01 {
public static void main(String[] args) throws ClassNotFoundException{
//1.类名.class
Class<String> cls1=String.class;
//2.对象.getClass()
Class cls2="博主最帅".getClass();
//3.Class.forName("报名+类名") ---推荐
Class cls3=Class.forName("java.lang.String");
System.out.println(cls1);
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
//获取父类的Class对象
Class cls4=cls1.getSuperclass();
System.out.println(cls4);
System.out.println(cls4 == Object.class);
//获取基本数据类型的Class对象
System.out.println(int.class);
System.out.println(Integer.class);
System.out.println(int.class==Integer.class);
//包装类型.TYPE,对应的基本数据类型的对象
System.out.println(int.class==Integer.TYPE);
}
}
反射属性
- 获得类中属性相关的方法
示例
/*
* 反射操作属性
* Field getDeclaredField(String name)
* Field[] getDeclaredFields()
* Field getField(String name)
* Field[] getFields()
*/
public static void testField(Class cls) throws Exception{
Field field=cls.getDeclaredField("name");
User user=(User) cls.newInstance();
field.setAccessible(true);
//为属性设置值
field.set(user, "wangwu");
//获取属性的值
System.out.println(field.get(user));
}
反射构造方法
- 获得类中构造器相关的方法
示例
/*
* 通过反射获取类型中的构造器
* Constructor<T> getConstructor(Class<?>... parameterTypes) 获取指定公共的共早期
* Constructor<?>[] getConstructors() 获取所有的公共的构造器
* Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 任意权限的其中一个
* Constructor<?>[] getDeclaredConstructors() 任意权限的所有的构造器
* 通过反射创建对象指定使用哪一个构造器
* Class中的newInstance()方法,默认根据表示的类型中的空构造创建对象
* 通过调用使用某一个指定的构造器,创建它所在类型的对象 构造器类中存在一个newInstance(实参)方法
*/
public static void testConstructor(Class cls) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
//1.获取构造器
Constructor cons=cls.getConstructor(String.class);
//获取所有的共早期
Constructor[] arr=cls.getDeclaredConstructors();
for(Constructor c:arr){
System.out.println(c);
}
System.out.println(cons);
//1.通过Class类的newInstance方法
Object obj=cls.newInstance();
System.out.println(obj);
//2.指定使用谋一个
User user=(User) cons.newInstance("zhangsan");
System.out.println(user);
//3.使用私有构造器
arr[1].setAccessible(true); //放开权限 默认false
User user2=(User) arr[1].newInstance("lisi","1234");
arr[1].setAccessible(false); //关闭权限
System.out.println(user2);
}
}
反射方法
- 获得类中方法相关的方法
示例
/*
* 反射操作方法
* 1.如何获取类中的方法
* Method getMethod(String name, Class<?>... parameterTypes) 公共的方法中谋一个
* Method[] getMethods() 公共的方法中所有的
* Method getDeclaredMethod(String name, Class<?>... parameterTypes) 所有方法中的谋一个
* Method[] getDeclaredMethods() 所有方法中的所有的
* 2.调用方法
* Object invoke(Object obj, Object... args)
*/
public static void testMethod(Class cls) throws Exception{
//1.获取方法
Method[] methods = cls.getMethods();
System.out.println(Arrays.toString(methods));
System.out.println(methods[7]);
//调用方法
User user=(User) cls.newInstance();
String str=(String) methods[7].invoke(user);
System.out.println(str);
//获取私有方法
Method me=cls.getDeclaredMethod("heihei", Integer.TYPE);
//方法权限
me.setAccessible(true);
me.invoke(user,100);
me.setAccessible(false);
}
总结
本文列举了反射机制使用过程中常用的、重要的一些类及其方法,更多信息和用法需要近一步的阅读Google提供的相关文档和示例。
在阅读Class类文档时发现一个特点,以通过反射获得Method对象为例,一般会提供四种方法:
- getMethod(parameterTypes)用来获取某个公有的方法的对象
- getMethods()获得该类所有公有的方法
- getDeclaredMethod(parameterTypes)获得该类某个方法
- getDeclaredMethods()。获得该类所有方法
带有Declared修饰的方法可以反射到私有的方法,没有Declared修饰的只能用来反射公有的方法。其他的Annotation、Field、Constructor也是如此。
我们可以使用setAccessible()
方法,这个方式是修改private
权限修饰符的,这个方法的默认值是false
,那么setAccessible(true);
就是修改成为public
权限的修饰符。但是我们修改完成后,还是需要使用setAccessible(false);
方法把权限修饰符修改回private
权限模式。
在ReflectClass类中还提供了两种反射PowerManager.shutdown()的方法,在调用的时候会输出如下log,提示没有相关权限。之前在项目中尝试反射其他方法的时候还遇到过有权限和没权限返回的值不一样的情况。如果源码中明确进行了权限验证,而你的应用又无法获得这个权限的话,建议就不要浪费时间反射了。