注解和动态代理

一、注解

1.什么是注解

注解:又称java标注,单独说注解他的作用就是注释。只有当注解和反射、插桩等技术一起使用才会有意义。

2.什么是元注解

元注解:简单的理解就是注解的注解。(给注解添加的注解叫做元注解)

@Retention(RetentionPolicy.RUNTIME) //保留到什么时候,代码中?编译期?运行期?
@Target({ElementType.TYPE})//test注解的作用目标。(用在哪里)
public @interface test {
}
复制代码

其中@Retention和@Target就是元注解。

3.常用的元注解介绍

@Retention:标志当前注解、保留到什么时候。
1.SOURCE:编译器运行后会丢掉改注解,及编译期注解就不存在了。
2.CLASS:编译期注解存在,但是在运行期注解就没了。(在android中编译成.dex后也会消失)
3.RUNTIME:在任何时期注解都存在。
复制代码
@Target:注解作用在哪些地方
1.TYPE:类、接口、枚举
2.FIELD:字段生声明,包括枚举常量
3.METHOD:方法。
4.PARAMETER:形参上。
5.CONSTRUCTOR:构造函数上。
6.LOCAL_VARIABLE:局部变量
7.ANNOTATION_TYPE:注解上。
8.PACKAGE:包上。
9.TYPE_PARAMETER:类型参数声明。1.8
10.TYPE_USE:类型使用。
复制代码

4.默认注解

Java中的注解默认实现Annotation接口。

package java.lang.annotation;
public interface Annotation {  
    boolean equals(Object obj);
    int hashCode();  
    String toString();
    Class<? extends Annotation> annotationType();
}
复制代码

二、注解的使用场景

image.png

1.源码级别的例子lint检查@IntDef注解

该注解用于替换枚举,因枚举的每个枚举参数都会生成一个对象占用空间大,所以通过@IntDef来进行替换。该注解只是用于lint检查,报的错误只是提醒,不会影响程序的编译和运行。

@Retention(SOURCE)//源码级别的注解
@Target({ANNOTATION_TYPE})//注解的注解。
public @interface IntDef {
    
    int[] value() default {};

  
    boolean flag() default false;

    
    boolean open() default false;
}
复制代码

2.源码级别注解,APT(annotation processor tools)注解处理器

APT技术是来源于javac命令,在编译成字节码的时候,添加的插件,该插件可以生成自动生成java类。(APT不会打包进apk,只会参与编译。)

image.png

3. 编辑期的注解。字节码插桩技术(字节码增强技术)

字节码插桩技术:简单的说就是在.class中写代码。(可以跳过语法检查,在apt中生成的类,无法掉用,只能用字节码插桩跳过语法检查,把代码加入到字节码里)

4.编译期的注解。AOP切面编程。(注解和字节码插桩技术)

通过注解,有注解的加入代码,没注解的不加入。(需要登陆的代码,都要加入登陆的操作,不需要的,则不加入。)

5.运行时的注解。反射技术

面试题1:final修饰的基本类型,能通过反射修改内容么?

答案:final修饰的非基本类型是可以修改的。基本类型因为在编译期就已经确定了值是多少,因为编译期的优化导致final类型的变量都直接修改成了常量值。所以导致反射的时候修改不了结果。如果想要修改需要在构造方法里去初始化fianl的值。

面试题2:反射的速度为什么慢?(影响其实很小)

答案:1)在调用invoke方法的时候需要装箱和拆箱的操作:基本数据类型,先装箱,再转换成object的数组。最后掉用的时候有需要拆箱的操作。

2)反射需要按名字检索类和方法:遍历类去寻找。

3)检查方法:检查方法的权限,检查方法的参数,包括形参和实参。

4)编译器无法对动态掉用的代码做优化,比如内联:反射涉及到动态解析的类型,影响内敛判断且无法进行JIT.

三、动态代理

1.动态代理的使用方法

public static void main(String[] args){
        jiatianlong hao = new jiatianlong();
        //返回一个新对象。及动态代理生成的一个新类。
        student student = (com.lzl.proces.proxy.student) Proxy.newProxyInstance(
                hao.getClass().getClassLoader(),//类加载器
                new Class[]{student.class},//接口数组
                new ProxyInvokeHandler(hao)//代理处理程序(可以理解成回调)。
        );
        student.play();
    }
复制代码
public class ProxyInvokeHandler implements InvocationHandler {
    private Object realObject;
    public ProxyInvokeHandler(Object realObject){
        this.realObject = realObject;
    }

    /**
     *
     * @param o 代理对象
     * @param method 调用方法
     * @param objects 方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

        return method.invoke(realObject,objects);
    }
}

复制代码

2.动态代理的原理

字节码生成技术:会帮你生成一个字节码(class对象),这个字节码是一个继承了proxy实现了相应接口的类。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享