JDK的元注解

种类

  1. @Retention : 指定注解的作用范围,三种SOURCE,CLASS,RUNTIME
  2. @Target : 指定注解可以在哪些地方使用
  3. Documented : 指定注解是否会在javadoc中体现
  4. Inherited : 子类会继承父类的注解

@Retention

  1. 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留多长时间, [@Rentention ](/Rentention ) 包含一个 RetentionPolicy 类型的成员变量, 使用 [@Rentention ](/Rentention ) 时必须为该 value 成员变量指定值: [@Retention ](/Retention ) 的三种值
  1. RetentionPolicy.SOURCE: 编译器使用后,直接丢弃这种策略的注释

  2. RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解。 这是默认

  3. RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以 通过反射获取该注解

结论:保留时间递增

@Target

  1. 注解 : 作用范围

@Documented

用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档,即在生成文档时,可以看到该注解.

说明 : 定义为Documented的注解必须设置Rentention值为RUNTIME

源码:

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Documented {
  5. }

@Inherited

被它修饰的 Annotation 将具有继承性,如果某个类使用了被 @Inherited 修饰的 Annotation ,则其子类将自动具有该注释

实际应用中,使用较少

annotation

  1. @Override:限定某个方法,是重写父类方法,该注解只能用于方法
  2. @Deprecated: 用于表示某个程序元素已过时
  3. @SuppressWarnings:抑制编译器警告

@Override

  1. @Override 注解放在fly方法上,表示子类的fly方法时重写了父类的fly

  2. 这里如果没有写 @Override 还是重写了父类fly

  3. 如果你写了@Override注解,编译器就会去检查该方法是否真的重写了父类的方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误

  4. 看看 @Override的定义

    1. @Target(ElementType.METHOD)
    2. @Retention(RetentionPolicy.SOURCE)
    3. public @interface Override {
    4. }


解读:

  1. 如果发现 @interface 不是 interface 表示一个 注解类,jdk1.5之后加入
  2. @Target表示注解可以放在哪些元素上(只能在方法上):修饰注解的注解,叫做元注解

@Deprecated

  1. @Deprecated 修饰某个元素, 表示该元素已经过时

  2. 即不在推荐使用,但是仍然可以使用

  3. 查看 @Deprecated 注解类的源码

  1. Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
  4. public @interface Deprecated {
  5. String since() default "";
  6. boolean forRemoval() default false;
  7. }
  1. 可以修饰方法,类,字段, 包, 参数 等等

  2. @Deprecated 可以做版本升级过渡使用

@SuppressWarnings

  1. 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings注解来抑制警告信息
  2. 在{“”} 中,可以写入你希望抑制(不显示)警告信息
  3. 可以指定的警告类型有 | 填入信息 | 作用 | | —- | —- | | all | 抑制所有警告 | | boxing | 抑制与封装/拆装作业相关的警告 | | cast | 抑制与强制转型作业相关的警告 | | dep-ann | 抑制与淘汰注释相关的警告 | | deprecation | 抑制与淘汰的相关警告 | | fallthrough | 抑制与switch陈述式中遗漏break相关的警告 | | finally | 抑制与未传回finally区块相关的警告 | | hiding | 抑制与隐藏变数的区域变数相关的警告 | | incomplete-switch | 抑制与switch陈述式(enum case)中遗漏项目相关的警告 | | javadoc | 抑制与javadoc相关的警告 | | nls | 抑制与非nls字串文字相关的警告 | | null | 抑制与空值分析相关的警告 | | rawtypes | 抑制与使用没有指定泛型的警告(传参时没有指定泛型的警告错误) | | resource | 抑制与使用Closeable类型的资源相关的警告 | | restriction | 抑制与使用不建议或禁止参照相关的警告 | | serial | 抑制与可序列化的类别遗漏serialVersionUID栏位相关的警告 | | static-access | 抑制与静态存取不正确相关的警告 | | static-method | 抑制与可能宣告为static的方法相关的警告 | | super | 抑制与置换方法相关但不含super呼叫的警告 | | synthetic-access | 抑制与内部类别的存取未最佳化相关的警告 | | sync-override | 抑制因为置换同步方法而遗漏同步化的警告 | | unchecked | 抑制与未检查的作业相关的警告 | | unqualified-field-access | 抑制与栏位存取不合格相关的警告 | | unused | 忽略没有使用的某个变量的警告错误 |
  1. 关于SuppressWarnings 作用范围是和你放置的位置相关通常我们可以放置具体的语句, 方法, 类上
    比如 @SuppressWarnings放置在 main方法,那么抑制警告的范围就是 main

  2. 查看源码

  1. @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface SuppressWarnings {
  4. String[] value();
  5. }
  1. 1. 放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE
  2. 2. 该注解类有数组 String[] values() 设置一个数组比如 {"rawtypes", "unchecked", "unused"}

枚举类

枚举第一种实现方式:

  1. 1. 枚举对应的英文
  2. 2. 枚举是一种常量的集合
  3. 3. 枚举属于一种特殊的类,里面只包含一组有限的特定的对象。

枚举第二种实现方式

  1. 自定义类实现枚举
  2. 使用 enum 关键字实现枚举

自定义类实现枚举

  1. 构造器私有化
  2. 本类内部创建一组对象[四个 春夏秋冬]
  3. 对外暴露对象(通过为对象添加 public final static 修饰符)
  4. 可以提供 get 方法,但是不要提供 set
  1. public static final Season SPRING = new Season("春天", "温暖");
  2. public static final Season WINTER = new Season("冬天", "寒冷");
  3. public static final Season AUTUMN = new Season("秋天", "凉爽");
  4. public static final Season SUMMER = new Season("夏天", "炎热");

enum 关键字实现枚举

  1. //演示使用 enum 关键字来实现枚举类
  2. enum Season2{
  3. SPRING("春天", "温暖"), WINTER("冬天", "寒冷"),AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热")/*, What()*/;
  4. private String name;
  5. private String desc;//描述
  6. private Season2() {//无参构造器
  7. }
  8. private Season2(String name, String desc) {
  9. this.name = name;
  10. this.desc = desc;
  11. }
  12. }
  1. 使用关键字 enum 替代 class

  2. 当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类, 而且是一个 final 类[如何证明],老师使用 javap 工 具来演示

  3. public static final Season SPRING = new Season(“春天”, “温暖”) 简化成

    1. SPRING("春天", "温暖") 解读 常量名(实参列表)
  1. 当有多个枚举对象时,使用,间隔,最后有一个分号结尾

  2. 如果使用 enum 来实现枚举,要求将定义常量对象,写在前面: 枚举对象必须放在枚举类的行首.

  3. 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略

enum常用方法

name()

输出枚举对象的名称

  1. public final String name() {
  2. return name;
  3. }

ordinal()

输出的是该枚举对象的次序/编号,从0开始编号

  1. public final int ordinal() {
  2. return ordinal;
  3. }

values

源码隐藏起来了

返回 Season2[] (含有定义的所有枚举对象)

  1. Season2[] values = Season2.values();
  2. System.out.println("===遍历取出枚举对象(增强for)====");
  3. for (Season2 season: values) {//增强for循环
  4. System.out.println(season);
  5. }

valueOf

将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常

  1. 根据你输入的 “AUTUMN” 到 Season2 的枚举对象去查找
  2. 如果找到了,就返回,如果没有找到,就报错

源码:

  1. public static <T extends Enum<T>> T valueOf(Class<T> enumType,
  2. String name) {
  3. T result = enumType.enumConstantDirectory().get(name);
  4. if (result != null)
  5. return result;
  6. if (name == null)
  7. throw new NullPointerException("Name is null");
  8. throw new IllegalArgumentException(
  9. "No enum constant " + enumType.getCanonicalName() + "." + name);
  10. }

compareTo

比较两个枚举常量,比较的就是编号(编号相减)

源码:

  1. public final int compareTo(E o) {
  2. Enum<?> other = (Enum<?>)o;
  3. Enum<E> self = this;
  4. if (self.getClass() != other.getClass() && // optimization
  5. self.getDeclaringClass() != other.getDeclaringClass())
  6. throw new ClassCastException();
  7. return self.ordinal - other.ordinal;
  8. }
  1. Season2.AUTUMN的编号[2] - Season2.SUMMER的编号[3]

细节

  1. 1. 使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而Java是单继承机制
  2. 2. enum实现的枚举类,仍然是一个类,所以还是可以实现接口的.s调用构造方法需要通过枚举对象调用.

实例:

  1. public class EnumMethod {
  2. public static void main(String[] args) {
  3. //使用Season2 枚举类,来演示各种方法
  4. Season2 autumn = Season2.AUTUMN;
  5. //输出枚举对象的名字
  6. System.out.println(autumn.name());
  7. //ordinal() 输出的是该枚举对象的次序/编号,从0开始编号
  8. //AUTUMN 枚举对象是第三个,因此输出 2
  9. System.out.println(autumn.ordinal());
  10. //从反编译可以看出 values方法,返回 Season2[]
  11. //含有定义的所有枚举对象
  12. Season2[] values = Season2.values();
  13. System.out.println("===遍历取出枚举对象(增强for)====");
  14. for (Season2 season: values) {//增强for循环
  15. System.out.println(season);
  16. }
  17. //valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
  18. //执行流程
  19. //1. 根据你输入的 "AUTUMN" 到 Season2的枚举对象去查找
  20. //2. 如果找到了,就返回,如果没有找到,就报错
  21. Season2 autumn1 = Season2.valueOf("AUTUMN");
  22. System.out.println("autumn1=" + autumn1);
  23. System.out.println(autumn == autumn1);
  24. //compareTo:比较两个枚举常量,比较的就是编号
  25. //老韩解读
  26. //1. 就是把 Season2.AUTUMN 枚举对象的编号 和 Season2.SUMMER枚举对象的编号比较
  27. //2. 看看结果
  28. /*
  29. public final int compareTo(E o) {
  30. return self.ordinal - other.ordinal;
  31. }
  32. Season2.AUTUMN的编号[2] - Season2.SUMMER的编号[3]
  33. */
  34. System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER));
  35. //补充了一个增强for
  36. // int[] nums = {1, 2, 9};
  37. // //普通的for循环
  38. // System.out.println("=====普通的for=====");
  39. // for (int i = 0; i < nums.length; i++) {
  40. // System.out.println(nums[i]);
  41. // }
  42. // System.out.println("=====增强的for=====");
  43. // //执行流程是 依次从nums数组中取出数据,赋给i, 如果取出完毕,则退出for
  44. // for(int i : nums) {
  45. // System.out.println("i=" + i);
  46. // }
  47. }
  48. }