您的位置:首页 > 编程语言 > Java开发

java 枚举原理

2014-07-23 14:25 190 查看
  每个枚举的成员其实就是您定义的枚举类型的一个实例(Instance)——换句话说,当定义了一个枚举类型后,在编译时刻就能确定该枚举类型有几个实例,分别是什么,在运行期间我们无法在使用该枚举类型创建新的实例了,这些实例在编译期间就已经完全确定下来了。

枚举的实际工程用处:在有限个选择项的环境中,如权限限制有系统管理员、管理员、vip用户、普通用户等,使用枚举

       我们回忆下所知道的java中的数据类型,java中的数据类型可以分为基本数据类型和引用数据类型。基本数据类型代表的是一种简单的数据结构,比如说我们经常所说的整型,浮点型,字符型,布尔型等。而在java当中有一种特殊的数据类型,我们叫做引用数据类型,而引用数据类型一共有四种,略懂java的人是知道的,这四种分别是类、数组、接口以及今天我要重点讲述的枚举。关于其他三种数据类型有机会我们再在一起讨论。

  那么到底什么是枚举,枚举的作用是什么呢?枚举类型最主要的作用是限定类型的取值。那么我们来看一个例子!

  我们首先要定义一个类,这个类是人类,非常简单!

  public class Person{

  }
  就可以了,那么我们知道,类是用来描述一组具有相同属性(域)和方法的一个抽象。那么我们都应该清楚每一个人类的对象,也就是说每个人类的具体个体都具有相同的一些属性,比如说姓名,年龄等。那么我们就可以在以上类当中加入相应的属性。

  public class Person{
  //名字
  private String name;
  //年龄
  private int age;
  }
  这里都比较容易理解,那么我们去看另外一个属性,这个属性我们叫做性别,总所周知,这个属性的取值只有两种,那么有和我较劲的同学不要提人妖这回事,人妖忽略。那么我们会发现,性别这个属性既然只有两个值,那么我们应该给这个属性定义为什么类型呢!有的同学说随便啊,比如说String类型就可以啊!那么我们来看一下,如果把性别属性定义为String类型会出现什么状况。
  public class Person{
  //名字
  private String name;
  //年龄
  private int age;
  //性别
  private String sex;
  //构造器
  public Person(String name,int age,String sex){
  this.name = name;
  this.age = age;
  this.sex = sex;
  }
}
  上面的例子我们看到,我添加了一个属性是性别,之后加入一个构造器,构造器最主要的作用是通过类来创建对象,上面的构造器是说当你创建一个人类对象的时候需要知道这个人的名字,年龄,以及性别。好了,那么我们来创建一个人类的对象,语法非常简单!

  Person p = new Person(“杨过”,32,”男”);

  我们一看,这是没有任何问题的,我们抛开名字和年龄不说,只看他的性别。

  Person这个类我们会发现,创建这个类的对象的时候构造器的第三个参数只要是字符串就可以了,那么我们可以不可以这么写。

  Person p = new Person(“杨过”,32,”大家好”);

  这句话在语法上我们看有错吗?答案是没有,因为构造器的第三个参数就是字符串,那么我传一个字符串给构造器当然一点问题都没有。虽然语法上没有任何问题,但是我们看这个对象在创建上是不合理的,为什么呢?因为性别只有男和女之分,不能有其他的!有聪明的人已经想到了,那么我们可以把sex性别这个属性定义为boolean类型啊!因为boolean类型只有两个取值,那么我们传参的时候就可以穿true或者false,用true代表男人,用false代表女人,那么可以不可以?当然没问题,可是为题是为什么要用true代表男人,不用true代表女人啊!你会想到其实无所谓了true代表谁都可以,只要之前我们有一个规定就可以了!可逆会发现true和false根本就没有直观性!那么这么多问题,我们到底要怎么解决呢!

  在枚举类型之前,java程序员的解决此类限定类型的值的时候用了一个非常巧妙的方法!他想到了新定义一个类型,这个类型只有两个值,这两个值,一个是男一个是女。那么怎么才能做到呢!好,我们先定义一个类型,怎么定义类型!我们说过类就是一种引用类型,那么引用类型的值是什么,之前我们讲过那就是对象。好了,我们现在要做的是创建一个类,这个类只能创建两个对象!
  public class Sex{

  }

  首先我们要考虑,这个类只能创建两个对象,我们会想到我们之前学过的单例设计模式中,要求一个类只能创建一个对象。这其实是一个道理的。我们要考虑创建对象要用到构造器,那么构造器不可能是public 的,如果构造器是public那么我们就可以创建无数个对象。好了,那么我们在上面的类当中加入构造器,这个构造器一定是私有的。

  public class Sex{
  private Sex(){
  }
  }
  那么我们可以在这个类中加入属性。
  public class Sex{
  private String sex;
  public static final Sex MALE = new Sex(“男”) ;
  public static final Sex FEMALE = new Sex(“女”);
  private Sex(String sex){
  this.sex = sex;
  }
  }
  关于这个例子我们可以看到,这个类只有两个对象,这两个对象分别是Sex类中的两个属性。

  之后我们可以将我们Person这个类的例子稍做修改。
  public class Person{
  private String name; //名字
  private int age; //年龄
  private Sex sex; //性别
  //构造器
  public Person(String name,int age,Sex sex){
  this.name = name;
  this.age = age;
  this.sex = sex;
  }
  }
  这个时候,我们创建这个对象的时候,如何创建呢?非常简单。
  Person p = new Person(“杨过”,32,new Sex(“男”));
  我们看可以吗?

  很明显是不可以的,因为Sex构造器是私有的,怎么办,Person构造器的第三个参数又需要Sex类型,到这里我们会发现,Sex类型的值只有两个分别是在Sex类中是静态的常量而且是公有的,那就好办了!我们可以通过这种方式创建对象!
  Person p = new Person(“杨过”,32,Sex.MALE);
  这个时候我们就可以看到这个人是个男人,因为我们知道male在Sex类中定义为男人!而且应为也是男人的意思!有直观又方便,那么有人说,我想看看他到底是男人还是女人怎么办!

  你会想到,把属性打印出来就可以了!
  System.out.println(p.sex);
  这个时候,打印的是这个对象的hashCode,我们只要在Sex类中加入toString方法就可以了!

  到这里,我们会发现人类中有一个sex属性,这个属性是Sex类型的,而且这个属性只有两个取值。那么以后我们在遇到这种限定类型的时候就可以用这种方法。比如说血型等。

  那么我们说了半天还没谈到枚举呢!其实在上面的文字中,我们已经不知不觉阐述了枚举的原理!枚举就是将新的类型做一个限定。我们来创建一个枚举。

  public enum Sex{
  MALE(“男”),FEMALE(“女”);
  }
  这就是枚举,这里是将Sex所有可能的取值都列举到这个枚举中,如果一个属性定义为枚举类型,只能到这个枚举中区寻找他的值!好了,我们改一下!
  public class Person{
  private String name; //名字
  private int age; //年龄
  private Sex sex; //性别
  //构造器
  public Person(String name,int age,Sex sex){
  this.name = name;
  this.age = age;
  this.sex = sex;
  }
  }

  创建对象的时候,同样Person p = new Person(“杨过”,32,Sex.MALE);就可以,这就是枚举!当然枚举还有很多东西需要大家去学习,但今天就说到这了,等有机会再深一步的去讲解!

——————————————————————使用枚举与不使用枚举(模拟枚举)代码对比————————————————————————————

上一篇:Java学习整理系列之Java枚举类型的使用http://blog.csdn.net/sup_heaven/article/details/35295851

本以为RED只是一个Color类的一个static final的实例而已。但后然发现不是这样的,先看看下面的一种枚举类型使用的代码。

package com.lxq.enumm;

public enum Color
{
RED{
public String getName(){
return "红色";
}
}
,GREEN{
public String getName(){
return "绿色";
}
}
,YELLOW{
public String getName(){
return "黄色";
}
};
public abstract String getName();
}


如果RED只是一个Color类的一个static final的实例,那么上面的代码就很让了费解了,为什么在枚举类型中可以有一个抽象方法,而每个枚举值可以对其重新实现?

别急,看了我对这个类的测试代码你就明白,测试代码如下:

import java.lang.reflect.Modifier;

public class EnumDemoFour{
public static void main(String[] args){
//打印该枚举值的名称
System.out.println(Color.RED.getName());
//打印该枚举值的类
System.out.println(Color.RED.getClass());
//打印该枚举值的类的父类
System.out.println(Color.RED.getClass().getSuperclass());
//打印该枚举值的类的父类的父类
System.out.println(Color.RED.getClass().getSuperclass().getSuperclass());
//打印该枚举类型的修饰符
System.out.println(Modifier.toString(Color.class.getModifiers()));
}
/*运行结果
红色
class com.lxq.enumm.Color$1
class com.lxq.enumm.Color
class java.lang.Enum
public abstract*/
}
该运行结果首先说明了RED和Color不是同一个类,而是前者是后者的一个子类;同时也说明了enum申明的其实是一个abstract的类,所以Color中可以有抽象方法。

那么,我们应该这么理解枚举类型的原理,首先enum Color继承了java.lang.Enum这个抽象类,但enum Color还是一个抽象类,所以它可以有抽象方法和非抽象方法。

而enum Color中的枚举值变量RED事实上上Color的一个匿名子类,所以它可以实现Color中的抽象方法,这样,当我们调用System.out.println(Color.RED.getName());

就是调用了匿名子类实现的方法。当然这些过程的很多事都有编译器等为我们做了,所以这里的代码很简单。

要是你不明白上面打印的内容,我再提供一个普通的类给你看看,还是类似的效果哦。

public abstract class TestInnerClass
{
public abstract void dosomething();
public static void main(String[] args){
TestInnerClass tic=new TestInnerClass(){
@Override
public void dosomething()
{
System.out.println("我是匿名子类");
}
};
tic.dosomething();
System.out.println(tic.getClass());
}
/*输出结果
我是匿名子类
class TestInnerClass$1
*/
}

最后再附上网上一个使用Java普通类模拟枚举的例子http://blog.csdn.net/xyang81/article/details/7185428,这个例子真的很好。

使用Java普通类模拟枚举
import java.util.HashMap;
import java.util.Map;

/**
* 模拟星期中的表示的天,每个星期天都表示一个对象
* 1、类中的每一个枚举成员都是该类的一个实例对象
* 2、构造函数私有化
* 3、提供操作枚举成员的抽象方法和静态方法
*/
public abstract class WeekDate {
/**
* 星期一
*/
public static final WeekDate MON = new WeekDate("MON",0) {//匿名子类
@Override
public WeekDate nextDay() {
return TUES;
}
@Override
public WeekDate preDay() {
return SUN;
}
@Override
public String toString() {
return "WeekDate.MON";
}
};

/**
* 星期二
*/
public static final WeekDate TUES = new WeekDate("TUES",1) {
@Override
public WeekDate nextDay() {
return WEDNES;
}
@Override
public WeekDate preDay() {
return MON;
}
@Override
public String toString() {
return "WeekDate.TUES";
}
};

/**
* 星期三
*/
public static final WeekDate WEDNES = new WeekDate("WEDNES",2) {
@Override
public WeekDate nextDay() {
return THURS;
}
@Override
public WeekDate preDay() {
return TUES;
}
@Override
public String toString() {
return "WeekDate.WEDNES";
}
};

/**
* 星期四
*/
public static final WeekDate THURS = new WeekDate("THURS",3) {
@Override
public WeekDate nextDay() {
return FRI;
}
@Override
public WeekDate preDay() {
return WEDNES;
}
@Override
public String toString() {
return "WeekDate.THURS";
}
};

/**
* 星期五
*/
public static final WeekDate FRI = new WeekDate("FRI",4){
@Override
public WeekDate nextDay() {
return SATUR;
}
@Override
public WeekDate preDay() {
return THURS;
}
@Override
public String toString() {
return "WeekDate.FRI";
}
};

/**
* 星期六
*/
public static final WeekDate SATUR = new WeekDate("SATUR",5){
@Override
public WeekDate nextDay() {
return SUN;
}
@Override
public WeekDate preDay() {
return FRI;
}
@Override
public String toString() {
return "WeekDate.SATUR";
}
};

/**
* 星期日
*/
public static final WeekDate SUN = new WeekDate("SUN",6){
@Override
public WeekDate nextDay() {
return MON;
}
@Override
public WeekDate preDay() {
return SATUR;
}
@Override
public String toString() {
return "WeekDate.SUN";
}
};

private static Map<String, WeekDate> valueMap = new HashMap<String, WeekDate>();

/**
* 枚举名称
*/
private final String name;

/**
* 枚举成员的顺序
*/
private final int ordinal;

private WeekDate(String name,int ordinal) {
this.name = name;
this.ordinal = ordinal;
}

/**
* 保存枚举成员
*/
private static WeekDate[] values = {
MON,TUES,WEDNES,THURS,FRI,SATUR,SUN
};

//初始化
static {
valueMap.put("MON", values[0]);
valueMap.put("TUES", values[1]);
valueMap.put("WEDNES", values[2]);
valueMap.put("THURS", values[3]);
valueMap.put("FRI", values[4]);
valueMap.put("SATUR", values[5]);
valueMap.put("SUN", values[6]);
}

/**
* 下一天
* @return
*/
public abstract WeekDate nextDay();

/**
* 前一天
* @return
*/
public abstract WeekDate preDay();

/**
* 枚举中的所有成员
* @return
*/
public static WeekDate[] values() {
return values;
}

/**
* 将一个字符串转换成一个枚举成员对象
* @param name 枚举名称
* @return 枚举对象
*/
public static WeekDate valueOf(String name) {
if (name.equalsIgnoreCase("MON")) {
return MON;
} else if (name.equalsIgnoreCase("TUES")) {
return TUES;
} else if (name.equalsIgnoreCase("WEDES")) {
return WEDNES;
} else if (name.equalsIgnoreCase("THURS")) {
return THURS;
} else if (name.equalsIgnoreCase("FRI")) {
return FRI;
} else if (name.equalsIgnoreCase("SATUR")) {
return SATUR;
} else if (name.equalsIgnoreCase("SUN")) {
return SUN;
} else {
throw new IllegalArgumentException("找不到" + name + "枚举类型!");
}
}

/**
* 优化字符串转枚举对象
* @param name 枚举名称
* @return 枚举对象
*/
public static WeekDate valueOf_2(String name) {
WeekDate value = valueMap.get(name.toUpperCase());
if (value == null) {
throw new IllegalArgumentException("找不到" + name + "枚举类型!");
}
return value;
}
public String getName() {
return name;
}
public int getOrdinal() {
return ordinal;
}
}


使用JDK5.0中提供的枚举特性

/**
* 枚举的应用
* 存储每周中的天份
*/
public enum WeekDateEnum {

MON {

@Override
public WeekDateEnum nextDay() {
return TUES;
}

@Override
public WeekDateEnum preDay() {
return SUN;
}

},  TUES {

@Override
public WeekDateEnum nextDay() {
return WEDNES;
}

@Override
public WeekDateEnum preDay() {
return MON;
}

},  WEDNES {

@Override
public WeekDateEnum nextDay() {
return THURS;
}

@Override
public WeekDateEnum preDay() {
return TUES;
}

},  THURS {

@Override
public WeekDateEnum nextDay() {
return FRI;
}

@Override
public WeekDateEnum preDay() {
return WEDNES;
}

},  FRI {

@Override
public WeekDateEnum nextDay() {
return SATUR;
}

@Override
public WeekDateEnum preDay() {
return THURS;
}

},  SATUR {

@Override
public WeekDateEnum nextDay() {
return SATUR;
}

@Override
public WeekDateEnum preDay() {
return FRI;
}

},  SUN {

@Override
public WeekDateEnum nextDay() {
return SATUR;
}

@Override
public WeekDateEnum preDay() {
return MON;
}

};

private WeekDateEnum() {}

/**
* 下一天
* @return
*/
public abstract WeekDateEnum nextDay();

/**
* 前一天
* @return
*/
public abstract WeekDateEnum preDay();

/**
* 枚举对象公共的toString方法,可以在case块中反馈自己想要返回的信息
*/
public String toString() {
switch (this) {
case MON:
return "WeekDateEnum.MON";
case TUES:
return "WeekDateEnum.TUES";
case WEDNES:
return "WeekDateEnum.WEDNES";
case THURS:
return "WeekDateEnum.THURS";
case FRI:
return "WeekDateEnum.FRI";
case SATUR:
return "WeekDateEnum.SATUR";
case SUN:
return "WeekDateEnum.SUN";
default:
return null;
}
}
}


枚举功能测试

/**
* 枚举功能测试
*/
public class EnumTest {

public static void main(String[] args) {

//使用普通JAVA类模拟枚举的应用
WeekDate weekDate = WeekDate.MON;       //获得一个枚举对象
//调用枚举中提供的方法
System.out.println(weekDate.nextDay());
System.out.println(weekDate.preDay());
System.out.println(weekDate.getName());
//获得枚举成员所在枚举成员列表中的位置
System.out.println(weekDate.getOrdinal());
//调用某一个枚举成员的方法
System.out.println(WeekDate.values()[0].preDay());
System.out.println("---------------遍历枚举成员,普通JAVA类模拟--------------------------");
for (WeekDate weekDate2 : WeekDate.values()) {
System.out.println(weekDate2);
}

System.out.println("\n=================================================================\n");

//使用JDK中提供的枚举特性功能应用
WeekDateEnum weekDateEnum = WeekDateEnum.MON;   //获得一个枚举对象
System.out.println(WeekDate.values().length);   //获得枚举成员数量
System.out.println(weekDateEnum.name());        //获得枚举的字符串名称
System.out.println(weekDateEnum.toString());    //打印枚举对象,已重写toString方法,默认打印枚举的名称
System.out.println(weekDateEnum.nextDay().ordinal());   //枚举成员列表中的位置
System.out.println(WeekDateEnum.valueOf("FRI").nextDay().ordinal());
System.out.println("---------------遍历枚举成员,使用JDK的枚举特性-------------------------");
for (WeekDateEnum enumDemo : WeekDateEnum.values()) {
System.out.println(enumDemo);
}

}

}


转载自:http://bbs.tianya.cn/post-414-50056-1.shtml

                  http://blog.csdn.net/sup_heaven/article/details/35559117

详情参考:http://blog.csdn.net/sup_heaven/article/details/35559117
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: