JavaseLearn25-注解

JavaseLearn25-注解

1.注解概述

  1. 注解,或者说叫注释类型。(Annotation)

  2. 注解Annotation是一种引用数据类型:

    • 编译后也是生成xxx.class文件
  3. 自定义注释的语法格式:

    • [修饰符列表] @interface 注解类型名{
          
      }
      
    • public @interface MyAnnotation {
      }
      
  4. 注解怎么用,用在哪儿?

    • 注解使用时的语法格式是:@注解类型名。如:@Override

    • 注解可以出现在类上、属性上、方法上、变量上等。

    • 注解还可以出现在注解类型上。

    • package com.tsccg.java;
      
      /**
       * @Author: TSCCG
       * @Date: 2021/07/14 20:03
       */
      @MyAnnotation01
      public class AnnotitationDemo01 {
          @MyAnnotation01
          private String name;
          @MyAnnotation01
          public AnnotitationDemo01(@MyAnnotation01 String name) {
              this.name = name;
          }
          @MyAnnotation01
          public void m1() {
              @MyAnnotation01
              int i = 10;
          }
          @MyAnnotation01
          public static void m2() {
      
          }
      }
      @MyAnnotation01
      interface Anno{
      
      }
      @MyAnnotation01
      enum Season{
          /**
           * 春,夏,秋,冬
           */
          @MyAnnotation01
          SPRING,SUMMER,AUTUMN,WINTER
      }
      
      /**
       * 自定义注解
       */
      @MyAnnotation01
      @interface MyAnnotation01 {
      }
      /**
       * 另一个自定义注解
       */
      @MyAnnotation01
      @interface AnotherAnnotation{
          
      }
      

2.JDK自带的注解

java.lang包下的注解类型

  1. Override(掌握)
    • 表示一个方法声明打算重写父类中的另一个方法声明。
  2. Deprecated(掌握)
    • 标示所注解的程序元素已过时。
  3. SuppressWarnings(知道即可)
    • 指示应该在注释元素,以及包含在该注释元素中的所有程序元素中,取消显示指示的编译器警告。

3.Override注解

  1. 只能注解方法。
  2. 标识性注解,给编译器做参考用。
  3. 编译器看到方法上有这个注解的时候,编译器会自动检查该方法是否重写了父类的方法。
    • 如果没有重写,则报错。
  4. 只在编译阶段起作用,与运行期无关。
public class AnnotationDemo02 {
    @Override
    public String toString(){
        return null;
    }
}

4.元注解

4.1什么是元注解?

我们先想一下,为什么Override注解只能使用在方法上呢?

我们可以查看一下Override源码:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

可见,有另一个注解@Target用来注解Override,作用是让Override只能用于方法上。

这种用于注解注解类型的注解,我们称之为元注解。

4.2常见的元注解

  • Target
  • Retention

4.3关于Target注解

  1. 这是一个元注解,用于标注”注解类型”的“注解”。
  2. Target注解用于限定其所注解的“注解类型”可以出现在哪些位置上。
    • @Target(ElementType.METHOD):表示被注解的“注解类型”只能出现在方法上。

4.4关于Retention注解

  1. 这是一个元注解,用于标注”注解类型”的“注解”。
  2. 用于限定被其所注解的“注解类型”最终保存的位置。
    • @Retention(RetentionPolicy.SOURCE):表示该注解最终被保留在java源文件里。
    • @Retention(RetentionPolicy.CLASS):表示该注解最终被保留在class文件里。
    • @Retention(RetentionPolicy.RUNTIME):表示该注解最终被保留在class文件里,并且可以被反射机制所读取。

5.Deprecated注解

Deprecated的作用:

  • Deprecated用于标示一个程序元素已过时。
  •  主要向其他程序员传达一个信息,告知已过时,有更好的解决方案。
    

6.注解中的属性

6.1使用含有属性的注解

定义如下注解:

注解中有三个属性,其中一个定义了默认值。

package com.tsccg.java.annotation;

/**
 * @Author: TSCCG
 * @Date: 2021/07/14 20:13
 */
public @interface MyAnnotation {
    //看似方法,实则为注解属性
    String name();
    int age();
    //定义默认值
    String address() default "中国";
}

在另一个类中使用该注解:

如果直接在方法上方用:@MyAnnotation 会报错。

需要填写注解属性:@MyAnnotation(name = "张三",age = 20)

address属性因为已设默认值,不写也没关系。

public class AnnotationDemo04 {
    /*
    报错,需要填写注解属性
    @MyAnnotation
    public void m1() {

    }*/
	//正确使用方式
    //@MyAnnotation(属性名1 = 属性值1,属性名2 = 属性值2)
    @MyAnnotation(name = "张三",age = 20)
    public void m1() {

    }
}

6.2当属性名为value时可省略

当属性名不是value时,使用时必须写上该属性名。

当一个注解中属性名为value,并且有且仅有一个属性,使用时可以不写属性名。(源码中有很多这样的注解)

当一个注解中有多个属性时,即使有属性名为value,使用时也不可以省略该属性名。

package com.tsccg.java.annotation;

/**
 * @Author: TSCCG
 * @Date: 2021/07/14 22:11
 */
public class AnnotationDemo04 {
    //当属性名不是value时,使用时必须写上该属性名
    @MyAnnotation1(name = "张三")
    public void m1() {

    }
    //当一个注解中属性名为value,并且有且仅有一个属性,使用时可以不写属性名(源码中有很多这样的注解)
    @MyAnnotation2("啦啦啦")
    public void m2() {

    }
    //当一个注解中有多个属性时,即使有属性名为value,使用时也不可以省略该属性名
    @MyAnnotation3(value = "啦啦啦",age = 20)
    public void m3() {

    }
}
@interface MyAnnotation1 {
    String name();
}
@interface MyAnnotation2 {
    String value();
}
@interface MyAnnotation3 {
    String value();
    int age();
}

6.3注解中的属性的类型

注解中属性的属性可以是:
byte、short、int、long、float、double、boolean、char
String、Class、枚举类型
以及以上所有类型的数组。

package com.tsccg.java.annotation.myannotation;

/**
 * @Author: TSCCG
 * @Date: 2021/07/15 10:29
 * 注解里可以使用什么类型的属性
 */
public @interface MyAnnotation02 {
    /*
    注解中属性的属性可以是:
        byte、short、int、long、float、double、boolean、char
        String、Class、枚举类型
        以及以上所有类型的数组
     */
    int value1();
    String value2();
    Season02 value3();
    Class value4();

    int[] value5();
    String[] value6();
    Season02[] value7();
    Class[] value8();

    //Object value();不可使用
}

/**
 * 定义一个枚举类型
 */
enum Season02 {
    //春夏秋冬
    SPRING,SUMMER,AUTOMN,WINTER
}

6.4使用含有数组属性的注解

自定义注解,含有数组属性:

package com.tsccg.java.annotation.myannotation;

/**
 * @Author: TSCCG
 * @Date: 2021/07/15 10:44
 */
public @interface MyAnnotation03 {
    int age();
    String[] address();
    Season03[] seasons();

}
/**
 * 定义一个枚举类型
 */
enum Season03 {
    //春夏秋冬
    SPRING,SUMMER,AUTOMN,WINTER
}

测试类:

package com.tsccg.java.annotation.myannotation;

/**
 * @Author: TSCCG
 * @Date: 2021/07/15 10:28
 */
public class AnnotationDemo05 {
    @MyAnnotation03(age = 20,address = {"广州","河南"},
            seasons = {Season03.SPRING,Season03.SUMMER})
    public void m1() {

    }
    //当数组中只有一个元素时,可省略大括号
    @MyAnnotation03(age = 20,address = "北京",
            seasons =Season03.AUTOMN)
    public void m2() {

    }
}

7.元注解Target和Retention的源码

7.1Retention源码

//元注解
public @interface Retention {
    //value属性,有且仅有一个属性,使用时可省略属性名
    RetentionPolicy value();
}

RetentionPolicy源码:

是一个枚举类型

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

7.2Target源码

//元注解
public @interface Target {
    //属性,是一个数组
    ElementType[] value();
}

ElementType源码:

是一个枚举类型,包含一个程序中的各个位置

public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}

7.3元注解的使用

标注一个注解只能使用在方法和属性上,并且能够被反射机制所访问到:

package com.tsccg.java.annotation.myannotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author: TSCCG
 * @Date: 2021/07/15 11:15
 */
//标注一个注解只能使用在方法和属性上,并且能够被反射机制所访问到
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation04 {
}

8.反射注解

8.1通过反射机制获取注解

自定义注解:

限制只能用于方法和属性上,并且能够被反射机制所访问

//标注一个注解只能使用在方法和属性上,并且能够被反射机制所访问到
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation04 {

}

反射:

package com.tsccg.java.annotation.annodemo;
import com.tsccg.java.annotation.myannotation.MyAnnotation04;
/**
 * @Author: TSCCG
 * @Date: 2021/07/15 16:17
 * 反射注解
 * 通过反射获取注解
 */
@MyAnnotation04
public class ReflectAnnotationDemo01 {
    public static void main(String[] args) throws Exception{
        //获取类的Class
        Class c = Class.forName("com.tsccg.java.annotation.annodemo.ReflectAnnotationDemo01");
        //该类的上面是否有@MyAnnotation04
        boolean isAnno = c.isAnnotationPresent(MyAnnotation04.class);
        System.out.println(isAnno);
        //如果该类被MyAnnotation04所注解,执行括号内代码
        if (isAnno) {
            //获取该注解
            MyAnnotation04 myAnnotation04 =
                    (MyAnnotation04)c.getAnnotation(MyAnnotation04.class);
            System.out.println(myAnnotation04);
        }
    }
}

结果:

true
@com.tsccg.java.annotation.myannotation.MyAnnotation04

8.2通过反射获取注解中的属性值

自定义注解:

package com.tsccg.java.annotation.myannotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author: TSCCG
 * @Date: 2021/07/15 17:08
 */
//标注一个注解只能使用在方法和属性上,并且能够被反射机制所访问到
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation05 {
    String name();
    int age();
    String address() default "中国";
}

反射获取属性值:

package com.tsccg.java.annotation.annodemo;

import com.tsccg.java.annotation.myannotation.MyAnnotation05;

import java.lang.reflect.Method;

/**
 * @Author: TSCCG
 * @Date: 2021/07/15 16:52
 */
public class ReflectAnnotationDemo02 {
    @MyAnnotation05(name = "张三", age = 22,address = "河南")
    public void m1() {

    }
    public static void main(String[] args) throws Exception {
        //获取类
        Class cra = Class.forName("com.tsccg.java.annotation.annodemo.ReflectAnnotationDemo02");
        //获取类中被注解方法
        Method m1 = cra.getDeclaredMethod("m1");
        //判断该方法上面是否有@MyAnnotation05
        if (m1.isAnnotationPresent(MyAnnotation05.class)) {
            //获取注解对象
            MyAnnotation05 myAnnotation05 = m1.getAnnotation(MyAnnotation05.class);
            //输出注解中的属性值
            System.out.println(myAnnotation05.name());
            System.out.println(myAnnotation05.age());
            System.out.println(myAnnotation05.address());
        }

    }
}

结果:

张三
22
河南

9.注解在开发中的作用

9.1作用分析

注解在程序中等同于一种标记。

如果程序元素上有这个注解会怎样,没有这个注解又会怎样。

下面通过一个例子体会:

9.2实例体会

9.2.1需求

自定义一个注解:@IdAnnotation。

  • 要求只能出现在类上面。
  • 并且当一个类上有这个注解时,要求这个类中必须有一个int类型的id属性。
    • 如果有则正常执行,如果没有则报异常。

9.2.2自定义注解

package com.tsccg.java.annotation.myannotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author: TSCCG
 * @Date: 2021/07/15 17:33
 * 并且当一个类上有这个注解时,要求这个类中必须有一个int类型的id属性。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface IdAnnotation {
    int id();
}

9.2.3功能实现类

package com.tsccg.java.annotation.annodemo;

import com.tsccg.java.annotation.myannotation.IdAnnotation;

import java.lang.reflect.Field;

/**
 * @Author: TSCCG
 * @Date: 2021/07/15 16:14
 * 当一个类上有这个注解时,要求这个类中必须有一个int类型的id属性。
 * 如果有则正常执行,如果没有则报异常。
 */
public class AnnotationDemo06 {
    public static void main(String[] args) throws Exception{
        //获取被注解类
        Class userclass = Class.forName("com.tsccg.java.annotation.annodemo.User");
        //定义一个boolean类型来代表最终的结果,默认设置为false
        boolean hasId = false;
        //判断被注解类上是否存在@IdAnnotation
        if (userclass.isAnnotationPresent(IdAnnotation.class)) {
            //通过反射获取被注解类中所有的属性
            Field[] fields = userclass.getDeclaredFields();
            //遍历所有属性
            for (Field field : fields) {
                //判断,如果当前的属性名为"id"且属性类型为int,则设置hasId为true,并终止遍历
                if ("id".equals(field.getName()) &&
                        "int".equals(field.getType().getSimpleName())) {
                    hasId = true;
                    break;
                }
            }
            //判断最终结果,如果hasId仍为false抛出异常
            if (!hasId) {
                throw new HasNotIdException("类中属性没有int类型的id");
            }
        }
    }
}

/**
 * 被注解类
 */
@IdAnnotation(id = 001)
class User {
    int id;
    String name;
    int age;
}
/**
 * 自定义异常
 */
class HasNotIdException extends Exception {
    public HasNotIdException() {
    }
    public HasNotIdException(String s) {
        super(s);
    }
}

9.2.4执行结果

如果被注解类中有int类型的id属性:程序正常执行,不报异常。

如果被注解类中没有int类型的id属性:

Exception in thread "main" com.tsccg.java.annotation.annodemo.HasNotIdException: 类中属性没有int类型的id
	at com.tsccg.java.annotation.annodemo.AnnotationDemo06.main(AnnotationDemo06.java:33)
posted @ 2021-07-14 22:42  TSCCG  阅读(42)  评论(0编辑  收藏  举报