在Android中使用反射基本上是为了调用系统api私有方法,导致都忘记了反射强大的功能。所以梳理了一下,其实发现很多都有在用。
Java反射的作用
要让Java程序运行起来,必须让Java类被Java虚拟机加载,否则不能运行。但是Java反射机制在编译时并不确定哪个类被加载了,而是在程序运行的时候才加载,即使用了在编译期并不知道的类。 比较灵活,在一些框架中使用的比较多(一些框架需要处理很多不同的逻辑,写死可能代码量就大了)。
那么总结的作用有三个:
- 调用私有方法,调用私有属性。比如AbstractList.removeRange等等。
- 动态创建对象,例如动态加载网络上的class文件,比较灵活。
- 动态代理,比较灵活,特别是代理的方法比较多的时候,如果使用普通的静态代理就需要为每一个方法写相应的代理方法,但是动态代理只需要在invoke方法中处理就行了。
动态代理写一个例子。 模仿Retrofit写一个(Retrofit的源码没有读过,只是了解使用了动态代理,当然也没有我的demo这么粗糙),返回是什么请求类型以及url。
// 定义GET请求注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
String value() default "no url";
}
// 定义POST请求注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
String value() default "no url";
}
// 业务逻辑需要处理的需求
public interface RequestApi {
@GET(value = "http://www.baidu.com")
String getLoginInfo();
@POST(value = "http://www.google.com")
String doLogin();
}
// Demo
public class RequestProxyDemo {
public static void main(String args[]) {
RequestApi api = (RequestApi) Proxy.newProxyInstance(RequestApi.class.getClassLoader(),
new Class[]{RequestApi.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Annotation[] annotations = method.getAnnotations();
StringBuilder stringBuilder = new StringBuilder();
if (annotations == null || annotations.length != 1) {
return null;
}
stringBuilder.append("这是一个");
String value = "";
if (annotations[0].annotationType() == POST.class) {
stringBuilder.append("POST");
value = ((POST)annotations[0]).value();
} else if (annotations[0].annotationType() == GET.class) {
stringBuilder.append("GET");
value = ((GET)annotations[0]).value();
}
stringBuilder.append("请求,请求的url: ").append(value);
return stringBuilder.toString();
}
});
System.out.println(api.doLogin());
System.out.println(api.getLoginInfo());
}
}
获取Class对象方法
使用反射的第一步,需要获取Class对象。总结有四种方法,其中1,2方法是通过已加载了的Class来获取,一般的作用是用来调用不能访问的私有属性和私有方法。3,4则直接通过class文件来动态的生成class对象,比如Android中一个按钮点击的操作是加载某个路径下面的class文件然后执行一个方法,现在想要改具体实现效果就可以只用改class文件不需要重新打包一个apk。
- 类名.class
- 对象.getClass()
- Class.forName(“”),可以用来动态的加载class文件。比如加载网络下载的class文件等等。
- 使用ClassLoader.loadClass(“”)
加载本地Class的一个demo。
javac NetworkClass.java
生成.class文件,同时删除NetworkClass.java
文件。
public class NetworkClass
{
private String name;
public NetworkClass(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
Demo调用class文件。
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class NetworkClassSample {
public static void main(String[] args) {
try {
// 加载NetworkClass。注意需要把NetworkClassSample.java和NetworkClass.class放在同一个目录里。
Class clazz = Class.forName("NetworkClass");
// 获取getName方法
Method method = clazz.getDeclaredMethod("getName");
Constructor constructor = clazz.getConstructor(String.class);
// 创建对象
Object object = constructor.newInstance("xixi");
method.setAccessible(true);
// 调用方法
System.out.println(method.invoke(object));
} catch (Exception e) {
e.printStackTrace();
}
}
}
常用API
Class.java
,主要是为了获取一些Class的信息,包括构造函数、方法、成员变量。
forName() // 通过class的名字获取Class对象
getClassLoader() // 获取Class加载器
getConstructor(Class<?>... parameterTypes) // 通过参数类型获取指定public构造函数
getDeclaredConstructor(Class<?>... parameterTypes) // 通过参数获取指定构造函数(包含私有构造函数)
getEnclosingConstructor() // 返回的是本地或者匿名类的构造方法
getField(String name) // 返回某个公共成员变量的域
getDeclaredField(String name) // 返回某个成员变量的域
getMethod(String name, Class<?>... parameterTypes) // 获取某个指定的公共方法
getDeclaredMethod(String name, Class<?>... parameterTypes) // 获取某个指定的方法
getEnclosingMethod(String name, Class<?>... parameterTypes) // 获取某个指定的本地或者匿名的方法
Field.java
,获取到Field对象以后就可以获取、修改对应的成员变量的值了。
get(Object obj) // 拿到obj对象该成员变量的值。
getInt(Object obj) // getFloat()、getLong()等八种基本类型的值
set(Object obj, Object value) // 设置obj对象该成员变量的值是value。
setInt(Object obj, int value) // setFloat()、setLong()等八种基本类型的值
Method.class
,方法类,可以用来触发方法。
invoke(Object obj, Object... args) // 调用obj对象的该方法,后面对应的是参数值。
Proxy.java
,代理类。
// 用来生成代理类的对象,注意这里的interfaces只能是接口并且数量不能超过65535。
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)