Java 中常量的分类:

    • 类常量
    • 接口常量
    • 枚举声明常量 ```java // 类常量 public class Demo { // 静态常量 public final static double PI = 3.14; // 实例常量 private final int height = 10; }

    // 接口常量 public interface Season { int Spring = 0; int Summer = 1; int Autumn = 2; int Winter = 3; }

    // 枚举类型常量 public enum Season { SPRING, SUMMER, AUTUMN, WINTER; }

    1. 枚举类型常量:JLSJava Language SpecificationJava语言规范)提倡枚举项全部大小,字母之间用下划线分隔。<br />枚举类型常量的优点:
    2. - 相较于接口常量,更加简单
    3. - 枚举类型更加稳定:在编译期间限定类型,不允许发生越界,即枚举类型的实例固定
    4. - 枚举类型具有内置方法** 枚举类.values()** 获取所有的实例
    5. - 枚举类型都是 java.lang.Enum 的子类,该基类提供了注入获得排序值的 ordinalcompareTo 等方法,可以自定义方法,包括静态方法、非静态方法,还能从根本上杜绝常量类被实例化
    6. ```java
    7. public enum Season {
    8. SPRING,
    9. SUMMER,
    10. AUTUMN,
    11. WINTER;
    12. }
    13. // Season 有四个实例:Season.SPRING、Season.SUMMER、Season.AUTUMN、Season.WINTER
    14. Season.SPRING instanceof Enum == true;
    15. // 枚举类实例数量
    16. Season.values().length == 4;
    17. // 根据name获取枚举实例
    18. Season.valueOf("SPRING");

    1、小心枚举类型带来的空值异常:使用枚举定义常量时,会伴有大量的 switch 语句判断,目的是为每个枚举项解释其行为

    在 java 中,switch 语句只能判断 byte、short、char、int、String(JDK 7支持),枚举类型也可以跟在 switch 后面的原因是在编译时,编译器判断出switch语句后的参数是枚举类型,然后就会根据枚举的排序值 ordinal() 继续匹配

    1. // 传入参数为Season实例对象,允许传入null值而不报错
    2. // 但是在执行时会抛出 NPE 空指针异常
    3. public void demo(Season season){
    4. switch (season){
    5. case AUTUMN:
    6. case SPRING:
    7. case WINTER:
    8. case SUMMER:
    9. default:
    10. System.out.println("end");
    11. }
    12. }
    13. // 等价于:
    14. public void demo(Season season){
    15. switch (season.ordinal()){
    16. case Season.AUTUMN.ordinal():
    17. case Season.SPRING.ordinal():
    18. case Season.WINTER.ordinal():
    19. case Season.SUMMER.ordinal():
    20. default:
    21. System.out.println("end");
    22. }
    23. }

    2、在 switch 的 default 代码块中增加 AssertionError 错误:switch 后跟枚举类型,case 后列出所有的枚举项,这是一个使用枚举的主流写法,在此写法中,枚举类型的成员已经被完全列举,但是可以在 default 中直接抛出一个 AssertionError ,这样可以保证在增加一个枚举项的情况下,若其他代码未修改,运行期马上就会报错

    1. public void demo(Season season){
    2. switch (season){
    3. case AUTUMN:
    4. case SPRING:
    5. case WINTER:
    6. case SUMMER:
    7. default:
    8. throw new AssertionError("存在未处理的枚举项!");
    9. }
    10. }

    3、使用 valueOf 前必须进行校验:因为当传入的参数不正确时,valueOf 会抛出 java.lang.IllegalArgumentException 异常,会中止程序的运行,因此在使用 valueOf 之前必须先进行校验

    1. // 方式一:使用try...catch...捕捉异常
    2. try {
    3. Season spring = Season.valueOf("spring");
    4. // 有该枚举项时的处理
    5. System.out.println(spring);
    6. } catch (IllegalArgumentException e) {
    7. System.out.println("无相关枚举项");
    8. }
    9. // 方式二:扩展枚举类:在valueOf之前先判断一下是否包含指定的枚举名称
    10. public enum Season {
    11. SPRING,
    12. SUMMER,
    13. AUTUMN,
    14. WINTER;
    15. public static boolean contains(String name){
    16. // 所有的枚举值
    17. Season[] seasons = values();
    18. // 遍历查找
    19. for (Season season : seasons) {
    20. if (season.name().equals(name)){
    21. return true;
    22. }
    23. }
    24. return false;
    25. }
    26. }

    4、用枚举实现工厂方法模式:工厂方法模式(Factory Method Pattern)是“创建对象的接口,让子类决定实例化哪一个类,并使一个类的实例化延迟到其子类”
    要创建的产品:

    1. // 车辆接口和相关实现
    2. interface Car {
    3. }
    4. class FordCar implements Car {
    5. }
    6. class BuickCar implements Car {
    7. }

    枚举非静态方法实现工厂方法模式:

    1. // 枚举实现的工厂类
    2. enum CarFactory {
    3. // 定义工厂类能生产汽车的类型
    4. FORD_CAR,
    5. BUICK_CAR;
    6. // 生产汽车
    7. public Car create(){
    8. switch (this){
    9. case FORD_CAR:
    10. return new FordCar();
    11. case BUICK_CAR:
    12. return new BuickCar();
    13. default:
    14. throw new AssertionError("无效参数");
    15. }
    16. }
    17. }
    18. // 使用:
    19. Car car = CarFactory.BUICK_CAR.create();

    通过抽象方法生成产品:

    1. enum CarFactory {
    2. FORD_CAR{
    3. // 在实例中进行实现
    4. public Car create(){
    5. return new FordCar();
    6. }
    7. },
    8. BUICK_CAR{
    9. public Car create(){
    10. return new BuickCar();
    11. }
    12. };
    13. // 抽象生产方法
    14. public abstract Car create();
    15. }
    16. // 使用:
    17. Car car = CarFactory.BUICK_CAR.create();

    使用枚举工厂的优点:

    1. 避免错误调用的发生:一般工厂模式中的生产方法支持三种类型的参数,类型参数、String参数、int参数,容易产生错误,如边界问题,null值等问题
    2. 性能更好,枚举的计算基于 int 类型的计算
    3. 降低类间耦合

    5、枚举项的数量限制在 64 个以内:java 提供的枚举集合,效率比普通集合高

    • EnumSet:元素必须为某一枚举的枚举项
    • EnumMap:key 必须为某一枚举的枚举项 ```java enum A { a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z, aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,kk,ll; }

    enum B { a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z, A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z, aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,kk,ll,mm; }

    A.values().length == 64; B.values().length == 65;

    EnumSet as = EnumSet.allOf(A.class); EnumSet bs = EnumSet.allOf(B.class); // 两个 as.getClass() == class java.util.RegularEnumSet; bs.getClass() == class java.util.JumboEnumSet; ``` EnumSet:

    • 当枚举项数量小于等于 64 时,创建一个 RegularEnumSet 实例对象
    • 当枚举项数量大于 64 时,创建一个 JumboEnumSet 实例对象

    原因:

    • EnumSet 使用了 long 类型来记录枚举的排序号(枚举排序值 ordinal 是从 0、1、… 依次地址,没有重号、跳号),将枚举项的 ordinal 映射到 long 的每一位,因此,long 有 64 位,所以 RegularEnumSet 枚举项集合只能负责不大于 64 位的枚举
    • 大于 64 的枚举由 JumboEnumSet 处理,JumboEnumSet 其实就是将枚举项按 64 个元素一组进行分组处理

    6、@Inherited:元注解,标识一个注解是否可以自动被继承