网站制作要学多久,东莞做网站建设公司,wordpress菜单删不掉,厦门建设局网站首页枚举类型是Java 5中新增的特性#xff0c;它是一种特殊的数据类型#xff0c;之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束#xff0c;但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。当需要定义一组常量时#xff0c;强烈建议使…枚举类型是Java 5中新增的特性它是一种特殊的数据类型之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。当需要定义一组常量时强烈建议使用枚举类。
使用枚举类的条件类的对象是有限个确定的。例如星期类它的对象只有星期一…星期日七个而且是确定的此时就可以把星期类定义为一个枚举类又例如性别类它的对象只有男和女两个而且是确定的此时同样可以把性别类定义为一个枚举类还有诸如季节等这种类的对象是有限个确定的都可以定义为一个枚举类。
1、枚举类的实现
在JDK1.5之前还没有枚举类型如果想要使用枚举类需要我们去自定义。在自定义枚举类时需要注意以下几点
(1)枚举类对象的属性不应允许被改动所以应该使用 private final 进行修饰
(2)枚举类使用 private final 修饰的属性应该在构造器中为其赋值
(3)枚举类的构造器要私有化保证不能在类的外部创建其对象否则就不能确定对象的个数
(4)在枚举类内部创建的枚举类的实例(枚举)对象要声明为public static final。
下面就拿季节举例来自定义一个枚举类。
public class Season {//1.声明Season对象的属性又因为枚举类对象的属性不应允许被改动, 所以应该使用 private final修饰//枚举类的使用 private final 修饰的属性应该在构造器中为其赋值private final String seasonName;private final String seasonDesc;//2.私有化构造器保证不能在类的外部创建其对象否则就不能确定对象的个数private Season(String seasonName,String seasonDesc){this.seasonNameseasonName;this.seasonDescseasonDesc;}//3.提供当前枚举类的多个枚举对象又因为枚举类是不可变的常量类所以需要声明为public static finalpublic static final Season SPRINGnew Season(春天,鸟语花香);public static final Season SUMMERnew Season(夏天,夏日炎炎);public static final Season AUTUMNnew Season(秋天,秋高气爽);public static final Season WINNERnew Season(冬天,寒风瑟瑟);//其他需求1获取枚举类对象的属性//只需要提供属性的get方法即可但是不能提供set方法因为枚举类是不可变的常量类不能被修改public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}//其他需求2:打印对象提供toString方法即可Overridepublic String toString() {return Season{ seasonName seasonName \ , seasonDesc seasonDesc \ };}
}
public class SeasonTest {public static void main(String[] args) {Season spring Season.SPRING;System.out.println(spring); //Season{seasonName春天, seasonDesc鸟语花香}}
}在JDK 1.5 中新增了enum关键字用于定义枚举类但是在使用时需要注意以下几点
(1)使用 enum 定义的枚举类默认继承了 java.lang.Enum类因此不能再继承其他类
(2)使用 enum 定义的枚举类默认使用final进行修饰不可以被继承(也从侧面说明了它是一个常量类)
(3)枚举类的构造器只能使用 private 权限修饰符
(4)枚举类的所有实例必须在枚举类中显式列出多个对象之间使用,“隔开末尾使用”;结束。
列出的实例系统会自动添加 public static final 进行修饰
(5)必须在枚举类的第一行声明枚举类对象
(6)若枚举类只有一个枚举对象, 则可以作为一种单例模式的实现方式。
下面还是使用季节举例来自定义一个枚举类。
//使用enum关键字定义枚举类
public enum Season2 {//1.提供当前枚举类的对象多个对象之间使用,隔开,末尾使用;结束//系统默认使用public static final修饰SPRING(春天,鸟语花香),SUMMER(夏天,夏日炎炎),AUTUMN(秋天,秋高气爽),WINNER(冬天,寒风瑟瑟);//2.声明Season对象的属性又因为枚举类对象的属性不应允许被改动, 所以应该使用 private final修饰private final String seasonName;private final String seasonDesc;//3.枚举类的构造器只能使用 private 权限修饰符// 私有化构造器是为了保证不能在类的外部创建其对象否则就不能确定对象的个数private Season2(String seasonName, String seasonDesc){this.seasonNameseasonName;this.seasonDescseasonDesc;}//其他需求获取枚举类对象的属性//只需要提供属性的get方法即可但是不能提供set方法而且也不允许提供set方法因为枚举类是不可变的常量类不能被修改public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}
}
public class SeasonTest {public static void main(String[] args) {Season2 spring Season2.SPRING;System.out.println(spring);//SPRING}
}2、Enum类中的常用方法
values()方法返回枚举类型的对象数组该方法可以很方便地遍历所有的枚举值
//使用方法如下
Season2[] seasons Season2.values();
for (int i 0; i seasons.length; i) {System.out.println(seasons[i]);
}
valueOf(String str)可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。
如不是会报运行时异常IllegalArgumentException
//使用方法如下
Season2 spring Season2.valueOf(SPRING);
System.out.println(spring);//SPRING
toString()返回当前枚举类对象的名称
//使用方法如下
Season2 spring Season2.SPRING;
System.out.println(spring.toString());//SPRING3、使用enum关键字定义枚举类实现接口
枚举类和普通类一样可以实现一个或多个接口。枚举类实现接口分为两种情况
情况一若枚举类的所有枚举对象在调用实现的接口方法时呈现相同的行为方式则只要统一实现该方法即可此时与普通类实现接口一样没有任何区别。
public interface Show {void show();
}
//使用enum关键字定义枚举类
public enum Season2 implements Show{//1.提供当前枚举类的对象多个对象之间使用,隔开,末尾使用;结束//系统默认使用public static final修饰SPRING(春天,鸟语花香),SUMMER(夏天,夏日炎炎),AUTUMN(秋天,秋高气爽),WINNER(冬天,寒风瑟瑟);//2.声明Season对象的属性又因为枚举类对象的属性不应允许被改动, 所以应该使用 private final修饰private final String seasonName;private final String seasonDesc;//3.枚举类的构造器只能使用 private 权限修饰符// 私有化构造器是为了保证不能在类的外部创建其对象否则就不能确定对象的个数private Season2(String seasonName, String seasonDesc){this.seasonNameseasonName;this.seasonDescseasonDesc;}//其他需求获取枚举类对象的属性//只需要提供属性的get方法即可但是不能提供set方法而且也不允许提供set方法因为枚举类是不可变的常量类不能被修改public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}//重写show()方法与普通类实现接口一样没有任何区别Overridepublic void show() {System.out.println(一年四季春夏秋冬);}
}
public class SeasonTest {public static void main(String[] args) {Season2 spring Season2.SPRING;spring.show();Season2 summer Season2.SUMMER;summer.show();Season2 autumn Season2.AUTUMN;autumn.show();Season2 winner Season2.WINNER;winner.show();}
}运行结果
情况二若枚举类的每个枚举对象在调用实现的接口方法时需要呈现出不同的行为方式,则可以让每个枚举对象分别来实现该方法
public interface Show {void show();
}
//使用enum关键字定义枚举类
public enum Season2 implements Show{//1.提供当前枚举类的对象多个对象之间使用,隔开,末尾使用;结束//系统默认使用public static final修饰SPRING(春天,鸟语花香){//每个枚举对象分别来实现该方法Overridepublic void show() {System.out.println(春天是一个鸟语花香的季节);}},SUMMER(夏天,夏日炎炎){Overridepublic void show() {System.out.println(夏天是一个夏日炎炎的季节);}},AUTUMN(秋天,秋高气爽){Overridepublic void show() {System.out.println(秋天是一个秋高气爽的季节);}},WINNER(冬天,寒风瑟瑟){Overridepublic void show() {System.out.println(冬天是一个寒风瑟瑟的季节);}};//2.声明Season对象的属性又因为枚举类对象的属性不应允许被改动, 所以应该使用 private final修饰private final String seasonName;private final String seasonDesc;//3.枚举类的构造器只能使用 private 权限修饰符// 私有化构造器是为了保证不能在类的外部创建其对象否则就不能确定对象的个数private Season2(String seasonName, String seasonDesc){this.seasonNameseasonName;this.seasonDescseasonDesc;}//其他需求获取枚举类对象的属性//只需要提供属性的get方法即可但是不能提供set方法而且也不允许提供set方法因为枚举类是不可变的常量类不能被修改public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}
}
public class SeasonTest {public static void main(String[] args) {Season2 spring Season2.SPRING;spring.show();Season2 summer Season2.SUMMER;summer.show();Season2 autumn Season2.AUTUMN;autumn.show();Season2 winner Season2.WINNER;winner.show();}
}运行结果 4、枚举类对switch的语句的影响
Java1.5新增enum关键字的同时也扩大了switch的语句使用范围。Java1.5之前switch中的值只能是简单数据类型比如int、byte、short、char, 有了枚举类型之后就可以使用枚举类的对象了。同时在switch表达式中使用enum定义的枚举类的对象作为表达式时, case子句可以直接使用枚举对象的名字, 无需添加枚举类作为限定。这样一来程序的控制选择就变得更加的方便看下面的例子
public enum WeekDay {// 定义一周七天的枚举类型Monday,Tuesday, Wednesday ,Thursday,Friday,Saturday,Sunday;
}
class Test{public static void getDay(WeekDay weekDay){switch (weekDay){case Monday:System.out.println(Today is Monday);break;case Tuesday:System.out.println(Today is Tuesday);break;case Wednesday:System.out.println(Today is Wednesday);break;case Thursday:System.out.println(Today is Thursday);break;case Friday:System.out.println(Today is Friday);break;case Saturday:System.out.println(Today is Saturday);break;case Sunday:System.out.println(Today is Sunday);break;default:System.out.println(data error);}}public static void main(String[] args) {WeekDay sunday WeekDay.Sunday;getDay(sunday);WeekDay friday WeekDay.Friday;getDay(friday);}
}运行结果 对于这些枚举的日期JVM都会在运行期构造成出一个简单的对象实例一一对应。这些对象都有唯一的identity类似整型数值一样switch语句就会根据此来identity进行执行跳转。
5、枚举类的线程安全问题
枚举类天生线程就是安全的下面我们就来进行验证。
先写一个简单的枚举类还是以季节类为例
public enum Season {SPRING,SUMMER,AUTUMN,WINNER;
}然后我们使用反编译看看枚举类代码到底是怎么实现的反编译后的代码内容如下 public final class zzuli.edu.Season extends java.lang.Enumzzuli.edu.Season {public static final zzuli.edu.Season SPRING;public static final zzuli.edu.Season SUMMER;public static final zzuli.edu.Season AUTUMN;public static final zzuli.edu.Season WINNER;private static final zzuli.edu.Season[] $VALUES;public static zzuli.edu.Season[] values();public static zzuli.edu.Season valueOf(java.lang.String);private zzuli.edu.Season();static {};
}
由上述代码可知每一个枚举类的枚举对象都是被public static final 进行修饰的又因为被static修饰的属性在类加载的时候就会被加载而且只会被加载一次所以枚举类天生就是线程安全的。
6、枚举类实现单例模式
实现单例模式的方法有很多种但是使用枚举类实现单例模式是最好、最安全的一种方式这种方式也是Effective Java作者Josh Bloch 提倡的方式。因为它天生线程安全不仅能避免多线程同步问题而且还能防止使用反射重新创建新的对象。
使用枚举类实现单例模式非常简单如下所示
public enum EnumSingle {INSTANCE;public EnumSingle getInstance(){return INSTANCE;}
}
下面使用代码进行测试看创建的对象是否是单例
public class Test {public static void main(String[] args) throws NoSuchMethodException {EnumSingle instance1 EnumSingle.INSTANCE;EnumSingle instance2 EnumSingle.INSTANCE;System.out.println(instance1instance2);}
}运行结果 由运行结果可知成功使用了单例模式。接下来测试使用反射能不能创建新的实例对象。
先来看一下使用反射创建实例对象newInstance方法的源码
[图片上传失败…(image-8ba546-1633679059670)]
由newInstance方法的源码可知反射在通过newInstance方法创建对象时会先检查该类是否是枚举类如果是则会抛出IllegalArgumentException(“Cannot reflectively create enum objects”)异常导致使用反射创建对象失败。下面我们就来测试一下
先看一下枚举类的源码是有参构造函数还是无参构造函数编译后的源码如下 由源码可知枚举类的构造函数为无参构造函数下面就使用反射获取枚举类的无参构造函数看使用反射是否能创建新的实例对象。
public class Test2 {public static void main(String[] args) throws Exception {EnumSingle instance1 EnumSingle.INSTANCE;ConstructorEnumSingle declaredConstructor EnumSingle.class.getDeclaredConstructor();declaredConstructor.setAccessible(true);EnumSingle instance2 declaredConstructor.newInstance();System.out.println(instance1);System.out.println(instance2);}
}运行结果 由运行抛出的异常可知并不是我们预期的newInstance方法中的IllegalArgumentException(“Cannot reflectively create enum objects”)异常而是NoSuchMethodException异常说明枚举类中并没有无参构造函数编译后的源码欺骗了我们。
接着我们通过javap反编译看一下枚举类的代码
[图片上传失败…(image-182813-1633679059670)]
public final class zzuli.edu.enumTest.EnumSingle extends java.lang.Enumzzuli.edu.enumTest.EnumSingle {public static final zzuli.edu.enumTest.EnumSingle INSTANCE;private static final zzuli.edu.enumTest.EnumSingle[] $VALUES;public static zzuli.edu.enumTest.EnumSingle[] values();public static zzuli.edu.enumTest.EnumSingle valueOf(java.lang.String);private zzuli.edu.enumTest.EnumSingle();public zzuli.edu.enumTest.EnumSingle getInstance();static {};
}
由上述反编译后的枚举类代码可知反编译后的枚举类中存在的仍然是无参构造函数说明反编译后的代码仍然骗了我们。下面我们就使用更专业的工具jad来进行反编译。
使用jad反编译后的枚举类源码如下所示
public final class EnumSingle extends Enum
{public static EnumSingle[] values(){return (EnumSingle[])$VALUES.clone();}public static EnumSingle valueOf(String name){return (EnumSingle)Enum.valueOf(zzuli/edu/enumTest/EnumSingle, name);}private EnumSingle(String s, int i){super(s, i);}public EnumSingle getInstance(){return INSTANCE;}public static final EnumSingle INSTANCE;private static final EnumSingle $VALUES[];static {INSTANCE new EnumSingle(INSTANCE, 0);$VALUES (new EnumSingle[] {INSTANCE});}
}
由jad反编译后的源码可知枚举类的构造函数为有参构造函数 EnumSingle(String s, int i)并且有两个参数。
下面我们就使用反射获取枚举类的有参构造函数看使用反射是否能创建新的实例对象。
public class Test3 {public static void main(String[] args) throws Exception {EnumSingle instance1 EnumSingle.INSTANCE;ConstructorEnumSingle declaredConstructor EnumSingle.class.getDeclaredConstructor(String.class,int.class);declaredConstructor.setAccessible(true);EnumSingle instance2 declaredConstructor.newInstance();System.out.println(instance1);System.out.println(instance2);}运行结果 由运行结果可知与我们预期的异常一样抛出了IllegalArgumentException(“Cannot reflectively create enum objects”)异常。 此时说明使用枚举类实现单例模式是十分安全的使用反射进行暴力破解也不能创建新的对象。