文章目录
1. 立即加载/“饿汉模式”
立即加载就是使用类的时候已经将对象创建完毕。
public class MyObject {
// 立即加载方式==饿汉模式
private static MyObject myObject = new MyObject();
private MyObject() {
}
public static MyObject getInstance() {
return myObject;
}
}
2. 延迟加载/“懒汉模式”
延迟加载就是在调用get()
方法时,实例才被创建,常见的办法就是在get()
方法中进行new
实例化。
public class MyObject {
private static MyObject myObject = new MyObject();
private MyObject() {
}
public static MyObject getInstance() {
if (myObject != null) {
} else {
myObject = new MyObject();
}
return myObject;
}
}
这种写法在多线程的环境中,就会出现取出多个实例的情况,与单例模式的初衷是相背离的。
2.1 延迟加载/“懒汉模式”的解决方案
使用DCL
双检查锁机制
public class MyObject {
private static MyObject myObject;
private MyObject() {
}
public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
synchronized (MyObject.class) {
if (myObject == null) {
myObject = new MyObject();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}
3. 使用静态内置类实现单例
public class MyObject {
private MyObject() {
}
// 内部类方式
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
}
4. 序列化和反序列化的单例模式实现
public class MyObject implements Serializable {
private static final long serialVersionUID = -2269405513415473373L;
private MyObject() {
}
// 内部类方式
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
protected Object readResolve() throws ObjectStreamException {
System.out.println("called readResolve method");
return MyObjectHandler.myObject;
}
}
public class SaveAndRead {
public static void main(String[] args) {
try {
MyObject myObject = MyObject.getInstance();
FileOutputStream fos = new FileOutputStream("save.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myObject);
oos.close();
fos.close();
FileInputStream fis = new FileInputStream("save.txt");
ObjectInputStream ios = new ObjectInputStream(fis);
MyObject res = (MyObject) ios.readObject();
ios.close();
fis.close();
System.out.println(myObject);
System.out.println(res);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
从内存读出而组装的对象破坏了单例的规则. 单例是要求一个JVM
中只有一个类对象的, 而现在通过反序列化,一个新的对象克隆了出来,与以前序列化的对象不能equals()
。readResolve()
用于替换从流中读取的对象。强制执行单例;读取对象时,将其替换为单例实例。这确保了通过序列化和反序列化单例不会创建另一个实例。
5. 使用enum枚举数据类型实现单例模式
枚举enum
和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用,可以应用这个特性使用单例设计模式。
public class MyObject {
public enum MyEnumSingleton {
singleton;
private Connection connection;
private MyEnumSingleton() {
// 初始化connection
}
public Connection getConnection() {
return connection;
}
}
public static Connection getConnection() {
return MyEnumSingleton.singleton.getConnection();
}
}
return connection;
}
}
public static Connection getConnection() {
return MyEnumSingleton.singleton.getConnection();
}
}