09-枚举&注解

1. 枚举类

(1) 如何自定义枚举类。 枚举类:类的对象是有限个的,确定的。

  • 私有化类的构造器,保证不能在类的外部创建其对象

  • 在类的内部创建枚举类的实例。声明为:public static final

  • 若类有属性,那么属性声明为:private final 。此属性在构造器中赋值。

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

  • 其中常用的方法:values() valueOf(String name);

  • 枚举类如何实现接口 :

    ①让类实现此接口,类的对象共享同一套接口的抽象方法的实现。

    ②让类的每一个对象都去实现接口的抽象方法,进而通过类的对象调用被重写的抽象方法时,执行的效果不同

public class TestSeason1 {
public static void main(String[] args) {
Season1 spring = Season1.SPRING;
System.out.println(spring);
spring.show();
System.out.println(spring.getSeasonName());
System.out.println();
//1.values()
Season1[] seasons = Season1.values();
for(int i = 0;i < seasons.length;i++){
System.out.println(seasons[i]);
}
//2.valueOf(String name):要求传入的形参name是枚举类对象的名字。
//否则,报java.lang.IllegalArgumentException异常
String str = "WINTER";
Season1 sea = Season1.valueOf(str);
System.out.println(sea);
System.out.println();
Thread.State[] states = Thread.State.values();
for(int i = 0;i < states.length;i++){
System.out.println(states[i]);
}
sea.show();
}
}
interface Info{
void show();
}
//枚举类
enum Season1 implements Info{
SPRING("spring", "春暖花开"){
public void show(){
System.out.println("春天在哪里?");
}
},
SUMMER("summer", "夏日炎炎"){
public void show(){
System.out.println("生如夏花");
}
},
AUTUMN("autumn", "秋高气爽"){
public void show(){
System.out.println("秋天是用来分手的季节");
}
},
WINTER("winter", "白雪皑皑"){
public void show(){
System.out.println("冬天里的一把火");
}
};
private final String seasonName;
private final String seasonDesc;
private Season1(String seasonName,String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Season [seasonName=" + seasonName + ", seasonDesc="
+ seasonDesc + "]";
}
// public void show(){
// System.out.println("这是一个季节");
// }
}

补充,来源网上博客:

枚举(enum)类型是 Java 5 新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示。

①常量的使用

在 JDK1.5 之前,我们定义常量都是:public static fianl....。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。

public enum Color {
RED, GREEN, BLANK, YELLOW
}

使用:

public class B {
public static void main(String[] args) {
System.out.println( isRed( Color.BLANK ) ) ; //结果: false
System.out.println( isRed( Color.RED ) ) ; //结果: true
}
static boolean isRed( Color color ){
if ( Color.RED.equals( color )) {
return true ;
}
return false ;
}
}

或者 switch 的使用:

public class B {
public static void main(String[] args) {
showColor( Color.RED );
}
static void showColor(Color color){
switch ( color ) {
case BLANK:
System.out.println( color );
break;
case RED :
System.out.println( color );
break;
default:
System.out.println( color );
break;
}
}
}

②自定义函数

public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
private String name ;
private int index ;
private Color( String name , int index ){
this.name = name ;
this.index = index ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}

使用:

public class B {
public static void main(String[] args) {
//输出某一枚举的值
System.out.println( Color.RED.getName() );
System.out.println( Color.RED.getIndex() );
//遍历所有的枚举
for( Color color : Color.values()){
System.out.println( color + " name: " + color.getName() + " index: " + color.getIndex() );
}
}
}

结果:

红色
1
RED name: 红色 index: 1
GREEN name: 绿色 index: 2
BLANK name: 白色 index: 3
YELLO name: 黄色 index: 4

我们现在来深入了解下枚举。

参考几篇文章:

在博文 [1] 中这样讲到,如下代码:

public class EnumDemo {
public static void main(String[] args){
//直接引用
Day day =Day.MONDAY;
}
}
//定义枚举类型
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

我们来反编译,可以看到:

//反编译Day.class
final class Day extends Enum
{
//编译器为我们添加的静态的values()方法
public static Day[] values()
{
return (Day[])$VALUES.clone();
}
//编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
public static Day valueOf(String s)
{
return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
}
//私有构造函数
private Day(String s, int i)
{
super(s, i);
}
//前面定义的7种枚举实例
public static final Day MONDAY;
public static final Day TUESDAY;
public static final Day WEDNESDAY;
public static final Day THURSDAY;
public static final Day FRIDAY;
public static final Day SATURDAY;
public static final Day SUNDAY;
private static final Day $VALUES[];
static
{
//实例化枚举实例
MONDAY = new Day("MONDAY", 0);
TUESDAY = new Day("TUESDAY", 1);
WEDNESDAY = new Day("WEDNESDAY", 2);
THURSDAY = new Day("THURSDAY", 3);
FRIDAY = new Day("FRIDAY", 4);
SATURDAY = new Day("SATURDAY", 5);
SUNDAY = new Day("SUNDAY", 6);
$VALUES = (new Day[] {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
});
}
}

从反编译的代码可以看出编译器确实帮助我们生成了一个 Day 类(注意该类是 final 类型的,将无法被继承)而且该类继承自 java.lang.Enum 类,该类是一个抽象类(稍后我们会分析该类中的主要方法),除此之外,编译器还帮助我们生成了 7 个 Day 类型的实例对象,分别对应枚举中定义的 7 个日期,这也充分说明了我们前面使用关键字 enum 定义的 Day 类型中的每种日期枚举常量也是实实在在的 Day 实例对象,只不过代表的内容不一样而已。

现在明白了:使用关键字 enum 定义的枚举类型,在编译期后,将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的 MONDAY 枚举类型对应 public static final Day MONDAY;,同时编译器会为该类创建两个方法,分别是 values() 和 valueOf()。

所以最前面的关于季节的代码:

public class Season1Demo{
public static void main(String[] args) {
Season spring = Season.WINTER;
spring.show();
Season.SPRING.show();
Season.AUTUMN.show();
}
}
interface Info {
void show();
}
// 枚举类
enum Season implements Info {
SPRING("spring", "春暖花开") {
// public void show() {
// System.out.println("春天在哪里?");
// }
},
SUMMER("summer", "夏日炎炎") {
//该实例重写了 show 方法
public void show() {
System.out.println("生如夏花");
}
},
AUTUMN("autumn", "秋高气爽") {
public void show() {
System.out.println("秋天是用来分手的季节");
}
},
WINTER("winter", "白雪皑皑") {
public void show() {
System.out.println("冬天里的一把火");
}
};
private final String seasonName;
private final String seasonDesc;
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Season [seasonName=" + seasonName + ", seasonDesc=" + seasonDesc + "]";
}
public void show(){
System.out.println("这是一个季节");
}
}

结果:

冬天里的一把火
这是一个季节
秋天是用来分手的季节

本人的小结:枚举 Season 最后转换成了 Season 类(继承了 Enum 类),然后在该类中定义好了类型为 Season 的成员变量 SPRINGSUMMERAUTUMNWINTER 并实例化。

再引用一篇博客看到的内容,加深理解:

enum 这个关键字,可以理解为跟 class 差不多,这也个单独的类。可以看到,上面的例子里面有属性,有构造方法,有 getter,也可以有 setter,但是一般都是构造传参数。还有其他自定义方法。那么在这些东西前面的,以逗号隔开的,最后以分号结尾的,这部分叫做,这个枚举的实例。也可以理解为,class new 出来的实例对象。这下就好理解了。只是,class,new 对象,可以自己随便 new,想几个就几个,而这个 enum 关键字,他就不行,他的实例对象,只能在这个 enum 里面体现。也就是说,他对应的实例是有限的。这也就是枚举的好处了,限制了某些东西的范围,举个栗子:一年四季,只能有春夏秋冬,你要是字符串表示的话,那就海了去了,但是,要用枚举类型的话,你在 enum 的大括号里面把所有的选项,全列出来,那么这个季节的属性,对应的值,只能在里面挑。不能有其他的。

所以:

SPRING("spring", "春暖花开") {
public void show() {
System.out.println("春天在哪里?");
}
},

该代码相当于就是 new 出了 Season 对象并实例化,该如果这里没写 show() 方法则调用的 Season 实现的 show() 方法,但这里重新定义了,相当于覆盖了 show() 方法。

再看一个例子(包含抽象方法的枚举类):

定义一个 Operation 枚举类,有 4 个枚举值 PLUS、MINUS、TIMES、DIVIDE,分别代表加、减、乘、除,该枚举类有一个 calculate() 方法,用于完成计算。

``` java public enum Operation { // 用于执行加法运算 PLUS { // 花括号部分其实是一个匿名内部子类 @Override public double calculate(double x, double y) { return x + y; } },

// 用于执行减法运算
MINUS { // 花括号部分其实是一个匿名内部子类
@Override
public double calculate(double x, double y) {
// TODO Auto-generated method stub
return x - y;
}
},
// 用于执行乘法运算
TIMES { // 花括号部分其实是一个匿名内部子类
@Override
public double calculate(double x, double y) {
return x * y;
}
},
// 用于执行除法运算
DIVIDE { // 花括号部分其实是一个匿名内部子类
@Override
public double calculate(double x, double y) {
return x / y;
}
};
//为该枚举类定义一个抽象方法,枚举类中所有的枚举值都必须实现这个方法
public abstract double calculate(double x, double y);

}

从上可以看出,掌握枚举,需要探究理解好枚举和枚举变量的实质是什么。
枚举最后转换就是一个特殊的类,其中的枚举变量就是实例化对象。可以就这么看:
``` java
public class HelloDemo2 {
public static void main(String[] args) {
Season1 SPRING = new Season1("spring", "春暖花开") {
public void show() {
System.out.println("春天在哪里?");
};
};
SPRING.show();
}
}
interface Info1{
void show();
}
class Season1 implements Info1{
private String seasonName;
private String seasonDesc;
public Season1(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
@Override
public void show() {
System.out.println("这是一个季节");
}
}

2. 注解Annotation

(1) JDK提供的常用的三个注解

@Override: 限定重写父类方法, 该注释只能用于方法
@Deprecated: 用于表示某个程序元素(, 方法等)已过时
@SuppressWarnings: 抑制编译器警告

(2) 如何自定义注解:以 SuppressWarnings 为例进行创建即可

(3) 元注解:可以对已有的注解进行解释说明。

①基本的 Annotation

  • 使用 Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素

  • 三个基本的Annotation:

    @override:限定重写父类方法,该注释只能用于方法
    @Deprecated:用于表示某个程序元素(类,方法等)已过时
    @SuppressWarnings:抑制编译器警告

②JDK 的元 Annotation

  • @Target:用于修饰 Annotation 定义,用于指定被修饰的 Annotation 能用于修饰哪些程序元素。@Target 也包含一个名为 value 的成员变量.

  • @Documented:用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档。

    • 定义为 Documented 的注解必须设置 Retention 值为 RUNTIME。

  • @Inherited:被它修饰的 Annotation 将具有继承性,如果某个类使用了被 @Inherited 修饰的 Annotation,则其子类将自动具有该注解,实际应用中,使用较少 。