Java高级特性之注解

  • Post author:
  • Post category:java


本博文涉及代码已上传至github,结合代码看更容易理解哟~链接:

https://github.com/ZNKForSky/AnotationDemo


概念

Java注解是

JDK1.5的新特性

,与注释比较类似,不同的是注释是给我们开发人员看的,注解是给代码看的,它是

代码层面的解释说明



注解的使用也很简单,语法规则:

@注解名称

,比如我们常见的“@Override”。


作用

①生成doc文档;

②使用反射对代码进行分析;

③编译检查。


JDK中预定义的一些注解:

①@Override: 检测方法是否是重写父类或父接口的;

②@Deprecated: 标识过时的内容;

③@SuppressWarnings: 压制警告,一般传入参数all,@SuppressWarnings(“all”)


自定义注解:

格式:


[元注解]


public @interface 注解名称{


属性列表;


}

eg.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface ZnkAnno {
String value();
}

ps:[ ] 表示可选。


注解本质:

通过javap命令反编译class文件,可以发现

注解的本质就是继承 java.lang.annotation.Annotation的接口。


注解中的属性(

其实就是接口中的抽象方法

):

语法:

1>属性的返回值必须是基本数据类型、String、Class、枚举、注解或者以上类型的数组;

2>定义属性后,在使用注解时要给属性赋值,如果定义属性时使用default关键字给了属性默认值,则可以不做赋值;如果只有一个名称为value的属性,则value可以省掉,直接定义值即可;数组赋值时需要{},如果数组中只有一个值,则可省略{}。


元注解

元注解就是用来解释说明注解的注解。

①@Target:描述注解能够作用的位置。

–ElementType常用取值:

  • TYPE:可以作用在类上;
  • FIELD:可以作用在属性上;
  • METHOD:可以作用在方法上。

PS:所有取值见下图。

②@Retention:描述注解被保留的阶段(

java代码SOURCE(源码阶段)、CLASS(字节码文件)、RUNTIME(运行时阶段)三个阶段


— @Retention(RetentionPolicy.RUNTIME) 表示当前被描述的注解会被保留到class字节码文件中,并会被JVM读取到

③@Documented:描述注解是否被抽取到api文档中

④@Inherited:描述注解是否被子类继承


注解的使用场景

上面我们已经了解到注解的保留级别包括SOURCE、CLASS、RUNTIME三个阶段,那我们根据保留级别不同,自然能想到注解的使用也存在不同场景。



级别



技术



备注


源码


APT


1.在编译期能够获取注解与注解声明的类,包括类中所有成员信息,一般用于生成额外的辅助类。


2.也可以用作语法检查。


字节码


字节码增强


在编译出


Class


后,通过修改


Class


数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。


运行时


反射


在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。




源码级别的注解示例:

1>APT(Annotation Processor Tools)

2>语法检查:在Android开发过程中,当我们需要设计接口供使用者调用时,如果需要对入参类型做限定,比如限定为资源ID、布局ID等类型参数,就可以通过@IdRes、@LayoutRes分别限定。同时我们可以利用@IntDef、@StringDef、@LongDef 定义自己的入参类型检查。

PS:我们也可以改变自定义限定注解语法检查的提示等级:

–字节码级别的注解在热修复技术中会用到,这里暂时不做详细描述(关键我自己还没整明白{>-<})。

–运行时的注解主要是与反射结合做一些操作,比如我们常用的ButterKnife,最初就是通过注解加反射的方式实现的,我给出的git代码里面也有实现findViewById的案例,感兴趣的小伙伴可以瞄一眼。


反射获取泛型真实类型(很少用到,了解即可)

当我们对泛型类进行反射时,需要拿到泛型信息来完成像Json反序列化的操作时,需要借助Type体系来完成。Type接口包含一个实现类Class和四个实现接口:

我们开发过程中经常会有这样一种场景,我们请求后台接口,后台返回的数据类型可能是这样的:

{


“data”:{


“name”:”luffy”,

“age”:18,

“sex”:”male”

},

“code”:200,

“message”:”查询成功”

}

我们会将后台返回的数据封装成一个Bean:

static class Response<T> {
        T data;
        int code;
        String message;

        public Response(T data, int code, String message) {
            this.data = data;
            this.code = code;
            this.message = message;
        }

        @Override
        public String toString() {
            return "Response{" +
                    "data=" + data +
                    ", code=" + code +
                    ", message='" + message + '\'' +
                    '}';
        }
    }

因为每个接口返回的data多数情况下类型是不同的,所以我们一般用泛型处理。比如现在某个接口返回的数据是

{


“name”:”luffy”,

“age”:18,

“sex”:”male”

}

我们将它封装成Bean:

    static class Data {
        String name;
        int age;
        String sex;

        public Data(String name, int age, String sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }

        @Override
        public String toString() {
            return "Data{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", sex='" + sex + '\'' +
                    '}';
        }
    }

我们测试如上的数据是否可以进行反序列化:如下图所示,出现了ClassCastException。

解决方案:

Type type = new TypeToken<Response<Data>>() {
}.getType();
Response<Data> response = gson.fromJson(result, type);

我们来看看TypeToken里面的具体实现:

我也按照这个思路试着实现了一下:

package com.luffy.reflect.Type;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 作者:<a href="https://blog.csdn.net/qq_35101450">张宁科CSDN主页</a><p>
 * 创建时间:2020/10/27 15:51<p>
 * 描述:参考TypeToken 解决Json反序列化问题
 */
public class TypeRefrence<T> {
    Type type;
    T t;

    protected TypeRefrence() {
        //1.获得泛型类型
        Type genericSuperclass = getClass().getGenericSuperclass();
        System.out.println("genericSuperclass的类型是: " + genericSuperclass.getClass());
        System.out.println("genericSuperclass ====== " + (genericSuperclass instanceof Class));
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        //2.因为泛型类型可以定义多个: MainActivity<T,E,K...> 所以返回是一个数组
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        type = actualTypeArguments[0];
    }

    public Type getType() {
        return type;
    }
}

关于注解先讲这么多,觉得能get到好东西的小伙伴记得关注加点赞哟, 不懂的地方也可以评论区留言与我交流~



版权声明:本文为qq_35101450原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。