在我们实际开发java项目过程中,突然有一天"领导老王"给了个任务, 公司系统需要支持商品管理的需求
比如水果有:苹果,香蕉,葡萄等等,电子产品有:电脑,手机,摄像机等等
我们一般新建商品类Goods
:
public class Goods { /** * 商品名称 */ private String name; /** * 商品类型 */ private Integer type; public Goods(String name, Integer type) { this.name = name; this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } }
然后我们就直接可以使用它:
public class GoodsTest { public static void main(String[] args) throws InterruptedException { Goods goods = new Goods("水果",1);//1代表苹果,2:香蕉,3:葡萄 System.out.println(goods.getName()); } }
但是有个问题,业务代码不清晰,有时候开发人员并不知道1、2、3
代表什么意思,而且在业务代码层里面直接写数字或者字符串也是非常危险的时,我们需要一种方案,既能将相关的状态,类型放在一起,又可以限制类的输入值,提升项目的安全性
我们可以使用接口常量来解决上面的问题
public interface StatusContentFace { public static final String fruit = "fruit"; public static final Integer apple = 1; public static final Integer banana = 2; public static final Integer grape = 3; //========================== public static final String eleProduct = "eleProduct"; public static final Integer computer = 101; public static final Integer phone = 102; public static final Integer camera = 103; }
我们再来看下测试类:
public class GoodsTest1 { public static void main(String[] args) throws InterruptedException { Goods goods = new Goods(StatusContentFace.fruit,StatusContentFace.apple); Goods goods_2 = new Goods(StatusContentFace.eleProduct,StatusContentFace.computer); System.out.println(goods.getName()); System.out.println(goods_2.getName()); } }
这样能够让相关的常量都在同一个接口文件中,接口常量,写起来比较简洁,但是为了让其他人知道每个常量的含义,最好写上注释。
但它同时有个问题,由于java中接口是支持多继承的
一般不建议用的,但接口常量也不是一无是处的,可以通过内部接口
来实现分组
效果
public class GoodsTest2 { public static void main(String[] args) throws InterruptedException { Goods goods = new Goods(Fruit.type,Fruit.banana); Goods goods_2 = new Goods(EleProduct.type,EleProduct.phone); System.out.println(goods.getName()); System.out.println(goods_2.getName()); } //常量分组 public interface Fruit { String type = "fruit"; Integer apple = 1; Integer banana = 2; Integer grape = 3; } public interface EleProduct { String type = "eleProduct"; Integer computer = 101; Integer phone = 102; Integer camera = 103; } }
这样我们可以把相关的常量都归为一类,更加简洁明了
我们一般常用的是类常量
方式:
public final class StatusConstant { private StatusConstant() {} //防止该类实例化 public static final String fruit = "fruit"; public static final Integer apple = 1; public static final Integer banana = 2; public static final Integer grape = 3; //========================== public static final String eleProduct = "eleProduct"; public static final Integer computer = 101; public static final Integer phone = 102; public static final Integer camera = 103; }
注意:一般用final关键字修饰 class 防止其被继承,并将其构造函数 private 化,防止被实例化
测试类:
public class GoodsTest3 { public static void main(String[] args) throws InterruptedException { Goods goods = new Goods(StatusConstant.fruit, StatusConstant.banana); Goods goods_2 = new Goods(StatusConstant.eleProduct, StatusConstant.phone); System.out.println(goods.getName()); System.out.println(goods_2.getName()); } }
我们可以发现类常量
的方式,的确很方便,也没有接口常量多继承
的烦恼。但是她所能承接的信息,维度不够,只能一个字段的去承接信息,然而当项目复杂的话,我们希望往往其能承接更多维度的信息,类似于对象一样,拥有更多的属性
{ "name": ..., "type": ..., ... }
这时候,我们本文的主角,枚举就闪亮登场了!
枚举是一种特殊的类,所有的枚举类都是Enum类的子类,就类似Object类一样,由于java类是单继承的,所以不能在继承其他类或者枚举了。
枚举变量不能使用其他的数据,只能使用枚举中常量赋值。能提高程序的安全性。
格式:
public enum 枚举名{ //枚举的取值范围 }
我们先定义一个枚举类,来定义常量:
public enum ContentEnums { Apple(1,"苹果"), Banana(2,"香蕉"), Grape(3,"葡萄"), Computer(101,"电脑"), Phone(102,"手机"), Camera(103,"摄像机"), Fruit(10010,"fruit"), EleProduct(10020,"eleProduct"); private Integer code; private String desc; ContentEnums(Integer code, String desc) { this.code = code; this.desc = desc; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } }
测试类:
public class GoodsTest4 { public static void main(String[] args) throws InterruptedException { Goods goods = new Goods(ContentEnums.Fruit.getDesc(), ContentEnums.Apple.getCode()); Goods goods_2 = new Goods(ContentEnums.EleProduct.getDesc(), ContentEnums.Phone.getCode()); System.out.println(goods.getName()); System.out.println(goods_2.getName()); } }
看到这大家可能就有疑问了,枚举
和常量类
相比,有什么优点吗?
if/else
ContentEnums content = ContentEnums.Apple; switch (content) { case Apple: System.out.println("苹果"); break; case Banana: System.out.println("香蕉"); break; case Grape: System.out.println("葡萄"); break; default: System.out.println("未找到匹配类型"); }
public enum MethodEnums { VERSION { @Override String getInfo() { return System.getProperty("java.version"); } }, DATE_TIME { @Override String getInfo() { return DateFormat.getDateInstance() .format(new Date()); } }; abstract String getInfo(); public static void main(String[] args) { for(MethodEnums csm : values()) { System.out.println(csm.getInfo()); } } }
结果:
1.8.0_271
2022-9-21
除了抽象方法,普通方法也是可以的,这里就不展示了
我们可以通过枚举来将相关的状态,类型放在一起,文章一开头,但我们怎么才能限制类的输入值呢?其实很简单,别被绕进去,我们只需将输入类型 改为指定的枚举即可
我们改造一下Goods类:
public class Goods { /** * 商品名称 */ private String name; /** * 商品类型 */ private Integer type; // public Goods(String name, Integer type) { // this.name = name; // this.type = type; // } public Goods() {//防止外部实例化 } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public static Goods addGoods(ContentEnums enums){ Goods goods = new Goods(); goods.setName(enums.getDesc()); goods.setType(enums.getCode()); return goods; } }
测试类:
public class GoodsTest5 { public static void main(String[] args) throws InterruptedException { Goods goods = Goods.addGoods(ContentEnums.Apple); Goods goods_2 = Goods.addGoods(ContentEnums.Computer); System.out.println(goods.getName()); System.out.println(goods_2.getName()); } }
这样,我们就可以限制创建对象时的输入值类型了
可以使用==
来比较 enum 实例,编译器会自动为你提供equals()
和 hashCode()
方法。Enum
类实现了 Comparable
接口,所以它具有 compareTo()
方法。同时,它还实现了 Serializable
接口。
枚举类型是天生线程安全的,并且只会装载一次,我们可以利用了枚举的这个特性来实现单例
public enum SingleInstance { INSTANCE; public void funDo() { System.out.println("doSomething"); } }
使用方式:SingleInstance.INSTANCE.funDo()
这种方法充分 利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。写法也极其简洁。