Java单例模式的实现与破坏
单例模式是⼀种设计模式,是在整个运⾏过程中只需要产⽣⼀个实例。那么怎样去创建呢,以下提供了⼏种⽅案。
⼀、创建单例对象
懒汉式
public class TestSingleton {
// 构造⽅法私有化
private TestSingleton(){}
// 声明实例
private static TestSingleton singleton;
// 提供外部调⽤⽅法,⽣成并获取实例
public static TestSingleton getInstance() {
if(singleton == null) {
singleton = new TestSingleton();
}
return singleton;
}
}
此⽅案是以时间换空间,启动时并不会执⾏任何操作,只有被调⽤时,采取实例化对象。不过这种⽅法在多线程下不安全,因为两个线程如果同时调⽤时,会同时通过⾮空验证的验证,造成创建两个对象的后果,有悖设计初衷。
针对多线程问题,应该加⼊双重⾮空判断:
public class TestSingleton {
// 构造⽅法私有化
private TestSingleton(){}
// 声明实例
private static volatile TestSingleton singleton;
// 提供外部调⽤⽅法,⽣成并获取实例
public static TestSingleton getInstance() {
if(singleton == null) {
synchronized (TestSingleton.class) {
if(singleton == null) {
singleton = new TestSingleton();
}
}
}
return singleton;
}
}
饿汉式
public class TestSingleton {
// 构造⽅法私有化
private TestSingleton(){}
// 声明并⽣成实例
private static TestSingleton singleton = new TestSingleton();
// 提供外部调⽤⽅法,获取实例
public static TestSingleton getInstance() {
return singleton;
}
}
以空间换时间,类⼀加载时,就对其进⾏实例化,后⾯调⽤时直接提供对象实例。
静态内部类实现懒加载
public class TestSingleton {
// 构造⽅法私有化
private TestSingleton(){}
private static class TestSingletonFactory{
private static TestSingleton singleton = new TestSingleton();
}
// 提供外部调⽤⽅法,获取实例
public static TestSingleton getInstance() {
return TestSingletonFactory.singleton;
}
}
当getInstance⽅法被调⽤时,才会初始化静态内部类TestSingletonFactory的静态变量singleton。此处由JVM来保障线程安全。⼆、破坏单例
实现单例后,按照预期结果应该所有对象都是同⼀个对象。但是以下有⼏种情况可以破坏单例的性质。
⾸先让单例类实现Serializable, Cloneable接⼝,以便实验。
public class TestSingleton implements Serializable, Cloneable{
private static final long serialVersionUID = 1L;
// 构造⽅法私有化
private TestSingleton(){}
private static class TestSingletonFactory{
private static TestSingleton singleton = new TestSingleton();
}
// 提供外部调⽤⽅法,获取实例
public static TestSingleton getInstance() {
return TestSingletonFactory.singleton;
}
}
序列化
// 获取实例
TestSingleton originSingleton = Instance();
// 写出对象
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(originSingleton);
// 写⼊对象
ByteArrayInputStream bis = new ByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
TestSingleton serializeSingleton = (TestSingleton) adObject();
/
/ 判断两个对象是否相等
System.out.println(originSingleton == serializeSingleton); // false
反射
// 反射
Class<TestSingleton> clazz = TestSingleton.class;
// 获取⽆参构造函数
Constructor<TestSingleton> constructor = DeclaredConstructor();
// 将私有设置为可见
constructor.setAccessible(true);
// ⽤构造器⽣成实例
TestSingleton instance = wInstance();
/
/ 判断两个对象是否相等
System.out.println(originSingleton == instance); // false
克隆
// 克隆
TestSingleton clone = (TestSingleton) originSingleton.clone();
System.out.println(originSingleton == clone); // false
三、修复破坏
对于这种预料之外的结果,我们应该怎样去控制呢?
序列化
添加readResolve⽅法,返回Object。
反射
添加全局可见变量,如果再次调⽤构造⽅法⽣成实例时,抛出运⾏时错误。
克隆
重写clone⽅法,直接返回单例对象。
public class TestSingleton implements Serializable, Cloneable{
private static final long serialVersionUID = 1L;
private static volatile boolean isCreated = false;//默认是第⼀次创建
// 构造⽅法私有化
private TestSingleton(){
if(isCreated) {
throw new RuntimeException("实例已经被创建");
}
单例模式的几种实现方式isCreated = true;
}
private static class TestSingletonFactory{
private static TestSingleton singleton = new TestSingleton();
}
// 提供外部调⽤⽅法,获取实例
public static TestSingleton getInstance() {
return TestSingletonFactory.singleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return getInstance();
}
/**
* 防⽌序列化破环
* @return
*/
private Object readResolve() {
return getInstance();
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论