相关文章
【合集】Java设计模式
应用
{@link Runtime#getRuntime()}
实现方式
静态成员变量(饿汉式)
1
2
3
4
5
6
7
8
9
|
public static class Singleton1 {
private static final Singleton1 INSTANCE = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return INSTANCE;
}
}
|
静态代码块(饿汉式)
1
2
3
4
5
6
7
8
9
10
11
12
|
public static class Singleton2 {
private static final Singleton2 INSTANCE;
private Singleton2() {
}
static {
INSTANCE = new Singleton2();
}
public static Singleton2 getInstance() {
return INSTANCE;
}
}
|
枚举(饿汉式)
1
2
3
|
public enum Singleton3 {
INSTANCE;
}
|
双重检查锁(懒汉式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public static class Singleton4 {
// 不加volatile修饰,多线程下,JVM的指令重排序有可能会导致空指针
// 添加volatile的双重检查锁模式是一种比较好的单例实现模式,解决了单例、性能、多线程安全问题。
private static volatile Singleton4 instance;
private Singleton4() {
}
public static Singleton4 getInstance() {
if (instance != null) {
return instance;
}
synchronized (Singleton4.class) {
if (instance != null) {
return instance;
}
instance = new Singleton4();
return instance;
}
}
}
|
静态内部类(懒加载)
JVM在加载外部类时不加载静态内部类,静态内部类在属性/方法被调用时才会被加载, 并初始化其静态属性。
静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。
优秀的单例模式,开源项目常用。
不加锁的情况下,保证了多线程下的安全,没有任何性能影响和空间的浪费。
1
2
3
4
5
6
7
8
9
10
11
|
public static class Singleton5 {
private Singleton5() {
}
private static class SingletonHolder {
private static final Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance() {
return SingletonHolder.INSTANCE;
}
}
|
序列化会破坏单例
解决方案
单例类中添加readResolve()方法,在反序列化时被反射调用,
如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public static class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
/**
* 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
* 反射调用单例类中的 readResolve 方法 Object rep = desc.invokeReadResolve(obj);
*/
@Serial
private Object readResolve() {
return INSTANCE;
}
}
|
原理
逐步进入源码
1
2
3
4
|
{@link ObjectInputStream#readObject(Class)}
{@link ObjectInputStream#readObject0(Class, boolean)}
return checkResolve(readOrdinaryObject(unshared));
{@link ObjectInputStream#readOrdinaryObject(boolean)}
|
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public static class Client {
public static void main(String[] args) throws Exception {
writeObjectToFile();
Singleton s1 = readObjectFromFile();
Singleton s2 = readObjectFromFile();
System.out.println(s1 == s2); // true
}
// 序列化方法
private static void writeObjectToFile() throws Exception {
try (
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(Client.class.getResource("/").getPath() + "test" + File.separator + "a.txt"));
) {
Singleton instance = Singleton.getInstance();
oos.writeObject(instance);
}
}
// 反序列化方法
private static Singleton readObjectFromFile() throws Exception {
try (
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Client.class.getResource("/").getPath() + "test" + File.separator + "a.txt"));
) {
Singleton instance = (Singleton) ois.readObject();
return instance;
}
}
}
|
反射会破坏单例
解决方案
反射第二次构造,INSTANCE肯定不为空抛出异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public static class Teacher {
private static final Teacher INSTANCE = new Teacher();
private Teacher() {
// 解决方法,反射第二次构造,INSTANCE肯定不为空抛出异常。
if (INSTANCE != null) {
throw new RuntimeException("禁止反射破坏单例");
}
}
public static Teacher getInstance() {
return INSTANCE;
}
}
|
测试
1
2
3
4
5
|
public static void main(String[] args) throws Exception {
Constructor<Teacher> declaredConstructor = Teacher.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor.newInstance() == declaredConstructor.newInstance()); // true
}
|