Java核心基础之自定义注解

编写代码边撸猫 2021-04-12 16:14:37 ⋅ 966 阅读

本文转载自掘金,作者-jack_xu。

主页:https://juejin.cn/user/1802854801877191

认识注解

注解( Annotation )相当于一种标记,在程序中加入注解就等于为程序打上某种标记,没有加,则等于没有任何标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上。

作用分类:

1、编写文档:通过代码里标识的注解生成文档【生成文档doc文档】

2、代码分析:通过代码里标识的注解对代码进行分析【使用反射】

3、编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

JDK中预定义的注解

JDK1.5之后内部提供的三个注解:

  • @Deprecated 意思是“废弃的,过时的”
  • @Override 意思是“重写、覆盖”
  • @SuppressWarnings 意思是“压缩警告”,作用:用于抑制编译器产生警告信息。

接下来我们依次介绍一下。

@Deprecated

该注解标注的内容,表示已过时,我们先定义两个方法,show1()方法因为过时了,功能跟不上需求了,所以我们在它上面打了一个@Deprecated注解,show2()是一个更强大的功能。

    @Deprecated
    public void show1(){
        // 发现过时了,功能跟不上需求了
    }

    public void show2(){
        // 功能更加强大的方法
    }

我们在调用的地方发现有一条红色的横线,意思就是说这个方法已经废弃了,不建议使用了。那既然过时了你直接把这个方法删了不就得了,又要留着又要告诉你不要使用,岂不是多此一举?其实这么做的原因就是为了做到向下兼容,你把它删了,那之前老代码引用这个方法的地方都得报错,所以我们需要通过这种方法才做到平滑过渡。

Java核心基础之自定义注解

 


还有JDK中自带的 Data 类,getYear() 方法上也有根红色的横线,这个也是因为打了@Deprecated 的注解,我们点进去看下确实如此。这样的例子还有很多,小伙们在平时的开发中注意观察下,过期的方法就不要使用啦...

Java核心基础之自定义注解

 

@Override

检测被该注解标注的方法是否是继承自父类(接口)的,这个也比较简单,它可以避免方法名和参数写错,起到检查的作用。

    @Override
    public String toString() {
        return "xxxx";
    }
复制代码

@SuppressWarnings

压制警告,我们看下图 str 没有用到,编辑器提示了一个粉红色的波浪线,并且告诉我们 variable 'str' is never used

Java核心基础之自定义注解

 

这只是其中一个例子,还有各种各样的警告,这时候我们会觉得很烦,怎么办呢,我们就可以用 @SuppressWarnings,告诉编辑器不要给我提示了,一般我们传递参数all,@SuppressWarnings("all"),此时波浪线就没有了。

Java核心基础之自定义注解

 

当然如果不想抑制所有的话,有些警告我们想看到的话,也可以抑制具体的值,如下表所示:

参数

说明

说明

all

to suppress all warnings

抑制所有警告

boxing

to suppress warnings relative to boxing/unboxing operations

抑制装箱、拆箱操作时候的警告

cast

to suppress warnings relative to cast operations

抑制映射相关的警告

dep-ann

to suppress warnings relative to deprecated annotation

抑制启用注释的警告

deprecation

to suppress warnings relative to deprecation

抑制过期方法警告

fallthrough

to suppress warnings relative to missing breaks in switch statements

抑制确在switch中缺失breaks的警告

finally

to suppress warnings relative to finally block that don’t return

抑制finally模块没有返回的警告

hiding

to suppress warnings relative to locals that hide variable

抑制与隐藏变量的局部变量相关的警告

incomplete-switch

to suppress warnings relative to missing entries in a switch statement

忽略没有完整的switch语句

nls

to suppress warnings relative to non-nls string literals

忽略非nls格式的字符

null

to suppress warnings relative to null analysis

忽略对null的操作

rawtypes

to suppress warnings relative to un-specific types when using generics on class params

使用generics时忽略没有指定相应的类型

restriction

to suppress warnings relative to usage of discouraged or forbidden references

抑制与不鼓励或禁止引用的使用有关的警告

serial

to suppress warnings relative to missing serialVersionUID field for a serializable class

忽略在serializable类中没有声明serialVersionUID变量

static-access

to suppress warnings relative to incorrect static access

抑制不正确的静态访问方式警告

synthetic-access

to suppress warnings relative to unoptimized access from inner classes

抑制子类没有按最优方法访问内部类的警告

unchecked

to suppress warnings relative to unchecked operations

抑制没有进行类型检查操作的警告

unqualified-field-access

to suppress warnings relative to field access unqualified

抑制没有权限访问的域的警告

unused

to suppress warnings relative to unused code

抑制没被使用过的代码的警告

自定义注解

除了JDK自带的注解,我们还可以编写自定义注解来满足我们平时开发中的一些需要,这个也是本篇文章的重点, 创建注解的格式如下:

// 元注解
public @interface 注解名称{
    // 属性列表
}
复制代码

自定义的注解反编译后的内容,注解的本质其实就是一个接口,继承Annotation父接口

public interface MyAnno extends java.lang.annotation.Annotation {

}
复制代码

下面写了一个注解的例子,我们来依次介绍下。。

Java核心基础之自定义注解

 

元注解

在注解类上使用另一个注解类,那么被使用的注解类就称为元注解,JDK中给我们提供的4个元注解

@Retention

@Retention 注解决定 MyAnnotation 注解的生命周期,描述注解被保留到的阶段,SOURCE < CLASS < RUNTIME

  • SOURCE:让 MyAnnotation 注解只在java源文件中存在,编译成 .class文件后注解就不存在了
  • CLASS:让 MyAnnotation 注解在java源文件(.java文件)中存在,编译成 .class文件后注解也还存在,被MyAnnotation 注解类标识的类被类加载器加载到内存中后 MyAnnotation 注解就不存在了
  • RUNTIME:让 MyAnnotation 这个注解的生命周期一直到程序运行时都存在

@Target

@Target 元注解决定了一个注解可以标识到哪些成分上,如标识在在类身上,或者属性身上,或者方法身上等成分

  • ElementType.TYPE:可以作用在类上
  • ElementType.METHOD:可以作用在方法上
  • ElementType.FIELD:可以作用在成员变量上

@Documented

描述注解是否被抽取到JavaDoc api中

@inherited

描述注解是否可以被子类继承

属性

上文中说过注解的本质就是接口,所以属性就是在接口中定义的抽象方法。 

语法:类型 属性名();返回的结果必须是如下类型:

  • 基本数据类型
  • String类型
  • 枚举类型
  • 注解
  • 以上类型的数组

上图中自定义了一个color的属性,在使用中如下:

//应用MyAnnotation注解的color属性
@MyAnnotation(color = "red")
public class AnnotationTest {

    public static void main(String[] args) {
        //用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
        MyAnnotation annotation = (MyAnnotation) AnnotationTest.class.getAnnotation(MyAnnotation.class);
        //输出red
        System.out.println(annotation.color());
    }

}
复制代码

属性赋值一些注意点

1、如果定义的属性时,使用 default 关键字给属性默认初始值,可以在使用注解时不赋值

// 指定默认值 在使用注解的时候没有给该属性赋值,那么就使用默认值
String color() default "blue";
复制代码

2、如果一个注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略掉value=部分。

 @SuppressWarnings("deprecation")
 @Retention(RetentionPolicy.RUNTIME)
 @Target( { ElementType.METHOD, ElementType.TYPE })
 public @interface MyAnnotation {
     String color() default "blue";//为属性指定缺省值
     String value();//定义一个名称为value的属性
 }

 @MyAnnotation("xxx")//使用的时候等价于@MyAnnotation(value="xxx")
复制代码

3、数组赋值的时候,值使用{}包裹,如果数组中只有一个值,那么{}可以省略

//数组类型的属性
int[] arrayAttr() default {1,2,4};
//使用的时候加上{}
@MyAnnotation(arrayAttr={2,4,5})
//如果数组属性只有一个值,这时候属性值部分可以省略大括号
@MyAnnotation(arrayAttr=2),
复制代码

4、枚举

//枚举类型的属性
EumTrafficLamp lamp() default EumTrafficLamp.RED;
//使用注解
@MyAnnotation(lamp=EumTrafficLamp.GREEN)
复制代码

5、注解的属性值又是一个注解

MetaAnnotation annotationAttr() default @MetaAnnotation("aaa");
复制代码

自定义注解的案例

下面我们通过注解的方式来实现一个通过反射创建一个对象的功能。

自定义注解:

/**
 * 自定义注解
 * 该注解表面要执行哪个类中的哪个方法
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InvokAnno {

    String className();

    String methodName();

}
复制代码

定义一个实体类:

public class Person {

    public void show() {
        System.out.println("person show ....");
    }

}
复制代码

通过反射创建对象:

@InvokAnno(className = "com.jackxu.annotation.Person", methodName = "show")
public class InvokeTest {
    public static void main(String[] args) throws Exception {
        // 获取类对象
        Class<InvokeTest> clazz = InvokeTest.class;
        // 获取类对象中的注解
        InvokAnno an = clazz.getAnnotation(InvokAnno.class);
        /**
         *  注解的本质是接口,获取到的an其实是接口的实现,即
         *  public class MyInvokAnno implements InvokAnno{
         *
         *      String className(){
         *          return "com.jackxu.annotation.Person";
         *      }
         *      String methodName(){
         *          return "show";
         *      }
         *  }
         */
        // 获取注解中对应的属性
        String className = an.className();
        String methodName = an.methodName();
        System.out.println(className);
        System.out.println(methodName);

        // 通过反射的方式实现接口的功能
        Class<?> aClass = Class.forName(className);
        Method show = aClass.getDeclaredMethod("show");
        // 执行对应的方法
        Object o = aClass.newInstance();
        show.invoke(o);
    }

}
复制代码

执行一下,好,大功告成!

Java核心基础之自定义注解

 

自定义注解一般我们应用在拦截器或者AOP里面,用来设计自己的框架,使代码看起来非常优雅。


全部评论: 0

    我有话说:

    Google发布Java 核心工具库——Guava 28.0

    Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库。这个库是为了方便编码,并减少编码错误。这个库提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和

    精品推荐:Java核心数据结构(List,Map,Set)使用技巧与优化

    JDK提供了一组主要的数据结构实现,如List、Map、Set等常用数据结构。这些数据都继承 java.util.Collection 接口,并位于 java.util 包内。

    「开源资讯」Guava 28.2 发布,Google 的 Java 核心工具库

    前言 Guava 28.2 发布了,Guava 是 Google 的一个开源项目,包含许多 Google 核心 Java 常用库,如:集合 [collections] 、缓存 [caching

    Java Web实战篇-代码

    代码美-小小的优化让你的代码Bug更少,执行效率更高

    Java注解文档生成工具—smart-doc,看完有替换swagger的冲动

    Tips:喜欢的话可以关注小萌哦~~~今天小萌给大家推荐的一个开源Java Restful API 文档生成

    JAVA实现附近范围内公交定位问题

    接上篇【前端实战篇:通过JS抓取城市所有站点与线路】获取附近定位信息

    GitHub精选:2018年11月份最热门的Java开源项目

    又到了揭晓 11 月份最热门 Java 开源项目排名的时候了,在本月的名单中,出现了几个新面孔,如Java 核心知识库、轻量级容错组件Resilience4j .....

    Gradle 5.0 RC2 发布,支持 Java 11、Kotlin DSL 生产就绪

    Gradle 5.0 RC2 发布了,Gradle 是一个基于 Apache Ant 和 Apache Maven 概念的项目自动化构建工具,支持依赖管理和多项目,类似 Maven,但比简单轻便。

    smart-doc 2.0.0 重磅发布,Java注解 API 文档生成工具

    smart-doc是一款同时支持java restful api和apache dubbo rpc接口文档生成的工具,smart-doc颠覆了传统类似swagger这种大量采用注解侵入来生成文档的

    Fizz Gateway 1.1.1 发布,基于 Java异步框架WebFlux开发的微服务网关

    Fizz Gateway 是一个基于 Java异步框架WebFlux开发的微服务网关,能够实现热服务编排、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理等目的,拥有强大的定义

    蚂蚁金服 Java RPC 开源框架—SOFARPC

    SOFARPC 是一个高可扩展性、高性能、生产级的 Java RPC 框架。

    微信开发神器全能微信Java开发工具包

    必须分享的微信神器 weixin-java-tools

    精品推荐:一览GitHub上最受程序欢迎的5大Java开源项目

    列举了GitHub上一些最流行的Java项目。从Mockitos到Guava,以及 java-design-patterns等供大家学习。

    2017 年度编程语言榜,Java 最流行、JavaScript 最没价值?

    2017 年度编程语言榜,Java 最流行、JavaScript 最没价值?

    Google 宣布正式开源 Jib ,帮助 Java 应用快速容器化

    Google 本周宣布开源一款新的 Java 工具 Jib ,旨在让开发者使用他们熟悉的工具更轻松地将 Java 应用程序容器化。

    Java Web实战篇:发布和运维必备的12条Linux命令

    作为一名Java起步的从业人员,学会一些常用的Linux命令是必须的。

    Java Web实战篇-轻松提高千万级数据库查询效率

    通过优化数据库设计、java后台和数据库优化达到提高千万级数据查询的效率。

    【轻阅读】基于 Java Spring cloud的开源在线教育系统调试实战

    转载:https://www.toutiao.com/i6759167063090004483开篇之前分享