概述

注解Annotation是一种引用数据类型,编译之后也是生成xxx.class文件,其定义的语法格式是:
[修饰符列表] @interface 注解类型名{ }
注解使用在类、属性、方法、变量、别的注解等上面,其实可以出现在几乎所有的标识符上,示例如下:

  1. package annotation;
  2. public @interface MyAnnotation {
  3. }
  1. package annotation;
  2. @MyAnnotation
  3. public class AnnotationTest01 {
  4. @MyAnnotation
  5. private int no;
  6. @MyAnnotation
  7. public String name;
  8. protected String password;
  9. @MyAnnotation
  10. public static final float PI = 3.14f;
  11. @MyAnnotation
  12. public void test(){
  13. @MyAnnotation
  14. int num;
  15. }
  16. @MyAnnotation
  17. public void run(@MyAnnotation String name){
  18. }
  19. }
  1. package annotation;
  2. @MyAnnotation
  3. public @interface OneAnnotatione {
  4. }

JDK内置的注解

java.lang包下的注释类型:
Deprecated是用@Deprecated注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择;
Override表示一个方法声明打算重写超类中的另一个方法声明;
SuppressWarning显示应该在注释元素中取消显示指定的编译器警告。

Override注解

  1. package annotation;
  2. /*
  3. @Target(ElementType.METHOD)
  4. @Retention(RetentionPolicy.SOURCE)
  5. public @interface Override {
  6. }
  7. ①只能注解方法
  8. ②这个注解是给编译器参考的,和运行阶段没有关系
  9. ③凡是java中的方法带有这个注解的,编译器都会进行编译检查,若没有重写父类编译器就会报错
  10. */
  11. public class AnnotationTest02 {
  12. @Override
  13. public String toString() {
  14. return super.toString();
  15. }
  16. }

注解最后编译生成.class文件。
以上,注解的注解称为元注解,@Target、@Retention就是元注解。

关于@Target注解

这是一个元注解,用来标注“注解类型”的“注解”,这个注解可以用来标注“被标注的注解”应该出现在哪些位置上。比如@Target(ElementType.METHOD)就表示被标注的注解可以出现在方法上。

关于@Retention注解

这是一个元注解,用来标记“被标记的注解”应该最终保存在哪里。
比如,@Retention(RetentionPolicy.SOURCE)就表示该注解最终只能被保留在java源文件中;
@Retention(RetentionPolicy.CLASS)表示该注解最终保留到class文件中;
@Retention(RetentionPolicy.RUNTIME)表示该注解最终保留到class文件中,并且可以被反射机制读取到。

Deprecated注解

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
  4. public @interface Deprecated {
  5. }
  1. package annotation;
  2. /*
  3. 已过时,有更好的替代,尽量不要调用
  4. */
  5. public class AnnotationTest03 {
  6. public static void main(String[] args) {
  7. //表示这个方法已过时
  8. AnnotationTest03 at = new AnnotationTest03();
  9. at.doSome(); //do some...
  10. AnnotationTest03.doSome(); //do some...
  11. AnnotationTest03.doOther(); //do other...
  12. }
  13. @Deprecated
  14. public static void doSome(){
  15. System.out.println("do some...");
  16. }
  17. public static void doOther(){
  18. System.out.println("do other...");
  19. }
  20. }

自定义注解

  1. package annotation;
  2. public @interface OneAnnotatione {
  3. String value();
  4. }
  1. package annotation;
  2. public @interface MyAnnotation {
  3. /**
  4. * 我们通常在注解中可以定义属性,以下这个是MyAnnotation的name属性
  5. * 看着像一个方法,实际上他就是一个属性name
  6. * @return
  7. */
  8. String name();
  9. String color();
  10. /**
  11. * 年龄属性默认为25
  12. * @return
  13. */
  14. int age() default 25;
  15. String value();
  16. }
  1. package annotation;
  2. public class AnnotationTest04 {
  3. //(属性名=属性值)
  4. @MyAnnotation(name = "zhangsan",color = "黑色",value = "xixixi")
  5. public void doSome(){
  6. System.out.println("do some...");
  7. }
  8. //注解属性只有value时,在使用时该属性名可以省略
  9. @OneAnnotatione("圣诞老人")
  10. public void doOther(){
  11. System.out.println("do other...");
  12. }
  13. }

自定义注解的使用

  1. package annotation;
  2. public @interface OneAnnotatione {
  3. String value();
  4. }
  1. package annotation;
  2. public @interface MyAnnotation {
  3. /**
  4. * 我们通常在注解中可以定义属性,以下这个是MyAnnotation的name属性
  5. * 看着像一个方法,实际上他就是一个属性name
  6. * @return
  7. */
  8. String name();
  9. String color();
  10. /**
  11. * 年龄属性默认为25
  12. * @return
  13. */
  14. int age() default 25;
  15. String value();
  16. }
  1. package annotation.annotation4;
  2. public @interface OtherAnnotation {
  3. int age();
  4. String[] email();
  5. Season[] seasonArray();
  6. }
  1. package annotation.annotation4;
  2. public enum Season {
  3. SPRING,SUMMER,AUTNMN,WINTER
  4. }
  1. package annotation.annotation4;
  2. public class OtherAnnotationTest {
  3. @OtherAnnotation(age = 25,email = {"zs@qq.com","ls@sohu.com"},seasonArray = Season.AUTNMN)
  4. public void doSome(){
  5. }
  6. //数组只有一个则大括号可省略
  7. @OtherAnnotation(age = 26,email = "zs@163.com",seasonArray = {Season.SPRING,Season.WINTER})
  8. public void doOther(){
  9. }
  10. }

通过反射获取注解属性值

  1. package annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD,ElementType.CONSTRUCTOR})
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface MyAnnotation {
  9. String value() default "杭州西湖区";
  10. }
  1. package annotation;
  2. @MyAnnotation
  3. public class MyAnnotationTest {
  4. @MyAnnotation
  5. int i;
  6. @MyAnnotation
  7. public void doSome(){
  8. }
  9. @MyAnnotation
  10. public MyAnnotationTest(){
  11. }
  12. }
  1. package annotation;
  2. import java.lang.annotation.Annotation;
  3. public class ReflectAnnotationTest {
  4. public static void main(String[] args) throws Exception{
  5. //获取这个类的对象
  6. Class c = Class.forName("annotation.MyAnnotationTest");
  7. //判断这个类是否有注解@MyAnnotation
  8. // boolean annotationPresent = c.isAnnotationPresent(MyAnnotation.class);
  9. // System.out.println(annotationPresent); //true
  10. //判断类上是否有这个注解,如果有,就拿到
  11. if (c.isAnnotationPresent(MyAnnotation.class)){
  12. //这一切都是因为@Retention(RetentionPolicy.RUNTIME)
  13. MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class);
  14. System.out.println(myAnnotation); //@annotation.MyAnnotation(value=杭州西湖区)
  15. //获取注解对象的属性怎么办?和调接口没区别
  16. String value = myAnnotation.value();
  17. System.out.println(value); //杭州西湖区
  18. }
  19. //判断String这个类上是否有@MyAnnotation这个注解
  20. Class c2 = Class.forName("java.lang.String");
  21. System.out.println(c2.isAnnotationPresent(MyAnnotation.class));//false
  22. }
  23. }

通过反射获取方法上的注解信息

  1. package annotation.annotation6;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.METHOD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface MyAnnotation {
  9. String username();
  10. String password();
  11. }
  1. package annotation.annotation6;
  2. import java.lang.reflect.Method;
  3. public class MyAnnotationTest {
  4. @MyAnnotation(username = "admin",password = "123")
  5. public void doSome(){
  6. }
  7. //获取MyAnnotationTest的doSome方法上面的注解信息
  8. public static void main(String[] args) throws Exception{
  9. Class c = Class.forName("annotation.annotation6.MyAnnotationTest");
  10. Method method = c.getDeclaredMethod("doSome");
  11. //判断该方法上是否有这个注解
  12. if (method.isAnnotationPresent(MyAnnotation.class)){
  13. MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
  14. System.out.println(annotation.username()); //admin
  15. System.out.println(annotation.password()); //123
  16. }
  17. }
  18. }

注解灵魂拷问 : 有什么用?

现在提一个需求:
假设有这样一个注解:@Id,这个注解只能出现在类上面.当这个类上有这个注解的时候,要求这个类中必须有一个int类型的属性,如果没有这个属性就报异常,如果有就正常执行.
image.png

  1. package annotation.annotation7;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Target;
  4. @Target(ElementType.TYPE)
  5. public @interface Id {
  6. }
  1. package annotation.annotation7;
  2. @Id
  3. public class User {
  4. // int id;
  5. String name;
  6. String password;
  7. }
  1. package annotation.annotation7;
  2. public class HasNotIdPropertyException extends RuntimeException {
  3. public HasNotIdPropertyException(String message) {
  4. super(message);
  5. }
  6. public HasNotIdPropertyException() {
  7. }
  8. }
  1. package annotation.annotation7;
  2. import java.lang.reflect.Field;
  3. public class HasIdAnnotationTest {
  4. public static void main(String[] args) throws Exception{
  5. Class userClass = Class.forName("annotation.annotation7.User");
  6. Field[] dfs = userClass.getDeclaredFields();
  7. boolean isOk = false;
  8. for (Field df : dfs) {
  9. if ("id".equals(df.getName())&&"int".equals(df.getType().getSimpleName())){
  10. isOk = true;
  11. break;
  12. }
  13. }
  14. while (!isOk){
  15. throw new HasNotIdPropertyException("被@Id标注的注解必须含有一个int类型的属性!");
  16. }
  17. }
  18. }

这里运行是成功的,就是说,当我们故意注释掉User类的id属性时,就会报这个异常;放开注释时,回父正常,代码正确。达到预期效果。
image.png

事实证明这个例子没啥用,即不写该自定义注解并标注在User类上,测试也能有相同效果。
记住获取反射注解的方法是:

  1. Class c = Class.forName("annotation.annotation6.MyAnnotationTest");
  2. Method method = c.getDeclaredMethod("doSome");
  3. MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);