在看设计模式时看到单例模式的最简便和最安全的方式就是应用枚举类。以前学习java基础的时候都说枚举类没有必要进行学习,现在才发现是因为很多人都对枚举类不了解,并不知道它的强大之处。枚举类对于需要建立有限个对象的类有很大优势,因此很适合用来建立单例模式,但是又不止局限于单例模式。
1. 首先是枚举的最基础的应用方式,即单例模式。
enum Student{
INSTANT;
private Student(){
System.out.println("INSTANT");
}
}
public class TestEnum {
public static void main(String[] args) {
// 构造两个Student对象
Student student1 = Student.INSTANT;
Student student2 = Student.INSTANT;
System.out.println(student1 == student2);
}
}
输出:
INSTANT
true
这是一个最简单的无参单例模式,自动调用Student类的构造函数,且只调用一次建立一个对象,之后都是将同一个对象赋给不同的引用。
还可以把单例和懒汉模式结合起来:
public class enumSingleton {
private static boolean flag = true;
private enumSingleton() {
synchronized (enumSingleton.class) {
if (flag) {
flag = !flag;
}else{
throw new Error("单例模式被侵犯!");
}
}
}
public static enumSingleton getInstance(){
return singleEnum.SINGLETON.getInstance();
}
private enum singleEnum{
SINGLETON;
private enumSingleton singleton;
//jvm保证枚举的构造方法只被调用一次
singleEnum(){
singleton = new enumSingleton();
}
private enumSingleton getInstance(){
return singleton;
}
}
}
2. 接下来看一下带参数的枚举类
enum Student{
INSTANT("hello"),SECOND,THIRD(3);
private Student(){
System.out.println("INSTANT");
}
private Student(String str){
System.out.println("Constructor " + str);
}
private Student(int i)
{
System.out.println("THIRD " + i);
}
}
public class TestEnum {
public static void main(String[] args) {
// 构造对象
Student student1 = Student.THIRD;
}
}
输出为:
Constructor hello
INSTANT
THIRD 3
我们只引用了Student.THIRD一个对象,但是三个构造函数都运行了,显然,我们可以看出建立任意一个实例,所有枚举都会被实例化,虽然在本例中看起来是浪费,但是在多次调用的情况下,能够大大的减少运行时间。此外,枚举后可以跟参数,从而来选择初始化的构造方法。
3.创建多个实例
enum Student{
INSTANT("hello"),SECOND,THIRD(3);
private Student(){
System.out.println("INSTANT");
}
private Student(String str){
System.out.println("Constructor " + str);
}
private Student(int i)
{
System.out.println("THIRD " + i);
}
public void run(){
System.out.println(this.name()+" run");
}
}
public class TestEnum {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student student1 = Student.INSTANT;
Student student2 = Student.SECOND;
Student student3 = Student.THIRD;
Student student4 = Student.THIRD;
System.out.println(student1 == student2);
System.out.println(student3 == student4);
student1.run();
student2.run();
student3.run();
}
}
输出:
Constructor hello
INSTANT
THIRD 3
false
true
INSTANT run
SECOND run
THIRD run
从本例可以看出,有几个枚举就会建立几个实例,并且相同枚举名称只能有一个实例。
4.在枚举内部定义成员方法
enum Student{
INSTANT("hello"),SECOND{
public void run(){
System.out.println("SECOND: " + this.name() + " run");
}
},THIRD(3){
public void say(){
System.out.println("THIRD: " + this.name() + " say");
}
};
private Student(){
System.out.println("INSTANT");
}
private Student(String str){
System.out.println("Constructor " + str);
}
private Student(int i)
{
System.out.println("THIRD " + i);
}
public void run(){
System.out.println(this.name() + " run");
}
}
public class TestEnum {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student student1 = Student.INSTANT;
Student student2 = Student.SECOND;
Student student3 = Student.THIRD;
Student student4 = Student.THIRD;
System.out.println(student1 == student2);
System.out.println(student3 == student4);
student1.run();
student2.run();
student3.run();
// student3.say();
}
}
输出为:
Constructor hello
INSTANT
THIRD 3
false
true
INSTANT run
SECOND: SECOND run
THIRD run
在枚举实例内部可以定义成员方法,优先使用实例内部的方法,可以理解为重写外部方法,另外,必须是外部有的方法,不然无法识别,因为最终实现的还算是Student对象,因此Student中没有的方法肯定就无法使用。因此,可以在外部定义抽象方法,这样每个实例都必须自己实现对应方法。
PS:
enum实现单例的好处:1.简单 2.线程安全 3.防止序列化生成新对象 序列化生成name,反序列化调用valueOf
enum中枚举个数过多时,可通过反射来调用,方便快捷。