一、枚举介绍
1.1 问题引出
要求创建季节(Season)对象,请设计并完成
class Season {
private String name;
private String desc;
//构造器
//getXXX
//setXXX
}
package com.hspedu.enum_;
public class Enumeration01 {
public static void main(String[] args) {
Season spring = new Season("春","春暖花开");
Season summer = new Season("夏","夏日炎炎");
Season autumn = new Season("秋","秋高气爽");
Season winter = new Season("冬","寒冬凛冽");
//因为对于季节而言,它的对象(具体值),是固定的四个,不会有更多
//按传统的写法, 不能体现季节是固定的四个对象
//优化方案 ===> 枚举类
Season other = new Season("它天","~~~~~")
}
}
创建Season对象有如下特点
- 季节的值是有限的几个值(Spring,Summer,Autumn,Winter)
- 只读,不要修改
1.2 枚举简述
- 枚举对应英文(enumeration,简写enum)
- 枚举是一组常量的集合
- 可以这样理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象
1.3 枚举的两种实现方式
- 自定义类实现枚举
- 不需要提供 setXXX方法,因为枚举对象值通常为只读
- 对枚举对象/属性使用 final + static 共同修饰,实现底层优化
- 枚举对象名通常使用全部大写,同常量的命名规范
- 枚举对象根据需要,也可以有多个属性 ```java package com.hspedu.enum_;
public class Season { private String name; private String desc; //自定义类实现枚举 //1. 将构造器设为私有,防止创建对象 //2. 去掉setXXX方法 //3. 在本类内部创建一组对象 //4. 优化, 可以加入 static final 修饰符,这样就不会每次调用都加载类 private Season(String name, String desc) { this.name = name; this.desc = desc; }
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
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("冬","寒冬凛冽");
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
```java
package com.hspedu.enum_;
public class Enumeration02 {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER);
System.out.println(Season.AUTUMN);
System.out.println(Season.WINTER);
}
}
- 使用enum 关键字实现枚举
当我们使用enum 关键字来开发枚举类时,默认会继承 Enum类,而且是一个final类【可以用 javap 反编译验证】
- 传统的 public static final Season2 SPRING = new Season2(“春天”,”温暖”); 简化成 SPRING(“春天”,”温暖”),这里必须知道,它调用的是哪个构造器
- 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
- 当有多个枚举对象时,使用,逗号间隔,最后有一个分号结尾
枚举对象必须放在枚举类的行首
enum Season2 {
//使用enum 实现枚举类
//1. 使用关键字 enum 代替 class
//2. 直接使用 SPRING("春","春暖花开")创建对象,
// SPRING("春","春暖花开") 解读:常量名(实参列表)
//3. 多个对象使用逗号分隔,最后一个对象用分号结束
//4. 要求把定义常量写在最前面
SPRING("春","春暖花开"),
SUMMER("夏","夏日炎炎"),
AUTUMN("秋","秋高气爽"),
WINTER("冬","寒冬凛冽"),
What; //调用无参构造器可省略()
private String name;
private String desc;
private Season2(String name, String desc) {
this.name = name;
this.desc = desc;
}
private Season2() {
//无参构造器
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
public String toString() {
return "Season2{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
1.4 小练习
下面代码是否正确,并说明表示的含义。
enum Gender {
BOY,GIRL;
}
答案:- 上面语法是正确的
- 有一个枚举类Gender,没有属性
- 有两个枚举对象 BOY,GIRL,使用的无参构造器创建
下面代码输出什么?
enum Gender2 {
BOY,GIRL;
}
Gender2 boy = Gender2.BOY;
Gender2 boy2 = Geder2.BOY;
System.out.println(boy); //输出BOY 本质就是调用 Gender的父类 Enum的 toString()
System.out.println(boy == boy2); //输出 true
二、Enum类成员方法
说明:使用关键字 enum 时,会隐式继承 Enum类,这样我们就可以使用 Enum类的相关方法
2.1 Enum 常用方法一览表
- toString() : Enum重写了,返回的是当前对象名,子类可以重写该方法,用于返回对象的属性信息
- name() : 返回当前对象名(常量名),子类中不能重写
- ordinal() : 返回当前对象的位置号,从0开始
- values() : 返回当前枚举类中所有常量,保存在数组中
- valueOf() : 将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
- compareTo() : 比较两个枚举常量,比较的就是编号
2.2 案例演示
package com.hspedu.enum_;
/**
* @author HarborGao
* @version 1.0
* 演示 Enum 类的各种方法的使用
*/
public class EnumMethod {
public static void main(String[] args) {
Season2 autumn = Season2.AUTUMN;
//输出枚举对象的名字
System.out.println(autumn.name());
//ordinal() 输出的是该枚举对象的次序/编号,从0开始编号
//AUTUMN 枚举对象是第三个,因此输出2
System.out.println(autumn.ordinal());
//从反编译可以看出,value方法,返回Season2数组
//含有定义的所有枚举对象
Season2[] values = Season2.values();
for (Season2 season : values) {
System.out.println(season);
}
//valueOf:将字符串转换成枚举对象,要求字符串必须为已有常量名,否则报异常
//执行流程
//1.根据输入的 "AUTUMN" 到 Season2 的枚举对象去找
//2.如果找到了,就返回,如果没有找到,就报错
Season2 autumn1 = Season2.valueOf("AUTUMN");
System.out.println("autumn1=" + autumn1);
System.out.println(autumn == autumn1);
//compareTo:比较两个枚举常量,比较的就是编号
//1. 就是把 Season2.SPRING枚举对象的编号 与 Season2.SUMMER枚举对象的编号 进行比较
/*
public final int compareTo(E o) { //底层源码
return self.ordinal - other.ordinal;
}
*/
//2. 返回的是Season2.SPRING的编号[0] - Season2.SUMMER的编号[1] = -1
System.out.println(Season2.SPRING.compareTo(Season2.SUMMER));
int[] nums = {1, 5, 10, 3};
//普通的 for 循环
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
//增强 for循环
for (int i : nums) { //每次从 nums数组中取1个数据,赋给变量i,全部取出,退出循环
System.out.println("i = " + i);
}
}
}
2.3 enum 注意事项和使用细节
- 使用 enum 关键字后,就不能再继承其他类了,因为enum 会隐式继承 Enum,而Java是单继承机制
- 枚举类和普通类一样,可以实现接口,形式如下:
enum 类名 implements 接口1,接口2 { }
2.4 小练习
声明 Week 枚举类,其中包含星期一至星期日的定义
MONDAY, TUESDAY, WEDNESDAY, THURSDAY,FRIDAY, SATURDAY, SUNDAY;使用 values 返回所有的枚举数组,并遍历,输出下图效果
三、注解
3.1 注解的理解
- 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌套在代码中的补充信息。
- 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替了Java EE 旧版中所遗留的繁冗代码和XML配置等。
3.2 Annotation 基本介绍
使用 Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用,用于修饰它支持的程序元素。
三个基本的 Annotation:
- @Override:限定某个方法,是重写父类方法,该注解只能用于方法
- @Deprecated:用于表示某个程序元素(类、方法等)已过时
- @SuppressWarnings:抑制编译器警告
3.3 @Override 注解
限定某个方法,表示重写父类方法,该注解只能用于方法
class Father {
public void fly(){
System.out.println("Father fly...");
}
}
class Son extends Father {
//解读:
//1. @Override 注解放在fly方法上,表示子类的fly方法是重写了父类的fly方法
//2. 如果这里没有写 @Override ,依然会重写父类的 fly
//3. 它的作用是,加了@Override 注解,编译器就会去检查该方法是否重写了父类的方法,
// 如果的确重写了,则编译通过,如果没有构成重写,则编译报错
@Oerride
public void fly() {
System.out.println("Son fly...");
}
}
补充说明:@interface的说明
@interface 不是 interface,是一个类(注解类) ,jdk 1.5 之后加入的
@Override 使用说明
- @Override 表示指定重写父类的方法(从编译层面验证),如果父类没有 fly方法,则会报错
- 如果不写@Override注解,而父类仍有public void fly(){},仍然构成重写
- @Override 只能修饰方法,不能修饰其他类,包,属性等等
- 查看@Override注解源码为 @Target(ElementType.METHOD),说明只能修饰方法
- @Target 是修饰注解的注解,称为元注解
3.4 @Deprecated 注解
用于表示某个程序元素(类、方法等)已过时
public class Deprecated_ {
public static void main(String[] args) {
A a = new A();
System.out.println(a.n1);
a.hi();
}
}
//解读:
//1. @Deprecated 修饰某个元素,表示该元素已经过时
//2. 即不再推荐使用,但仍然可以使用
@Deprecated
class A {
@Deprecated
public int n1 = 10;
@Deprecated
public void hi() {
}
}
@Deprecated 使用说明
- 用于表示某个程序元素(类、方法等)已过时
- 可以修饰方法、类、字段、包、参数 等等
- @Target(value = {CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE})
- @Deprecated 的作用可以做到新旧版本的兼容和过渡
3.5 @SuppressWarnings 注解
抑制编译器警告
package com.hspedu.annotation_;
import java.util.ArrayList;
import java.util.List;
public class SuppressWarnings_ {
//解读
//1. 当我们不希望看到这些警告的时候,可以使用 @SuppressWarnings注解 抑制警告信息
//2. 在{""}中,可以写入希望抑制(不显示)警告的信息
//3. 可以指定的抑制警告(见下方)
//4. @SuppressWarnings 的作用范围和放置的位置有关
// 比如 放在 main方法,那么抑制警告的范围就是 main
// 通常放置在具体的语句、方法、类
//5. 看看 @SuppressWarnings 的源码
/*
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
*/
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("merry");
int i = 1;
System.out.println(list.get(1));
}
}
@SuppressWarning 中的属性介绍以及属性说明
all,抑制所有警告
boxing,抑制与封装/拆装作业相关的警告
cast,抑制与强制转型作业相关的警告
dep-ann,抑制与淘汰注释相关的警告
deprecation,抑制与淘汰的相关警告
fallthrough,抑制与switch陈述式中遗漏break相关的警告
finally,抑制与未传回finally区块相关的警告
hiding,抑制与隐藏变数的区域变数相关的警告
incomplete-switch,抑制与switch陈述式(enum case)中遗漏项目相关的警告
javadoc,抑制与javadoc相关的警告
nls,抑制与非nls字串文字相关的警告
null,抑制与空值分析相关的警告
rawtypes,抑制与使用raw类型相关的警告
resource,抑制与使用Closeable类型的资源相关的警告
restriction,抑制与使用不建议或禁止参照相关的警告
serial,抑制与可序列化的类别遗漏serialVersionUID栏位相关的警告
static-access,抑制与静态存取不正确相关的警告
static-method,抑制与可能宣告为static的方法相关的警告
super,抑制与置换方法相关但不含super呼叫的警告
synthetic-access,抑制与内部类别的存取未最佳化相关的警告
sync-override,抑制因为置换同步方法而遗漏同步化的警告
unchecked,抑制与未检查的作业相关的警告
unqualified-field-access,抑制与栏位存取不合格相关的警告
unused,抑制与未用的程式码及停用的程式码相关的警告
四、JDK 的元 Annotation(元注解,了解)
4.1 元注解的基本介绍
JDK 的元 Annotation 用于修饰其他 Annotation
4.2 元注解的种类
- Retention 指定注解的作用范围,三种 SOURCE, CLASS, RUNTIME
- Target 指定注解可以在哪些地方使用
- Documented 指定该注解是否会在 javadoc 体现
- Inherited 子类会继承父类的注解
4.3 @Retention 注解
说明:
只能用于修饰一个 Annotation 定义,用于指定该Annotation 可以保留多长时间,@Retention 包含一个 RetentionPolicy 类型的成员变量,使用 @Retention 时,必须为该 value 成员变量指定值:
@Retention的三种值
- RetentionPolicy.SOURCE:编译器使用后,直接丢弃这种策略的注解
- RetentionPolicy.CLASS:编译器将把注解记录在 class 文件中,当运行 Java 程序时, JVM 不会保留这些注解。这是默认值
- RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中,当运行Java程序时,JVM 会保留注解,程序可以通过反射获取该注解
4.4 @Target 注解
说明:
用于修饰 Annotation 定义,用于指定被修饰的 Annotation 能用于修饰哪些程序元素。@Target 也包含一个名为 value 的成员变量。
作用范围(限定值):
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
4.5 @Documented 注解
基本说明
用于 指定被该元 Annotation 修饰的 Annotation 类将被 Javadoc 工具提取成文档,即在生成文档时,可以看到该注解。
注意:定义为 Documented 的注解必须设置 Retention值为 RUNTIME
4.6 @Inherited 注解
被它修饰的 Annotation 将具有继承性,如果某个类使用了被 @Inherited 修饰的 Annotation,则其子类将自动具有该注解。