设为首页 加入收藏

TOP

Java设计模式之单例模式
2019-05-12 14:28:15 】 浏览:109
Tags:Java 设计模式 单例 模式

核心作用:
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

常见的应用场景:
1、windows的任务管理器(Task Manager)
2、Windows的回收站(Recycle Bin)
3、项目中,读取配置文件的类,一般也是单例模式,没有必要每次读取配置的时候都new一个对象
4、网站的计数器,不用单例模式,难以统一
5、应用程序的日志应用,一般都用单例实现,这一般是由共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加
6、数据库连接池的设计,
操作系统的文件系统,一个操作系统只能有一个文件系统
7、Application也是单例应用
8、在Spring中,每个Bean默认是单例的,方便Spring管理
9、在Servlet编程中,每个Servlet也是单例的
10、在SpringMVC框架/Struct1中,控制器也是单例

单例模式的优点:
1、由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如得去配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留在内存的方式来解决
2、单例模式可以在系统设置全局的访问点,优化环境共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理

常见的五种单例模式实现方式:
1、饿汉式(线程安全,调用效率高,但是,不能延时加载)
2、赖汉式(线程不安全,调用效率不高,但是可以延时加载)
其他:
3、双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题,不建议使用)
4、静态内部类式(线程安全,调用效率高,但是,可以延时加载)
5、枚举单例(线程安全,调用效率高,不能延时加载)

主要:
1、饿汉式(线程安全,调用效率高,但是,不能延时加载)

package com.silence.singleton;
/**
* 测试饿汉式单例模式
* @author zhuxiang
*
*/
public class SingletonDemo1 {

//类初始化时立即加载这个对象(没有延时加载的优势)。加载类时是天然的线程安全
private static SingletonDemo1 instance = new SingletonDemo1();
//私有构造器
private SingletonDemo1(){
}

//方法没有同步,调用效率高
public static SingletonDemo1 getInstance(){
    return instance;
}

}

2、赖汉式(线程不安全,调用效率不高,但是可以延时加载)

package com.silence.singleton;

/**
* 测试懒汉式单例模式
* @author zhuxiang
*
*/
public class SingletonDemo2 {

//类初始化时,不立即初始化这个对象(具有延时加载的优势)。
private static SingletonDemo2 instance;

//私有构造器
private SingletonDemo2(){
}

//方法同步,调用效率低
public static synchronized SingletonDemo2 getInstance(){
    if (null == instance) instance = new SingletonDemo2(); 
    return instance;
}

}

其他:
3、双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题,不建议使用)
4、静态内部类式(线程安全,调用效率高,但是,可以延时加载)

package com.silence.singleton;

/**
* 测试静态内部类式单例模式
* 这种方式,线程安全,调用效率高,并且实现了延时加载
* @author zhuxiang
*
*/
public class SingletonDemo4 {

private static class SingletonInnerClass {
    private static final SingletonDemo4 instance = new SingletonDemo4();
}

//私有构造器
private SingletonDemo4(){
}

//方法没有同步,调用效率高
public static SingletonDemo4 getInstance(){
    return SingletonInnerClass.instance;
}

}

5、枚举单例(线程安全,调用效率高,不能延时加载)

package com.silence.singleton;

/**
* 测试枚举式的单例模式
* 效果挺好的,但是没有延时加载的特点
* 可以有效的防止反射和反序列化的漏洞
* @author zhuxiang
*
*/
public enum SingletonDemo5 {

//这个枚举元素,本身就是一个单例对象!
INSTANCE;

//添加自己需要的操作
public void singletonOperation(){

}

}

//测试用例如下,可以将SingletonDemo4 换成其他的单例实现类
package com.silence.singleton;

public class Client {

public static void main(String[] args) {
    SingletonDemo4 instance = SingletonDemo4.getInstance();
    SingletonDemo4 instance2 = SingletonDemo4.getInstance();
    System.out.println(instance);
    System.out.println(instance2);

//枚举类型测试 System.out.println(SingletonDemo5.INSTANCE==SingletonDemo5.INSTANCE);
}
}

几种单例模式中,除了枚举类型之外,其他的都可以用反射和反序列化的方法进行破解:
package com.silence.singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

/**
* 测试反射和反序列化破解单例模式
*
* @author zhuxiang
*
*/
@SuppressWarnings(“all”)
public class Client2_6 {

public static void main(String[] args) throws Exception {
    SingletonDemo6 instance = SingletonDemo6.getInstance();
    SingletonDemo6 instance2 = SingletonDemo6.getInstance();
    System.out.println(instance);
    System.out.println(instance2);

    //通过反射的方式直接调用私有构造器破解单例模式
    /*Class<SingletonDemo6> clz = (Class<SingletonDemo6>) Class.forName("com.silence.singleton.SingletonDemo6");
    Constructor<SingletonDemo6> constructor = clz.getDeclaredConstructor(null);
    constructor.setAccessible(true);
    SingletonDemo6 instance3 = constructor.newInstance();
    SingletonDemo6 instance4 = constructor.newInstance();
    System.out.println(instance3);
    System.out.println(instance4);
    */

    //通过反序列化的方式破解单例模式,创建多个对象
    FileOutputStream fos = new FileOutputStream("d:/myjava/a.txt");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(instance);
    oos.close();
    fos.close();

    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/myjava/a.txt"));
    SingletonDemo6 ins = (SingletonDemo6) ois.readObject();
    System.out.println(ins);
}

}

为了防止被反射和反序列化破解,在实现单例模式的时候可以用如下的模式防范
package com.silence.singleton;

import java.io.ObjectStreamException;
import java.io.Serializable;

/**
* 测试懒汉式反射和反序列化的破解单例模式
* @author zhuxiang
*
*/
public class SingletonDemo6 implements Serializable{

//类初始化时,不立即初始化这个对象(具有延时加载的优势)。
private static SingletonDemo6 instance;

//私有构造器
private SingletonDemo6(){
    //避免反射破解单例
    if (null != instance) {
        throw new RuntimeException();
    }
}

//方法同步,调用效率低
public static synchronized SingletonDemo6 getInstance(){
    if (null == instance) instance = new SingletonDemo6(); 
    return instance;
}

//避免反序列化破解单例模式,在反序列化时直接执行该方法,返回instance对象,不返回反序列化的对象
private Object readResolve() throws ObjectStreamException{
    return instance;
}

}

对于这么多的单例模式,我们在使用的时候一般如何选择呢?总结如下:
1)、单例对象占用资源少,不需要延时加载 时,枚举式要好于饿汉式
2)、单例对象占用资源多,需要延时加载时,静态内部类式好于懒汉式
一般来说,饿汉式相对较不错比其他都快。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇BloomFilter 下一篇关于Kafka的那些坑(topic命名,..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目