java枚举⽐较⼤⼩写_Java枚举(enum)的学习
Java 枚举(enum)的学习
枚举的定义
在定义枚举类型时我们使⽤的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。枚举类型Day中分别定义了从周⼀到周⽇的值,这⾥要注意,值⼀般是⼤写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型⼀样,定义为⼀个单独的⽂件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提⽰我们改进,但务必记住枚举表⽰的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的。
//枚举类型,使⽤关键字enum
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
枚举实现原理
public class EnumDemo {
public static void main(String[] args){
Day day =Day.MONDAY;
}
}
enum Day {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
编译前⾯定义的EnumDemo.java⽂件后分别⽣成了Day.class和EnumDemo.class⽂件,⽽Day.class就是枚举类型,这也就验证前⾯所说的使⽤关键字enum定义枚举类型并编译后,编译器会⾃动帮助我们⽣成⼀个与枚举相关的类。我们再来看看反编译Day.class⽂件:
//反编译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实例对象,只不过代表的内容不⼀样⽽已。注意编译器还为我们⽣成了两个静态⽅法,分别是values()和 valueOf(),稍后会分析它们的⽤法,到此我们也就明⽩了,使⽤关键字enum定义的枚举类型,在编译期后,也将转换成为⼀个实实在在的类,⽽在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的MONDAY枚举类型对应public static final Day MONDAY;,同时编译器会为该类创建两个⽅法,分别是values()和valueOf()
枚举常见⽅法
枚举类的valueOf() ⽅法:
它的作⽤是传来⼀个字符串,然后将它转变为对应的枚举变量。前提是你传的字符串和定义枚举变量的字符串⼀抹⼀样,区分⼤⼩写。如果你传了⼀个不存在的字符串,那么会抛出异常。
枚举类的values()⽅法。
这个⽅法会返回包括所有枚举变量的数组。在该例中,返回的就是包含了七个星期的Day[]。可以⽅便的⽤来做循环。
枚举变量的toString()⽅法。
该⽅法直接返回枚举定义枚举变量的字符串,⽐如MONDAY就返回【”MONDAY”】。
枚举变量的.ordinal()⽅法。
默认情况下,枚举类会给所有的枚举变量⼀个默认的次序,该次序从0开始,类似于数组的下标。⽽.ordinal()⽅法就是获取这个次序(或者说下标)
枚举变量的compareTo()⽅法。
该⽅法⽤来⽐较两个枚举变量的”⼤⼩”,实际上⽐较的是两个枚举变量的次序,返回两个次序相减后的结果,如果为负数,就证明变量1”⼩于”变量2 (变量1pareTo(变量2),返回【变量1.ordinal() - 变量2.ordinal()】
枚举变量的name()⽅法。
它和toString()⽅法的返回值⼀样,事实上,这两个⽅法本来就是⼀样的: 这两个⽅法的默认实现是⼀样的,唯⼀的区别是,你可以重写toString⽅法。name变量就是枚举变量的字符串形式。
到此对于抽象类Enum类的基本内容就介绍完了,这⾥提醒⼤家⼀点,Enum类内部会有⼀个构造函数,该构造函数只能有编译器调⽤,我们是⽆法⼿动操作的,不妨看看Enum类的主要源码:
//实现了Comparable
public abstract class Enum>
implements Comparable, Serializable {
private final String name; //枚举字符串名称
public final String name() {
return name;
}
private final int ordinal;//枚举顺序值
public final int ordinal() {
return ordinal;
//枚举的构造⽅法,只能由编译器调⽤
protected Enum(String name, int ordinal) {
this.name = name;
}
public String toString() {
return name;
}
public final boolean equals(Object other) {
return this==other;
}
//⽐较的是ordinal值
public final int compareTo(E o) {
Enum> other = (Enum>)o;
Enum self = this;
if (Class() != Class() && // optimization
throw new ClassCastException();
enum类型如何使用dinal - dinal;//根据ordinal值⽐较⼤⼩
}
@SuppressWarnings("unchecked")
public final Class getDeclaringClass() {
//获取class对象引⽤,getClass()是Object的⽅法
Class> clazz = getClass();
//获取⽗类Class对象引⽤
Class> zuper = Superclass();
return (zuper == Enum.class) ? (Class)clazz : (Class)zuper;
}
public static > T valueOf(Class enumType,
String name) {
//umConstantDirectory()获取到的是⼀个map集合,key值就是name值,value则是枚举变量值//enumConstantDirectory是class对象内部的⽅法,根据class对象获取⼀个map集合的值
T result = umConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + CanonicalName() + "." + name);
}
//.....省略其他没⽤的⽅法
}
编译器⽣成的Values⽅法与ValueOf⽅法
values()⽅法和valueOf(String name)⽅法是编译器⽣成的static⽅法,因此从前⾯的分析中,在Enum类中并没出现values()⽅法,但valueOf()⽅法还是有出现的,只不过编译器⽣成的valueOf()⽅法需传递⼀个name参数,⽽Enum⾃带的静态⽅法valueOf()则需要传递两个⽅法,从前⾯反编译后的代码可以看出,编译器⽣成的valueOf⽅法最终还是调⽤了Enum类的valueOf⽅法,下⾯通过代码来演⽰这两个⽅法的作⽤:
Day[] days2 = Day.values();
System.out.println("day2:"+String(days2));
Day day = Day.valueOf("MONDAY");
System.out.println("day:"+day);
/**
输出结果:
day2:[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
day:MONDAY
*/
从结果可知道,values()⽅法的作⽤就是获取枚举类中的所有变量,并作为数组返回,⽽valueOf(String name)⽅法与Enum类中的valueOf⽅法的作⽤类似根据名称获取枚举变量,只不过编译器⽣成的valueOf⽅法更简洁些只需传递⼀个参数。这⾥我们还必须注意到,由于values()⽅法是由编译器插⼊到枚举类中的static⽅法,所以如果我们将枚举实例向上转型为Enum,那么values()⽅法将⽆法被调⽤,因为Enum类中并没有values()⽅法,valueOf()⽅法也是同样的道理,注意是⼀个参数的。
//正常使⽤
Day[] ds=Day.values();
//向上转型Enum
Enum e = Day.MONDAY;
//⽆法调⽤,没有此⽅法
//e.values();
枚举与Class对象
上述我们提到当枚举实例向上转型为Enum类型后,values()⽅法将会失效,也就⽆法⼀次性获取所有枚举实例变量,但是由于Class对象的存在,即使不使⽤values()⽅法,还是有可能⼀次获取到所有枚举实例变量的,在Class对象中存在如下⽅法:
通过getEnumConstants()⽅法,同样可以轻⽽易举地获取所有枚举实例变量下⾯通过代码来演⽰这个功能: