你好!在本文中,我们将向您介绍Java 枚举。想象一下,您被赋予了以下任务:创建一个实现星期几的类。乍一看,这似乎相当简单。您的代码看起来像这样:

public class DayOfWeek {

   private String title;

   public DayOfWeek(String title) {
       this.title = title;
   }

   public static void main(String[] args) {
       DayOfWeek dayOfWeek = new DayOfWeek("Saturday");
       System.out.println(dayOfWeek);
   }

   @Override
   public String toString() {
       return "DayOfWeek{" +
               "title='" + title + '\'' +
               '}';
   }
}
一切似乎都很好,但有一个问题:您可以将任何文本传递给DayOfWeek类的构造函数。这意味着有人可以创建一个名为“Frog”、“Cloud”或“azaza322”的星期几。这显然不是我们期望的行为,因为一周只有 7 天,而且每一天都有特定的名称。因此,我们的任务是以某种方式限制DayOfWeek类的可能值的范围。在 Java 1.5 出现之前,开发人员不得不独立地发明他们自己的解决方案来解决这个问题,因为该语言没有现成的解决方案。在那些日子里,如果程序员需要限制值的数量,他们会这样做:

public class DayOfWeek {

   private String title;

   private DayOfWeek(String title) {
       this.title = title;
   }

   public static DayOfWeek SUNDAY = new DayOfWeek("Sunday");
   public static DayOfWeek MONDAY = new DayOfWeek("Monday");
   public static DayOfWeek TUESDAY = new DayOfWeek("Tuesday");
   public static DayOfWeek WEDNESDAY = new DayOfWeek("Wednesday");
   public static DayOfWeek THURSDAY = new DayOfWeek("Thursday");
   public static DayOfWeek FRIDAY = new DayOfWeek("Friday");
   public static DayOfWeek SATURDAY = new DayOfWeek("Saturday");

   @Override
   public String toString() {
       return "DayOfWeek{" +
               "title='" + title + '\'' +
               '}';
   }
}
以下是您应该注意的事项:
  • 构造函数是私有的。如果构造函数标有private修饰符,则不能用于创建对象。由于该类只有一个构造函数,因此永远无法创建DayOfWeek对象。

    
    	public class Main {
    
       		public static void main(String[] args) {
          
           			DayOfWeek sunday = new DayOfWeek(); // Error!
       		}
    }
    

  • 当然,该类确实具有所需数量的公共静态对象,这些对象已正确初始化(使用正确的星期几名称)。

    这允许这些对象在其他类中使用。

    
    	public class Person {
    
       		public static void main(String[] args) {
    
           			DayOfWeek sunday = DayOfWeek.SUNDAY;
    
           			System.out.println(sunday);
      		 }
    }
    

    输出:

    DayOfWeek{title = '星期日'}

这种方法很大程度上解决了这个问题。我们一周有 7 天可供支配,没有人可以创造新的一天。这个解决方案由 Joshua Bloch 在他的Effective Java一书中提供。顺便说一句,那本书非常酷,是所有 Java 开发人员的必读之书。 如何使用 Enum 类 - 2随着 Java 1.5 的发布,该语言获得了针对此类情况的现成解决方案:Java EnumsJava中的枚举也是一个类。它专门“微调”来解决这样的问题,即创造一定的有限范围的价值。Java 的创建者已经有了现成的示例(例如,C 已经有了enum),因此他们能够创建最好的变体。

那么什么是 Java 枚举?

让我们重温我们的DayOfWeek示例:

public enum DayOfWeek {

   SUNDAY,
   MONDAY,
   TUESDAY,
   WEDNESDAY,
   THURSDAY,
   FRIDAY,
   SATURDAY
}
现在看起来简单多了 :) 在内部,我们的Enum有 7 个静态常量。这就是我们可以用它来实现程序的东西。例如,让我们编写一个程序来确定学生今天是否需要上学。我们的学生将有一个每日时间表,由StudentSchedule类表示:

public class StudentSchedule {

   private DayOfWeek dayOfWeek;
   // ... other fields


   public DayOfWeek getDayOfWeek() {
       return dayOfWeek;
   }

   public void setDayOfWeek(DayOfWeek dayOfWeek) {
       this.dayOfWeek = dayOfWeek;
   }
}
schedule 对象的dayOfWeek变量决定今天是哪一天。这是我们的学生班级:

public class Student {

   private StudentSchedule schedule;
   private boolean goToSchool;

   public void wakeUp() {
      
       if (this.schedule.getDayOfWeek() == DayOfWeek.SUNDAY) {
           System.out.println("Hooray, you can sleep more!");
       } else {
           System.out.println("Damn, time for school again :(");
       }
   }
}
wakeUp()方法中,我们使用Java Enum来确定学生下一步应该做什么。我们甚至没有提供有关DayOfWeek中每个字段的详细信息,我们也不需要:很明显一周中的几天应该如何工作。如果我们以当前的形式使用它,任何开发人员都会理解我们的代码中发生了什么。Enum的另一个便利示例是它的常量可以与 switch 语句一起使用。例如,让我们编写一个严格饮食的程序,其中按天安排菜肴:

public class VeryStrictDiet {
   public void takeLunch(DayOfWeek dayOfWeek) {
       switch (dayOfWeek) {
           case SUNDAY:
               System.out.println("Sunday Dinner! You can even enjoy something a little sweet today.");
               break;
           case MONDAY:
               System.out.println("Lunch for Monday: chicken noodle soup!");
               break;
           case TUESDAY:
               System.out.println("Tuesday, today it's celery soup :(");
               break;
               //... and so on to the end
       }
   }
}
这是Enums优于 Java 1.5 之前使用的旧解决方案的优势之一——旧解决方案不能与switch一起使用。 关于枚举,您还需要了解什么 Enum是一个真正的类,具有它所包含的所有可能性。例如,如果当前对星期几的实现不充分,您可以向DayOfWeek添加变量、构造函数和方法:

public enum DayOfWeek {
  
   SUNDAY ("Sunday"),
   MONDAY ("Monday"),
   TUESDAY ("Tuesday"),
   WEDNESDAY ("Wednesday"),
   THURSDAY ("Thursday"),
   FRIDAY ("Friday"),
   SATURDAY ("Saturday");

   private String title;

   DayOfWeek(String title) {
       this.title = title;
   }

   public String getTitle() {
       return title;
   }

   @Override
   public String toString() {
       return "DayOfWeek{" +
               "title='" + title + '\'' +
               '}';
   }
}
现在我们的枚举常量有一个标题字段、getter 和重写的toString方法。 与普通类相比, Enum有一个严重的限制——它不能被继承。此外,枚举具有特征方法:
  • values() :返回Enum中所有值的数组:

    
    public static void main(String[] args) {
       		System.out.println(Arrays.toString(DayOfWeek.values()));
    }
    

    输出:

    [DayOfWeek{title = 'Sunday'}, DayOfWeek{title = 'Monday'}, DayOfWeek{title = 'Tuesday'}, DayOfWeek{title = 'Wednesday'}, DayOfWeek{title = 'Thursday'}, DayOfWeek{title = '星期五'}, DayOfWeek{title = '星期六'}]

  • ordinal():返回常量的序数。编号从零开始:

    
    	public static void main(String[] args) {
    
       		int sundayIndex = DayOfWeek.SUNDAY.ordinal();
       		System.out.println(sundayIndex);
    }
    

    输出:

    0

  • valueOf():返回与传递的名称对应的枚举对象:

    
    public static void main(String[] args) {
       DayOfWeek sunday = DayOfWeek.valueOf("SUNDAY");
       System.out.println(sunday);
    }
    

    输出:

    DayOfWeek{title = '星期日'}

笔记:我们使用大写字母来指定枚举字段。这些是常量,因此它们使用全部大写而不是驼峰式