反射
目录介绍
-
1.反射概述
- 1.1 反射概述
- 1.2 获取class文件对象的三种方式
- 1.3 反射常用的方法介绍
- 1.4 反射的定义
- 1.5 反射的组成
- 1.6 反射的作用有哪些
-
2.反射的相关使用
- 2.1.1 通过反射获取无参构造方法并使用
- 2.1.2 通过反射获取带参构造方法并使用
- 2.1.3 通过反射获取私有构造方法并使用
- 2.1.4 通过反射获取成员变量并使用
- 2.1.5 通过反射获取无参无返回值成员方法并使用
- 2.1.6 通过反射获取带参无返回值成员方法并使用
- 2.1.7 通过反射获取带参带返回值成员方法并使用
- 2.1.8 通过反射获取无参带返回值成员方法并使用
-
3.相关知识点
- 3.1.1 设置setAccessible(true)暴力访问权限
- 3.1.2 获取Filed两个方法的区别
- 3.1.3 获取Field的类型
- 3.1.4 Method获取方法名,获取方法参数
- 3.1.5 Method方法的invoke()方法执行
关于链接
0.问题答疑
- 0.1 被反射的类是否一定需要无参构造方法?为什么?
- 0.2 反射的使用有什么优势和劣势?为什么说反射可以降低耦合?
- 0.3 反射比较损耗性能,为什么这样说?能否通过案例对比说明反射机制损耗性能……
- 0.4 反射是一种具有与类进行动态交互能力的一种机制,为什么要强调动态交互呢?
- 0.5 Java反射中的setAccessible()方法是否破坏了类的访问规则
1.反射概述
1.1 反射概述
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
- 对于任意一个对象,都能够调用它的任意一个方法和属性;
- 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 要想解剖一个类,必须先要获取到该类的字节码文件对象。
- 而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象
1.2 获取class文件对象的三种方式
1.3 通过反射获取无参构造方法并使用
- A:获取所有构造方法
- public Constructor<?>[] getConstructors()
- public Constructor<?>[] getDeclaredConstructors()
- B:获取单个构造方法
- public Constructor getConstructor(Class<?>... parameterTypes)
- public Constructor getDeclaredConstructor(Class<?>... parameterTypes)
- 方法关键字
- getDeclareMethods() 获取所有的方法
- getReturnType() 获取方法的返回值类型
- getParameterTypes() 获取方法的传入参数类型
- getDeclareMethod("方法名,参数类型.class,....") 获得特定的方法
- 构造方法关键字
- getDeclaredConstructors() 获取所有的构造方法
- getDeclaredConstructors(参数类型.class,....) 获取特定的构造方法
- 成员变量
- getDeclaredFields 获取所有成员变量
- getDeclaredField(参数类型.class,....) 获取特定的成员变量
- 父类和父接口
- getSuperclass() 获取某类的父类
- getInterfaces() 获取某类实现的接口
1.4 反射的定义
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
1.5 反射的组成
1.6 反射的作用有哪些
2.反射的相关使用
2.1.4 通过反射获取成员变量[包含私有]并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils");
Constructor constructor = clazz.getDeclaredConstructor(Context.class);
//设置安全检查,访问私有构造函数必须
constructor.setAccessible(true);
Object[] obj = new Object[]{mContext};
//创建实例
ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj);
//反射修改私有变量
// 获取声明的 code 字段,这里要注意 getField 和 getDeclaredField 的区别
Field gradeField = clazz.getDeclaredField("code");
// 如果是 private 或者 package 权限的,一定要赋予其访问权限
gradeField.setAccessible(true);
// 修改 student 对象中的 Grade 字段值
gradeField.set(clazzObj, 2);
LogUtils.e("点击3----"+clazzObj.getCode());
2.1.5 通过反射获取无参无返回值成员方法[包含私有]并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils");
Constructor constructor = clazz.getDeclaredConstructor(Context.class);
//设置安全检查,访问私有构造函数必须
constructor.setAccessible(true);
Object[] obj = new Object[]{mContext};
//创建实例
ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj);
// 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别
Method goMethod = clazz.getDeclaredMethod("getMethod");
// 赋予访问权限
goMethod.setAccessible(true);
// 调用 goToSchool 方法。
goMethod.invoke(clazzObj);
2.1.6 通过反射获取带参无返回值成员方法并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils");
Constructor constructor = clazz.getDeclaredConstructor(Context.class);
//设置安全检查,访问私有构造函数必须
constructor.setAccessible(true);
Object[] obj = new Object[]{mContext};
//创建实例
ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj);
//这种不行,注意getDeclaredMethod和invoke方法需要传递参数
/*Method copyText = clazz.getDeclaredMethod("copyText");
copyText.setAccessible(true);
copyText.invoke(clazzObj,"测试复制这个功能");*/
// 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别
Method copyText = clazz.getDeclaredMethod("copyText",String.class);
// 赋予访问权限
copyText.setAccessible(true);
// 调用 copyText 方法。
copyText.invoke(clazzObj,"测试复制这个功能");
2.1.7 通过反射获取带参带返回值成员方法并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils");
Constructor constructor = clazz.getDeclaredConstructor(Context.class);
//设置安全检查,访问私有构造函数必须
constructor.setAccessible(true);
Object[] obj = new Object[]{mContext};
//创建实例
ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj);
//这种不行,注意getDeclaredMethod和invoke方法需要传递参数
/*Method copyText = clazz.getDeclaredMethod("copyText");
copyText.setAccessible(true);
copyText.invoke(clazzObj,"测试复制这个功能");*/
// 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别
Method copyText1 = clazz.getDeclaredMethod("copyText",String.class,String.class);
// 赋予访问权限
copyText1.setAccessible(true);
// 调用 copyText 方法
boolean isSuccess = (boolean) copyText1.invoke(clazzObj,"测试复制这个功能","1111");
3.相关知识点
3.1.1 设置.setAccessible(true)暴力访问权限
- 一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。
Field gradeField = clazz.getDeclaredField("code");
// 如果是 private 或者 package 权限的,一定要赋予其访问权限
gradeField.setAccessible(true);
Method goMethod = clazz.getDeclaredMethod("getMethod");
// 赋予访问权限
goMethod.setAccessible(true);
3.1.2 获取Filed两个方法的区别
- 两者的区别就是 getDeclaredField() 获取的是 Class 中被 private 修饰的属性。 getField() 方法获取的是非私有属性,并且 getField() 在当前 Class 获取不到时会向祖先类获取。
//获取所有的属性,但不包括从父类继承下来的属性
public Field[] getDeclaredFields() throws SecurityException {}
//获取自身的所有的 public 属性,包括从父类继承下来的。
public Field[] getFields() throws SecurityException {}
3.1.3 获取Field的类型
- 可以看到 getGenericType() 确实把泛型都打印出来了,它比 getType() 返回的内容更详细。
public Type getGenericType() {}
public Class<?> getType() {}
Field[] filed2 = clazz.getFields();
for ( Field f : filed2 ) {
System.out.println("Field :"+f.getName());
System.out.println("Field type:"+f.getType());
System.out.println("Field generic type:"+f.getGenericType());
System.out.println("-------------------");
}
//打印值
07-31 17:20:41.027 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :cars
07-31 17:20:41.027 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:interface java.util.List
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:java.util.List<com.ycbjie.ycpaidian.four.Car>
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :map
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:class java.util.HashMap
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:java.util.HashMap<java.lang.Integer, java.lang.String>
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :name
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:class java.lang.String
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:class java.lang.String
3.1.4 Method获取方法名,获取方法参数
//获取方法名
//获取方法参数
//返回的是一个 Parameter 数组,在反射中 Parameter 对象就是用来映射方法中的参数。
public Parameter[] getParameters() {}
//Method获取方法名
Method[] declaredMethods1 = clazz.getDeclaredMethods();
for ( Method m : declaredMethods1 ) {
System.out.println("method name:"+m.getName());
}
//获取方法参数
for ( Method m : declaredMethods1 ) {
System.out.println("获取方法参数method name:"+m.getName());
//获取参数
Parameter[] paras;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
paras = m.getParameters();
for (Parameter c : paras ) {
System.out.println("获取参数parameter :"+c.getName()+" "+c.getType().getName());
}
}
//获取所有的参数类型
Class[] pTypes = m.getParameterTypes();
for ( Class c : pTypes ) {
System.out.print("参数类型method para types:"+ c.getName());
}
System.out.println();
System.out.println("==========================================");
}
//打印日志如下所示:
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:copyText
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:copyText
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg1 java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String参数类型method para types:java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:getCode
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:getUserInfo
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:setCode
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 int
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:int
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
3.1.5 Method方法的invoke()方法执行
关于我的博客