概述
注解Annotation是一种引用数据类型,编译之后也是生成xxx.class文件,其定义的语法格式是:
[修饰符列表] @interface 注解类型名{ }
注解使用在类、属性、方法、变量、别的注解等上面,其实可以出现在几乎所有的标识符上,示例如下:
package annotation;public @interface MyAnnotation {}
package annotation;@MyAnnotationpublic class AnnotationTest01 {@MyAnnotationprivate int no;@MyAnnotationpublic String name;protected String password;@MyAnnotationpublic static final float PI = 3.14f;@MyAnnotationpublic void test(){@MyAnnotationint num;}@MyAnnotationpublic void run(@MyAnnotation String name){}}
package annotation;@MyAnnotationpublic @interface OneAnnotatione {}
JDK内置的注解
java.lang包下的注释类型:
Deprecated是用@Deprecated注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择;
Override表示一个方法声明打算重写超类中的另一个方法声明;
SuppressWarning显示应该在注释元素中取消显示指定的编译器警告。
Override注解
package annotation;/*@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}①只能注解方法②这个注解是给编译器参考的,和运行阶段没有关系③凡是java中的方法带有这个注解的,编译器都会进行编译检查,若没有重写父类编译器就会报错*/public class AnnotationTest02 {@Overridepublic String toString() {return super.toString();}}
注解最后编译生成.class文件。
以上,注解的注解称为元注解,@Target、@Retention就是元注解。
关于@Target注解
这是一个元注解,用来标注“注解类型”的“注解”,这个注解可以用来标注“被标注的注解”应该出现在哪些位置上。比如@Target(ElementType.METHOD)就表示被标注的注解可以出现在方法上。
关于@Retention注解
这是一个元注解,用来标记“被标记的注解”应该最终保存在哪里。
比如,@Retention(RetentionPolicy.SOURCE)就表示该注解最终只能被保留在java源文件中;
@Retention(RetentionPolicy.CLASS)表示该注解最终保留到class文件中;
@Retention(RetentionPolicy.RUNTIME)表示该注解最终保留到class文件中,并且可以被反射机制读取到。
Deprecated注解
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})public @interface Deprecated {}
package annotation;/*已过时,有更好的替代,尽量不要调用*/public class AnnotationTest03 {public static void main(String[] args) {//表示这个方法已过时AnnotationTest03 at = new AnnotationTest03();at.doSome(); //do some...AnnotationTest03.doSome(); //do some...AnnotationTest03.doOther(); //do other...}@Deprecatedpublic static void doSome(){System.out.println("do some...");}public static void doOther(){System.out.println("do other...");}}
自定义注解
package annotation;public @interface OneAnnotatione {String value();}
package annotation;public @interface MyAnnotation {/*** 我们通常在注解中可以定义属性,以下这个是MyAnnotation的name属性* 看着像一个方法,实际上他就是一个属性name* @return*/String name();String color();/*** 年龄属性默认为25* @return*/int age() default 25;String value();}
package annotation;public class AnnotationTest04 {//(属性名=属性值)@MyAnnotation(name = "zhangsan",color = "黑色",value = "xixixi")public void doSome(){System.out.println("do some...");}//注解属性只有value时,在使用时该属性名可以省略@OneAnnotatione("圣诞老人")public void doOther(){System.out.println("do other...");}}
自定义注解的使用
package annotation;public @interface OneAnnotatione {String value();}
package annotation;public @interface MyAnnotation {/*** 我们通常在注解中可以定义属性,以下这个是MyAnnotation的name属性* 看着像一个方法,实际上他就是一个属性name* @return*/String name();String color();/*** 年龄属性默认为25* @return*/int age() default 25;String value();}
package annotation.annotation4;public @interface OtherAnnotation {int age();String[] email();Season[] seasonArray();}
package annotation.annotation4;public enum Season {SPRING,SUMMER,AUTNMN,WINTER}
package annotation.annotation4;public class OtherAnnotationTest {@OtherAnnotation(age = 25,email = {"zs@qq.com","ls@sohu.com"},seasonArray = Season.AUTNMN)public void doSome(){}//数组只有一个则大括号可省略@OtherAnnotation(age = 26,email = "zs@163.com",seasonArray = {Season.SPRING,Season.WINTER})public void doOther(){}}
通过反射获取注解属性值
package annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD,ElementType.CONSTRUCTOR})@Retention(RetentionPolicy.RUNTIME)public @interface MyAnnotation {String value() default "杭州西湖区";}
package annotation;@MyAnnotationpublic class MyAnnotationTest {@MyAnnotationint i;@MyAnnotationpublic void doSome(){}@MyAnnotationpublic MyAnnotationTest(){}}
package annotation;import java.lang.annotation.Annotation;public class ReflectAnnotationTest {public static void main(String[] args) throws Exception{//获取这个类的对象Class c = Class.forName("annotation.MyAnnotationTest");//判断这个类是否有注解@MyAnnotation// boolean annotationPresent = c.isAnnotationPresent(MyAnnotation.class);// System.out.println(annotationPresent); //true//判断类上是否有这个注解,如果有,就拿到if (c.isAnnotationPresent(MyAnnotation.class)){//这一切都是因为@Retention(RetentionPolicy.RUNTIME)MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class);System.out.println(myAnnotation); //@annotation.MyAnnotation(value=杭州西湖区)//获取注解对象的属性怎么办?和调接口没区别String value = myAnnotation.value();System.out.println(value); //杭州西湖区}//判断String这个类上是否有@MyAnnotation这个注解Class c2 = Class.forName("java.lang.String");System.out.println(c2.isAnnotationPresent(MyAnnotation.class));//false}}
通过反射获取方法上的注解信息
package annotation.annotation6;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface MyAnnotation {String username();String password();}
package annotation.annotation6;import java.lang.reflect.Method;public class MyAnnotationTest {@MyAnnotation(username = "admin",password = "123")public void doSome(){}//获取MyAnnotationTest的doSome方法上面的注解信息public static void main(String[] args) throws Exception{Class c = Class.forName("annotation.annotation6.MyAnnotationTest");Method method = c.getDeclaredMethod("doSome");//判断该方法上是否有这个注解if (method.isAnnotationPresent(MyAnnotation.class)){MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);System.out.println(annotation.username()); //adminSystem.out.println(annotation.password()); //123}}}
注解灵魂拷问 : 有什么用?
现在提一个需求:
假设有这样一个注解:@Id,这个注解只能出现在类上面.当这个类上有这个注解的时候,要求这个类中必须有一个int类型的属性,如果没有这个属性就报异常,如果有就正常执行.
package annotation.annotation7;import java.lang.annotation.ElementType;import java.lang.annotation.Target;@Target(ElementType.TYPE)public @interface Id {}
package annotation.annotation7;@Idpublic class User {// int id;String name;String password;}
package annotation.annotation7;public class HasNotIdPropertyException extends RuntimeException {public HasNotIdPropertyException(String message) {super(message);}public HasNotIdPropertyException() {}}
package annotation.annotation7;import java.lang.reflect.Field;public class HasIdAnnotationTest {public static void main(String[] args) throws Exception{Class userClass = Class.forName("annotation.annotation7.User");Field[] dfs = userClass.getDeclaredFields();boolean isOk = false;for (Field df : dfs) {if ("id".equals(df.getName())&&"int".equals(df.getType().getSimpleName())){isOk = true;break;}}while (!isOk){throw new HasNotIdPropertyException("被@Id标注的注解必须含有一个int类型的属性!");}}}
这里运行是成功的,就是说,当我们故意注释掉User类的id属性时,就会报这个异常;放开注释时,回父正常,代码正确。达到预期效果。
事实证明这个例子没啥用,即不写该自定义注解并标注在User类上,测试也能有相同效果。
记住获取反射注解的方法是:
Class c = Class.forName("annotation.annotation6.MyAnnotationTest");Method method = c.getDeclaredMethod("doSome");MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
