【JVM】利用枚举实现单例模式完全讲解!!

6 篇文章 0 订阅

目录

代码

程序简单讲解

继续深入


代码

首先直接给使用枚举实现单例模式的代码:

 
public class User {
    //私有化构造函数
    private User(){ }
 
    //定义一个静态枚举类
    static enum SingletonEnum{
        //创建一个枚举对象,该对象天生为单例
        INSTANCE;
        private User user;
        //枚举的构造函数天生且必须是private类型的,可以不指定
         SingletonEnum(){
            user=new User();
        }
        public User getInstnce(){
            return user;
        }
    }
 
    //对外暴露一个获取User对象的静态方法
    public static User getInstance(){
        return SingletonEnum.INSTANCE.getInstnce();
    }
}
 
public class Test {
    public static void main(String [] args){
        System.out.println(User.getInstance());
        System.out.println(User.getInstance());
        System.out.println(User.getInstance()==User.getInstance());
    }
}
结果为true

Effective Java》一书有一句话:单元素的枚举类型已经成为实现Singleton的最佳方法。因此下次面试官让大家写单例模式的时候直接把他甩给面试官就可以啦!并且这种单例模式是线程安全的,昨天的教训,面试官让我写三种线程安全的单例模式,结果我只会两种,今天特地过来总结一下!!


程序简单讲解

首先简单讲解一下:内部的静态枚举类里面有一个实例:INSTANCE,这个实例其实就是枚举类的实例,这个枚举类里面有一个我们需要的实例化的类,就是User类,通过私有化这个枚举类的构造方法,使得只有在这个枚举类的内部才能构造他的User属性。然后通过一个public类型的函数使得外界可以得到枚举类型的这个User属性。最后User类则通过一个public static的方法来开放给外界获得这个单例。


继续深入

当然,面试官会让你讲解为什么他是线程安全的,以及为什么它可以实现单例模式,我已经把答案给大家准备好了。

1--为什么它可以实现单例?

比如我们使用如下方式建立了一个枚举:

enum Type{
    A,B,C,D;
} 

但是实际上我们建立的是下面这样一个类:

final class Type extends Enum{
    public static final Type A;
    public static final Type B;
    ...
}

也就是我们实际上创建了一个类,这个类继承自Enum,(其实最后这个类还是一个final类型的,也就是说他不可继承),枚举里面的每个成员,实际上都是这个类的一个public static final类型的变量,这个变量的类型就是这个枚举类。也就是说这个枚举的成员变量是不可改变的。

此外,enum的构造方法天生就是private的(默认并且只能是private),也就是说这个构建枚举实例的过程不是我们做的(不允许被外界调用),是内部自动完成的,只要我们访问量枚举里面的实例,就会自动调用这个构造函数。同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。

2--为什么这种方法是线程安全的?

因为JVM保证枚举类的构造函数只会被调用一次。如果我们对枚举类型的代码进行反编译们就可以发现,他的构造函数经过反编译之后变成了static代码块了。反编译之后的代码如下:

public static final T SPRING;
public static final T SUMMER;
public static final T AUTUMN;
public static final T WINTER;
private static final T ENUM$VALUES[];
static
{
    SPRING = new T("SPRING", 0);
    SUMMER = new T("SUMMER", 1);
    AUTUMN = new T("AUTUMN", 2);
    WINTER = new T("WINTER", 3);
    ENUM$VALUES = (new T[] {
    SPRING, SUMMER, AUTUMN, WINTER
    });
}

那么到这里,为什么线程安全的其实原因就和之前的静态内部类实现单例模式为什么线程安全的原因类似了。就是因为static代码块在类初始化时,会有一个锁,只有获得了这个锁的线程才会执行<clinit>代码的内容。这里我就不细说了,因为我的上一篇文章主要就是说这个的,大家可以去看看:传送门

当然,使用枚举来实现单例模式还有很多其他的好处,但是我暂时还没有总结,以后有机会再写一篇文章吧!


参考

https://blog.csdn.net/qq_35590091/article/details/105751757

https://www.cnblogs.com/jason1990/p/10218236.html

https://www.jb51.net/article/163911.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值