一、枚举类
(一)枚举类的使用
- 枚举类:类的对象个数是有限个,确定的
- 当需要定义一组常量时建议使用枚举类
- 如果枚举类中只有一个对象,则可作为单例模式的实现方式
(二)定义枚举类
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;
}
}