枚举类型
枚举常量 - 类型安全的常量 - 公共的静态的常量[final]属性 - 不可变的
jdk5.0开始提供的,以前的作用就是用来替代常量接口的
回忆常量接口 - 管理和维护项目中所有的常量的
public interface IConsts{ //最全的写法 - 接口中只能出现公开的静态的常量属性 //public static final int Car = 1; //精简的写法 //public int CAR = 1; //最精简的写法 int CAR = 1; }
关键字
使用enum关键字来定义一个枚举类型的
switch()中的参数类型可以是byte,short,int,char,enum,String,Byte,Short,Integer
特点
枚举常量,多个枚举常量,使用,隔开.最后一个枚举常量不需要使用逗号了.
如果最后一个枚举常量下面还有代码的话,那么需要使用分号隔开
允许存在构造 - 访问修饰符不能是公开的,protected
枚举类型是不能够被实例化的
枚举类型中可以提供普通属性
每个枚举类型默认都会自动继承java.lang.Enum
枚举类型中是可以存在抽象方法的,但是每个枚举常量必须要实现这个抽象方法
枚举类型不支持再去extends另外一个枚举类型
枚举类型不支持再去手动extends另外一个类
应用
- 如果某些属性具有固定的一些标识,就可以考虑定义成枚举类型.
- 比如:User实体类中维护了一个性别属性Gender[性别的枚举类型]
- 比如:Order实体类也会存在固定的状态 - 未付款,已付款.已下单未付款.取消订单等…
字符串->枚举常量
比如一个注册页面
用户名 tom 性别 单选按钮 男 单选按钮 女 确定按钮 未来.当点击确定按钮,后台接受到M,F - 后台接受到的数据都是字符串数据.
我们是不能够直接将这个字符串数据设置到实体类中的枚举常量属性上的.
每个自定义的枚举类型Gender.java默认都会继承java.lang.Enum
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { }
/** * 本类用来演示: String->枚举类型 */ public class StringToEnumDemo { public static void main(String[] args) { String gender = "F";//接受到页面传过来的数据 - 都是字符串类型 //字符串类型是不能够直接设置给实体类的那个枚举常量属性上的 //System.out.println(FString.class); // System.out.println(Integer.class); //Class<?> c = String.class; //System.out.println(c); //将字符串转换成枚举常量类型 //注意 - 字符串的值一定是和枚举常量的名称是保持一致的. //否则 - java.lang.IllegalArgumentException: // No enum constant tech.aistar.day14.enums.Gender.S Gender g = Enum.valueOf(Gender.class,gender); User user = new User(1,"tom",g); System.out.println(user); } }
枚举单例
- 它是effective java作者极力推荐的写法 - 枚举实例 - 1. 枚举类型天生就是线程安全的.2. 可以防止序列化或者反射来破坏这种单例的.
- 单例 - 保证在整个应用程序中,某个类的具体的实例永远只有1个.
- 什么时候需要把类做成单例的 - 这个类是一个重量级的类[类的创建和销毁的成本比较高.]
枚举常量是类型安全的常量???
public enum Gender{
F,M
}
因为使用枚举常量 - 第一步肯定是先加载枚举类型Gender - JVM通过类加载器[java.lang.ClassLoader]来加载枚举类型
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
//使用到了synchronized关键字
synchronized (getClassLoadingLock(name)) {
//.... 加载类,接口,枚举类型到JVM内存
}
}
很多线程都在调用Gender.M - > 都想尝试加载Gender枚举类型到JVM内存.但是由于loadClass加载的那块代码使用到了synchronized[锁]
只能由一个线程进去执行,并且只会执行一次.
由于每个枚举常量F 本质 就是 public static final Gender F = new Gender();//使用到了static关键字
//意味着当枚举类型一旦被加载了,枚举常量立即就会被分配空间以及初始化,并且机会只有一次.所以在整个内存中F常量也就只会存在一份.
//无论在哪里被调用.无论被多少给线程调用 - 大家拿到的都是同一个/同一份那个枚举常量.
具体的代码的实现
/**
* 本类用来演示: 枚举单例实现方式
*/
public class Singleton04 implements Serializable {
private Singleton04(){
System.out.println("私有化构造");
}
//内部的枚举类型
private enum SingletonEnum{
//枚举常量的实例
INSTANCE;//public static final SingletonEnum INSTANCE = new SingletonEnum();
//final修饰的变量要赋值
private final Singleton04 instance;
//枚举类型提供一个空参构造 - 枚举类型是不能new的
SingletonEnum(){
instance = new Singleton04();
}
//提供一个普通方法
public Singleton04 getInstance(){
return instance;
}
}
//外部类肯定是要提供一个方法,返回自己的一个唯一实例的
public static Singleton04 getInstance(){
return SingletonEnum.INSTANCE.getInstance();
}
}
class TestSingle04{
public static void main(String[] args) {
System.out.println(s1 == s2);//true
}
}
枚举单例写法
/**
* 本类用来演示: 枚举单例 - 最精简的写法 - 多线程安全的写法 - 饿汉模式
*/
public enum Singleton05 {
//public static final Singleton05 INSTANCE = new Singleton05();
INSTANCE;
Singleton05(){
System.out.println("比较繁琐的操作的事情,费时费力的事情");
}
public static Singleton05 getInstance(){
return INSTANCE;
}
}
对比饿汉模式的写法
public class Singleton01 {
//2. 初始化一个变量,该变量就是该类的唯一实例[对象]
private static Singleton01 instance = new Singleton01();
//1. 私有化构造
private Singleton01(){
//比较费时费力的代码,可能需要更多的时间
System.out.println("Singleton01...");
}
//3. 提供一个公开的静态的方法来返回这个类的唯一实例
public static Singleton01 getInstance(){
return instance;
}
}
枚举单例阻止序列化的破坏
-
序列化单例对象s1
-
反序列化封装到单例对象s3
-
s1 == s3 ;// 仍然为true
/** * 本类用来演示: 枚举单例 - 最精简的写法 * 只能用来玩枚举类型,才不会被序列化破坏!!! */ public enum Singleton05 { //public static final Singleton05 INSTANCE = new Singleton05(); INSTANCE; Singleton05(){ System.out.println("比较繁琐的操作的事情,费时费力的事情"); } public static Singleton05 getInstance(){ return INSTANCE; } } class TestSingleton05{ public static void main(String[] args) { Singleton05 s1 = Singleton05.getInstance(); Singleton05 s2 = Singleton05.getInstance(); System.out.println(s1 == s2);//true //序列化 try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("src/tech/aistar/design/ss.txt"))){ //对单例进行序列化操作 out.writeObject(s1); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //反序列化 try(ObjectInputStream in = new ObjectInputStream(new FileInputStream("src/tech/aistar/design/ss.txt"))){ Singleton05 s3 = (Singleton05) in.readObject(); System.out.println(s1 == s3);//true } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
枚举细节 - 抽象方法
枚举类型中是可以存在抽象方法的,但是每个枚举常量必须要实现这个抽象方法
/**
* 本类用来演示: 季节的枚举类型
*/
public enum Season{
//枚举类型中是可以存在抽象方法的,但是每个枚举常量必须要实现这个抽象方法
SPRING("春天"){
@Override
public Season next() {
return Season.SUMMER;
}
},
SUMMER("夏天") {
@Override
public Season next() {
return Season.AUTUMN;
}
},
AUTUMN("秋天") {
@Override
public Season next() {
return Season.WINTER;
}
},
WINTER("冬天") {
@Override
public Season next() {
return Season.SPRING;
}
};
private String sign;
Season(){
}
Season(String sign) {
this.sign = sign;
}
public String getSign() {
return sign;
}
//提供一个抽象方法 - 但是要求所有的枚举常量都要重写这个抽象方法
public abstract Season next();
}
class TestSeason{
public static void main(String[] args) {
Season s = Season.SPRING;
for(;;){
System.out.println(s+"-"+s.getSign());
try {
//让程序睡一秒钟
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
s = s.next();
}
}
}