java 单例模式
一、定义及应用
单例模式,是一种常用的软件设计模式,在使用这个设计模式时,单例对象的类要保证只有一个实例存在。
许多时候整个系统只需要有一个全局对象,这样有利于我们协调整个系统的行为。比如服务器程序中,服务器程序的配置信息存储在一个文件中,使用一个单例对象同意读取,服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了复杂环境下的配置管理。同样,线程池、连接池也是通过单例模式进行管理。
二、实现
1)、两懒汉方式
public class Singleton{//非线程安全 private static Singleton instance; private Singleton(){}; public static Singleton getInstance(){ if(instance==null){ instance=new singleton(); } return instance; } } public class Singleton{//线程安全 private static Singleton instance; private Singleton(){}; public static synchronized Singleton getInstance(){ if(instance==null){ instance=new singleton(); } return instance; } }
2)、饿汉方式
public class Singleton{ private static final Singleton instance=new Singleton(); private Singleton(){}; public static Singleton getInstance(){ return instance; } }
3)、内部类
public class Singleton{//懒加载 线程安全 private static class SingletonHolder{ private static final Singlton instance=new Singleton(); } private Singleton(){}; public static Singleton getInstance(){ return SingletonHolder.instance; } }
4)、枚举
public enum Singleton{ INSTANCE; //....other function }
写枚举单例的方式,它可能包含实例变量和实例方法,但是简单来说我什么都没用,需要注意的是如果你使用实例方法,你就需要确保方法的线程安全性,避免它会影响对象的状态。通常情况下枚举里面创建实例是线程安全的,但是其它的方法就需要编程者自己去考虑了。
5)、双校验
public class Singleton{ private volatile static Singleton instance;//volatile关键字保证当instance变量被初始 private Singleton(){};//化Singleton实例后,多个线程正确的处理instance变量 public static Singleton getInstance(){ if(instance==null){ synchronize(Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } }
三、防止破坏单例模式
1、阻止clone()方法创建单例实例的另一个实例
1)override clone()方法
protected Object clone() throws CloneNotSupportedException{ throw new CloneNotSupportedException(); }
2)一个是单例类一定要是final的,这样用户就不能继承它了
如果有其他方法可以交流交流!
2、防止反射破坏
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Singletom { private static final Singletom instance=new Singletom(); private Singletom(){}; public static Singletom getInstance(){ return instance; } public static void main(String [] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException{ Class<Singletom> clazz = Singletom.class; Constructor<Singletom> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); System.out.println(Singletom.instance == constructor.newInstance()); } }
打印出 false
解决方法:没找到答案 期望交流
3、防止系列化破坏
破坏代码:
public class Singleton implements Serializable{ private static final long serialVersionUID = 1L; private static final Singleton instance=new Singleton(); private Singleton(){}; public static Singleton getInstance(){ return instance; } public static void main(String [] args){
// 支持java.io.Serializable的对象都可以写入流中 ByteArrayOutputStream bos=new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(Singleton.instance); // 根据字节流生成对象 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); Singleton singleton = (Singleton) ois.readObject(); System.out.println(Singleton.instance == singleton); } }
解决办法:
//在类中实现readResovle方法 private Object readResolve(){ return instance; }
输出为true
4、防止不同类装载器破坏
如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
解决办法:
private static Class getClass(String classname)throws ClassNotFoundException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if(classLoader == null) classLoader = Singleton.class.getClassLoader(); return (classLoader.loadClass(classname)); }
参考:http://www.oracle.com/technetwork/articles/java/singleton-1577166.html