19 注解
19.1 注解概述
注解,或者叫注释
注释Annotation是一种引用数据类型,编译之后也是xxx.class文件
怎么自定义注解呢?语法格式?
修饰符列表\]@interface 注解类型名{} 注解怎么使用?用在哪里 注解使用:@注解类姓名,可以使用在类上,属性上,方法上,变量上等...,注解还可以出现在注解类型上 2. 代码实现 新建Annotation定义注解 ```java package com.bjpowernode._15Annotation; /* 自定义注解 */ public @interface MyAnnotation00 { } ``` 在类上,属性上,方法上,变量,接口,枚举上等..., ```java package com.bjpowernode._15Annotation; /** 1.注解,或者叫注释 2.注释Annotation是一种引用数据类型,编译之后也是xxx.class文件 3.怎么自定义注解呢?语法格式? [修饰符列表]@interface 注解类型名{} */ @MyAnnotation00 public class AnnotationTest01 { @MyAnnotation00 private int no; @MyAnnotation00 public AnnotationTest01(){} @MyAnnotation00 public static void m1(){ int i =100; } @MyAnnotation00 public void m2(){ } } @MyAnnotation00 interface MyInterface{ } @MyAnnotation00 enum Season { SPRING,SUMMER,AUTUMN,WINTER } ``` 甚至注解用在注解上面 ```java package com.bjpowernode._15Annotation; @MyAnnotation00 public @interface OtherAnnotation { } ``` ### 19.2 JDK自带的注解 #### 19.2.1 种类 java.lang包下 Deprecated:这个注解表示被标记的元素已"过时"。它的主要目的是在不破坏现有代码的前提下,为开发者提供一个过渡期,表明存在更好的替代方案,并警示新代码不要继续使用 Override:被注解的方法旨在重写(或实现)其父类(或接口)中声明的方法 SupressWarnings(了解):在注解的元素(及该元素包含的所有程序元素)中,应取消显示指定的编译器警告 元注解:见下 #### 19.2.2 Override注解 ```java package com.bjpowernode._15Annotation; /* 关于JDK lang包下的Override注解: public @interface Override{ } */ //@Override 编译报错 public class AnnotationTest02 { //@Override 编译报错 private int no; //@Override是标识性注解,只能出现在方法上,是给编译器参考的,和运行阶段没有关系 //凡是java中的方法有这个注释的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器会报错 //例如下面的toString()方法写错了,携程ToString,@Override就会检查此方法有没有错误,认为他没有重写报错 @Override public String toString() { return super.toString(); } public static void main(String[] args) { } } ``` #### 19.2.3 元注解 * 概念 就是注解的注解 @Override为什么不能出现在类或者其他上面 因为源码上面有一行注解@Target(ElementType.METHOD),表示只能出现在方法上,这个就是元注解 @Retention(RetentionPolicy.SOURCE)表示@Override保存在源文件当中,而不是在AnnotationTest02注解文件当中 ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { } ``` * 常见元注解:TargetRetention Target:用来标注"注解类型"的"注解",这个Target注解用来标注"被标注的注解"可以出现在哪一些位置上 Retention:用来标注"注解类型"的"注解",用来标注"被标注的注解"可以出现在哪些位置上。例如@Retention(RetentionPolicy.SOURCE)表示该注解只被保留在java源文件中,编译后生成class就没了 @Retention(RetentionPolicy.CLASS)表示该注解被保存在class文件中 @Retention(RetentionPolicy.RUNTIME)表示该注解被保存在class当中,并且可以被反射机制所读取 #### 19.2.4 Deprecated * 注解源码 ```java @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { } ``` @Retention(RetentionPolicy.RUNTIME)表示注解已经保存在字节码文件中了, 并且可以被程序所读到 @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})既可以在构造器,属性,变量,方法,包上,参数上,类上都可以出现 * 代码实现 ```java package com.bjpowernode._15Annotation; @Deprecated//表示这个类已经过时了 public class AnnotationTest03 { public static void main(String[] args) { AnnotationTest03.doSome1(); AnnotationTest03 at = new AnnotationTest03(); at.doOther(); } //Deprecated这个注解标注的元素已过时 //主要是向其他程序原传递一个信息,已过时,有更好的解决方案存在 @Deprecated public static void doSome1(){ System.out.println("do something!"); } @Deprecated public void doSome2(){ System.out.println("do something!"); } @Deprecated public static void doOther(){ System.out.println("do other..."); } } class T { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { AnnotationTest03 at1 = new AnnotationTest03(); //这个会出现------,表示@Deprecated的信息是保留在注解已经保存在字节码文件中了, 并且可以被程序所读到 at1.doSome2();//在下面Problems会出现警告:'doSome2()' is deprecated Class c = Class.forName("java.lang.Date"); Object obj = c.newInstance(); } } ``` ### 19.3 实现自定义注解 #### 19.3.1 自定义注解指定属性以及使用自定义有属性的注解 自定义注解 ```java package com.bjpowernode._15Annotation; public @interface MyAnnotion02 { /** * 我们通常在注解中可以定义属性,以下这个是MyAnnotation的name属性 * 看着像1个方法,但是是属性name * @return */ String name(); String color(); int age() default 29;//默认值29 } ``` java类使用自定义注解 ```java package com.bjpowernode._15Annotation; @Deprecated()//可以不指定值,因为在新的jdk9中,使用deflaut制定了since的默认值 public class Annotation04 { //@MyAnnotion02():编译报错的原因,如果一个注解当中有属性,那么必须给属性赋值 //指定name属性的值就好了 @MyAnnotion02(name="zhangsan",color= "红色")//age默认值设置好了,可以不用写 public void doSome(){ } } ``` #### 19.3.2 自定义注解运用在方法上 1. 注解属性名称省略(仅限属性名是value的时候并且只有一个属性) * 注解类 ```java package com.bjpowernode._15Annotation; public @interface OtherAnnotation04 { String name(); } ``` * 类运用自定义注解 ```java package com.bjpowernode._15Annotation; public class AnnotationTest05 { @MyAnnotation03(value="hehe") public void doSome(){ } @MyAnnotation03("haha")///注解属性名称省略(仅限属性名是value的时候并且只有一个属性) public void doOther(){ } } ``` 2. 注解属性名称不可省略 * 注解类 ```java package com.bjpowernode._15Annotation; public @interface OtherAnnotation04 { String name(); } ``` * 类运用自定义注解 ```java package com.bjpowernode._15Annotation; public class OtherAnnotationTest06 { @OtherAnnotation04("test") public void doSome(){ } } ``` #### 19.3.3 属性的注解类型 1. 可以是什么类型 ```JAVA package com.bjpowernode._15Annotation; public @interface MyAnnotation05 { /* 注解当中的属性可以是什么类型? 可以是byte short int long float double boolean char String Class 枚举类 以及以上每一种数据形式 */ int value1(); String value2(); int[] value3(); String[] value4(); Season value5();//自建的枚举类型 Season[] value6();//枚举数组 Class parameterType();//ClASS类 Class[] parameters();//CLASS数组 } ``` 枚举类 ```java package com.bjpowernode._15Annotation; public enum Season07 { SPRING,SUMMER,AUTUMN,WINTER } ``` 2. 数组只有一个可以省略大括号的注解 * 注解 ```java package com.bjpowernode._15Annotation; public @interface OtherAnnotation05 { int age(); /* 邮箱地址属性,支持多个 */ String[] email(); /** * 季节数组,Season是枚举类型 * @return */ Season[] seasonArray(); } ``` * 类使用自定义注解 ```java package com.bjpowernode._15Annotation; public class OtherAnnotationTest08 { @OtherAnnotation05(age=25,email={"zhangsan@123.com","zhangsan@souhu.com"}, seasonArray={Season.SPRING,Season.SUMMER}) public void doSome(){ } //数据只有一个,可以省略大括号 @OtherAnnotation05(age=25,email="zhangsan@123.com",seasonArray=Season.SPRING) public void doOther(){ } } ``` #### 19.3.4 @Retention源码 因此@Retention(RetentionPolicy.RUNTIME)其实就是@Retention(value=RetentionPolicy.RUNTIME),并且RetentionPolicy是一个枚举类 ```java @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { } ``` ```java @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); } ``` ```java public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME } ``` #### 19.3.5 @Target源码 因此@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})的意思是传入一个 ElementType\[\]枚举的数组 ```java @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { } ``` ```java @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value(); } ``` ```java public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Formal parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE, /** * Type parameter declaration * * @since 1.8 */ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE } ``` 来个运用 * 注解 ```java package com.bjpowernode._15Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //只允许该注解可以标注类,方法,value=可以省略 @Target(value = {ElementType.TYPE,ElementType.METHOD}) //希望注解可以被反射 @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation06 { } ``` * 使用自定义注解的类 ```java package com.bjpowernode._15Annotation; public class MyAnnotationTest09 { //@MyAnnotation06 报错:'@MyAnnotation06' not applicable to field,因为自定义的注解只能在类和方法上, // 不能在属性上,如果想要加上ElementType.FIELD int i; //@MyAnnotation06 报错:'@MyAnnotation06' not applicable to constructor不能出现在构造方法上 public MyAnnotationTest09(){ } @MyAnnotation06 public void doSome(){ //@MyAnnotation06 报错:'@MyAnnotation06' not applicable to local variable不能出现在局部变量上 int i; } } ``` ### 19.4 反射注解 1. 反射机制代码实现获取类的注解 * 注解 ```java package com.bjpowernode._15Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //只允许该注解可以标注类,方法,value=可以省略 @Target(value = {ElementType.TYPE,ElementType.METHOD}) //希望注解可以被反射 @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation06 { /* value属性 */ //String value() default "北京大兴区"; String value(); } ``` * 使用注解的类 ```java package com.bjpowernode._15Annotation; @MyAnnotation06("上海浦东区") public class MyAnnotationTest09 { //@MyAnnotation06 报错:'@MyAnnotation06' not applicable to field,因为自定义的注解只能在类和方法上, // 不能在属性上,如果想要加上ElementType.FIELD int i; //@MyAnnotation06 报错:'@MyAnnotation06' not applicable to constructor不能出现在构造方法上 public MyAnnotationTest09(){ } // @MyAnnotation06 public void doSome(){ //@MyAnnotation06 报错:'@MyAnnotation06' not applicable to local variable不能出现在局部变量上 int i; } } ``` * 程序入口类通过反射获取注解 ```java package com.bjpowernode._15Annotation; import java.lang.annotation.Annotation; public class RefletAnnotationTest10 { public static void main(String[] args) throws ClassNotFoundException { //先获取类 Class c = Class.forName("com.bjpowernode._15Annotation.MyAnnotationTest09"); //判断类上面是否有Annotation注解类型,即是否有MyAnnotation06注解 System.out.println(c.isAnnotationPresent(MyAnnotation06.class));//true //判断String类上是否存在这个注解,是没有我们自定义的这个注解的 Class stringClass = Class.forName("java.lang.String"); System.out.println(stringClass.isAnnotationPresent(MyAnnotation06.class));//false //如果存在我们想要知道的那个自定义注解 if(c.isAnnotationPresent(MyAnnotation06.class)){ //获取该注解对象 //转型的原因是 getAnnotation 返回的是Annotation类型。如果不转型 不能调用MyA你notation的属性 MyAnnotation06 myAnnotation = (MyAnnotation06)c.getAnnotation(MyAnnotation06.class); System.out.println("类上面的注解对象"+myAnnotation);//类上面的注解对象@com.bjpowernode._15Annotation.MyAnnotation06() //获取对象的属性怎么办?跟调接口没区别 String value = myAnnotation.value(); System.out.println(value);//北京大兴区 不用默认值写在注解上就输出:上海浦东区 } } } ``` 2. 反射机制代码实现获取方法的注解 注解 ```java package com.bjpowernode._15Annotation; 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.METHOD) public @interface MyAnnotation07 { String username(); String password(); } ``` 使用自定义注解的类 ```java package com.bjpowernode._15Annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class MyAnnotationTest10 { @MyAnnotation07(username = "admin",password = "123") public void doSome(){ } public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { //获取MyAnnotationTest的doSome方法上面的注解信息 Class c = Class.forName("com.bjpowernode._15Annotation.MyAnnotationTest10"); //获取方法doSome()方法 Method doSomeMethod = c.getDeclaredMethod("doSome"); //判断该方法上是否存在这个注解 if(doSomeMethod.isAnnotationPresent(MyAnnotation07.class)){ MyAnnotation07 myAnnotation = (MyAnnotation07)doSomeMethod.getAnnotation(MyAnnotation07.class); System.out.println(myAnnotation.username());//admin System.out.println(myAnnotation.password());//123 } } } ``` 2. 案例 需求:假设有这样一个注解,叫做@id,这个注解只能出现在类上面,当类上有这个注解的时候,要求这个类中必须要有一个int类型的id属性。如果没有这个属性就报异常,如果有这个属性则正常执行 思考注解什么作用?注解当中等同于一个标记,如果元素上有这个注解怎么办,没有这个注解怎么办 注解 ```java package com.bjpowernode._15Annotation; 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) public @interface Id09 { } ``` User类 ```java package com.bjpowernode._15Annotation; @Id09 public class User11 { int id; String name; String password; } ``` 反射注解类 ```java package com.bjpowernode._15Annotation; import java.lang.reflect.Field; public class AnnotationTest11 { public static void main(String[] args) throws ClassNotFoundException { Class userClass = Class.forName("com.bjpowernode._15Annotation.User11"); //判断类上是否含有Id注解 if(userClass.isAnnotationPresent(Id09.class)){ //当一个上面有一个Id注解的时候,要求类中必须存在int类型的id属性 Field[] fields = userClass.getDeclaredFields(); boolean isOk = false; for (Field field : fields) { if("id".equals(field.getName())&&"int".equals(field.getType().getSimpleName())){ //表示类是合法的 isOk = true; break; } } //判断是否合法 if(!isOk){ throw new HasnotIdPropertyException12("被@Id标注的类中必须要有一个int类型的id属性"); } } } } ``` 异常类 ```java package com.bjpowernode._15Annotation; public class HasnotIdPropertyException12 extends RuntimeException{ public HasnotIdPropertyException12(){ } public HasnotIdPropertyException12(String s){ super(s); } } ```