单例模式以及通过反射和序列化破解单例模式
来源:互联网 发布:北京亚信数据有限公司 编辑:程序博客网 时间:2024/04/28 06:16
- 饿汉方式
- 懒汉方式
- 双重检查加锁懒汉方式
- 内部类方式
- 枚举方式
破解单例模式有两种方式:通过反射的方式和通过序列化的方式。下面将一一对此进行分析。
饿汉方式非常简单,即使用一个初始化的静态变量,代码如下:
1
public
class
EagerSingleton {
2
private
static
final
EagerSingleton instance =
new
EagerSingleton();
3
4
private
EagerSingleton(){}
5
6
public
static
EagerSingleton getInstance() {
7
return
instance;
8
}
9
}
懒汉模式和饿汉模式类似,只是静态变量定义时不进行初始化,调用getInstance()时才进行初始化,这就需要考虑多线程时问题,使用synchronized关键字修饰方法即可:
01
class
LazySingleton {
02
private
static
LazySingleton instance =
null
;
03
private
LazySingleton() {}
04
public
static
synchronized
LazySingleton getInstance() {
05
if
(instance ==
null
) {
06
instance =
new
LazySingleton();
07
}
08
return
instance;
09
}
10
}
懒汉模式实际是一种懒加载,但是为了避免多线程时单例失效,必须对getInstance()方法进行同步。可以使用双重检查方式来避免对方法全部进行加锁:
01
class
DoubleCheckSingleton {
02
private
static
volatile
DoubleCheckSingleton instance =
null
;
03
private
DoubleCheckSingleton() {}
04
public
static
DoubleCheckSingleton getInstance() {
05
if
(instance ==
null
) {
06
synchronized
(DoubleCheckSingleton.
class
) {
07
if
(instance ==
null
) {
08
instance =
new
DoubleCheckSingleton();
09
}
10
}
11
}
12
return
instance;
13
}
14
}
这个需要几个注意点:静态类变量必须声明为private static volatile,这样可以 写入操作 happens-before 于每一个后续的同一个字段的读操作。在getInstance()方法中,第一次判断null后,使用同步防止多线程时破坏单例。
第四种是使用内部类的方式,也是一种懒加载的方式实现:
1
class
InnerClassLazySingleton {
2
private
static
class
SingletonCreator {
3
private
static
final
InnerClassLazySingleton instance =
new
InnerClassLazySingleton();
4
}
5
public
static
InnerClassLazySingleton getInstance() {
6
return
SingletonCreator.instance;
7
}
8
}
内部类只在第一次调用的时候才会被类加载器加载,实现了懒加载
同时由于instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性
但是上面四种方式都可以通过反射的方式来破坏单例:
1
InnerClassLazySingleton eagerSingleton = InnerClassLazySingleton.getInstance();
2
Constructor<InnerClassLazySingleton> constructor = InnerClassLazySingleton.
class
.getDeclaredConstructor();
3
constructor.setAccessible(
true
);
4
5
InnerClassLazySingleton eagerSingletonFromRef = constructor.newInstance();
6
System.out.println(
"使用反射破解饥渴单例模式:"
+ (eagerSingleton == eagerSingletonFromRef ?
"否"
:
"是"
));
并且如果实现了Serializable接口的话,通过序列化的方式也可以破坏单例:
1
ByteArrayOutputStream baos =
new
ByteArrayOutputStream();
2
ObjectOutputStream oos =
new
ObjectOutputStream(baos);
3
oos.writeObject(eagerSingleton);
4
5
ObjectInputStream ois =
new
ObjectInputStream(
new
ByteArrayInputStream(baos.toByteArray()));
6
InnerClassLazySingleton eagerSingletonFromSerial = (InnerClassLazySingleton) ois.readObject();
7
System.out.println(
"使用序列化破解饥渴单例模式:"
+ (eagerSingleton == eagerSingletonFromSerial ?
"否"
:
"是"
));
可以通过在单例类中添加readResolve()方法的方式来解决:
01
class
EagerSingleton
implements
Serializable {
02
private
static
final
EagerSingleton instance =
new
EagerSingleton();
03
private
EagerSingleton() {}
04
public
static
EagerSingleton getInstance() {
05
return
instance;
06
}
07
08
private
Object readResolve() {
09
System.out.println(
"print from readResolve() method"
);
10
return
getInstance();
11
}
12
13
private
void
readObject(ObjectInputStream ois)
throws
IOException, ClassNotFoundException {
14
System.out.println(
"print from readObject() method"
);
15
ois.defaultReadObject();
16
}
17
}
readResolve()方法是用来替换从流中读取的对象的,它在readObject(ObjectInputStream)方法之后被调用,readObject()方法即从流中读取对象的方法。这样就可以避免使用序列化方式破坏单例。
但是上面的四种方式还是无法避免反射的方式来破坏单例的情况,可以使用枚举的方式实现单例:
1
enum
EnumSingleton {
2
INSTANCE;
3
4
public
static
void
method() {
5
6
}
7
}
通过反射方式创建实例时会抛出 Exception in thread "main" java.lang.NoSuchMethodException: net.local.singleton.EnumSingleton.<init>() 异常。并且使用枚举类Enum实现了序列化接口,并且也实现了readResolve()方法,因为使用序列化方式也没法破坏,是最理想的单例模式实现。
- 单例模式以及通过反射和序列化破解单例模式
- 单例模式以及通过反射和序列化破解单例模式
- 序列化 反射破坏单例模式
- 反射的方式破解单例模式
- 设计模式——单例模式(防止序列化以及反射机制侵犯)
- 单例模式的破解 (反射及序列化方式)
- 反射 序列化 克隆对单例模式的破坏
- 单例设计模式反射,序列化漏洞及解决方案
- 单例模式的破解和保护
- 单例模式,防止反射和反序列化漏洞
- 通过反射创建单例模式的另一个实例
- 通过java反射机制,获取单例模式中的方法
- 单例模式的序列化
- 单例模式以及多线程
- java 单例模式剖析(涉及同步,Junit,log4j,反射,类载入器,多线程,序列化)
- 单例模式(多线程不安全,序列化不安全,反射不安全实例)
- 懒汉和饿汉单例模式以及单例模式的概念
- 设计模式2--破解单例
- (笔记)动画
- sqlite操作总结(一)
- iOS Facebook POP
- sql with as用法详解
- 【SSH系列】Hibernate映射 -- 一对一单向关联映射
- 单例模式以及通过反射和序列化破解单例模式
- java—数据类型
- Makefile 编写实例
- HBase 常见Shell命令总结
- ft5x0x驱动分析
- Python 教程 安桌APP 开源并免费下载
- [转载]11条Java异常处理的最佳实践
- 深入HBase架构解析
- SQLyog 使用介绍