java单例模式几种实现方法
这篇博客就写一下我最近在学习中突然发现有些忘记的java单例模式的一些实现方法,我们平常见过的就是有饿汉式、懒汉式、双端检索、静态内部类和枚举等的五种方式实现。
饿汉式
饿汉式,差不多的意思就是很饿,想要立马得到想要的食物,在这里的实现,也是如此,在定义静态变量的时候立即就初始化创建了变量,这样的效率较高,但是可能造成没有用这个对象,就已经创建在内存中,造成了内存的浪费,这个也是我们常见的一种方式。代码如下
/**
* @author ZHAN jgZHAN
* @create 2020-07-31 15:04
* 饿汉式的单例模式
*/
public class Ehan {
//定义静态变量时立即创建变量
private static Ehan instance=new Ehan();
//将构造方法变成私有化,使之不能直接实例化
private Ehan(){}
public static Ehan getInstance(){
return instance;
}
}
懒汉式
懒汉式,差不多的意思就是比较懒,不会在声明变量的时候就初始化创建,而是在真正是要使用的时候才会判断时候已经创建好了,没有才会去创建出来,这样的效率相比饿汉式慢,但是节约了资源,但第一次加载时需要实例化,反映稍慢一些,而且线程不安全,所以在多线程不能正常工作。代码如下
/**
* @author ZHAN jgZHAN
* @create 2020-07-31 14:58
*/
public class Lanhan {
//定义静态变量
private static Lanhan instance;
//将构造方法变成私有化,使之不能直接实例化
private Lanhan(){}
public static Lanhan getInstance(){
if (instance==null) {
instance=new Lanhan();
}
return instance;
}
}
双端检索
双端检索说的通俗易懂一点就是相当于线程安全版本的懒汉式通过关键字volatile防止编译器的命令重排,再用synchronized同步代码块,使得整个代码处于线程安全,代码如下
/**
* @author ZHAN jgZHAN
* @create 2020-07-31 15:24
* 双端检索相当于线程安全版本的懒汉式
*/
public class Doublecheck {
//volatile主要防止编译器的命令重排
private volatile static Doublecheck instance;
//将构造方法变成私有化,使之不能直接实例化
private Doublecheck(){}
public static Doublecheck getInstance(){
if (instance==null){
synchronized (Doublecheck.class){
if (instance==null) {
instance = new Doublecheck();
}
}
}
return instance;
}
}
静态内部类
就是在类的内部定义一个静态的内部类,然后再静态内部类定义一个静态终态的自身变量,然后再定义一个公共的静态方法获取静态内部类的静态变量,代码实现如下:
**
* @author ZHAN jgZHAN
* @create 2020-07-31 15:52
* 静态内部类
*/
public class SingletonInside {
//定义一个静态内部类实现初始化
private static class SingletonInsideHolder{
private static final SingletonInside instance =new SingletonInside();
}
//将构造方法变成私有化,使之不能直接实例化
private SingletonInside(){}
//定义一个方法,返回SingletonInside的静态成员变量instance
public static SingletonInside getInstance(){
return SingletonInsideHolder.instance;
}
}
枚举式
枚举的方式简单粗暴,线程安全,效率也是较高,使用方法也是比较简单,可以直接使用枚举类里面的方法,代码如下
/**
* @author ZHAN jgZHAN
* @create 2020-07-31 16:02
* 枚举方式实现单例模式
*/
public enum SingletonEnum {
INSTANCE;//名字自定义
public void doSomething(){
System.out.println("调用枚举单例的相关方法");
}
}
测试
接下来我们可以对上面的一些类进行测试,有没有达到单例模式的效果
public class Singletondemo {
public static void main(String[] args) {
//饿汉
Ehan e1=Ehan.getInstance();
Ehan e2=Ehan.getInstance();
System.out.println("e1 than e2 "+(e1==e2));
//懒汉
Lanhan l1=Lanhan.getInstance();
Lanhan l2=Lanhan.getInstance();
System.out.println("l1 than l2 " + (l1==l2));
//双端检索
Doublecheck d1=Doublecheck.getInstance();
Doublecheck d2=Doublecheck.getInstance();
System.out.println("d1 than d2 " + (d1==d2));
//静态内部类方式
SingletonInside si1=SingletonInside.getInstance();
SingletonInside si2=SingletonInside.getInstance();
System.out.println("si1 than si2 " + (si1==si2));
//枚举方式
SingletonEnum se1=SingletonEnum.INSTANCE;
SingletonEnum se2=SingletonEnum.INSTANCE;
System.out.println("se1 than se2 " + (se1==se2));
//枚举的单例模式可以直接调用方法
SingletonEnum.INSTANCE.doSomething();
}
}
运行结果如下:
小结
总的来说我们经常用的还是饿汉式和双端检索比较多,听说式枚举的方式出来的比较晚,所以用的比较少,其实枚举还是比较推荐使用的。懒汉式不适用与多线程,不推荐使用。内部静态类也不能抵抗序列化反序列化的攻击,也不是很推荐使用。