为什么用枚举,枚举怎么用

 

public enum Season {
	SPRING, SUMMER, AUTUMN, WINTER;
}

1.Season.values()方法和Season.ordinal()方法

  • Season.values()方法会返回包括所有枚举变量的数据。
  • Season s = Season.valueOf("SPRING");->SPRING
  • Season.ordinal()方法正是可以获取其次序的方法
for (Season s: Season.values()){
            System.out.println(s + ".ordinal() --> "+s.ordinal());
        }
SPRING.ordinal() --> 0
SUMMER.ordinal() --> 1
AUTUMN.ordinal() --> 2
WINTER.ordinal() --> 3 

2.Season.toString()方法和Season.name()方法

  • Season.toString()方法会返回枚举定义枚举变量时的字符串。此方法同Season.name()方法是一样的。
  • 实现过程也是一样
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
    public String toString() {
        return name;
    }
     public final String name() {
        return name;
    }
  }
  • 但它们之间唯一的区别是,toString()方法可以重写,但name()方法被final修饰了,不能重写。

3.Season.compareTo()方法

  • 这个方法用于比较两个枚举变量的“大小”,实际上比较的是两个枚举变量之间的次序,并返回次序相减之后的结果。
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;
    }
  • compareTo()方法中会先判断是否属于同一个枚举的变量,然后再返回差值。

4.枚举与switch

  • 枚举是JDK1.5才有的特性,同时switch也更新了。使用switch进行条件判断的时候,条件整数一般只能是整型,字符型,而枚举型确实也被switch所支持。
public enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}
class SeasonSwitch{
    public void judge(Season s){
        switch (s){
            case SPRING:
                System.out.println("spring");
                break;
            case SUMMER:
                System.out.println("summer");
                break;
            case AUTUMN:
                System.out.println("autumn");
                break;
            case WINTER:
                System.out.println("winter");
                break;
        }
    }

5.枚举的高级使用

public enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}
SPRING对应的ordinal值对应的就是0,SUMMER对应的就是1。
如果我们想将SPRING的值为1,那么就需要自己定义变量:
public enum Season {
    SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);

    private int value;

    private Season(int value){
        this.value = value;
    }
}
  • 我们想对一个枚举变量做两个维度的描述
public enum Season {
    SPRING(1, "spring"), SUMMER(2, "summer"), AUTUMN(3, "autumn"), WINTER(4, "winter");

    private int value;
    private String lab;

    private Season(int value, String lab){
        this.value = value;
        this.lab = lab;
    }
}
  • 构造函数必须私有化,但也不是一定要写private,事实上枚举的构造函数默认并强制为private,写public是无法通过编译的。
  • ordinal还是按照它的规则给每个枚举变量按次序赋值,自定义变量与默认的ordinal属性并不冲突。

6.枚举的原理

public enum Season {
    SPRING() {
        @Override
        public Season getNextSeason() {
            return SUMMER;
        }
    }, SUMMER() {
        @Override
        public Season getNextSeason() {
            return AUTUMN;
        }
    }, AUTUMN() {
        @Override
        public Season getNextSeason() {
            return WINTER;
        }
    }, WINTER() {
        @Override
        public Season getNextSeason() {
            return SPRING;
        }
    };

    public abstract Season getNextSeason();
}
  • 通过javac编译生成5个.class文件

 

  • 通过javap反编译Season.class文件
public abstract class com.jessica.test.Season extends java.lang.Enum<com.jessica.test.Season> {
  public static final com.jessica.test.Season SPRING;
  public static final com.jessica.test.Season SUMMER;
  public static final com.jessica.test.Season AUTUMN;
  public static final com.jessica.test.Season WINTER;
  public static com.jessica.test.Season[] values();
  public static com.jessica.test.Season valueOf(java.lang.String);
  public abstract com.jessica.test.Season getNextSeason();
  com.jessica.test.Season(java.lang.String, int, com.jessica.test.Season$1);
  static {};
}
  • 也就是说,这四个枚举常量分别使用了内部类来实现。
  • 同时还添加了两个方法values()和valueOf(String s)。我们使用的是默认的无参构造函数,但现在的构造函数有两个参数。还生成了一个静态代码块。
  • 通过javap -c -v对Season.class反编译,汇总

。。。。。。。。。。。。

  • 静态代码块部分做的工作,就是分别设置生成的四个公共静态常量字段的值,同时编译器还生成一个静态字段$VALUES,保存的是枚举类型定义的所有枚举常量。相当于以下代码:
Season SPRING = new Season1();
Season SUMMER = new Season2();
Season AUTUMN = new Season3();
Season WINTER = new Season4();
Season[] $VALUES = new Season[4];
$VALUES[0] = SPRING;
$VALUES[1] = SUMMER;
$VALUES[2] = AUTUMN;
$VALUES[3] = WINTER;
  • values()方法

values()方法是一个公共的静态方法,所以我们可以直接调用该方法,返回枚举的数组。而这个方法实现的是,将静态代码块中初始化的$VALUES字段的值克隆出来,并且强制转换成Season[]类型返回,就相当于以下代码:

public static Season[] values(){
 return (Season[])$VALUES.clone();
}
  • valueof()方法
  • valueOf()也是一个公共的静态方法,所以可以直接调用这个方法并返回参数字符串表示的枚举变量,另外,这个方法的实现是调用Enum.valueOf()方法,并把类型强制转换为Season,它相当于如下的代码:
public static Season valueOf(String s){
 return (Season)Enum.valueOf(Season.class, s);
}
  • 内部类
  • 通过Javap反编译
Compiled from "Season.java"
final class Season$1 extends Season {
  Season$1(java.lang.String, int);
  public Season getNextSeason();
}
  • 为什么Season1的构造函数有两个入参呢?关于这个问题,我们还是得从Season的父类Enum说起。
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;
    }
    ......
}
  • 从Enum中我们可以看到,每个枚举都定义了两个属性,name和ordinal,name表示枚举变量的名称,而ordinal则是根据变量定义的顺序授予的整型值,从0开始。

7.枚举与单例

  • 枚举类实现单例模式相当硬核,因为枚举类型是线程安全的,且只会装载一次。使用枚举类来实现单例模式,是所有的单例实现中唯一一种不会被破坏的单例模式实现。
public class SingletonObject {

    private SingletonObject() {
    }

    private enum Singleton {
        INSTANCE;

        private final SingletonObject instance;

        Singleton() {
            instance = new SingletonObject();
        }

        private SingletonObject getInstance() {
            return instance;
        }
    }

    public static SingletonObject getInstance() {
        return Singleton.INSTANCE.getInstance();
    }
}

7.彩蛋-Javap命令行

javap <options> <classes>

其中classes就是你要反编译的class文件。

在命令行中直接输入javap或javap -help可以看到javap的options有如下选项:

 

-hep  --hep  -?        输出此用法消息

-version                 版本信息,其实是当前javap所在jdk的版本信息,不是cass在哪个jdk下生成的。

-v  -verbose             输出附加信息(包括行号、本地变量表,反汇编等详细信息)

-                         输出行号和本地变量表

-pubic                    仅显示公共类和成员

-protected               显示受保护的/公共类和成员

-package                 显示程序包/受保护的/公共类 和成员 (默认)

-p  -private             显示所有类和成员

-c                       对代码进行反汇编

-s                       输出内部类型签名

-sysinfo                 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)

-constants               显示静态最终常量

-casspath &t;path>        指定查找用户类文件的位置

-bootcasspath &t;path>    覆盖引导类文件的位置

一般常用的是-v -l -c三个选项。

javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。

javap -l 会输出行号和本地变量表信息。

javap -c 会对当前class字节码进行反编译生成汇编代码。

查看汇编代码时,需要知道里面的jvm指令,可以参考官方文档:

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html

另外通过jclasslib工具也可以看到上面这些信息,而且是可视化的,效果更好一些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值