JAVA的23种设计模式---单例模式
来源:互联网 发布:淘宝店多少单一颗心 编辑:程序博客网 时间:2024/05/16 14:34
概要:
该文章参考了《设计模式之禅》一书及大牛Lena-Yang的博客文章
1.该文章阐述了单例模式的基础及进阶代码表现方式;
2.该文章适合初学设计模式的技术人员研习;
3.该文章有许多不足之处,请各位大咖指正,喷子绕道;
正文:
单例模式:确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
基础的单例模式代码实现:
1.饿汉式单例:
public class Singleton { //饿汉式单例,实例化过程也可以写在静态代码块中 private static final Singleton SINGLETON = new Singleton(); //私有构造器,限制产生多个对象 private Singleton(){ } //获取实例对象 public static Singleton getSingleton(){ return SINGLETON; } //类中其他方法,尽量是static public static void doSomething(){ }}
2.懒汉式单例:
public class Singleton { //懒汉式单例,不推荐,仅用于基础原理展示 private static Singleton singleton = null; //私有构造器,限制产生多个对象 private Singleton(){ } //获取实例对象,不安全,需加锁 public static synchronized Singleton getSingleton(){ if(singleton == null){ singleton = new Singleton(); } return singleton; }}
进阶的单例模式代码实现:
1.基于懒汉式的双重检查实现:
public class Singleton { //双重检查单例 private static volatile Singleton singleton = null; //私有构造器,限制产生多个对象 private Singleton(){ } //双重检查获取实例对象 public static Singleton getSingleton(){ if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; }}
注:
a:关键字volatile(不稳定的):把该变量声明为volatile,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取,为了防止多线程环境下的不安全情况的发生。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。
b:Double-Check locking概念
c:Happens-Before概念:在每一个构造函数结束的地方都有一个freeze动作,在构造函数返回前,所有的final成员变量都要完成初始化。使用volatile来修饰static的singleton也可以保证成功,然而一些jvm并没有正确实现volatile语义,所以这种方法并不是那么保险。
2.基于饿汉式的静态内部类实现:
public class Singleton { //私有构造器,限制产生多个对象 private Singleton(){ } //静态内部类产生实例对象,JVM帮助我们保证了线程的安全性 private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } //获取实例对象,利用了JVM类加载时的特性,第一次使用SingletonInstance时才进行初始化 public static Singleton getInstance() { return SingletonInstance.INSTANCE; }}
注:
a:Lazy initialization holder class模式:综合使用了Java的类级内部类和多线程缺省同步锁的知识,很巧妙的同时实现了延迟加载和线程安全。
b:以上方式含有反射及反序列化漏洞,简单来讲通过这两种方式获取对象会破坏单例模式,产生不同的实例对象。
解决方式:
单例类:
import java.io.ObjectStreamException;import java.io.Serializable;/** * 静态内部类实现方式为例 * 线程安全,调用效率高,并且实现了延迟加载 * 解决反射和反序列化漏洞 * @author Administrator * */public class Singleton implements Serializable{ //虽然在这个例子中没用,但是养成习惯 private static final long serialVersionUID = 1L; //私有构造器,限制产生多个对象 private Singleton(){ //防止反射获取多个对象的漏洞 if (null != SingletonInstance.INSTANCE) throw new RuntimeException(); } //静态内部类产生实例对象,JVM帮助我们保证了线程的安全性 private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } //获取实例对象,利用了JVM类加载时的特性,第一次使用SingletonInstance时才进行初始化,调用效率高 public static Singleton getInstance() { return SingletonInstance.INSTANCE; } //防止反序列化获取多个对象的漏洞 private Object readResolve() throws ObjectStreamException { return SingletonInstance.INSTANCE; } }
测试类:
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;/** * 获取实例对象测试类 * @author Administrator * */public class Attack { public static void main(String[] args) { Singleton sc1 = Singleton.getInstance(); Singleton sc2 = Singleton.getInstance(); //sc1,sc2是同一个对象 System.out.println(sc1); System.out.println(sc2); //通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞) try { Class<Singleton> clazz = (Class<Singleton>) Class.forName(Singleton.class.getName()); Constructor<Singleton> c = clazz.getDeclaredConstructor(null); c.setAccessible(true); // 跳过权限检查 Singleton sc3 = c.newInstance(); Singleton sc4 = c.newInstance(); // sc3,sc4不是同一个对象 System.out.println("通过反射的方式获取的对象sc3:" + sc3); System.out.println("通过反射的方式获取的对象sc4:" + sc4); } catch (Exception e) { System.err.println("单例模式被破坏"); } //通过反序列化的方式构造多个对象(类需要实现Serializable接口) try { //1.把对象sc1写入硬盘文件 FileOutputStream fos = new FileOutputStream("object.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(sc1); oos.close(); fos.close(); //2.把硬盘文件上的对象读出来 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.out")); //如果对象定义了readResolve()方法,readObject()会调用readResolve()方法。从而解决反序列化的漏洞 Singleton sc5 = (Singleton) ois.readObject(); //反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。 System.out.println("对象定义了readResolve()方法,通过反序列化得到的对象:" + sc5); ois.close(); } catch (Exception e) { // TODO: handle exception } }}
3.枚举实现:
public enum Singleton { SINGLETON; public void whateverMethod() { }}
注:JDK1.5后可以使用
阅读全文
0 0
- JAVA的23种设计模式---单例模式
- java的设计模式---单例模式
- java的设计模式---单例模式
- java的设计模式--单例模式
- java 23种设计模式之单例模式
- 23种Java设计模式_单例模式_1
- Java 23种设计模式之单例模式
- JAVA中23种设计模式之单例模式
- java 23种设计模式 04 单例模式
- java 设计模式,单例设计模式
- java设计模式-单例设计模式
- java设计模式-单例设计模式
- Java设计模式 单例设计模式
- Java设计模式------单例设计模式
- java设计模式----->单例设计模式
- java设计模式:单例设计模式
- java设计模式-----单例设计模式
- Java设计模式----单例设计模式
- 信息安全工程师 学习笔记(三十五)
- android之单位
- Selenium遇到Iframe,无法直接准确定位
- Gogland use goimports
- UNIX命令行快速入门4
- JAVA的23种设计模式---单例模式
- x&(x-1) 表达式
- 信息安全工程师 学习笔记(三十六)
- MySQL必知必会
- Apache eagle监控hive任务问题
- linux 下多线程错误 undefined reference to `sem_init'
- mybatis事务相关知识点
- 如何处理List条目中含有EditText,并且EditText中文字或者数字可以修改
- 虚拟化相关概念与技术整理(2)——硬件辅助虚拟化简介