ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
本文转载自[秒懂,Java 注解 (Annotation)你可以这样学](https://blog.csdn.net/briblue/article/details/73824058),内容有删改。 注解可以理解为标签,是对代码贴上的标签。 # 注解语法 ## 注解的定义 注解通过关键字`@interface`定义。 ```java public @interface TestAnnotation { } ``` ## 注解的应用 ```java @TestAnnotation public class Test{ } ``` ## 元注解 元注解是可以注解到注解上的注解,能够应用到其他注解上面。元注解也是标签,是一张特殊的标签,作用是给其他普通标签进行解释说明的。 元注解有@Retention、@Documented、@Target、@Inherited、@Repeatable共5种。 ### @Retention @Retention解释说明了这个注解的存活时间,取值有: * RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器编译时将会被丢弃忽视 * RetentionPolicy.CLASS 注解只被保留到编译进行时,不会被加载到JVM中 * RetentionPolicy.RUNTIME 注解可以保留到程序运行时,会被加载到JVM中,在程序运行时可以获取到该注解 ```java @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { } ``` 上面代码中,指定TestAnnotation可以在程序运行时获取到,它的生命周期很长。 ### @Documented @Documented的作用是能够将注解中的元素包含到Javadoc中 ### @Target @Target指定了注解运用的地方,也就是该注解的使用场景被限制了,取值有: * ElementType.ANNOTATION_TYPE 可以给一个注解进行注解 * ElementType.CONSTRUCTOR可以给构造方法进行注解 * ElementType.FIELD可以给属性进行注解 * ElementType.LOCAL_VARIABLE可以给局部变量进行注解 * ElementType.METHOD可以给方法进行注解 * ElementType.PACKAGE可以给一个包进行注解 * ElementType.PARAMETER可以给一个方法内的参数进行注解 * ElementType.TYPE可以给一个类型进行注解,比如类、接口、枚举 ### @Inherited 如果一个父类被一个@Inherited注解过的注解进行注解的话,它的子类没有被任何注解应用的话,这个子类就继承了超类的注解: ```java @Inherited @Retention(RetentionPolicy.RUNTIME) @interface Test {} @Test public class A {} public class B extends A {} ``` 此时B也拥有@Test这个注解。 ### @Repeatable @Repeatable的作用是标明一个注解可以被多次应用,也就是可以多次取值: ```java @interface Persons { Person[] value(); } @Repeatable(Persons.class) @interface Person{ String role default ""; } @Person(role="artist") @Person(role="coder") @Person(role="PM") public class SuperMan{ } ``` 可以理解为,Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。 ## 注解的属性 注解的属性也叫成员变量,注解只有成员变量,没有成员方法。注解的成员变量以“无形参的方法”的形式来声明,方法名定义了该成员变量的名字,返回值定义了该成员变量的类型。注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { int id(); String msg(); } ``` @TestAnnotation注解中拥有id和msg两个属性,使用的时候,应该给他们进行赋值。 ```java @TestAnnotation(id = 3, msg = "hello annotation") public class Test {} ``` 注解属性的默认值需要使用default关键字进行指定: ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { public int id() default -1; public String msg() default "Hi"; } ``` 当只有一个属性时,可以简写如下: ```java @TestAnnotation(3) public class Test {} ``` 当没有属性时,可以简写如下: ```java @TestAnnotation public class Test {} ``` # Java中预置的注解 1、@Deprecated注解用来标记过时的方法、类、成员变量等,编译器在编译阶段遇到这个注解时会发出提醒警告。 2、@Override注解标记子类中的方法是复写的父类的,可以让编译器帮我们检查方法重写是否成功。 3、@SuppressWarnings注解标记阻止编译器进行警告 4、@SafeVarargs是参数安全类型注解,它的目的是提醒开发者不要用参数做一些不安全的操作。 # 注解的提取 注解的提取可以理解为将注解标签在合适的时候撕下来,检阅上面的信息。注解的提取需要通过反射获得。 1、通过Class对象的isAnnotationPresent方法来判断是否应用了注解: ```java public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {} ``` 2、然后通过getAnnotation方法或getAnnotations方法来获取注解: ```java public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {} public Annotation[] getAnnotations() {} ``` 如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。比如: ```java @TestAnnotation() public class Test { public static void main(String[] args) { boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class); if ( hasAnnotation ) { TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class); System.out.println("id:"+testAnnotation.id()); System.out.println("msg:"+testAnnotation.msg()); } } } ``` 除了获取类上的注解,同样可以通过反射获取成员变量、成员方法上的注解,示例如下: ```java @TestAnnotation(msg="hello") public class Test { @Check(value="hi") int a; @Perform public void testMethod(){} @SuppressWarnings("deprecation") public void test1(){ Hero hero = new Hero(); hero.say(); hero.speak(); } public static void main(String[] args) { boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class); if ( hasAnnotation ) { TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class); //获取类的注解 System.out.println("id:"+testAnnotation.id()); System.out.println("msg:"+testAnnotation.msg()); } try { Field a = Test.class.getDeclaredField("a"); a.setAccessible(true); //获取一个成员变量上的注解 Check check = a.getAnnotation(Check.class); if ( check != null ) { System.out.println("check value:"+check.value()); } Method testMethod = Test.class.getDeclaredMethod("testMethod"); if ( testMethod != null ) { // 获取方法中的注解 Annotation[] ans = testMethod.getAnnotations(); for( int i = 0;i < ans.length;i++) { System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName()); } } } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(e.getMessage()); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(e.getMessage()); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(e.getMessage()); } } } ``` 需要注意的是,如果一个注解要在运行时被成功提取,那么 @Retention(RetentionPolicy.RUNTIME) 是必须的。 # 注解使用场景 注解的用处有: * 提供信息给编译器:编译器可以利用注解来探测错误和警告信息 * 编译时的处理:软件工具可以利用注解信息来生成代码、Html文档或其他处理 * 运行时的处理:可以在程序运行时接受提取 当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。 本文转载自[秒懂,Java 注解 (Annotation)你可以这样学](https://blog.csdn.net/briblue/article/details/73824058),内容有删改。