一、枚举类

(一)枚举类的使用

  • 枚举类:类的对象个数是有限个确定的
  • 当需要定义一组常量时建议使用枚举类
  • 如果枚举类中只有一个对象,则可作为单例模式的实现方式

(二)定义枚举类

1. 自定义枚举类 (JDK5.0前)

  • 声明Season对象私有属性private final修饰
  • 私有化类的构造器,给对象属性赋值
  • 提供当前枚举类的多个对象,声明为public static final
  • 实现其他功能:获取枚举类属性、提供toString()
class Season {
    // 1. 声明Season对象的属性, 用private final修饰
    private final String seasonName;
    private final String seasonDesc;

    // 2. 私有化类的构造器, 并给对象的属性赋值
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    // 3. 提供当前枚举类的多个对象, 声明为public static final
    public static final Season SPRING = new Season("春天", "春暖花开");
    public static final Season SUMMER = new Season("夏天", "夏日炎炎");
    public static final Season AUTUMN = new Season("秋天", "秋高气爽");
    public static final Season WINTER = new Season("冬天", "白雪皑皑");

    // 4. 其他诉求:获取枚举类的属性
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    // 4. 其他诉求:提供toString()
    @Override
    public String toString() {
        return "Season{ seasonName= " + seasonName + ", seasonDesc= " + seasonDesc + '}';
    }
}
@Test public void test() {
        Season1 spring = Season1.SPRING;
        System.out.println(spring); // 如果不重写toString() 输出:com.enumtest.Season1@36fc695d
    }

2. 使用关键字enum定义枚举类

  • 首先提供当前枚举类的对象,多个对象间用" , "分隔,末尾对象用" ; "结束
  • 声明对象属性,用private final修饰
  • 私有化类的构造器,给对象的属性赋值
  • 实现其他功能:获取枚举类属性、提供toString()
  • 创建的枚举类默认继承自class java.lang.Enum
enum Season {
    // 1. 提供当前枚举类的对象,多个对象间用","隔开,末尾对象用";"结束
    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "夏日炎炎"),
    AUTUMN("秋天", "秋高气爽"),
    WINTER("冬天", "白雪皑皑");

    // 2. 声明Season对象的属性, 用private final修饰
    private final String seasonName;
    private final String seasonDesc;

    // 3. 私有化类的构造器, 并给对象的属性赋值
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
}
@Test public void test() {
        Season summer = Season.SUMMER;
        System.out.println(summer); // 没有重写toString() 输出:SUMMER
        System.out.println(Season.class.getSuperclass()); // class java.lang.Enum
    }

3. Enum类的主要方法

1)values():返回枚举类的对象数组,方便遍历所有枚举值

Season[] values = Season.values();
    for (int i = 0; i < values.length; i++) {
        System.out.println(values[i]); // SPRING, SUMMER, AUTUMN, WINTER
    }

    Thread.State[] values1 = Thread.State.values();
    for (int i = 0; i < values1.length; i++) {
        System.out.println(values1[i]); // NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
    }

2)valueOf(String objName):返回枚举类中对象名为objName的对象

  • 没有找到会报异常:IllegalArgumentException
Season winter = Season.valueOf("WINTER");
    System.out.println(winter); // WINTER

    Season winter1 = Season.valueOf("WINTER1"); // 没有找到报异常:IllegalArgumentException

3)toString():返回当前枚举类对象(常量)的名称

Season spring = Season.SPRING; 
    System.out.println(spring.toString()); // SPRING

4. 实现接口的枚举类

1)实现接口在enum类中实现抽象方法

  • 与普通类实现接口方式相同,任何枚举类对象执行该方法得到相同结果
enum Season implements Info{
    // 1. 提供当前枚举类的对象,多个对象间用","隔开,末尾对象用";"结束
    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "夏日炎炎"),
    AUTUMN("秋天", "秋高气爽"),
    WINTER("冬天", "白雪皑皑");

    // ...省略其他结构...

    @Override
    public void show() {
        System.out.println("这是一个Season");
    }
}

interface Info{
    void show();
}

2)枚举类对象分别实现接口中的抽象方法

enum Season implements Info{
    // 1. 提供当前枚举类的对象,多个对象间用","隔开,末尾对象用";"结束
    SPRING("春天", "春暖花开"){
        @Override
        public void show() {
            System.out.println("This is Spring");
        }
    },
    SUMMER("夏天", "夏日炎炎") {
        @Override
        public void show() {
            System.out.println("This is Summer");
        }
    },
    AUTUMN("秋天", "秋高气爽") {
        @Override
        public void show() {
            System.out.println("This is Autumn");
        }
    },
    WINTER("冬天", "白雪皑皑") {
        @Override
        public void show() {
            System.out.println("This is Winter");
        }
    };
    // ...省略其他结构...
}

interface Info{
    void show();
}

二、注解 Annotation

1. 注解概述

  • 5.0新增代码的特殊标记, 编译运行时被读取不改变原有逻辑情况下在源文件嵌入补充信息
  • 修饰包、类、构造器、方法、成员变量、参数、局部变量的声明, 被保存在“name = value”中
  • 未来开发模式都是基于注解的,JPA、Spring2.5以上、Hibernate 3.x、Strusts2 都是基于注解
  • 框架 = 注解 + 反射 + 设计模式

2. 常见示例

1)生成文档相关的注解 

  • @author, @version, @see(参考转向), @since, @param, @return, @exception
  • @param, @return, @exception 只用于方法
  • @param, @exception 可并列多个
  • 格式要求
  • @param 形参名 形参类型 形参说明
  • @return 返回值类型 返回值说明
  • @exception 异常类型 异常说明

2)编译时进行格式检查(JDK内置三个基本注解)

  • @Override: 限定重写父类方法,只用于方法
  • @Deprecated: 表示修饰的元素(类/方法等)已过时,表示所修饰结构危险或又更好选择
  • @SuppressWarnings: 抑制编译器警告
@SuppressWarnings("unused")
        int num = 10;

        @SuppressWarnings({"unused", "rawtypes"})
        ArrayList list = new ArrayList();

3)跟踪代码的依赖性,实现替代配置文件功能

  • Service3.0提供注解,不需要在web.xml中进行Servlet部署
  • Spring框架中“事务”的管理
  • 单元测试@Test
  • @BeforeTest: 标记静态方法,在类初始化时只执行一次
  • @AfterTest: 标记非静态方法,在所有方法完成后只执行一次
  • @Before: 标记非静态方法,在@Test前执行,在每个@Test执行前都执行
  • @After: 标记非静态方法,在@Test后执行,在每个@Test执行后都执行
  • @Ignore: 标记本次不参与测试的方法,表示“某些方法尚未完成,暂不参与此次测试”
  • 这些方法都是配合@Test使用,单独使用没有意义

3. 自定义Annotation

1)自定义注解 (参照SuppressWarnings)

  • 注解声明为 @Interface,自动继承java.long.annotation.Annotation接口
  • 内部定义成员称为配置参数,通常用value表示,可指定初始值,使用default关键字修饰
  • 参数类型只能是八种数据类型String, Class, enum, Annotation, 及以上所有类型的数组
  • 若无成员则称为标记,起标识作用(@Override); 包含成员的注解使用时需指定成员变量的值
  • 自定义注解必须配注解的信息处理流程(反射)才有意义
public @interface MyAnnotation1 {
    String value(); // 无default值
}

@MyAnnotation1(value = "hi") // value一定要有值
class Person { 
    @MyAnnotation1("hello") // 如果只有一个value,可以省略value不写
    public Person() { }
}
public @interface MyAnnotation2 {
    String value() default "hello"; // 有default值
}

@MyAnnotation2(value = "hi") // value可有可无,可以更改
class Person { 
    @MyAnnotation2 // 可以不写value值
    public Person() { }
}

4. JDK提供的4种元注解

  • 元注解与元数据
  • 元注解:修饰(解释说明)现有注解的注解
  • 元数据:修饰现有数据的数据  eg. String name = "Tom"; (String name为元数据)
  • 4种元注解:Retention, Target, Documented, Inherited
  • 自定义注解通常会指明两个元注解:@Retention,@Target

1)Retention

  • 指明被修饰注解的生命周期,包含一个RetentionPolicy类型成员变量
  • 必须为value成员变量指定值
  • SOURCE: 源文件中有效(源文件保留),编译时丢弃(javac命令忽视它)
  • CLASS: class文件中有效(class保留),默认行为(编译保留, java命令忽视它, 内存不加载)
  • RUNTIME: 运行时有效(运行时保留),  加载到内存中, 通过反射获取

2)Target

  • 指定被修饰的注解能用于那些程序元素
  • TYPE: Class, interface(including annotation type), or enum declaration
  • FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE
  • ANNOTATION_TYPE, PACKAGE, TYPE_USE, MODULE

3)Documented

  • 被修饰的注解将被javadoc提取为文档默认情况javadoc不包括注解

4)Inherited

  • 被修饰的注解具有继承性(子类自动拥有被修饰的注解)
@Test
public void testAnnotation() {
    Class<Students> studentsClass = Students.class; // 反射
    Annotation[] annotations = studentsClass.getAnnotations();
    for (int i = 0; i < annotations.length; i++) {
        System.out.println(annotations[i]);
    }
    // @MyAnnotation加@Inherited时,子类未标注@MyAnnotation会打印出父类的 value = hi
    // @MyAnnotation未加@Inherited时,子类未标注@MyAnnotation不会打印出任何value值
}

public @interface MyAnnotation {
    String value() default "hello";
}

@MyAnnotation(value = "hi")
class Person {...}

class Students extends Person{...}

5. 利用反射获取注解信息

--- 后续补充 ---

6. JDK8 中的注解新特性

1)可重复注解

  • 在MyAnnotation上声明@Repeatable,成员值为My Annotations.class
  • 要求MyAnnotation的Target和Retention等元注解 与 MyAnnotations相同
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,METHOD})
public @interface MyAnnotations {
    MyAnnotation[] value();
}

@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,METHOD})
public @interface MyAnnotation {
    String value() default "hello";
}

@MyAnnotation(value = "hi")
@MyAnnotation(value = "halo")
class Person { ... }

2)类型注解

  • JDK8起 @Target 参数类型值多了2种,使得注解可应用在任何地方
  • ElementType.TYPE_PARAMETER: 该注解能写在类型变量声明的语句中 (泛型声明)
  • ElementType.TYPE_USE: 该注解能写在任何表示类型的语句中
@Target({TYPE, METHOD, TYPE_PARAMETER, TYPE_USE})
public @interface MyAnnotation {
    String value() default "hello";
}

class Generic<@MyAnnotation T> { // 如果不加 TYPE_PARAMETER 该句会报错

    // 如果不加 TYPE_USE 以下用法会报错
    public void show() throws @MyAnnotation RuntimeException {
        ArrayList<@MyAnnotation String> list = new ArrayList<>();
        int num = (@MyAnnotation int) 10L;
    }
}