设计模式
单例模式,顾名思义就是一个类只能有一个实例。在系统应用中一个类只有一个对象实例。
单例模式分为五种
在类加载时就为类创建一个对象实例。
实现方式有两种
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
//构造方法需要变为私有,提供getInstance获取对象实例
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
public class HungrySingleton {
private static final HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
//构造方法需要变为私有,提供getInstance获取对象实例
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
在需要的时候对类进行实例化,创建对象实例。
public class LazySimpleSingleton {
private static LazySimpleSingleton lazySimpleSingleton = null;
private LazySimpleSingleton(){}
public static LazySimpleSingleton getInstance(){
if (lazySimpleSingleton == null){
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
缺点: 易产生线程不安全。
使用synchronized保证线程安全
public class LazySimpleSingleton {
private static LazySimpleSingleton lazySimpleSingleton = null;
private LazySimpleSingleton(){}
public static synchronized LazySimpleSingleton getInstance(){
if (lazySimpleSingleton == null){
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
缺点: 虽说jdk1.6之后对synchronized做了性能优化,但是还是会有一些性能影响,容易造成这个类都被锁住,所以使用如下2.2.3.更优化的一种方式实现。
说明:
CPU执行时会转换成JVM指令执行
会出现2./3.指令重排序的问题,使用volatile解决(对所有线程可见)
1. 分配内存给这个对象
2. 初始化这个对象
3. 将初始化好的对象和内存地址建立关联,赋值。
4. 用户初次访问
public class LazyDoubleSingleton {
private static volatile LazyDoubleSingleton lazyDoubleSingleton = null;
private LazyDoubleSingleton(){}
public static LazyDoubleSingleton getInstance(){
if (lazyDoubleSingleton == null){
synchronized(LazyDoubleSingleton.class){
if(lazyDoubleSingleton == null)
lazyDoubleSingleton = new LazyDoubleSingleton();
}
}
return lazyDoubleSingleton;
}
}
/**
* 此种方式最优,内部类变量只有在外部方法调用的时候才会被实例化
* 类加载顺序 :按照静态代码的顺序加载,构造函数最后,静态内部类不调用不会被初始化。
*/
public class LazyInnerClassSingleton {
/**
* 使用此种判断可以防止被反射创建实例
* RuntimeException属于比较严重的错误,系统问题或程序员写的代码有误,JVM必须停止运行改正错误,
* 所以系统不会继续运行,但Exception会继续运行。
*/
private LazyInnerClassSingleton(){
if (LazyHolder.LAZY_SINGLETON != null ){
throw new RuntimeException("对象实例已存在。。。");
}
}
public static final LazyInnerClassSingleton getInstance(){
return LazyHolder.LAZY_SINGLETON;
}
private static class LazyHolder{
public static final LazyInnerClassSingleton LAZY_SINGLETON = new LazyInnerClassSingleton();
}
public static void main(String[] args) {
try{
Constructor constructor = LazyInnerClassSingleton.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyInnerClassSingleton l = (LazyInnerClassSingleton)constructor.newInstance(null);
LazyInnerClassSingleton l2 = LazyHolder.LAZY_SINGLETON;
System.out.println(l == l2);
// 运行结果:抛出异常
}catch (Exception e){
e.printStackTrace();
}
}
}
说明:因为构造方法中加了为空抛异常的判断,所以在使用反射创建实例的时候会抛出异常。
序列化单例即用实现序列化接口创建单例。
import java.io.Serializable;
/**
* 实现序列化
*/
public class SerializableSingleton implements Serializable {
private static final SerializableSingleton SINGLEION = new SerializableSingleton();
//构造方法需要变为私有,提供getInstance获取对象实例
private SerializableSingleton(){}
public static SerializableSingleton getInstance(){
return SINGLEION;
}
/**
* 在ObjectInputSteam中有下面一段代碼確定是否需要新建實例
* if (obj != null &&
* handles.lookupException(passHandle) == null &&
* desc.hasReadResolveMethod())
* 實際上是創建了兩個實例,只不過由readResolve判斷是否已經有實例,有則覆盖反序列化
* 生成的對象實例 。
* @return
*/
private Object readResolve(){
return SINGLEION;
}
}
说明: 在没有readResolve方法可以通过反序列化的方式创建新的对象实例,如以下代码所示,加上就可以阻止被反序列化,原因看代码中的注解。
import com.zm.singleton.serializable.SerializableSingleton;
import java.io.*;
public class SerializableSingletonTest {
public static void main(String[] args) {
SerializableSingleton singleton1 = null;
SerializableSingleton singleton2 = SerializableSingleton.getInstance();
try{
FileOutputStream fos = new FileOutputStream("SerializableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(singleton2);
oos.flush();
oos.close();
fos.close();
FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
singleton1 = (SerializableSingleton) ois.readObject();
ois.close();
fis.close();
System.out.println(singleton1 == singleton2);
}catch (Exception e){
e.printStackTrace();
}
}
}
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ContainerSingleton {
private static Map ioc = new ConcurrentHashMap();
private ContainerSingleton(){}
/**
* 不加synchronized會出現線程不安全問題,因為ConcurrentHashMap只控制自己內部的方法
* 而當前方法並不受控制。
* @param className
* @return
*/
public static Object getInstance(String className){
try {
synchronized (ioc) {
if (!ioc.containsKey(className)) {
Object instance = Class.forName(className).newInstance();
ioc.put(className, instance);
return instance;
} else {
return ioc.get(className);
}
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
注意: 容器式单例的创建方法中需要加上synchronized,原因见注释。
使用枚举的方式创建单例,
优点: 简单、方便,不需要new对象。
/**
* 枚举式單例,即註冊式單例。
* 在jdk层面就为枚举类单例不被反序列化和反射保驾护航
* 在枚举的class文件中可以看到INSTANCE初始化代碼是放在static代码块中,以饿汉式的方式创建实例。
* 枚举式的class文件中构造函数带有参数
* 只會被初始化一次
*/
public enum EnumSingleton {
INSTANCE;
// 用于测试两个对象是否相同
private Object data;
public static EnumSingleton getInstance(){return INSTANCE;}
public Object getData() {
return data;
}
}
public class ThreadLocalSingleton {
private ThreadLocalSingleton(){}
private static final ThreadLocal threadLocalSingleton = new ThreadLocal(){
@Override
protected ThreadLocalSingleton initialValue(){
return new ThreadLocalSingleton();
}
};
public static ThreadLocalSingleton getInstance(){
return threadLocalSingleton.get();
}
}
缺点: 存在线程不安全问题,在ThreadLocal本线程内安全,但在其他线程中就容易出现线程不安全。