深入学习java源码之Enum.valueOf()与ordinal()

深入学习java源码之Enum.valueOf()与ordinal()

静态变量

七个静态变量来代表星期几,以后只要引用和静态变量就可以了,而不用自己输入012….你这么写:

public class Weekday {
    public final static int SUN = 0;
    public final static int MON = 1;
    public final static int TUE = 2;
    public final static int WED = 3;
    public final static int THU = 4;
    public final static int FRI = 5;
    public final static int SAT = 6;

}

私有构造方法后,外界就不能创建该类的对象了,这样就避免了星期八星期九的出现,所有Weekday的对象都在该类内部创建。

public class Weekday {

    private Weekday(){}

    public final static Weekday SUN = new Weekday();
    public final static Weekday MON = new Weekday();
    public final static Weekday TUE = new Weekday();
    public final static Weekday WED = new Weekday();
    public final static Weekday THU = new Weekday();
    public final static Weekday FRI = new Weekday();
    public final static Weekday SAT = new Weekday();

    public static Weekday  getNextDay(Weekday nowDay){
        if(nowDay == SUN) {
            return MON;
        }else if(nowDay == MON) {
            return TUE;
        }else if(nowDay == TUE) {
            return WED;
        }else if(nowDay == WED) {
            return THU;
        }else if(nowDay == THU) {
            return FRI;
        }else if(nowDay == FRI) {
            return SAT;
        }else {
            return SUN;
        }
    }

    public static void printNowDay(Weekday nowDay){
        if(nowDay == SUN)
            System.out.println("sunday");
        else if(nowDay == MON)
            System.out.println("monday");
        else if(nowDay == TUE)
            System.out.println("tuesday");
        else if(nowDay == WED)
            System.out.println("wednesday");
        else if(nowDay == THU)
            System.out.println("thursday");
        else if(nowDay == FRI)
            System.out.println("friday");
        else
            System.out.println("saturday");
    }

}

class Test1{
    public static void main(String[] args) {
        Weekday nowday = Weekday.SUN;
        Weekday.printNowDay(nowday);
        Weekday nextDay = Weekday.getNextDay(nowday);
        System.out.print("nextday ====> ");
        Weekday.printNowDay(nextDay);
    }
}

当你需要一个整形数据的时候,只需要Weekday.toInt(Weekday.SUN);,看起来你好像完成了你的任务。

但是,你有没有发现,这样写,好麻烦啊。如果想要扩展一下功能,大量的ifelse会让人眼花缭乱。

枚举类

public enum Weekday {
    SUN,MON,TUS,WED,THU,FRI,SAT
}

看起来和上面的静态变量使用方式差不多,而且默认的toString方法返回的就是对应的名字。

class Test2{
    public static void main(String[] args) {
        Weekday sun = Weekday.SUN;
        System.out.println(sun); // 输出 SUN
    }
}

就是因为在没有枚举类的时候,我们要定义一个有限的序列,比如星期几,男人女人,春夏秋冬,一般会通过上面那种静态变量的形式,但是使用那样的形式如果需要一些其他的功能,需要些很多奇奇怪怪的代码。所以,枚举类的出现,就是为了简化这种操作。没有那么多烦人的ifelse,世界都清净了。

public enum Weekday {
    SUN(0),MON(1),TUS(2),WED(3),THU(4),FRI(5),SAT(6);

    private int value;

    private Weekday(int value){
        this.value = value;
    }

    public static Weekday getNextDay(Weekday nowDay){
        int nextDayValue = nowDay.value;

        if (++nextDayValue == 7){
            nextDayValue =0;
        }

        return getWeekdayByValue(nextDayValue);
    }

    public static Weekday getWeekdayByValue(int value) {
        for (Weekday c : Weekday.values()) {
            if (c.value == value) {
                return c;
            }
        }
        return null;
    }
}

class Test2{
    public static void main(String[] args) {
        System.out.println("nowday ====> " + Weekday.SAT);
        System.out.println("nowday int ====> " + Weekday.SAT.ordinal());
        System.out.println("nextday ====> " + Weekday.getNextDay(Weekday.SAT)); // 输出 SUN

        //输出:
        //nowday ====> SAT
        //nowday int ====> 6
        //nextday ====> SUN
    }
}

枚举类在定义的时候会自动为每个变量添加一个顺序,从0开始,假如你希望0代表星期天,1代表周一。。。并且你在定义枚举类的时候,顺序也是这个顺序,那你可以不用定义新的变量,就像这样:

public enum Weekday {
    SUN,MON,TUS,WED,THU,FRI,SAT
}

我们对上面的代码做了一些改变:

首先,我们在每个枚举变量的后面加上了一个括号,里面是我们希望它代表的数字。

然后,我们定义了一个int变量,然后通过构造函数初始化这个变量。

你应该也清楚了,括号里的数字,其实就是我们定义的那个int变量。这句叫做自定义变量。

public enum Weekday {
    MON(1),TUS(2),WED(3),THU(4),FRI(5),SAT(6),SUN(0);

    private int value;

    private Weekday(int value){
        this.value = value;
    }
}

请注意:这里有三点需要注意:

一定要把枚举变量的定义放在第一行,并且以分号结尾。 

构造函数必须私有化。事实上,private是多余的,你完全没有必要写,因为它默认并强制是private,如果你要写,也只能写private,写public是不能通过编译的。 

自定义变量与默认的ordinal属性并不冲突,ordinal还是按照它的规则给每个枚举变量按顺序赋值。

方法使用

public enum Weekday {
    SUN,MON,TUS,WED,THU,FRI,SAT
}

class Test3{
    public static void main(String[] args) {
        System.out.println(Weekday.valueOf("mon".toUpperCase()));
        //MON

        for (Weekday w : Weekday.values()){
            System.out.println(w + ".ordinal()  ====>" +w.ordinal());
        }
        //SUN.ordinal()  ====>0
        //MON.ordinal()  ====>1
        //TUS.ordinal()  ====>2
        //WED.ordinal()  ====>3
        //THU.ordinal()  ====>4
        //FRI.ordinal()  ====>5
        //SAT.ordinal()  ====>6

        System.out.println("Weekday.MON.compareTo(Weekday.FRI) ===> " + Weekday.MON.compareTo(Weekday.FRI));
        System.out.println("Weekday.MON.compareTo(Weekday.MON) ===> " + Weekday.MON.compareTo(Weekday.MON));
        System.out.println("Weekday.MON.compareTo(Weekday.SUM) ===> " + Weekday.MON.compareTo(Weekday.SUN));
        //Weekday.MON.compareTo(Weekday.FRI) ===> -4
        //Weekday.MON.compareTo(Weekday.MON) ===> 0
        //Weekday.MON.compareTo(Weekday.SUM) ===> 1

        System.out.println("Weekday.MON.name() ====> " + Weekday.MON.name());
        //Weekday.MON.name() ====> MON

    }
}

Weekday.valueOf() 方法:

它的作用是传来一个字符串,然后将它转变为对应的枚举变量。前提是你传的字符串和定义枚举变量的字符串一抹一样,区分大小写。如果你传了一个不存在的字符串,那么会抛出异常。

Weekday.values()方法。

这个方法会返回包括所有枚举变量的数组。在该例中,返回的就是包含了七个星期的Weekday[]。可以方便的用来做循环。

枚举变量的toString()方法。

该方法直接返回枚举定义枚举变量的字符串,比如MON就返回【”MON”】。

枚举变量的.ordinal()方法。

默认请款下,枚举类会给所有的枚举变量一个默认的次序,该次序从0开始,类似于数组的下标。而.ordinal()方法就是获取这个次序(或者说下标)

枚举变量的compareTo()方法。

该方法用来比较两个枚举变量的”大小”,实际上比较的是两个枚举变量的次序,返回两个次序相减后的结果,如果为负数,就证明变量1”小于”变量2 (变量1.compareTo(变量2),返回【变量1.ordinal() - 变量2.ordinal()】)

枚举类的name()方法。

它和toString()方法的返回值一样,事实上,这两个方法本来就是一样的: 

枚举类的其他用法

switch语句中使用

enum Signal {
    GREEN, YELLOW, RED
}

public class TrafficLight {
    Signal color = Signal.RED;

    public void change() {
        switch (color) {
        case RED:
            color = Signal.GREEN;
            break;
        case YELLOW:
            color = Signal.RED;
            break;
        case GREEN:
            color = Signal.YELLOW;
            break;
        }
    }
}

枚举类不能继承其他类,但是还是可以实现接口的

public interface Behaviour {
    void print();

    String getInfo();
}

public enum Color implements Behaviour {
    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;
    }

    // 接口方法
    @Override
    public String getInfo() {
        return this.name;
    }

    // 接口方法
    @Override
    public void print() {
        System.out.println(this.index + ":" + this.name);
    }
}

接口组织枚举

public interface Food {
    enum Coffee implements Food {
        BLACK_COFFEE, DECAF_COFFEE, LATTE, CAPPUCCINO
    }

    enum Dessert implements Food {
        FRUIT, CAKE, GELATO
    }
}

使用枚举创建的单例模式:

public enum EasySingleton{
    INSTANCE;
}

代码就这么简单,你可以使用EasySingleton.INSTANCE调用它,比起你在单例中调用getInstance()方法容易多了。

我们来看看正常情况下是怎样创建单例模式的:

用双检索实现单例:

下面的代码是用双检索实现单例模式的例子,在这里getInstance()方法检查了两次来判断INSTANCE是否为null,这就是为什么叫双检索的原因,记住双检索在java5之前是有问题的,但是java5在内存模型中有了volatile变量之后就没问题了。

public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

你可以访问DoubleCheckedLockingSingleTon.getInstance()来获得实例对象。

用静态工厂方法实现单例:

public class Singleton{
    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

你可以调用Singleton.getInstance()方法来获得实例对象。

上面的两种方式就是懒汉式和恶汉式单利的创建,但是无论哪一种,都不如枚举来的方便。而且传统的单例模式的另外一个问题是一旦你实现了serializable接口,他们就不再是单例的了。但是枚举类的父类【Enum类】实现了Serializable接口,也就是说,所有的枚举类都是可以实现序列化的,这也是一个优点。

Modifier and Type Method and Description
protected Object clone()

抛出CloneNotSupportedException。

int compareTo(E o)

将此枚举与指定的对象进行比较以进行订购。

boolean equals(Object other)

如果指定的对象等于此枚举常量,则返回true。

protected void finalize()

枚举类不能有finalize方法。

<E> getDeclaringClass()

返回与此枚举常量的枚举类型相对应的Class对象。

int hashCode()

返回此枚举常量的哈希码。

String name()

返回此枚举常量的名称,与其枚举声明中声明的完全相同。

int ordinal()

返回此枚举常数的序数(其枚举声明中的位置,其中初始常数的序数为零)。

String toString()

返回声明中包含的此枚举常量的名称。

static <T extends Enum<T>>
T
valueOf(<T> enumType, String name)

返回具有指定名称的指定枚举类型的枚举常量。

java源码

package java.lang;

import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, 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;
        this.ordinal = ordinal;
    }

    public String toString() {
        return name;
    }

    public final boolean equals(Object other) {
        return this==other;
    }

    public final int hashCode() {
        return super.hashCode();
    }

    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
	
    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }	
	
    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }	
	
    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }	
	
    protected final void finalize() { }	
	
    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize enum");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }
}

猜你喜欢

转载自blog.csdn.net/qq_35029061/article/details/86405622
今日推荐