1、枚举
1.1、需求引出
要求创建季节(Season) 对象,请设计并完成
package Enumeration01;
public class Enumeration01 {
public static void main(String[] args) {
// 要求创建季节(Season) 对象, 请设计并完成
Season spring = new Season("春天", "温暖");
Season winter = new Season("冬天", "寒冷");
Season summer = new Season("夏天", "炎热");
Season autumn = new Season("秋天", "凉爽");
// 以下都是不合理的
autumn.setName("XXX");
autumn.setDesc("非常的热...");
// 因为对于季节而已, 他的对象(具体值), 是固定的四个, 不会有更多
// 按照这个设计类的思路, 不能体现季节是固定的四个对象
// 因此, 这样的设计不好 ===> 枚举类[枚: 一个一个 举: 例举, 即把具体的对象一个一个例举出来的类, 就称为枚举类]
Season other = new Season("红天", "~~~");
}
}
class Season {
private String name; // 名称
private String desc; // 描述
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
}
1.2、分析问题
创建 Season 对象的特点
- 季节的值是有限的几个值(spring,summer,autumn,winter)
- 只读,不需要修改
1.3、解决方案 ==> 枚举
- 枚举对应英文(enumeration, 简写 enum)
- 枚举是一组常量的集合
- 可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象
1.4、枚举的二种实现方式
- 自定义类实现枚举
- 使用 enum 关键字实现枚
1.4.1、自定义类实现枚举
- 不需要提供 setXxx方法,因为枚举对象值通常为只读
- 对枚举对象/属性使用 final + static 共同修饰,实现底层优化
- 枚举对象名通常使用全部大写,常量的命名规范
- 枚举对象根据需要,也可以有多个属性
package Enumeration01;
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);
}
}
class Season {
private String name;
private String 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("冬天", "寒冷");
// 1. 将构造器私有化, 目的防止直接 new
// 2. 去掉 setXxx 方法, 防止属性被修改
// 3. 在 Season 内部, 直接创建固定的对象
// 4. 优化, 可以加入 final 修饰符
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
自定义类实现枚举的特点
- 构造器私有化
- 本类内部创建一组对象[四个 春夏秋冬]
- 对外暴露对象(通过为对象添加 public final static 修饰符)
- 可以提供 get 方法,但是不要提供 set
1.4.2、enum 关键字实现枚举
package Enumeration01;
public class Enumeration03 {
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 Season {
// 1. 使用关键字 enum 替代 class
// 2. public static final Season SPRING = new Season("春天", "温暖") 直接使用 SPRING("春天", "温暖") 常量名(实参列表)
// 3. 如果有多个常量(对象), 使用逗号间隔即可
// 4. 如果使用 enum 来实现枚举, 要求将定义常量对象写在前面
// 5. 如果使用的是无参构造器, 创建常量对象, 则可以省略 () // What()
SPRING("春天", "温暖"), SUMMER("夏天", "炎热"),
AUTUMN("秋天", "凉爽"), WINTER("冬天", "寒冷");
private String name;
private String desc; // 描述
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
// 无参构造器
private Season() {
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
enum 关键字实现枚举注意事项
- 当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类,而且是一个 final 类,可以使用 javap 工具来演示
- 传统的 public static final Season2 SPRING = new Season2(“春天”, “温暖”); 简化成 SPRING(“春天”, “温暖”), 这里必须知道,它调用的是哪个构造器.
- 如果使用无参构造器 创建枚举对象,则实参列表和小括号都可以省略
- 当有多个枚举对象时,使用逗号间隔,最后有一个分号结尾
- 枚举对象必须放在枚举类的行首
1.5、案例演示
下面代码是否正确, 并说明表示的含义
enum Gender{
BOY , GIRL; // 这里其实就是调用 Gender 类的无参构造器
}
- 上面语法是 ok
- 有一个枚举类 Gender, 没有属性
- 有两个枚举对象 BOY,GIRL,使用的无参构造器创建
1.6、enum 常用方法说明
1.6.1、定义
说明:使用关键字 enum 时,会隐式继承 Enum 类,这样就可以使用 Enum 类相关的方法
public abstract class Enum<E extends Enum>
implements Comparable, Serializable {
}
1.6.2、代码演示
- toString:Enum 类已经重写过了,返回的是当前对象
名,子类可以重写该方法,用于返回对象的属性信息- name:返回当前对象名(常量名),子类中不能重写
- ordinal:返回当前对象的位置号,默认从 0 开始
- values:返回当前枚举类中所有的常量
- valueOf:将字符串转换成枚举对象,要求字符串必须
为已有的常量名,否则报异常- compareTo:比较两个枚举常量,比较的就是编号
package Enumeration01;
public class EnumMethod {
public static void main(String[] args) {
// 使用 Season 枚举类, 来演示各种方法
Season autumn = Season.AUTUMN;
// 输出枚举对象的名字【 name() 】
System.out.println(autumn.name()); // AUTUMN
// 输出的是该枚举对象的次序/编号, 从0开始编号【 ordinal() 】
// AUTUMN 枚举对象是第三个, 因此输出 2
System.out.println(autumn.ordinal()); // 2
// 从反编译可以看出 values 方法, 返回 Season[] 【 values() 】
// 含有定义的所有枚举对象
System.out.println("===遍历取出枚举对象(增强 for)===");
Season[] values = Season.values();
for (Season season1 : values) {
System.out.println(season1); // Season{name='春天', desc='温暖'}
// Season{name='夏天', desc='炎热'}
// Season{name='秋天', desc='凉爽'}
// Season{name='冬天', desc='寒冷'}
}
// 将字符串转换成枚举对象, 要求字符串必须为已有的常量名, 否则报异常【 valueOf 】
// 执行流程
// 1. 根据你输入的 "AUTUMN" 到 Season 的枚举对象去查找
// 2. 如果找到了就返回, 如果没有找到就报错
Season autumn1 = Season.valueOf("AUTUMN");
System.out.println("autumn1=" + autumn1); // autumn1=Season{name='秋天', desc='凉爽'}
System.out.println(autumn == autumn1); // true
// Season autumn2 = Season.valueOf("AUTUMN1"); // 报错
// 比较两个枚举常量, 比较的就是编号【 compareTo() 】
// 就是把 Season.AUTUMN 枚举对象的编号 和 Season.SUMMER 枚举对象的编号
// 通过源码可以到看到 compareTo 的含义: return self.ordinal - other.ordinal;
System.out.println(Season.AUTUMN.compareTo(Season.SUMMER)); // 1
// 增强 for 循环的使用
int[] nums = {1, 2, 3, 4};
System.out.println("普通 for 循环");
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i] + "\t");
}
System.out.println("\n增强 for 循环");
// 执行流程是 依次从 nums 数组中取出数据, 赋给 i, 如果取出完毕, 则退出 for
for (int i: nums) {
System.out.print(i + "\t");
}
}
}
1.6.3、案例演示
声明 Week 枚举类, 其中包含星期一至星期日的定义, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
使用 values 返回所有的枚举数组, 并遍历
package Enumeration01;
// 声明 Week 枚举类, 其中包含星期一至星期日的定义, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
// 使用 values 返回所有的枚举数组, 并遍历
public class EnumExercise02 {
public static void main(String[] args) {
// 获取到所有的枚举对象, 即数组
Week[] weeks = Week.values();
System.out.println("===所有星期的信息如下===");
for (Week week : weeks) {
System.out.println(week);
}
}
}
enum Week {
MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"),
FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期天");
private String name;
Week(String name) {
this.name = name;
}
@Override
public String toString() {
return "Week{" +
"name='" + name + '\'' +
'}';
}
}
1.6.4、enum 实现接口
- 使用 enum 关键字后,就不能再继承其它类了,因为 enum 会隐式继承 Enum,而 Java 是单继承机制
- 枚举类和普通类一样,可以实现接口,如下形式:
enum 类名 implements 接口1,接口2 {}
package Enumeration01;
public class EnumDetail {
public static void main(String[] args) {
Music.CLASSICMUSIC.playing(); // 播放好听的音乐...
}
}
class A {
}
// 1.使用 enum 关键字后, 就不能再继承其它类了, 因为 enum 会隐式继承 Enum,而Java 是单继承机制
// enum Season2 extends A {} // 报错
// 2.enum 实现的枚举类, 仍然是一个类, 所以还是可以实现接口的
interface IPlaying {
public void playing();
}
enum Music implements IPlaying {
CLASSICMUSIC;
@Override
public void playing() {
System.out.println("播放好听的音乐...");
}
}
2、注解
2.1、定义
- 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息
- 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息
- 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等,在 JavaEE 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替 java EE 旧版中所遗留的繁冗代码和 XML 配置等
2.2、基本的 Annotation 介绍
2.2.1、定义
使用 Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用,用于修饰它支持的程序元素
2.2.2、三个基本的 Annotation
- @Override:限定某个方法,是重写父类方法, 该注解只能用于方法
- @Deprecated:用于表示某个程序元素(类,,方法等)已过时
- @SuppressWarnings:抑制编译器警告
2.2.2.1、@Override 注解
package annotation_;
public class Override_ {
public static void main(String[] args) {
}
}
class Father { // 父类
public void fly() {
System.out.println("Father fly...");
}
public void say() {
}
}
class Son extends Father { // 子类
// 1. @Override 注解放在 fly 方法上, 表示子类的 fly 方法时重写了父类的 fly
// 2. 这里如果没有写 @Override 还是重写了父类 fly
// 3. 如果你写了@Override 注解, 编译器就会去检查该方法是否真的重写了父类的方法, 如果的确重写了则编译通过, 如果没有构成重写则编译错误
// 4. @Override 的定义
// 解读: 如果发现 @interface 表示一个 注解类
/*
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
*/
@Override //说明
public void fly() {
System.out.println("Son fly....");
}
@Override
public void say() {
}
}
2.2.2.2、@Deprecated 注解
package annotation_;
public class Deprecated_ {
public static void main(String[] args) {
A a = new A();
a.hi();
System.out.println(a.n1);
}
}
// 1. @Deprecated 修饰某个元素, 表示该元素已经过时
// 2. 即不在推荐使用, 但是仍然可以使用
// 3. 查看 @Deprecated 注解类的源码
// 4. 可以修饰方法, 类, 字段, 包, 参数等
// 5. @Deprecated 可以做版本升级过渡使用
/*
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
*/
@Deprecated
class A {
@Deprecated
public int n1 = 10;
@Deprecated
public void hi() {
}
}
2.2.2.3、@SuppressWarnings 注解
@SuppressWarnings:抑制编译器警告
package annotation_;
import java.util.ArrayList;
import java.util.List;
public class SuppressWarnings_ {
// 1. 当我们不希望看到这些警告的时候, 可以使用 SuppressWarnings 注解来抑制警告信息
// 2. 在{""} 中, 可以写入你希望抑制(不显示)警告信息
// 3. 可以指定的警告类型有
// (1) all, 抑制所有警告
// (2) boxing, 抑制与封装/拆装作业相关的警告
// (3) cast, 抑制与强制转型作业相关的警告
// (4) dep-ann, 抑制与淘汰注释相关的警告
// (5) deprecation, 抑制与淘汰的相关警告
// (6) fallthrough, 抑制与 switch 陈述式中遗漏 break 相关的警告
// (7) finally, 抑制与未传回 finally 区块相关的警告
// (8) hiding, 抑制与隐藏变数的区域变数相关的警告
// (9) incomplete-switch, 抑制与 switch 陈述式(enum case)中遗漏项目相关的警告
// (10) javadoc, 抑制与 javadoc 相关的警告
// (11) nls, 抑制与非 nls 字串文字相关的警告
// (12) null, 抑制与空值分析相关的警告
// (13) rawtypes, 抑制与使用 raw 类型相关的警告
// (14) resource, 抑制与使用 Closeable 类型的资源相关的警告
// (15) restriction, 抑制与使用不建议或禁止参照相关的警告
// (16) serial, 抑制与可序列化的类别遗漏 serialVersionUID 栏位相关的警告
// (17) static-access, 抑制与静态存取不正确相关的警告
// (18) static-method, 抑制与可能宣告为 static 的方法相关的警告
// (19) super, 抑制与置换方法相关但不含 super 呼叫的警告
// (20) synthetic-access, 抑制与内部类别的存取未最佳化相关的警告
// (21) sync-override, 抑制因为置换同步方法而遗漏同步化的警告
// (22) unchecked, 抑制与未检查的作业相关的警告
// (23) unqualified-field-access, 抑制与栏位存取不合格相关的警告
// (24) unused, 抑制与未用的程式码及停用的程式码相关的警告
// 4. 关于 SuppressWarnings 作用范围是和你放置的位置相关
// 比如 @SuppressWarnings 放置在 main 方法, 那么抑制警告的范围就是 main
// 通常我们可以放置具体的语句, 方法, 类
// 5. 看看 @SuppressWarnings 源码
// (1) 放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE
// (2) 该注解类有数组 String[] values() 设置一个数组比如 {"rawtypes", "unchecked", "unused"}
/*
@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("mary");
@SuppressWarnings({"unused"})
int i;
System.out.println(list.get(1));
}
public void f1() {
@SuppressWarnings({"rawtypes"})
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("mary");
@SuppressWarnings({"unused"})
int i;
System.out.println(list.get(1));
}
}
2.3、JDK 的元 Annotation(元注解)
2.3.1、元注解的基本介绍
JDK 的元 Annotation 用于修饰其他 Annotation
元注解: 本身作用不大,只是为了看源码时能读懂即可
2.3.2、元注解的种类
- Retention // 指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
- Target // 指定注解可以在哪些地方使用
- Documented // 指定该注解是否会在 javadoc 体现
- Inherited // 子类会继承父类注解
2.3.2.1、@Retention 注解
只能用于修饰一个 Annotation 定义,用于指定该 Annotation 可以保留多长时间,@Rentention 包含一个 RetentionPolicy类型的成员变量,使用 @Rentention 时必须为该 value 成员变量指定值
@Retention 的三种值
- RetentionPolicy.SOURCE:编译器使用后,直接丢弃这种策略的注释
- RetentionPolicy.CLASS:编译器将把注解记录在 class 文件中,当运行 Java 程序时,JVM 不会保留注解,这是默认值
- RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中,当运行 Java 程序时,JVM 会保留注解,程序可以通过反射获取该注解
2.3.2.2、@Target 注解
2.3.2.3、@Documented 注解