目 录CONTENT

文章目录

从0开始学Java——枚举和注解(18)

Eric
2022-01-23 / 0 评论 / 0 点赞 / 190 阅读 / 3,902 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-12-12,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1 枚举

1.1 概述

  • 类的对象只有有限个,确定的。例如:

    • 星期:Monday(星期一)、......、Sunday(星期天)。
    • 性别:Man(男)、Woman(女)。
    • 季节:Spring(春节)......Winter(冬天)。
    • 支付方式:Cash(现金)、WeChatPay(微信)、Alipay(支付宝)、BankCard(银 行卡)、CreditCard(信用卡)。
    • 就职状态:Busy、Free、Vocation、Dimission。
    • 订单状态:Nonpayment(未付款)、Paid(已付款)、Delivered(已发货)、 Return(退货)、Checked(已确认)Fulfilled(已配货)。
    • 线程状态:创建、就绪、运行、阻塞、死亡。
  • 枚举类型本质也是也是一种类,只不多是这个类的对象是固定的几个,而不能随意让用户创建。

  • 在JDK 5之前,需要程序员自定义枚举类。

  • 在JDK 5之后,Java支持通过enum关键字来快速的定义枚举类型。

1.2 JDK 5之前

  • 在JDK 5之前如何声明枚举类?

    • ① 私有化构造器,保证在类的外部不能创建其对象。
    • ② 在类的内部创建枚举类的实例,通过public static final修饰。
    • ③ 对象如果有实例变量,通过private final修饰,并在构造器中进行初始化。
  • 示例:

package top.open1024.demo18;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-10 16:58
 */
public class Gender {
    // 在类的内部创建枚举类的实例,通过`public static final`修饰。
    public static final Gender MAN = new Gender("男");
    public static final Gender WOMAN = new Gender("女");
    // 对象如果有实例变量,通过`private final`修饰,并在构造器中进行初始化。
    private final String gender;

    // 私有化构造器,保证在类的外部不能创建其对象。
    private Gender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Gender{" + "gender='" + this.gender + '\'' + '}';
    }
}
package top.open1024.demo18;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-10 17:03
 */
public class Person {

    private Gender gender;
    private String name;
    private int age;

    public Person(Gender gender, String name, int age) {
        this.gender = gender;
        this.name = name;
        this.age = age;
    }

    public Gender getGender() {
        return this.gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "gender=" + this.gender + ", name='" + this.name + '\'' + ", age=" + this.age + '}';
    }
}
package top.open1024.demo18;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-10 17:09
 */
public class Test {
    public static void main(String[] args) {
        Person person = new Person(Gender.MAN, "张三", 18);
        System.out.println(person);
    }
}

1.3 JDK 5之后

  • 语法:
权限修饰符 enmu 枚举类名 {
    // 常量对象列表
}
权限修饰符 enmu 枚举类名 {
    // 常量对象列表
    
    // 其他成员列表
}
  • 解释:

    • ① 枚举类的常量列表必须在枚举类的首行,因为是常量,所以建议大写。
    • ② 如果常量列表后面没有其他代码,那么;可以省略,否则;不可以省略。
    • ③ 常量对象之间必须用,隔开;结尾。
    • ④ 编译器给枚举类默认提供的是private修饰的无参构造器,如果枚举类需要的是无参构造器,则不需要声明,写常量对象列表的时候也不需要加参数。
    • ⑤ 如果枚举类需要的是有参构造器,需要手动定义private的有参构造,调用有参构造的方法就是在常量对象名后面加(实参列表)即可。
    • ⑥ 枚举类默认继承的是java.lang.Enum类,所以不能再继承其他的类。

img

    • ⑦ JDK 5之后的switch支持枚举,即case后面可以写枚举常量对象名。
    • ⑧ 枚举类如果有其他属性,建议也声明为final的,因为常量对象在逻辑上应该不可变。
  • 示例:

package top.open1024.demo19;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 09:05
 */
public enum Color {
    RED, GREEN, BLUE
}
  • 示例:
package top.open1024.demo19;

/**
 * 性别
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 08:56
 */
public enum Gender {

    MAN("男"), WOMAN("女");

    private final String name;

    Gender(String name) {
        this.name = name;
    }
}

1.4 枚举常用方法

  • 返回的是常量名(对象名),可以继续手动重写该方法!
public String toString()
  • 返回枚举类型的对象数组。
public static T[] values()
  • 将字符串转换为对应的枚举对象,要求字符串必须是枚举类对象名,否则将报异常。
public Enum valueOf(String str)
  • 返回的是常量名(对象名),很少用
public final String name()
  • 返回的是常量的次序号,默认从0开始
public final int ordinal()
  • 示例:
package top.open1024.demo19;

/**
 * 性别
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 08:56
 */
public enum Gender {

    MAN("男"), WOMAN("女");

    private final String name;

    Gender(String name) {
        this.name = name;
    }
}
package top.open1024.demo19;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 09:28
 */
public class Test {
    public static void main(String[] args) {
        Gender[] values = Gender.values();
        for (Gender value : values) {
            System.out.println(value);
        }

        Gender man = Gender.valueOf("MAN");
        System.out.println(man);
    }
}
  • 示例:
package top.open1024.demo20;

/**
 * 天气
 * 
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 08:58
 */
public enum Season {

    SPRING("春天", "春暖花开"), SUMMER("夏天", "夏日炎炎"), AUTUMN("秋天", "秋高气爽"), WINTER("冬天", "白雪皑皑");

    // 名称
    private final String name;
    // 描述
    private final String desc;

    Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }
}
package top.open1024.demo20;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 09:40
 */
public class Test {
    public static void main(String[] args) {
        Season[] values = Season.values();
        for (Season value : values) {
            switch (value) {
                case SPRING:
                    System.out.println(Season.SPRING.getName());
                    break;
                case SUMMER:
                    System.out.println(Season.SUMMER.getName());
                    break;
                case AUTUMN:
                    System.out.println(Season.AUTUMN.getName());
                    break;
                case WINTER:
                    System.out.println(Season.WINTER.getName());
                    break;
            }
        }
    }
}

1.5 枚举实现接口(了解)

  • 和普通Java类一样,枚举类可以实现一个或多个接口。

  • 如果每个枚举值在调用实现的接口方法呈现的是相同的行为方法,则只需要统一实现该方法即可。

  • 如果需要每个枚举值在调用实现的接口方法呈现不同的行为方式,则可以让每个枚举值分别来实现该方法。

  • 示例:

package top.open1024.demo21;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 09:50
 */
public interface Run {

    void run();
}
package top.open1024.demo21;

/**
 * 
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 08:56
 */
public enum Gender implements Run {

    MAN, WOMAN;

    @Override
    public void run() {
        System.out.println("走路啊");
    }
}
package top.open1024.demo21;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 09:50
 */
public class Test {
    public static void main(String[] args) {
        Gender man = Gender.MAN;
        man.run(); // 走路啊

        Gender woman = Gender.WOMAN;
        woman.run(); // 走路啊

    }
}
  • 示例:
package top.open1024.demo22;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 09:50
 */
public interface Run {

    void run();
}
package top.open1024.demo22;

/**
 * 
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 08:56
 */
public enum Gender implements Run {

    MAN {
        @Override
        public void run() {
            System.out.println("男生大步流星的走路");
        }
    },
    WOMAN {
        @Override
        public void run() {
            System.out.println("女生婀娜多姿的走路");
        }
    }

}
package top.open1024.demo22;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-13 09:56
 */
public class Test {
    public static void main(String[] args) {
        Gender man = Gender.MAN;
        man.run(); // 男生大步流星的走路

        Gender woman = Gender.WOMAN;
        woman.run(); // 女生婀娜多姿的走路
    }
}

2 注解

2.1 概述

2.1.1 什么是注解?

  • 从JDK 5开始,Java增加了对元数据(MetaData)的支持,也就是Annotation(注解)。

  • 注解其实就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证和部署。

  • 注解可以像修饰符一样被使用,可以用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在注解的name=value中。

2.1.2 注解的作用

  • 执行编译器的检查,例如:@Override注解。

  • 分析代码:主要的用途,用来替代配置文件。

  • 用在框架里面,注解开发。

2.2 JDK内置的三种基本注解

  • @Override:限定重写父类方法,该注解只能用于方法。

  • @Deprecated:用于表示所修饰的元素(类、方法等)已经过时。通常是因为所修饰的结构危险或存在更好的选择。

  • @SuppressWarnings:抑制编译器警告。

2.3 自定义注解

2.3.1 注解的定义

  • 自定义注解必须使用@interface关键字。

  • 自定义注解自动继承了java.lang.annotation.Annotation接口,换言之,注解是一种特殊的接口。

  • 语法:

修饰符 @interface 注解名 {
    属性类型 属性名() default 默认值;
}
  • 注解的属性类型只能是:

    • ① String。
    • ② 8种基本数据类型。
    • ③ 枚举类型。
    • ④ 注解类型。
    • ⑤ Class类型。
    • ⑥ 以上类型的一维数组类型。
  • 可以在定义注解的属性的时候使用default关键字指定初始化值。

  • 如果注解只有一个属性,建议属性的名称为value。

  • 如果自定义的注解含有属性,那么使用的时候必须给注解的属性赋值,除非它有默认值。格式是属性=值,如果只有一个属性,且名称是value,怎可以省略value=

  • 没有属性的注解称为标记,有属性的注解称为元数据注解

  • 示例:

package top.open1024.demo11;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 17:47
 */
public enum Color {
    RED, GREEN, BLUE
}
package top.open1024.demo11;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 17:46
 */
public @interface MyAnnotation {
    // 基本数据类型
    int num();

    // String类型
    String value();

    // 枚举类型
    Color color();

    // Class类型
    Class clazz();

    // 注解类型
    Override override();

    // 以上类型的数组类型
    String[] values();
}

2.3.2 注解的使用

  • 语法:
@注解名(属性名=值,属性名2=值,...)
  • 特殊情况:

    • ① 注解属性有默认值。
属性类型 属性名() default 默认值;
    • ② 如果注解的属性类型是一维数组,当数组的值只有一个的时候,可以省略{}
@MyAnnotation(ss={"aa","bb"})
@MyAnnotation(ss="aa")
    • ③ 如果只有一个属性要赋值的时候,且属性名为value,赋值的时候可以省略value=
  • 示例:

package top.open1024.demo11;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 17:47
 */
public enum Color {
    RED, GREEN, BLUE
}
package top.open1024.demo11;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 17:46
 */
public @interface MyAnnotation {
    // 基本数据类型
    int num();

    // String类型
    String value();

    // 枚举类型
    Color color();

    // Class类型
    Class clazz();

    // 注解类型
    Override override();

    // 以上类型的数组类型
    String[] values();
}
package top.open1024.demo11;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 18:03
 */
public class Person {
    @MyAnnotation(num = 0, value = "你好,open1024", color = Color.RED, clazz = Person.class, override = @Override, values = {"aa","bb"})
    private String name;
}

2.4 元注解

2.4.1 概述

  • 元注解是用于修饰自定义注解的。

2.4.2 常用的元注解

  • JDK 5提供了四个标准的元注解,分别是:

    • @Retention注解:
      • 定义该注解可以保留在哪个代码阶段,值为RetentionPolicy类型。
      • RetentionPolicy.SOURCE:只在源码阶段保留。
      • RetentionPolicy.CLASS:在源码和字节码上保留,默认。
      • RetentionPolicy.RUNTIME:在源码、字节码和运行阶段保留。
    • @Target注解:
      • 定义该注解可以作用在什么范围,默认注解可以在任何位置,值为ElementType的枚举值。
      • ElementType.TYPE:作用在类、接口(包括注解类型)或enum上。
      • ElementType.FIELD:作用在属性上。
      • ElementType.METHOD:作用在方法上。
      • ElementType.PARAMETER:作用在参数上。
      • ElementType.CONSTRUCTOR:作用在构造器上。
      • ElementType.LOCAL_VARIABLE:作用在局部变量上。
      • ElementType.ANNOTATION_TYPE:作用在注解上。
      • ElementType.PACKAGE:作用在包上。
      • ElementType.TYPE_PARAMETER:作用在类型声明上,如泛型声明(JDK8新增)。
      • ElementType.TYPE_USE:作用在使用类型的任何语句中(JDK8新增)。
    • @Document注解:
      • 定义该注解可以被javadoc工具提取成文档。默认情况下,javadoc是不包括注解的。
    • @Inherit注解:
      • 定义该注解具有继承性。如果某个类使用被@Inherit注解修饰的注解,则该子类将自动具有该注解。
  • JDK 8 提供了新的注解,@Repeatable注解,可重复注解,值是Class<? extends Annotation>类型。

  • 示例:

package top.open1024.demo11;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 17:47
 */
public enum Color {
    RED, GREEN, BLUE
}
package top.open1024.demo11;

import java.lang.annotation.*;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 17:46
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface MyAnnotation {
    // 基本数据类型
    int num();

    // String类型
    String value();

    // 枚举类型
    Color color();

    // Class类型
    Class clazz();

    // 注解类型
    Override override();

    // 以上类型的数组类型
    String[] values();
}
package top.open1024.demo11;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 18:03
 */
public class Person {
    @MyAnnotation(num = 0, value = "你好,open1024", color = Color.RED, clazz = Person.class, override = @Override,
        values = {"aa", "bb"})
    private String name;
}

2.4.3 反射获取注解信息

  • 注解解析的目的:

    • ① 获取类、成员变量、成员方法、方法参数、构造函数等上面的注解对象。
    • ② 获取注解对象的属性。
    • ③ 判断某个类、成员变量、成员方法等上面是否有某个注解。
  • JDK 5在java.lang.reflect包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素。

  • 当一个注解被定义为运行时注解后,该注解才是运行时可见,当class文件被加载时,保存在class文件中的注解才会被虚拟机读取。

  • Class、Method、Package、Field、Constructor、Package、Parameter等实现了AnnotatedElement接口。

img

  • 程序员可以调用AnnotatedElement对象的方法获取注解信息。

  • 判断指定的注解是否存在:

default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass){}
  • 获取指定类型的注解,如果没有,返回null:
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
  • 获取自己的所有注解:
Annotation[] getDeclaredAnnotations();
  • 获取所有的注解,包括从父类继承下来的注解:
Annotation[] getAnnotations();
  • 根据指定的类型获取所有的注解,包括从父类继承下来的注解:
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {}
  • 获取指定类型的注解,如果没有,返回null:
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {}
  • 根据指定的类型获取自己的所有注解:
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {}
  • 示例:
package top.open1024.demo12;

import java.lang.annotation.*;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 20:20
 */
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
package top.open1024.demo12;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 20:21
 */
@MyAnnotation
public class Demo {

    @MyAnnotation
    public void run01() {

    }

    public void run02() {

    }
}
package top.open1024.demo12;

import java.lang.reflect.Method;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-10-07 20:21
 */
public class Test {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<?> clazz = Demo.class;

        boolean annotationPresent = clazz.isAnnotationPresent(MyAnnotation.class);

        System.out.println("annotationPresent = " + annotationPresent);

        Method run01 = clazz.getDeclaredMethod("run01");

        boolean annotationPresent1 = run01.isAnnotationPresent(MyAnnotation.class);
        System.out.println("annotationPresent1 = " + annotationPresent1);
    }
}
0

评论区