枚举类型、单例模式

概要:

枚举类型是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.提供静态接口,把唯一的对象返回

 

posted @ 2019-11-04 18:00  acehm  阅读(1115)  评论(0)    收藏  举报