java元注解_Java 元注解 使用示例

注解的注解,称为元注解。

@Target

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)。

所修饰的对象范围:

ElementType:

TYPE:类、接口(包括注解类型)和枚举的声明

FIELD:字段声明(包括枚举常量)

METHOD:方法声明

PARAMETER:参数声明

CONSTRUCTOR:构造函数声明

LOCAL_VARIABLE:本地变量声明

ANNOTATION_TYPE:注解类型声明

PACKAGE:包声明

TYPE_PARAMETER:类型参数声明,JavaSE8引进,可以应用于类的泛型声明之处

TYPE_USE:能标注任何类型名称 JavaSE8引进,此类型包括类型声明和类型参数声明,是为了方便设计者进行类型检查

在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

注意:如果一个注解没有指定@Target注解,则此注解可以用于除了TYPE_PARAMETER和TYPE_USE以外的任何地方。

/***

描述 : 使用TYPE_PARAMETER类型,该注解只能在类型参数声明时使用

*@author: blackcat

* @date : 2020/8/6 15:04*/@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE_PARAMETER)public @interfaceTypeParammeterAnnotation {

}

/***

描述 : 使用TYPE_USE类型,该注解能标注任何类型名称

*@author: blackcat

* @date : 2020/8/6 15:38*/@Target(ElementType.TYPE_USE)

@Retention(RetentionPolicy.RUNTIME)public @interfaceTypeUseAnnotation {

}

/***

描述 : 泛型类型声明时,使用使用TYPE_PARAMETER类型,编译通过

*@author: blackcat

* @date : 2020/8/6 15:05*/

public class TestTypeParameter{/**使用TYPE_PARAMETER类型,会编译不通过*/

/*public @TypeParammeterAnnotation T test(@TypeParammeterAnnotation T a){

new ArrayList();

return a;

}*/}

/***

描述 : ElementType.TYPE_USE使用示例

* 使用TYPE_USE类型,该注解能标注任何类型名称

*@author: blackcat

* @date : 2020/8/6 15:33*/

public classTestTypeUse {/**泛型类型声明时,使用TYPE_USE类型,编译通过*/

public static @TypeUseAnnotation class TypeUseClass extends@TypeUseAnnotation Object {/**使用TYPE_USE类型,编译通过*/

public void foo(@TypeUseAnnotation T t) throws@TypeUseAnnotation Exception {

}

}//如下注解的使用都是合法的

@SuppressWarnings({ "rawtypes", "unused", "resource"})public static void main(String[] args) throwsException {

TypeUseClass typeUseClass = new @TypeUseAnnotation TypeUseClass<>();

typeUseClass.foo("");

List list1 = new ArrayList<>();

List extends Comparable> list2 = new ArrayList<>();

@TypeUseAnnotation String text= (@TypeUseAnnotation String)newObject();

java.util. @TypeUseAnnotation Scanner console= newjava.util.@TypeUseAnnotation Scanner(System.in);

}

}

/***

描述 : ElementType.METHOD只能修饰方法。

*@author: blackcat

* @date : 2020/8/6 16:37*/@Target(ElementType.METHOD)public @interfaceMethodAnnotation {

}

/***

描述 : 测试

*@author: blackcat

* @date : 2020/8/6 16:38*/

public classTestMethodAnnotation {/**编译不通过ElementType.METHOD 不能修饰成员变量,只能修饰方法。*/

/*@MethodAnnotation

String name;*/@MethodAnnotationpublic voidtest(){}

}

对于其他的ElementType注解元素,看ElementType 类的注释即可知道如何使用。

@Retention

这种类型的注解会被保留到那个阶段. 有三个值:

RetentionPolicy.SOURCE —— 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;

RetentionPolicy.CLASS —— 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;

RetentionPolicy.RUNTIME —— 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。

那怎么来选择合适的注解生命周期呢?

首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。

一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,比如@Deprecated使用RUNTIME注解

如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;

如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,使用SOURCE 注解。

注解@Override用在方法上,当我们想重写一个方法时,在方法上加@Override,当我们方法的名字出错时,编译器就会报错

注解@Deprecated,用来表示某个类或属性或方法已经过时,不想别人再用时,在属性和方法上用@Deprecated修饰

注解@SuppressWarnings用来压制程序中出来的警告,比如在没有用泛型或是方法已经过时的时候

/***

描述 : 示不仅会在编译后的class文件中存在,而且在运行时保留。

* 因此它们主要用于反射场景,可以通过getAnnotation方法获取。

*@author: blackcat

* @date : 2020/8/6 16:08*/@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)public @interfaceCountry {/**国家名称*/String name();/**国家语言*/String[] languages();

}

/***

描述 : 默认策略,表示注解会在编译后的class文件中存在,但是在运行时,不会被VM保留。

*@author: blackcat

* @date : 2020/8/6 16:09*/@Target(ElementType.TYPE)

@Retention(RetentionPolicy.CLASS)public @interfaceRegion {/**地区名称*/String name();/**所属国家*/String country();

}

/***

描述 : 表示注解会在编译时被丢弃

*@author: blackcat

* @date : 2020/8/6 16:10*/@Target(ElementType.TYPE)

@Retention(RetentionPolicy.SOURCE)public @interfaceHome {/**成员*/String[] members();/**地址*/String address();*

描述 : 测试

*@author: blackcat

* @date : 2020/8/6 16:14*/@Country(

name= "China",

languages= {"Chinese"}

)

@Region(

name= "GuangDong",

country= "China")

@Home(

members= {"Wolffy","Wolnie","Wilie"},

address= "Qingqing grasslands")public classTestAnnotation {public static voidmain(String[] args) {

Annotation[] annotations= TestAnnotation.class.getAnnotations();

System.out.println("获取能保留到运行时的注解:");for(Annotation annotation :annotations){

System.out.println(annotation.toString());

}

/*

print:

获取能保留到运行时的注解:

@com.blackcat.metaannotation.retention.Country(name=China, languages=[Chinese])

*/

} }

@Documented

是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。

默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了@Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。

/***

描述 : 用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档

*@author: blackcat

* @date : 2020/8/6 16:47*/@Documented

@Target({ ElementType.TYPE, ElementType.METHOD })public @interfaceMyDocumented {

String value()default "这是@Documented注解";

}

/***

描述 :测试document

*@author: blackcat

* @date : 2020/8/6 16:48*/@MyDocumentedpublic classTestDocumented {/*** 测试document*/@MyDocumentedpublicString Test() {return "测试document";

}

}

打开 Java 文件所在的目录,分别输入如下两条命令行:

javac -encoding utf-8MyDocumented.java TestDocumented.java

javadoc-encoding utf-8 -d doc MyDocumented.java TestDocumented.java

在当前目录下生成的doc文件夹里的index.html可以看到以下内容。

7f8b5c8261e1b03a0021acec0456ca73.png

@Inherited

是一个标记注解,用来指定该注解可以被继承。使用@Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。

注意:此注解只对注解标记的超类有效,对接口是无效的。

/***

描述 : 简称 用@Inherited标记的注解

* 注意:此注解只对注解标记的超类(被继承的类-父类)有效,对接口是无效的。

*@author: blackcat

* @date : 2020/8/7 9:10*/@Inherited

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)public @interfaceAbbreviation {/**简称*/String value();

}

/***

描述 : 名称 用@Inherited标记的注解

* 注意:此注解只对注解标记的超类(被继承的类-父类)有效,对接口是无效的。

*@author: blackcat

* @date : 2020/8/7 9:10*/@Inherited

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)public @interfaceName {/**名称*/String value();

}

/***

描述 : 大写 没有用@Inherited标记的注解

*@author: blackcat

* @date : 2020/8/7 9:11*/@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)public @interfaceUpperCaseName {/**大写英文名称*/String value();

}

声明一些接口和类用于举例:

/***

描述 : 有机体

*@author: blackcat

* @date : 2020/8/7 9:13*/@UpperCaseName("ORGANISM")

@Abbreviation("Ogm")

@Name("Organism")public interfaceOrganism {

}

/***

描述 : 植物

*@author: blackcat

* @date : 2020/8/7 9:14

*

* Inherited只作用于子类与父类之间的继承

* Plant接口继承Organism接口,在Organism接口上标记的注解,Plant获取不到*/

public interface Plant extendsOrganism{

}

/***

描述 : 花

*@author: blackcat

* @date : 2020/8/7 9:14*/@UpperCaseName("FLOWER")

@Abbreviation("Flr")

@Name("Flower")public abstract class Flower implementsPlant {

}

/***

描述 : 玫瑰

*@author: blackcat

* @date : 2020/8/7 9:15

*

* Rose类继承Flower抽象类,在Flower抽象类上标记的注解,如果注解是被@Inherited标记的,都可以获取到*/

public class Rose extendsFlower {

}

/***

描述 : 动物

*@author: blackcat

* @date : 2020/8/7 9:15*/@UpperCaseName("ANIMAL")

@Abbreviation("Ani")

@Name("Animal")public interface Animal extendsOrganism{

}

/***

描述 : 哺乳动物

*@author: blackcat

* @date : 2020/8/7 9:15

*

* Mamanl抽象类实现Animal接口,在Animal接口上标记的注解,Mammal获取不到*/

public abstract class Mammal implementsAnimal {

}

/***

描述 : 猴子

*@author: blackcat

* @date : 2020/8/7 9:16*/@UpperCaseName("MONKEY")

@Abbreviation("Mky")

@Name("Monkey")public class Monkey extendsMammal{

}

/***

描述 : 金丝猴

*@author: blackcat

* @date : 2020/8/7 9:16

*

* Roxellanae类继承Monkey类,在Monkey抽象类上标记的注解,如果注解是被@Inherited标记的,都可以获取到;

* 如果被@Inherited标记的注解父类和子类重复标记,则返回子类的注解*/@Name("Roxellanae")public class Roxellanae extendsMonkey {

}

测试:

packagecom.blackcat.metaannotation.inherited;importjava.lang.annotation.Annotation;/***

描述 : 测试

*@author: blackcat

* @date : 2020/8/7 9:17

*

* 说明:

* 01 Abbreviation 注解标记 @Inherited

* 02 Name 注解标记 @Inherited

* 03 UpperCaseName 注解 没有标记 @Inherited

*

* Organism(接口 01,02,03)

* Organism(接口 01,02,03)

*

* 结论:

* 没有被@Inherited注解标记的注解,如:@UpperCaseName注解,就不具有继承特性,在子类中获取不到父类的注解。

*

* Inherited注解继承概念跟我们理解的面向对象继承概念不一样,它只作用于子类与父类之间的继承。

* 如:Rose类就从Flower父类中继承了@Abbreviation和@Name注解;对于接口之间的继承和类与接口之间的实现,这两种继承关系不起作用。

* 如:Plant接口继承Organism接口、Mamanl类实现Animal接口,就没能继承@Abbreviation和@Name注解。

*

* Inherited注解标记的注解,在使用时,如果父类和子类都使用的注解是同一个,那么子类的注解会覆盖父类的注解。

* 如:Roxellanae类用@Name注解标记了,Monkey类也用@Name注解标记了,那么Roxellanae类注解,会覆盖Monkey的@Name注解。*/

public classTestInheritedAnnotation {public static voidmain(String[] args){

Annotation[] annotations= Plant.class.getAnnotations();

System.out.print("Plant接口继承Organism接口,在Organism接口上标记的注解,Plant获取不到:");for(Annotation annotation : annotations){

System.out.print(annotation.toString()+" ");

}

annotations= Mammal.class.getAnnotations();

System.out.print("\nMamanl抽象类实现Animal接口,在Animal接口上标记的注解,Mammal获取不到:");for(Annotation annotation : annotations){

System.out.print(annotation.toString()+" ");

}

annotations= Rose.class.getAnnotations();

System.out.print("\nRose类继承Flower抽象类,在Flower抽象类上标记的注解,如果注解是被@Inherited标记的,都可以获取到:");for(Annotation annotation : annotations){

System.out.print(annotation.toString()+" ");

}

annotations= Roxellanae.class.getAnnotations();

System.out.print("\nRoxellanae类继承Monkey类,在Monkey抽象类上标记的注解,如果注解是被@Inherited标记的,都可以获取到;如果被@Inherited标记的注解父类和子类重复标记,则返回子类的注解:");for(Annotation annotation : annotations){

System.out.print(annotation.toString()+" ");

}

}

}

输出结果:

Plant接口继承Organism接口,在Organism接口上标记的注解,Plant获取不到:

Mamanl抽象类实现Animal接口,在Animal接口上标记的注解,Mammal获取不到:

Rose类继承Flower抽象类,在Flower抽象类上标记的注解,如果注解是被@Inherited标记的,都可以获取到:@com.blackcat.metaannotation.inherited.annotation.Abbreviation(value=Flr) @com.blackcat.metaannotation.inherited.annotation.Name(value=Flower)

Roxellanae类继承Monkey类,在Monkey抽象类上标记的注解,如果注解是被@Inherited标记的,都可以获取到;如果被@Inherited标记的注解父类和子类重复标记,则返回子类的注解:@com.blackcat.metaannotation.inherited.annotation.Abbreviation(value=Mky) @com.blackcat.metaannotation.inherited.annotation.Name(value=Roxellanae)

@Repeatable

Java 8 新增加的,它允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。

Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。

/***

描述 : 声明一个重复注解类

*@author: blackcat

* @date : 2020/8/7 10:38

*

* 创建重复注解 Schedule 时加上了 @Repeatable 注解,指向存储注解 Schedules,

* 这样在使用时就可以直接重复使用 Schedule 注解。*/@Repeatable(Schedules.class)public @interfaceSchedule {

String dayOfMonth()default "1月";

String dayOfWeek()default "1周";int hour() default 12;

}

/***

描述 : 声明一个容器注解类

*@author: blackcat

* @date : 2020/8/7 10:38*/@Retention(RetentionPolicy.RUNTIME)public @interfaceSchedules {

Schedule[] value();

}

packagecom.blackcat.metaannotation.repeatable;importjava.lang.reflect.Method;/***

描述 : 测试

*@author: blackcat

* @date : 2020/8/7 10:38*/@Schedule(dayOfMonth="2月")

@Schedule(dayOfWeek="2周", hour=24)public classTestRepetableAnnotation {

@Schedule(dayOfMonth="3月")

@Schedule(dayOfWeek="3周", hour=23)public voiddoPeriodicCleanup(){}public static void main(String[] args) throwsNoSuchMethodException {

Method doPeriodicCleanup= TestRepetableAnnotation.class.getMethod("doPeriodicCleanup");

Schedules schedules= doPeriodicCleanup.getAnnotation(Schedules.class);

System.out.println("获取标记方法上的重复注解:");for(Schedule schedule: schedules.value()){

System.out.println(schedule);

}

System.out.println("获取标记类上的重复注解:");if (TestRepetableAnnotation.class.isAnnotationPresent(Schedules.class)){

schedules= TestRepetableAnnotation.class.getAnnotation(Schedules.class);for(Schedule schedule: schedules.value()){

System.out.println(schedule);

}

}

}

}

打印结果:

获取标记方法上的重复注解:

@com.blackcat.metaannotation.repeatable.Schedule(hour=12, dayOfMonth=3月, dayOfWeek=1周)

@com.blackcat.metaannotation.repeatable.Schedule(hour=23, dayOfMonth=1月, dayOfWeek=3周)

获取标记类上的重复注解:

@com.blackcat.metaannotation.repeatable.Schedule(hour=12, dayOfMonth=2月, dayOfWeek=1周)

@com.blackcat.metaannotation.repeatable.Schedule(hour=24, dayOfMonth=1月, dayOfWeek=2周)

打印结果说明:

示例:方法上的@Schedule(dayOfMonth="3月")。其中dayOfMonth="3月",而hour与dayOfWeek则为@Schedule注解里的默认值。

其他打印结果类似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值