1.注解的含义和应用场景
注解的作用或者意义:
单独的注解是一种注释,他需要结合其他如反射、插桩的等技术才有意义
元注解:
@Target //作用目标,作用在什么地方
ElementType.ANNOTATION_TYPE 可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。
@Retention //保留时
RetentionPolicy.SOURCE - 标记的注解仅保留在源级别中,并被编译器忽略。
RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。
RetentionPolicy.RUNTIME - 标记的注解由 JVM 保留,因此运行时环境可以使用它。
@Retention 三个值中 SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、
CLASS。
注解的不同级别应用场景
-
RUNTIME
反射 -
SOURCE
APT
annotation processor tools 注解处理器
apt 其实就可以看成javac的一个小插件
APT 不会打包进入APK,只会在编译时参与编译 -
CLASS
- 字节码插桩*
在class中写代码
java有语法检查,没有引用的类用不了,但是class已经经过了语法检查,所以随意 修改
AOP 面向切面编程
2.反射为什么慢?
Java 反射效率低主要原因是:
1.Method#invoke 方法会对参数做封装和解封操作
- invoke 方法的参数是 Object[] 类型,也就是说,如果方法参数是简单类型的话,需要在此转化成 Object 类型,例如 long ,在 javac compile 的时候 用了Long.valueOf() 转型,也就大量了生成了Long 的 Object, 同时 传入的参数是Object[] 数值,那还需要额外封装 object 数组。而在 MethodAccessorGenerator#emitInvoke 方法里我们看到,生成的字节码时,会把参数数组拆解开来,把参数恢复到没有被 Object[] 包装前的样子,同时还要对参数做校验,这里就涉及到了解封操作。因此,在反射调用的时候,因为封装和解封,产生了额外的不必要的内存浪费,当调用次数达到一定量的时候,还会导致 GC
2.需要检查方法可见性
- 反射时每次调用都必须检查方法的可见性(在 Method.invoke 里)
3.需要校验参数
- 反射时也必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0 里或者生成的 Java 版 MethodAccessor.invoke 里)
4.反射方法难以内联
- Method#invoke 就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得 Method.invoke() 自身难以被内联到调用方
5.JIT 无法优化
Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.""
- 因为反射涉及到动态加载的类型,所以无法进行优化。
2.Java动态代理原理
Object o = Proxy.newProxyInstance(this.getClassLoader(), this.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
动态代理的参数函数
类加载器
要代理的接口
回调
3.Retrofit中的实践
create中 动态代理获取对象,获取所有的方法注解,参数注解以及所有参数
网友评论