Android -从浅到懂使用反射机制
程序员文章站
2022-03-07 22:03:02
定义JAVA反射机制是在运行状态中,对于任何一个类,都能够知道这个类的所有属性和方法;对于任何一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。使用场景反射是在运行时获取确定类型,绑定对象。常见的两个使用场景运行时获取对象的所有信息泛型擦除在运行时获取类这里的运行时指的是程序在运行后。相应的还有编译时,编译时是编译器将源代码翻译成机器能识别的代码。举个例子说明编译时和运行时的区别:微信图片list1 没有声明...
定义
JAVA反射机制是在运行状态中,对于任何一个类,都能够知道这个类的所有属性和方法;
对于任何一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。
使用场景
反射是在运行时获取确定类型,绑定对象。
常见的两个使用场景
- 运行时获取对象的所有信息
- 泛型擦除
在运行时获取类
这里的运行时指的是程序在运行后。相应的还有编译时,编译时是编译器将源代码翻译成机器能识别的代码。
举个例子说明编译时和运行时的区别:
泛型擦除
val list = arrayListOf<Int>(1,2,3)
val clz = Class.forName("java.util.ArrayList")
val method = clz.getMethod("add", Object::class.java)
method.invoke(list,"hhh")
Log.e(TAG,"list:${list.toString()}")
输出:
list:[1, 2, 3, hhh]
反射的利弊
利
- 运行时类型的判断
- 动态类加载,动态代理使用反射。
弊
- 性能问题,反射相当于一系列解释操作。
- JVM 不会去优化这部分代码
- 代码不易阅读
为什么会出现性能问题?
-
Method#invoke
方法会对参数做封装和解封操作 - 需要检查方法可见性
- 需要校验参数
- 反射方法难以内联
-
JVM
无法优化:Java
文档中介绍
invoke
方法源码:
class Class {
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 1. 检查方法权限
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
// 2. 获取方法
Method method = getMethod0(name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
// 3. 返回方法的拷贝
return getReflectionFactory().copyMethod(method);
}
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 1. 检查方法权限
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
// 2. 获取方法
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
// 3. 返回方法的拷贝
return getReflectionFactory().copyMethod(method);
}
}
如何使用
想要使用反射机制,就必须要先获取到该类的字节码文件对象
(.class)
,通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class
类型的对象。
获取字节码文件对象
- 1、根据类名:
类名.class
- 2、根据对象:
对象.getClass()
- 3、根据全限定类名:
Class.forName(全限定类名)
常用的是第一种。
获取构造函数
构造函数分为两种:带参和不带参
- 获取不带参构造函数
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getDeclaredConstructor();
Object user = constructor.newInstance();
- 获取带参构造函数
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor(int.class,String.class);
Object user = constructor.newInstance(1,"张三");
获取成员变量
成员变量一般为公有和私有
- 获取公有成员变量
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Field declaredField = classs.getField("userName");
// 赋新值
declaredField.set(user, "李四");
- 获取私有成员变量
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Field declaredField = classs.getDeclaredField("userName");
// 因为属性是私有的,所有需要打开可见权限
declaredField.setAccessible(true);
// 赋新值
declaredField.set(user, "李四");
获取方法
成员方法一般为公有和私有,同时方法也分有参和无参
- 获取公有成员无参方法
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Method method = classs.getMethod("getUserName");
method.invoke(user);
- 获取公有成员有参方法
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Method method = classs.getMethod("getUserName", String.class);
method.invoke(user, "王五");
- 获取私有成员无参方法
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Method method = classs.getDeclaredMethod("getUserName");
// 因为属性是私有的,所有需要打开可见权限
method.setAccessible(true);
method.invoke(user);
- 获取私有成员有参方法
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getConstructor();
Object user = constructor.newInstance();
Method method = classs.getDeclaredMethod("getUserName",String.class);
// 因为属性是私有的,所有需要打开可见权限
method.setAccessible(true);
method.invoke(user, "王五");
Tip
- 一般来说,构造函数大部分是 public ,而成员变量和方法大部分是 private。public 权限直接正常调用即可、private需要在调用前增加
setAccessible(true)
- 获取构造函数、成员变量、成员方法都有一个获取所有的方法,根据需要去调用。如:
getConstructor:获取单个构造函数,getConstructors:获取所有构造函数
总结
反射的作用是在运行时动态获取对象信息。
实现步骤:
- 获取
Class
:有三种方式,常用的是Class.forName("类全名称")
- 获取构造函数:通过
Class
对象调用 如:clz.getConstructor()
得到构造函数 - 创建对象:通过构造函数创建对象 如:
con.newInstance()
没有 Declared
前缀会返回所有的 public
方法,包括父类的方法。
而加 Declared
前缀会返回所有自己定义的方法,public,protected,private
都在此,但是不包括父类的方法。
这也正是 getMethod
和 getDeclaredMethod
的区别。
- 获取私有构造函数
Class classs = Class.forName("com.xxx.xxx.User");
Constructor constructor = classs.getDeclaredConstructor();
// 因为属性是私有的,所有需要打开可见权限
constructor.setAccessible(true);
Object user = constructor.newInstance();
- 获取私有成员变量
...
Field declaredField = classs.getDeclaredField("userName");
// 因为属性是私有的,所有需要打开可见权限
declaredField.setAccessible(true);
- 获取私有成员方法
...
Method method = classs.getDeclaredMethod("getUserName");
// 因为属性是私有的,所有需要打开可见权限
method.setAccessible(true);
反射耗时的原因:
-
invoke
方法需要做封装和解封操作 - 需要类型检查、权限检查
- 需要校验参数
- 需要检查方法的可见性
-
JVM
无法做优化
参考
- Reflection:Java反射机制的应用场景
- 深入浅出Java反射原理和使用场景
- java反射机制详解
- Java 反射 -超详细讲解(附源码)
- java反射为什么慢
- 强大的反射库 FreeReflection
本文地址:https://blog.csdn.net/ITxiaodong/article/details/110642378