枚举类型、单例模式
概要:
枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。下面先来看看什么是枚举?如何定义枚举?
/** * 描述:java的枚举类型 * 一般某个地方只取有限个数的值,定义为枚举 * 关键字num+枚举类型名称 */ enum Way{ //定义的枚举类型,名字叫Way UP,RIGHT,DOWN,LEFT; //是Way类型定义的四个类型(相当于类的对象) }
相当简洁,在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。枚举类型Way中分别定义了从上、下、右四个方向的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的Way右四个方向。那么该如何使用呢?如下:
enum Way{ //定义的枚举类型,名字叫Way UP,RIGHT,DOWN,LEFT; //相当于类只定义了四个对象 } public class EnumTestCase { public static void main(String[] args){ Way way1=Way.DOWN;//获取对象 Way way2=Way.LEFT; } }
注:枚举类型取值的个数是固定的,不可扩展或缩小;所以枚举类型的取值相当于对象的名称,不能new一个对象 (//Way way=new Way();//枚举类型不能被实例化)
枚举的构造函数是私有化的,当有几个取值时,构造函数就会被执行几次:

也可以定义一个私有的成员变量,在构造函数中传入相应的参数;这样,在每个取值后都要传进参数(相当于new对象后必须传入构造函数里参数类型的值)。
在枚举类型中写一个方法,打印每个取值(UP.RIGGT...)的值(传进的data)。
enum Way{ //定义的枚举类型,名字叫Way UP(1),RIGHT(2),DOWN(3),LEFT(4); //是Way类型定义的四个类型(相当于类的对象) private int data; //枚举类型的默认构造函数是私有的 private Way(int data){ this.data=data; System.out.println("Way()");//构造四次 } public void show(){ System.out.println("data:"+data); } } public class EnumTestCase { public static void main(String[] args) { //Way way=new Way();//枚举类型不能被实例化 Way way1=Way.UP;//获取对象 Way way2=Way.RIGHT; way1.show(); //打印对象的值 way2.show(); } }

ordinal()方法:获取值得出现位置(0-最大值)
/** Most programmers will have no use for this method. It is * designed for use by sophisticated enum-based data structures, such * as {@link java.util.EnumSet} and {@link java.util.EnumMap}. * * @return the ordinal of this enumeration constant */ public final int ordinal() { return ordinal; }

枚举类的私有构造函数不能通过反射来访问,因为Constructor类的newInstance方法已经对枚举类型做了限制(newInstance源码):
if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects");
Class c=Way.class; Constructor con=c.getDeclaredConstructor(int.class); con.setAccessible(true); con.newInstance(20); //Exception in thread "main" java.lang.NoSuchMethodException: Way.<init>(int)
设计一个单例类,只能访问这个类唯一的一个实例对象:
/** *需求:设计单例类,只能访问这个类唯一的一个实例对象 */ class Singleton{ //3.提供一个公有的函数接口,把唯一的obj对象进行返回(定义成静态方法,为了返回对象) public static Singleton instance(){ return obj; } //2.创建这个类型唯一的一个对象(自己的类,能访问自己私有的构造函数) private static Singleton obj=new Singleton(); //1.构造函数私有化(应该由类的设计者提供对象,用户不能随意地new对象,每new一次就会产生一个对象) private Singleton(){ } } public class SingletonTestCase { public static void main(String[] args) throws Exception{ // Singleton s1=new Singleton();构造函数私有化,所以不能new对象 Singleton s1=Singleton.instance(); Singleton s2=Singleton.instance(); Singleton s3=Singleton.instance(); //所以获取了三个相同的对象 System.out.println(s1==s2);//true System.out.println(s1==s3);//true } }
但是,这种方式不够安全,依然可以通过反射创建新的对象访问构造函数:
Singleton s1=Singleton.instance(); Singleton s2=Singleton.instance(); Singleton s3=Singleton.instance(); //所以获取了三个相同的对象 System.out.println(s1==s2);//true System.out.println(s1==s3);//true //Singleton s=new Singleton(); //不安全,这种方式可以通过反射创建对象 Class s=Class.forName("Singleton"); Constructor c=s.getDeclaredConstructor(); c.setAccessible(true); Singleton s4=(Singleton)c.newInstance(); System.out.println(s1==s4);//false

改进,利用枚举设计单例类,使只能返回一个对象
/** * 定义一个Teacher类,name age sex三个属性,设计成单例类 * 不能通过反射生成新对象 * 注意,在Constructor的enum类型进行判断,不能通过反射创建枚举类型的对象 */ enum Teacher{ TEACHER_OBJ("zhang san",20,"women");//构造函数带参数时需要在对象后传入参数属性 private String name; private int age; private String sex; } //枚举的构造默认也是private Teacher(String name,int age,String sex) { this.name = name; this.age=age; this.sex=sex; } @Override public String toString() { return "Teacher{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex+ '}'; } } public class Test { public static void main(String[] args) { Teacher t1=Teacher.TEACHER_OBJ; System.out.println(t1); } }

如果构造函数不传参数,可以给属性添加set、get方法,在main函数里进行设置:
enum Teacher{ TEACHER_OBJ; private String name; private int age; private String sex; public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } //枚举的构造默认也是private Teacher(){ } } public class Test { public static void main(String[] args) { Teacher t1=Teacher.TEACHER_OBJ; t1.setAge(20); t1.setName("laura"); t1.setSex("women"); System.out.print(t1.getSex()+" "); System.out.print(t1.getName()+" "); System.out.print(t1.getAge()); } }
women laura 20
懒加载(懒汉单例模式):对象初始化在第一次获取对象的时候发生
快加载(饿汉单例模式):对象的初始化先与第一次获取对象(上面例子先new了一个对象,不管有没有调用)
饿汉式:简单来说就是空间换时间,因为上来就实例化一个对象,占用了内存;(也不管你用还是不用)
懒汉式:简单的来说就是时间换空间,与饿汉式正好相反
改进为懒汉模式:(懒汉单例模式更好,只有调用instance方法的时候才会创建对象;饿汉先创建了对象,但是如果不调用instance方法,就会白白创建对象!)
class Singleton{ //3.提供一个公有的函数接口,把唯一的obj对象进行返回(定义成静态方法,为了返回对象) public static Singleton instance(){ //懒汉模式的改进,如果没有创建过,就创建;创建了就直接返回第一次创建好的对象 if(obj==null){ obj=new Singleton(); } return obj; } //2.创建这个类型唯一的一个对象(自己的类,能访问自己私有的构造函数 private static Singleton obj=null; //懒汉模式 //1.构造函数私有化(应该由类的设计者提供对象,用户不能随意地new对象,每new一次就会产生一个对象) private Singleton(){ } }
其实这种方法就是将singleton类中实例化方法加了一个final属性,就是不允许更改其值,所以在外类进行对其进行修改时,是不会允许的,同样达到了单例的效果。
class Singleton{ public static Singleton instance(){ return obj; } //将对象改为final类型 private static final Singleton obj=new Singleton(); private Singleton(){ } }
总结:
设计单例类的步骤:1.构造函数私有化 2.创建一个实例(获取实例再创建:懒汉模式) 3.提供静态接口,把唯一的对象返回
浙公网安备 33010602011771号