java注解是jdk1.5以后新出的特性,注解提升了Java语言的表达能力,有效地实现了应用功能和底层功能的分离,框架/库的程序员可以专注于底层实现。

1、Java内置注解

主要有三个:

@Override:用于标明此方法覆盖了父类的方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Deprecated:用于标明已经过时的方法或类,源码如下

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@SuppressWarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}

value是一个数组,可以有如下值:

  • deprecation:使用了不赞成使用的类或方法时的警告;
  • unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
  • fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
  • path:在类路径、源文件路径等中有不存在的路径时的警告;
  • serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
  • finally:任何 finally 子句不能正常完成时的警告;
  • all:关于以上所有情况的警告。

三个综合使用的示例:

 //注明该类已过时,不建议使用
@Deprecated
class A{
public void A(){ } //注明该方法已过时,不建议使用
@Deprecated()
public void B(){ }
} class B extends A{ @Override //标明覆盖父类A的A方法
public void A() {
super.A();
} //去掉检测警告
@SuppressWarnings({"uncheck","deprecation"})
public void C(){ }
//去掉检测警告
@SuppressWarnings("uncheck")
public void D(){ }
}

2、自定义注解

2.1、注解定义

Java提供了一些定义好的注解如@SuppressWarnings、@Override等。也可以借助元注解来自定义注解,其格式示例如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
@Documented
@Inherited
public @interface Mark {
}

2.1.1、四个元注解

@Target,指定注解的应用目标,如@Target(ElementType.METHOD)。目标可以是多个,用{}表示,如@Target({TYPE, FIELD, METHOD, PARAMETER}),若未声明@Target,默认为适用于所有类型。目标是个枚举值ElementType,可以是:

  • TYPE:表示类、接口(包括注解),或者枚举声明
  • FIELD:字段,包括枚举常量
  • METHOD:方法
  • PARAMETER:方法中的参数
  • CONSTRUCTOR:构造方法
  • LOCAL_VARIABLE:本地变量
  • ANNOTATION_TYPE:注解类型
  • PACKAGE:包
  • TYPE_PARAMETER:表示注解可以用于标注类型参数(java 1.8新加入),如class D<@Parameter T> { }
  • TYPE_USE:表示注解可以用于标注任意类型(不包括class)(java 1.8新加入),如用于标注父类或接口:class Image implements @Rectangular Shape { }

 @Retention,表示注解信息保留到什么时候。若未声明@Retention,则默认为CLASS。Retention取值只能有一个,类型为RetentionPolicy,它是一个枚举,有三个取值:

  • SOURCE:只在源码中保留,编译器将代码编译为字节码后就会丢掉注解信息。如@Override、Lombok的大多数注解如@Data
  • CLASS:保留到字节码文件中,但JVM将class文件加载到内存时注解丢弃。
  • RUNTIME:一直保留到运行时,故能在运行时被JVM或其他使用反射机制的代码所读取和使用。如@Deprecated、Spring中的@Controller、@Autowired、@RequestMapping等。

@Documented,表示注解包含到Javadoc中,即被修饰者在javadoc中仍然带着该注解。若未声明则默认不被包含
@Inherited,表示子类是否会继承父类的注解。若未声明则默认不会被继承。注:这里说的继承是指被注解修饰的类的继承。注解本身是不支持继承的,因此定义注解时不能使用关键字extends来继承某个注解。

另:@Repeatable,Java1.8新增了@Repeatable元注解,以允许对同一个位置重复使用相同注解。在Java1.8前没法对同一个元素重复使用同一个注解,要实现该效果只能为注解定义一个数组元素以接收多个值。

//Java8前无法这样使用
@FilterPath("/web/update")
@FilterPath("/web/add")
public class A {} //只能为@FilePath注解定义一个数组元素
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterPath {
String [] value();
} //使用
@FilterPath({"/update","/add"})
public class A { }

Java1.8之前

//使用Java8新增@Repeatable原注解
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)//参数指明接收的注解class
public @interface FilterPath {
String value();
} @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface FilterPaths {
FilterPath[] value();
} //使用案例
@FilterPath("/web/update")
@FilterPath("/web/add")
@FilterPath("/web/delete")
class AA{ }

Java1.8之后借助@Repeatable元注解

2.1.2、注解参数

可以为注解定义一些参数,定义方式为:在注解内定义一些方法,方法返回值类型表示参数的类型。

参数的类型不是什么都可以的,合法的类型有:8钟基本类型(不允许使用包装类型)、String、Class、枚举(enum)、注解(Annotation)、以及这些类型的数组。倘若使用了其他数据类型,编译器将会丢出一个编译错误。

如对于如下注解,可以这样使用:使用: @SuppressWarnings(value={"deprecation","unused"})

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}

注:

  • 当注解只有一个参数,且名称为value时,提供参数值时可以省略"value=",如@SuppressWarnings({"deprecation","unused"})
  • 参数定义时可以使用default指定一个默认值。
  • 元素不能有不确定的值:必须要么具有默认值,要么在使用注解时提供元素的值,且值不能为null(无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值)。

2.1.3、注解特点

注解的定义不支持继承:无法使用关键字extends来继承某个@interface,因为注解在编译后,编译器会让注解自动继承java.lang.annotation.Annotation接口。

import java.lang.annotation.Annotation;
//反编译后的代码
public interface DBTable extends Annotation
{
public abstract String name();
}

注解可以组合:虽然注解的定义不能继承,但可以通过类似组合的写法来继承其他注解的功能,如RestController就拥有了@Controller、@ResponseBoddy的功能(另外可以发现上面的元注解也是组合了其他元注解以拥有相应的功能),其定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController { /**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
* @since 4.0.1
*/
@AliasFor(annotation = Controller.class)
String value() default ""; }

另外,javax validation中自定义validation注解也是通过组合@Constraint注解来实现的,详情可参阅:https://www.cnblogs.com/z-sm/p/4872259.html

2.2、注解解析器

注解解析有两种方式:运行时通过反射解析;编译时解析。

2.2.1、运行时解析

通过反射。

在运行时被解析的注解必须将@Retention设为RetentionPolicy.RUNTIME。与反射相关的类(Class、Field、Method、Constructor)中定义了一些方法:如Student.class.getAnnotation,可以利用反射机制在运行时获取注解信息。方法如:

    //获取所有的注解
public Annotation[] getAnnotations()
//获取所有本元素上直接声明的注解,忽略inherited来的
public Annotation[] getDeclaredAnnotations()
//获取指定类型的注解,没有返回null
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
//判断是否有指定类型的注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

2.2.2、编译时解析

通过SPI调用注解解析器从而改变AST。

定义好注解解析器代码,在编译时javac会通过SPI加载并执行注解解析器,注解解析器可以修改抽象语法是(AST)从而实现注解应有的功能。定义注解解析器的两种方式:

Annotation Processing Tool:JDK5引入,JDK8废除。废弃原因:集成在com.sun.mirror非标准包下;没有集成到javac中,需要额外运行

Pluggable Annotation Processing API:JDK6引入,作为前者的替代。编译时(javac执行时)会调用该API,故可通过实行该API来改变编译行为。

例子:Lombok通过改变编译后的AST来达到修改代码的目的、AspectJ也是在编译期改变目标代码来实现类似于静态代理功能。

3、参考资料

  1. 注解——老马说编程
  2. 深入理解Java注解类型(推荐)

Java注解小记的更多相关文章

  1. Java注解

    Java注解其实是代码里的特殊标记,使用其他工具可以对其进行处理.注解是一种元数据,起到了描述.配置的作用,生成文档,所有的注解都隐式地扩展自java.lang.annotation.Annotati ...

  2. 19.Java 注解

    19.Java注解 1.Java内置注解----注解代码 @Deprecated                                    //不推荐使用的过时方法 @Deprecated ...

  3. Java注解入门

    注解的分类   按运行机制分:   源码注解:只在源码中存在,编译后不存在 编译时注解:源码和编译后的class文件都存在(如@Override,@Deprecated,@SuppressWarnin ...

  4. java注解(Annotation)解析

    注解(Annotation)在java中应用非常广泛.它既能帮助我们在编码中减少错误,(比如最常见的Override注解),还可以帮助我们减少各种xml文件的配置,比如定义AOP切面用@AspectJ ...

  5. JAVA 注解的几大作用及使用方法详解

    JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...

  6. attilax.java 注解的本质and 使用最佳实践(3)O7

    attilax.java 注解的本质and 使用最佳实践(3)O7 1. 定义pojo 1 2. 建立注解By eclipse tps 1 3. 注解参数的可支持数据类型: 2 4. 注解处理器 2 ...

  7. paip.java 注解的详细使用代码

    paip.java 注解的详细使用代码 作者Attilax 艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/att ...

  8. JAVA 注解的几大作用及使用方法详解【转】

    java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解 ...

  9. 框架基础——全面解析Java注解

    为什么学习注解? 学习注解有什么好处? 学完能做什么? 答:1. 能够读懂别人写的代码,特别是框架相关的代码: 2. 让编程更加简洁,代码更加清晰: 3. 让别人高看一眼. spring.mybati ...

随机推荐

  1. C# 服务端获取客户端 系统/浏览器/IP

    /// <summary> /// 获取客户端操作系统版本 /// </summary> /// <returns></returns> public ...

  2. Bzoj5332: [Sdoi2018]旧试题

    国际惯例的题面首先我们进行一些相对显然的数学变化.解释一下第二行的那个变形,如果一个数是ijk的因数,那么它一定能被分解成三部分分别是i,j,k的因数.我们钦定一个质数只能在三部分的一个中出现.如果一 ...

  3. Convert a VMDK (VMWare) file to VHDX (Hyper-V)

    https://www.meziantou.net/2016/09/09/convert-a-vmdk-vmware-file-to-vhdx-hyper-v Microsoft provides a ...

  4. git的安装以及入门

    安装:https://blog.csdn.net/itpinpai/article/details/48105445 (1)下载文件 初始化 git init 连远程服务器 git remote ad ...

  5. no console to display at this time

    no console to display at this time我把控制台关掉,重新run as 还是同样问题,于是运行其他项目,但是其他项目能正常运行,说明项目写的有问题,而不是控制台的问题

  6. .NetCore中EFCore for MySql整理(三)之Pomelo.EntityFrameworkCore.MySql

    一.Pomelo.EntityFrameworkCore.MySql简介 Git源代码地址:https://github.com/PomeloFoundation/Pomelo.EntityFrame ...

  7. boost 线程安全队列

    threadnullmethodsprocessingobjectsignal // QueueImplementation.cpp : Defines the entry point for the ...

  8. DNS缓存中毒是怎么回事?

    近来,网络上出现互联网漏洞——DNS缓存漏洞,此漏洞直指我们应用中互联网脆弱的安全系统,而安全性差的根源在于设计缺陷.利用该漏洞轻则可以让用户无法打开网页,重则是网络钓鱼和金融诈骗,给受害者造成巨大损 ...

  9. [猜你喜欢]冠军“yes,boy!”分享,含竞赛源代

    [猜你喜欢]冠军“yes,boy!”分享,含竞赛源代码  DataCastle运营 发表于 2016-7-20 17:31:52      844  3  5 我是Yes,boy! ,来自东北大学计算 ...

  10. SpringBoot集成Redis分布式锁以及Redis缓存

    https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...