-
2021-02-12 16:58:48
澄清一下,你的问题不是关于“
Java中的动态类加载”,而是关于动态类枚举 – 你知道如何加载类,你只是不知道你想要什么类.
从该页面开始,这里有一些应该有效的示例代码:
public static Class[] getClasses(String pckgname)
throws ClassNotFoundException {
ArrayList classes = new ArrayList();
// Get a File object for the package
File directory = null;
try {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
String path = '/' + pckgname.replace('.', '/');
URL resource = cld.getResource(path);
if (resource == null) {
throw new ClassNotFoundException("No resource for " + path);
}
directory = new File(resource.getFile());
} catch (NullPointerException x) {
throw new ClassNotFoundException(pckgname + " (" + directory
+ ") does not appear to be a valid package");
}
if (directory.exists()) {
// Get the list of the files contained in the package
String[] files = directory.list();
for (int i = 0; i < files.length; i++) {
// we are only interested in .class files
if (files[i].endsWith(".class")) {
// removes the .class extension
classes.add(Class.forName(pckgname + '.'
+ files[i].substring(0, files[i].length() - 6)));
}
}
} else {
throw new ClassNotFoundException(pckgname
+ " does not appear to be a valid package");
}
Class[] classesA = new Class[classes.size()];
classes.toArray(classesA);
return classesA;
}
更多相关内容 -
java 枚举遍历键值对 属性类Properties 类加载器
2011-12-26 17:21:22java 枚举遍历键值对 属性类Properties 类加载器 -
java-枚举 - 啃不动地大坚果的个人空间 - OSCHINA - 中文开源技术交流社区
2021-03-10 04:29:46枚举不能被继承,但可以通过接口扩充功能所有的enum都继承自java.lang.Enumle类,由于java不支持多重继承,所以enum不能在继承其他类。3.除了不能继承,枚举可以看作是一个普通类4.可以使用接口来组织枚举interface ...1.枚举的常用方法
enum.values() 获取所有枚举值
enum.ordinal() 获取枚举的序号值
2.枚举不能被继承,但可以通过接口扩充功能
所有的enum都继承自java.lang.Enumle类,
由于java不支持多重继承,所以enum不能在继承其他类。
3.除了不能继承,枚举可以看作是一个普通类
4.可以使用接口来组织枚举
interface Mycategory{
enum BOOK{BOOK1,BOOK2,BOOK3};
enum NOTE{NOTE1,NOTE2,NOTE3};
}
5.EnumSet
EnumSet的基础是long,一个long有64位,一个enum实例只需要一位bit就可以表示是否存在。
EnumSet中数据的输出顺序与add顺序无关,其会按照枚举的定义顺序输出内容。
EnumSet操作与其他set相似
声明EnumSet EnumSet set= EnumSet.noneOf(BOOK.class);
EnumSet set= EnumSet.noneOf(BOOK.class);
set.add(BOOK.BOOK2);
System.out.println(set);
System.out.println(EnumSet.complementOf(set));
6.EnumMap
EnumMap是一种特殊Map,它的key必须是enum。
由于枚举的限制,其内部可以实现类似数组的数据存放,速度可以很快。
enum的每个实例作为一个键在EnumMap中都是存在的,如果没有显式的赋值,
则key对应的值为null。
EnumMap中数据的输出顺序与put顺序无关,其会按照枚举的定义顺序输出内容。
EnumMap的声明方式 EnumMap map= new EnumMap(BOOK.class);
EnumMap map= new EnumMap(BOOK.class);
map.put(BOOK.BOOK2, "22");
7.枚举实质
①枚举类型实际上会被Java编译器转换为一个对应的类,这个类继承了Java API中的java.lang.Enum类。
②Enum类有两个实例变量name和ordinal,在构造方法中需要传递,name(), toString(), ordinal(), compareTo(), equals()方法都是由Enum类根据其实例变量name和ordinal实现的。values和valueOf方法是编译器给每个枚举类型自动添加的
enum Color{
RED(255, 0, 0), BLUE(0, 255, 0), GREEN(0, 0, 255);
}
//其实际反编译结果为:
public final class Color extends java.lang.Enum {
public static final Color RED;
public static final Color BLUE;
public static final Color BLACK;
public static final Color YELLOW;
public static final Color GREEN;
public static Color[] values();
public static Color valueOf(java.lang.String);
static {};
}
-
Java枚举详解
2020-12-05 21:48:21枚举是一个被命名的整型...而使用 Java 枚举类型 enum 可以更贴近地表示这种常量。声明枚举 声明枚举时必须使用 enum 关键字,然后定义枚举的名称、可访问性、基础类型和成员等。枚举声明的语法如下: enum-modifier枚举的场景及历史:
枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。枚举在曰常生活中很常见,例如一个人的性别只能是“男”或者“女”,一周的星期只能是 7 天中的一个等。类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型。
在 JDK 1.5 之前没有枚举类型,那时候一般用接口常量来替代。而使用 Java 枚举类型 enum 可以更贴近地表示这种常量。
一、声明定义枚举
声明枚举时必须使用 enum 关键字,然后定义枚举的名称、可访问性、基础类型和成员等。枚举声明的语法如下:
enum-modifiers enum enumname:enum-base { enum-body, }
其中:
enum-modifiers 表示枚举的修饰符主要包括 public、private 和 internal;
enumname 表示声明的枚举名称;
enum-base 表示基础类型;如果没有显式地声明基础类型的枚举,那么意味着它所对应的基础类型是 int。
enum-body 表示枚举的成员,它是枚举类型的命名常数。任意两个枚举成员不能具有相同的名称,且它的常数值必须在该枚举的基础类型的范围之内,多个枚举成员之间使用逗号分隔。
例 1:
下面代码定义了一个表示性别的枚举类型 SexEnum ,一个表示颜色的枚举类型 Color。
public enum SexEnum { male,female; } public enum Color { RED,BLUE,GREEN,BLACK; }
之后便可以通过枚举类型名直接引用常量,如 SexEnum.male、Color.RED。
使用枚举还可以使 switch 语句的可读性更强,例如以下示例代码:
enum Signal { //定义一个枚举类型 GREEN,YELLOW,RED } public class TrafficLight { Signal color=Signal.RED; public void change() { switch(color) { case RED: color=Signal.GREEN; break; case YELLOW: color=Signal.RED; break; case GREEN: color=Signal.YELLOW; break; } } }
二、枚举类详解
Java 中的每一个枚举都继承自 java.lang.Enum 类。当定义一个枚举类型时,每一个枚举类型成员都可以看作是 Enum 类的实例,这些枚举成员默认都被 final、public, static 修饰,当使用枚举类型成员时,直接使用枚举名称调用成员即可。
所有枚举实例都可以调用 Enum 类的方法,常用方法如表 1 所示。
表1 Enum类的常用方法
方法名称 描述 values() 以数组形式返回枚举类型的所有成员 valueOf() 将普通字符串转换为枚举实例 compareTo() 比较两个枚举成员在定义时的顺序 ordinal() 获取枚举成员的索引位置 例 1:
通过调用枚举类型实例的 values() 方法可以将枚举的所有成员以数组形式返回,也可以通过该方法获取枚举类型的成员。
下面的示例创建一个包含 3 个成员的枚举类型 Signal,然后调用 values() 方法输出这些成员。
enum Signal { //定义一个枚举类型 GREEN,YELLOW,RED; } public static void main(String[] args) { for(int i=0;i<Signal.values().length;i++) { System.out.println("枚举成员:"+Signal.values()[i]); } }
输出结果如下:
枚举成员:GREEN 枚举成员:YELLOW 枚举成员:RED
例 2:
创建一个示例,调用 valueOf() 方法获取枚举的一个成员,再调用 compareTo() 方法进行比较,并输出结果。具体实现代码如下:
public class TestEnum { public enum Sex { //定义一个枚举 male,female; } public static void main(String[] args) { compare(Sex.valueOf("male")); //比较 } public static void compare(Sex s) { for(int i=0;i<Sex.values().length;i++) { System.out.println(s+"与"+Sex.values()[i]+"的比较结果是:"+s.compareTo(Sex.values()[i])); } } }
上述代码中使用 Sex.valueOf(“male”) 取出枚举成员 male 对应的值,再将该值与其他枚举成员进行比较。
最终输出结果如下:
male与male的比较结果是:0
male与female的比较结果是:-1
例 3 :
通过调用枚举类型实例的 ordinal() 方法可以获取一个成员在枚举中的索引位置。下面的示例创建一个包含 3 个成员的枚举类型 Signal,然后调用 ordinal() 方法输出成员及对应索引位置。
具体实现代码如下:
public class TestEnum1 { enum Signal { //定义一个枚举类型 GREEN,YELLOW,RED; } public static void main(String[] args) { for(int i=0;i<Signal.values().length;i++) { System.out.println("索引"+Signal.values()[i].ordinal()+",值:"+Signal.values()[i]); } } }
输出结果如下:
索引0,值:GREEN 索引1,值:YELLOW 索引2,值:RED
三、为枚举添加方法
Java 为枚举类型提供了一些内置的方法,同时枚举常量也可以有自己的方法。此时要注意必须在枚举实例的最后一个成员后添加分号,而且必须先定义枚举实例。
例 4:
下面的代码创建了一个枚举类型 WeekDay,而且在该类型中添加了自定义的方法。
enum WeekDay { Mon("Monday"), Tue("Tuesday"), Wed("Wednesday"), Thu("Thursday"), Fri("Friday"), Sat("Saturday"), Sun("Sunday"); //以上是枚举的成员,必须先定义,而且使用分号结束 private final String day; private WeekDay(String day) { this.day=day; } public static void printDay(int i) { switch(i) { case 1: System.out.println(WeekDay.Mon); break; case 2: System.out.println(WeekDay.Tue); break; case 3: System.out.println(WeekDay.Wed); break; case 4: System.out.println(WeekDay.Thu); break; case 5: System.out.println(WeekDay.Fri); break; case 6: System.out.println(WeekDay.Sat); break; case 7: System.out.println(WeekDay.Sun); break; default: System.out.println("wrong number!"); } } public String getDay() { return day; } }
上面代码创建了 WeekDay 枚举类型,下面遍历该枚举中的所有成员,并调用 printDay() 方法。示例代码如下:
public static void main(String[] args) { for(WeekDay day:WeekDay.values()) { System.out.println(day+"====>"+day.getDay()); } WeekDay.printDay(5); }
输出结果如下:
Mon====>Monday Tue====>Tuesday Wed====>Wednesday Thu====>Thursday Fri====>Friday Sat====>Saturday Sun====>Sunday Fri
Java 中的 enum 还可以跟 Class 类一样覆盖基类的方法。下面示例代码创建的 Color 枚举类型覆盖了 toString() 方法。
public class Test { public enum Color { RED("红色",1),GREEN("绿色",2),WHITE("白色",3),YELLOW("黄色",4); //成员变量 private String name; private int index; //构造方法 private Color(String name,int index) { this.name=name; this.index=index; } //覆盖方法 @Override public String toString() { return this.index+"-"+this.name; } } public static void main(String[] args) { System.out.println(Color.RED.toString()); //输出:1-红色 } }
四、EnumMap 与 EnumSet
为了更好地支持枚举类型,java.util 中添加了两个新类:EnumMap 和 EnumSet。使用它们可以更高效地操作枚举类型。
EnumMap 类
EnumMap 是专门为枚举类型量身定做的 Map 实现。虽然使用其他的 Map(如 HashMap)实现也能完成枚举类型实例到值的映射,但是使用 EnumMap 会更加高效。
HashMap 只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以 EnumMap 使用数组来存放与枚举类型对应的值,使得 EnumMap 的效率非常高。
例5:
下面是使用 EnumMap 的一个代码示例。枚举类型 DataBaseType 里存放了现在支持的所有数据库类型。针对不同的数据库,一些数据库相关的方法需要返回不一样的值,例如示例中 getURL() 方法。//定义数据库类型枚举 public enum DataBaseType { MYSQUORACLE,DB2,SQLSERVER } //某类中定义的获取数据库URL的方法以及EnumMap的声明 private EnumMap<DataBaseType,String>urls=new EnumMap<DataBaseType,String>(DataBaseType.class); public DataBaseInfo() { urls.put(DataBaseType.DB2,"jdbc:db2://localhost:5000/sample"); urls.put(DataBaseType.MYSQL,"jdbc:mysql://localhost/mydb"); urls.put(DataBaseType.ORACLE,"jdbc:oracle:thin:@localhost:1521:sample"); urls.put(DataBaseType.SQLSERVER,"jdbc:microsoft:sqlserver://sql:1433;Database=mydb"); } //根据不同的数据库类型,返回对应的URL //@param type DataBaseType 枚举类新实例 //@return public String getURL(DataBaseType type) { return this.urls.get(type); }
在实际使用中,EnumMap 对象 urls 往往是由外部负责整个应用初始化的代码来填充的。这里为了演示方便,类自己做了内容填充。
从本例中可以看出,使用 EnumMap 可以很方便地为枚举类型在不同的环境中绑定到不同的值上。本例子中 getURL 绑定到 URL 上,在其他的代码中可能又被绑定到数据库驱动上去。
EnumSet 类
EnumSet 是枚举类型的高性能 Set 实现,它要求放入它的枚举常量必须属于同一枚举类型。EnumSet 提供了许多工厂方法以便于初始化,如表 2 所示。
表2 EnumSet 类的常用方法
方法名称 描述 allOf(Class element type) 创建一个包含指定枚举类型中所有枚举成员的 EnumSet 对象 complementOf(EnumSet s) 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象,并包含所有 s 中未包含的枚举成员 copyOf(EnumSet s) 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象,并与 s 包含相同的枚举成员 noneOf(<Class elementType) 创建指定枚举类型的空 EnumSet 对象 of(E first,e…rest) 创建包含指定枚举成员的 EnumSet 对象 range(E from ,E to) 创建一个 EnumSet 对象,该对象包含了 from 到 to 之间的所有枚举成员 EnumSet 作为 Set 接口实现,它支持对包含的枚举常量的遍历。
for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) { doSomeThing(op); }
---------------------------------------------------------------------------------------
枚举的 7 种使用方法及为什么建议你使用枚举
枚举是 JDK 1.5 新增的数据类型,使用枚举我们可以很好的描述一些特定的业务场景,比如一年中的春、夏、秋、冬,还有每周的周一到周天,还有各种颜色,以及可以用它来描述一些状态信息,比如错误码等。
枚举类型不止存在在 Java 语言中,在其它语言中也都能找到它的身影,例如 C# 和 Python 等,但我发现在实际的项目中使用枚举的人很少,所以本文就来聊一聊枚举的相关内容,好让朋友们对枚举有一个大概的印象,这样在编程时起码还能想到有“枚举”这样一个类型。
本文的结构目录如下:
枚举的 7 种使用方法
很多人不使用枚举的一个重要的原因是对枚举不够熟悉,那么我们就先从枚举的 7 种使用方法说起。
用法一:常量
在 JDK 1.5 之前,我们定义常量都是
public static final...
,但有了枚举,我们就可以把这些常量定义成一个枚举类了,实现代码如下:public enum ColorEnum { RED, GREEN, BLANK, YELLOW }
用法二:switch
将枚举用在 switch 判断中,使得代码可读性更高了,实现代码如下:
enum ColorEnum { GREEN, YELLOW, RED } public class ColorTest { ColorEnum color = ColorEnum.RED; public void change() { switch (color) { case RED: color = ColorEnum.GREEN; break; case YELLOW: color = ColorEnum.RED; break; case GREEN: color = ColorEnum.YELLOW; break; } } }
用法三:枚举中增加方法
我们可以在枚举中增加一些方法,让枚举具备更多的特性,实现代码如下:
public class EnumTest { public static void main(String[] args) { ErrorCodeEnum errorCode = ErrorCodeEnum.SUCCESS; System.out.println("状态码:" + errorCode.code() + " 状态信息:" + errorCode.msg()); } } enum ErrorCodeEnum { SUCCESS(1000, "success"), PARAM_ERROR(1001, "parameter error"), SYS_ERROR(1003, "system error"), NAMESPACE_NOT_FOUND(2001, "namespace not found"), NODE_NOT_EXIST(3002, "node not exist"), NODE_ALREADY_EXIST(3003, "node already exist"), UNKNOWN_ERROR(9999, "unknown error"); private int code; private String msg; ErrorCodeEnum(int code, String msg) { this.code = code; this.msg = msg; } public int code() { return code; } public String msg() { return msg; } public static ErrorCodeEnum getErrorCode(int code) { for (ErrorCodeEnum it : ErrorCodeEnum.values()) { if (it.code() == code) { return it; } } return UNKNOWN_ERROR; } }
以上程序的执行结果为:状态码:1000 状态信息:success
用法四:覆盖枚举方法
我们可以覆盖一些枚举中的方法用于实现自己的业务,比如我们可以覆盖
toString()
方法,实现代码如下:public class EnumTest { public static void main(String[] args) { ColorEnum colorEnum = ColorEnum.RED; System.out.println(colorEnum.toString()); } } enum ColorEnum { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4); // 成员变量 private String name; private int index; // 构造方法 private ColorEnum(String name, int index) { this.name = name; this.index = index; } //覆盖方法 @Override public String toString() { return this.index + ":" + this.name; } }
以上程序的执行结果为:1:红色
用法五:实现接口
枚举类可以用来实现接口,但不能用于继承类,因为枚举默认继承了
java.lang.Enum
类,在 Java 语言中允许实现多接口,但不能继承多个父类,实现代码如下:public class EnumTest { public static void main(String[] args) { ColorEnum colorEnum = ColorEnum.RED; colorEnum.print(); System.out.println("颜色:" + colorEnum.getInfo()); } } interface Behaviour { void print(); String getInfo(); } enum ColorEnum implements Behaviour { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4); private String name; private int index; private ColorEnum(String name, int index) { this.name = name; this.index = index; } @Override public void print() { System.out.println(this.index + ":" + this.name); } @Override public String getInfo() { return this.name; } }
以上程序的执行结果为:
1:红色
颜色:红色用法六:在接口中组织枚举类
我们可以在一个接口中创建多个枚举类,用它可以很好的实现“多态”,也就是说我们可以将拥有相同特性,但又有细微实现差别的枚举类聚集在一个接口中,实现代码如下:
public class EnumTest { public static void main(String[] args) { // 赋值第一个枚举类 ColorInterface colorEnum = ColorInterface.ColorEnum.RED; System.out.println(colorEnum); // 赋值第二个枚举类 colorEnum = ColorInterface.NewColorEnum.NEW_RED; System.out.println(colorEnum); } } interface ColorInterface { enum ColorEnum implements ColorInterface { GREEN, YELLOW, RED } enum NewColorEnum implements ColorInterface { NEW_GREEN, NEW_YELLOW, NEW_RED } }
以上程序的执行结果为:
RED
NEW_RED用法七:使用枚举集合
在 Java 语言中和枚举类相关的,还有两个枚举集合类
java.util.EnumSet
和java.util.EnumMap
,使用它们可以实现更多的功能。使用
EnumSet
可以保证元素不重复,并且能获取指定范围内的元素,示例代码如下:import java.util.ArrayList; import java.util.EnumSet; import java.util.List; public class EnumTest { public static void main(String[] args) { List<ColorEnum> list = new ArrayList<ColorEnum>(); list.add(ColorEnum.RED); list.add(ColorEnum.RED); // 重复元素 list.add(ColorEnum.YELLOW); list.add(ColorEnum.GREEN); // 去掉重复数据 EnumSet<ColorEnum> enumSet = EnumSet.copyOf(list); System.out.println("去重:" + enumSet); // 获取指定范围的枚举(获取所有的失败状态) EnumSet<ErrorCodeEnum> errorCodeEnums = EnumSet.range(ErrorCodeEnum.ERROR, ErrorCodeEnum.UNKNOWN_ERROR); System.out.println("所有失败状态:" + errorCodeEnums); } } enum ColorEnum { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLOW("黄色", 4); private String name; private int index; private ColorEnum(String name, int index) { this.name = name; this.index = index; } } enum ErrorCodeEnum { SUCCESS(1000, "success"), ERROR(2001, "parameter error"), SYS_ERROR(2002, "system error"), NAMESPACE_NOT_FOUND(2003, "namespace not found"), NODE_NOT_EXIST(3002, "node not exist"), NODE_ALREADY_EXIST(3003, "node already exist"), UNKNOWN_ERROR(9999, "unknown error"); private int code; private String msg; ErrorCodeEnum(int code, String msg) { this.code = code; this.msg = msg; } public int code() { return code; } public String msg() { return msg; } }
以上程序的执行结果为:
去重:[RED, GREEN, YELLOW]
所有失败状态:[ERROR, SYS_ERROR, NAMESPACE_NOT_FOUND, NODE_NOT_EXIST, NODE_ALREADY_EXIST, UNKNOWN_ERROR]EnumMap
与HashMap
类似,不过它是一个专门为枚举设计的Map
集合,相比HashMap
来说它的性能更高,因为它内部放弃使用链表和红黑树的结构,采用数组作为数据存储的结构。EnumMap
基本使用示例如下:import java.util.EnumMap; public class EnumTest { public static void main(String[] args) { EnumMap<ColorEnum, String> enumMap = new EnumMap<>(ColorEnum.class); enumMap.put(ColorEnum.RED, "红色"); enumMap.put(ColorEnum.GREEN, "绿色"); enumMap.put(ColorEnum.BLANK, "白色"); enumMap.put(ColorEnum.YELLOW, "黄色"); System.out.println(ColorEnum.RED + ":" + enumMap.get(ColorEnum.RED)); } } enum ColorEnum { RED, GREEN, BLANK, YELLOW; }
以上程序的执行结果为:RED:红色
使用注意事项
阿里《Java开发手册》对枚举的相关规定如下,我们在使用时需要稍微注意一下。
【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。
【参考】枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。
假如不使用枚举
在枚举没有诞生之前,也就是 JDK 1.5 版本之前,我们通常会使用
int
常量来表示枚举,实现代码如下:public static final int COLOR_RED = 1; public static final int COLOR_BLUE = 2; public static final int COLOR_GREEN = 3;
但是使用
int
类型可能存在两个问题:第一,
int
类型本身并不具备安全性,假如某个程序员在定义int
时少些了一个final
关键字,那么就会存在被其他人修改的风险,而反观枚举类,它“天然”就是一个常量类,不存在被修改的风险(原因详见下半部分);第二,使用
int
类型的语义不够明确,比如我们在控制台打印时如果只输出 1...2...3 这样的数字,我们肯定不知道它代表的是什么含义。那有人就说了,那就使用常量字符呗,这总不会还不知道语义吧?实现示例代码如下:
public static final String COLOR_RED = "RED"; public static final String COLOR_BLUE = "BLUE"; public static final String COLOR_GREEN = "GREEN";
但是这样同样存在一个问题,有些初级程序员会不按套路出牌,他们可能会直接使用字符串的值进行比较,而不是直接使用枚举的字段,实现示例代码如下:
public class EnumTest { public static final String COLOR_RED = "RED"; public static final String COLOR_BLUE = "BLUE"; public static final String COLOR_GREEN = "GREEN"; public static void main(String[] args) { String color = "BLUE"; if ("BLUE".equals(color)) { System.out.println("蓝色"); } } }
这样当我们修改了枚举中的值,那程序就凉凉了。
枚举使用场景
枚举的常见使用场景是单例,它的完整实现代码如下:
public class Singleton { // 枚举类型是线程安全的,并且只会装载一次 private enum SingletonEnum { INSTANCE; // 声明单例对象 private final Singleton instance; // 实例化 SingletonEnum() { instance = new Singleton(); } private Singleton getInstance() { return instance; } } // 获取实例(单例对象) public static Singleton getInstance() { return SingletonEnum.INSTANCE.getInstance(); } private Singleton() { } // 类方法 public void sayHi() { System.out.println("Hi,Java."); } } class SingletonTest { public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); singleton.sayHi(); } }
因为枚举只会在类加载时装载一次,所以它是线程安全的,这也是《Effective Java》作者极力推荐使用枚举来实现单例的主要原因。
知识扩展
枚举为什么是线程安全的?
这一点要从枚举最终生成的字节码说起,首先我们先来定义一个简单的枚举类:
public enum ColorEnumTest { RED, GREEN, BLANK, YELLOW; }
然后我们再将上面的那段代码编译为字节码,具体内容如下:
public final class ColorEnumTest extends java.lang.Enum<ColorEnumTest> { public static final ColorEnumTest RED; public static final ColorEnumTest GREEN; public static final ColorEnumTest BLANK; public static final ColorEnumTest YELLOW; public static ColorEnumTest[] values(); public static ColorEnumTest valueOf(java.lang.String); static {}; }
从上述结果可以看出枚举类最终会被编译为被
final
修饰的普通类,它的所有属性也都会被static
和final
关键字修饰,所以枚举类在项目启动时就会被 JVM 加载并初始化,而这个执行过程是线程安全的,所以枚举类也是线程安全的类。小贴士:代码反编译的过程是先用 javac 命令将 java 代码编译字节码(.class),再使用 javap 命令查看编译的字节码。
枚举比较小技巧
我们在枚举比较时使用 == 就够了,因为枚举类是在程序加载时就创建了(它并不是
new
出来的),并且枚举类不允许在外部直接使用new
关键字来创建枚举实例,所以我们在使用枚举类时本质上只有一个对象,因此在枚举比较时使用 == 就够了。并且我们在查看枚举的
equlas()
源码会发现,它的内部其实还是直接调用了 == 方法,源码如下:public final boolean equals(Object other) { return this==other; }
总结
本文我们介绍了枚举类的 7 种使用方法:常量、switch、枚举中添加方法、覆盖枚举方法、实现接口、在接口中组织枚举类和使用枚举集合等,然后讲了如果不使用枚举类使用
int
类型和String
类型存在的一些弊端:语义不够清晰、容易被修改、存在被误用的风险,所以我们在适合的环境下应该尽量使用枚举类。并且我们还讲了枚举类的使用场景——单例,以及枚举类为什么是安全的,最后我们讲了枚举比较的小技巧,希望本文对你有帮助。 -
java枚举使用怎么配置?java枚举是什么?
2021-04-10 11:15:16这是java中的一个小功能,因为很少使用到所以不常见,今天我们就来聊聊枚举是什么以及枚举的使用吧。枚举是什么?一般的说,枚举是一种特殊数据类型,说它特殊是因为,它既是类型却又比类型多了一些特殊约束,这些...小伙伴们知道枚举吗?这是java中的一个小功能,因为很少使用到所以不常见,今天我们就来聊聊枚举是什么以及枚举的使用吧。
枚举是什么?
一般的说,枚举是一种特殊数据类型,说它特殊是因为,它既是类型却又比类型多了一些特殊约束,这些约束的存在造就了枚举的简洁,安全性以及便捷性。java中定义枚举需要使用enum关键字,隐含了所创建的类型都是java.lang.Enum类的子类(java.lang.Enum是一个抽象类)。枚举类型符合通用模式Class
Enum>,而E表示枚举类型的名称。枚举类型的每一个值都映射到protected Enum(String name,int
ordinal)构造函数中,在这里,每个值的名称都转换成一个字符串,并且序数设置表示了此设置被创建的顺序。
枚举可以根据Integer、Long、Short或Byte中的任意一种数据类型来创建一种新型变量。这种变量能设置为已经定义的一组之中的一个,有效地防止用户提供无效值。该变量可使代码更加清晰,因为它可以描述特定的值。
枚举怎么使用?
使用场景一:import java.util.Enumeration;
public class Months
{
public static String[] months = {
"JAN"
, "FEB"
, "MAR"
};
public static Enumeration elements()
{
return new MonthsEnumerator();
}
}
class MonthsEnumerator implements Enumeration
{
private int idx = 0;
public /*static*/ boolean hasMoreElements()
{
return (idx
}
public /*static*/ Object nextElement()
{
return Months.months[idx++];
}
}
使用场景二:/**
* 这是比较规范的一个设计:
* 它是线程安全的,不可变的,并且很好的解决了序列化中的多实例问题
* 但是它仍然无法规避一个类可以被多个ClassLoader加载,这样仍然会出现多个实例
* 因为按照虚拟机规范:如果相同的一个类同时被多个类加载器加载,虚拟机会认为这几个类不是同一个类,会共存
*/
public class Month implements Comparable, java.io.Serializable
{
public static final Month JAN = new Month("January");
public static final Month FEB = new Month("February", 28);
public static final Month MAR = new Month("March");
public static final Month APR = new Month("April", 30);
public static final Month MAY = new Month("May");
public static final Month JUN = new Month("June", 30);
public static final Month JUL = new Month("July");
public static final Month AUG = new Month("August");
public static final Month SEP = new Month("September", 30);
public static final Month OCT = new Month("October");
public static final Month NOV = new Month("November", 30);
public static final Month DEC = new Month("December");
private static final Month[] MONTH_ARRAY = {
JAN
, FEB
, MAR
, APR
, MAY
, JUN
, JUL
, AUG
, SEP
, OCT
, NOV
, DEC
};
private static int count = 0;
private final String name;
private final int number;
private final int days;
private Month(String name)
{
this(name, 31);
}
private Month(String name, int days)
{
this.name = name;
this.days = days;
this.number = ++count;
}
public String getName()
{
return name;
}
public String getAbbev()
{
return name.substring(0, Math.min(3, getName()
.length()));
}
public int getNumber()
{
return number;
}
public int getDays()
{
return days;
}
public String toString()
{
return name;
}
@Override
public int compareTo(Object o)
{
return getNumber() - ((Month) o)
.getNumber();
}
//readResolve这个方法目的是解决序列化的单例问题
//保证该类对象在序列化后,再进行反序列化时使用在jvm中只有一个对象
Object readResolve() throws java.io.ObjectStreamException
{
return MONTH_ARRAY[number - 1];
}
}
从一些场景可以看出,在JDK的枚举类型出现之前,主要依托Enumeration这个枚举器接口,具体的设计需要手动实现,而实现了Enumeration接口的类负责对设计的类似枚举类进行迭代。
这就是枚举的所有介绍了,有关java入门的更多相关知识,请一直关注我们的网站了解吧。
-
Java枚举——高级用法与深入解读
2022-01-14 17:35:19枚举类型在C#或C++,java,VB等一些计算机编程语言中是一种基本数据类型而不是构造数据类型,而在C语言等计算机编程语言中是一种构造数据类型 。它用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它... -
Java枚举
2021-03-06 21:46:49一、背景所属包java.lang包jdk1.5后引入的新特性二、基本使用(一)、 创建一个枚举public enum TestEnum {A,B,C;}以上,创建了一个简单的枚举,这个枚举里有三个枚举项,分别是A,B,C。需要注意的是:A,B,C每一个都... -
深入理解Java枚举类型(enum)
2017-05-13 18:27:14【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) ... 出自【zejian的博客】 关联文章: 深入理解Java类型信息...深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解 -
java 中类的加载顺序(转)
2021-03-10 04:30:161、虚拟机在首次加载Java类时,会对静态初始化块、静态成员变量、静态方法进行一次初始化2、只有在调用new方法时才会创建类的实例3、类实例创建过程:按照父子继承关系进行初始化,首先执行父类的初始化块部分,然后... -
java 枚举类使用反射
2022-03-09 22:10:40目录前言一、枚举类二、反射获取枚举三、反射调用枚举方法 前言 跟普通类使用反射基本一样。 一、枚举类 public enum OperateType { add("添加", 0), delete("删除", 1), select("查询", 2), update("修改", 3)... -
java 枚举
2021-02-27 13:52:44在今天的学习中遇到了一个问题,就是枚举类和普通的java类的构造方法不同。已在百度中解决。枚举被设计成是单例模式,即枚举类型会由JVM在加载的时候,实例化枚举对象,你在枚举类中定义了多少个就会实例化多少个,... -
Java的枚举与静态常量
2021-07-16 20:01:37I'm looking at some Java code that are maintained by other parts of the company, incidentally some former C and C++ devs. One thing that is ubiquitous is the use of static integer constants, such ascl... -
JAVA枚举类获取JDBC数据库连接
2021-03-01 08:46:53上一篇文章用用两种方式包装了SQLServer、MySQL、Oracle三种数据库的对应连接获取方式,这篇文章将通过JAVA枚举类做一个包装,使得通过枚举类也可以获取数据库连接。附上代码:package com.jdbc;import java.sql.... -
Java枚举的反向查找
2021-02-26 12:56:07java的枚举常常被用来替代常量值,每个枚举值代表一个特定的常数。在反序列化时有常常需要用到常数到枚举的转换,这就涉及到枚举的反向查找。1、从枚举名反向查找枚举这种方法是最先使用也最为简便的可以用到枚举的... -
Java 枚举和初始化
2022-05-10 19:57:02枚举类(JDK5.0) 创建固定数量对象的简化方式 枚举类的格式 public enum 枚举类名 { 对象名1(实参),对象名2(实参),......,对象名n(实参); } 获取对象的格式: 枚举类类名.对象名; 枚举类的注意事项 1.枚举类... -
Java 枚举&注解
2021-03-09 20:00:21枚举类如何自定义枚举类JDK1.5之前需要自定义枚举类JDK 1.5 新增的 enum 关键字用于定义枚举类若枚举只有一个成员, 则可以作为一种单例模式的实现方式//枚举类class Season{//1.提供类的属性,声明为private final... -
java枚举的实现原理
2021-02-12 17:17:58基本使用首先,所有枚举类型都有一个基类:java.lang.Enum抽象类,里面提供了一些基础属性和基础方法。枚举类型不仅可以定义枚举常量,还可以定义属性、构造方法、普通方法、抽象方法等,比如,我们定义了一个包含... -
java枚举的高端玩法
2022-03-09 17:44:54先简单介绍一下枚举的用法,据目前咱的了解,一般来说 对于业务中,那些固定的数据,都可以使用枚举,从而提高代码逼格。假设,你现在需要写一个商品的实体类,除了那些id啊,名字啊,价格啊,可能你还需要记录一下... -
Java枚举类型(enum)详解
2021-11-06 10:45:01文章目录理解枚举类型枚举的定义枚举实现原理枚举的常见方法Enum抽象类常见方法编译器生成的Values方法与ValueOf方法枚举与...枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一 -
JAVA 枚举单例模式
2021-02-12 10:04:26关于单例模式的实现有很多种,网上也分析了如今实现单利模式最好用枚举,好处不外乎三点:1.线程安全 2.不会因为序列化而产生新实例 3.防止反射攻击但是貌似没有一篇文章解释ENUM单例如何实现了上述三点,请高手解释... -
JSON 文件映射成 Java 枚举
2021-02-12 21:26:16原标题:JSON 文件映射成 Java 枚举编译:ImportNew/唐尤华medium.com/better-programming/map-a-json-file-to-enum-in-java-d399bac0759d本文以瑞士奶酪为例介绍了如何从头把 JSON 文件映射成 Java 枚举。... -
java枚举原来还能这么用
2021-02-26 08:45:03前言相信不少java开发者写过状态变更的业务,比如订单流程、请假流程等等。一般会搞一个状态标识来标识生命周期的某个阶段。很多人会写这种逻辑:如果流程几十个岂不是要if到爆炸。还有这“0”、“1”是几个意思?... -
关于java:具有延迟加载功能的单元素枚举类型单例
2021-04-10 09:53:56我想知道如何使用Java枚举在具有延迟加载功能的Java中实现SingleTone模式。 因为枚举只是类。 第一次使用类时,它会由JVM加载,并完成所有静态初始化。 枚举成员是static,因此都将被初始化。有谁知道我如何使用带有... -
Java中的枚举类型
2021-03-22 10:33:05java 枚举值比较用 == 和 equals 方法没啥区别,两个随便用都是一样的效果。因为枚举 Enum 类的 equals 方法默认实现就是通过 == 来比较的;类似的 Enum 的 compareTo 方法比较的是 Enum 的 ordinal 顺序大小;类似... -
Java枚举—枚举进阶
2020-12-16 11:13:52上一节我们讲了[枚举初识]里面主要讲了枚举的实现原理,我们从编译器的角度看了枚举的底层实现以及枚举常用的方法 今天我们看一下枚举添加自定义方法和构造函数,枚举的抽象和接口,枚举与switch 和基于枚举的单例,... -
Java枚举源码级理解
2021-04-21 17:05:59title: Java枚举源码级理解 date: 2020-11-08 16:47:39 tags: [Java枚举, 源码理解, Java源码理解, 枚举, JavaBase, Java基础] categories: Java Java中枚举体的一些东西很怪,看了源码之后恍然大悟,收获颇丰 ... -
JAVA枚举的作用与好处
2021-02-13 00:18:45JAVA枚举的作用与好处(2012-12-13 11:33:16)标签:我有信息示例好处任何it枚举是一种规范它规范了参数的形式,这样就可以不用考虑类型的不匹配并且显式的替代了int型参数可能带来的模糊概念 枚举像一个类,又像一个... -
Java枚举类型(enum)和枚举单例模式
2021-02-26 16:40:59枚举的定义//枚举类型,使用关键字enumenum Day {MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY, SUNDAY}枚举的引用public class EnumDemo {public static void main(String[] args){//直接引用Day day =... -
Java语法细节 - 内存和枚举
2021-03-08 07:03:25Java申请DirectBuffer/*-------JAVA直接操作内存-------------* 申请100m的直接内存,不会申请在java堆上面* 打开资源管理器可以看到内存占用会增加100m,但是java堆上的内存却没有增加!*/ByteBuffer buffer = ... -
Java枚举:两个枚举类型,每个都包含相互引用?
2021-02-12 15:19:10有没有办法解决由两个枚举相互引用引起的类加载问题?我有两组枚举,foo和bar,定义如下:public class EnumTest {public enum Foo {A(Bar.Alpha),B(Bar.Delta),C(Bar.Alpha);private Foo(Bar b) {this.b = b;}public ...