单例模式:
- 定义:一个类只有一个实例,该实例提供全局唯一访问点。
- 应用场景:全局配置文件,线程池,数据库连接池。
- 实现单例模式要考虑多个维度:
1)懒加载
2)线程安全
3)能否防止反射入侵——通过反射的方式是可以访问私有的方法的,而我们防止对象被多次实例化的方式之一是将类的构造器设置 为私有的,所以反射可以破坏这个条件
防止反射入侵可以在构造方法中抛出异常来达到目的,在第二次调用构造器的时候直接抛出异常。如下:
private Singleton(){
if(Singleton != null){
throw new RuntimeException("单例构造器仅能被调用一次");
}
}
如下代码验证通过反射可以生成两个不同的实例:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Singleton {
private static Singleton instance = null;
private Singleton() { }
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public static void main(String[] args) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException,
InstantiationException {
Singleton singleton = Singleton.getInstance();
Constructor<Singleton> constructor = Singleton.class
.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton newSingleton = constructor.newInstance();
System.out.println(singleton == newSingleton);
}
}
4)反序列化时是否安全,防止反序列化时生成多个对象的方式是添加一个readResolve方法,在反序列化时会调用我们
自定义的readResolve方法,我们再在反序列化时调用我们添加的readResolve方法返回当前单例对象
private Object readResolve(){
return singleton;
}
如下代码验证通过反序列化可以生成两个不同实例:
import java.io.*;
public class Singleton implements Serializable{
private static Singleton instance = null;
private Singleton() { }
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Singleton instance = Singleton.getInstance();
File singletonFile = new File("singleton");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(
new FileOutputStream(singletonFile));
objectOutputStream.writeObject(instance);
ObjectInputStream objectInputStream = new ObjectInputStream(
new FileInputStream(singletonFile));
Singleton newInstance = (Singleton) objectInputStream.readObject();
System.out.println(instance == newInstance);
}
}
单例模式分为两种情况:
1、懒汉模式:
1)定义:懒汉模式是指在需要使用这个类时才去加载这个类;
2)使用场景:(1)实例化过程比较耗时;(2)某个功能模块在用户场景下并不会使用。
3)实现方式:
a、
/**
* 懒加载但线程不安全
*/
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
b、
/**
* 懒加载且线程安全但效率低
*/
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
c、
/**
* 懒加载且线程安全,通过缩小加锁范围,因而效率更高
*/
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
d、
/**
* 懒加载,线程安全
*
*/
public class Singleton {
private static Singleton instance = null;
private Singleton() { }
public static Singleton getInstance() {
}
// 两个知识点决定了该种方式实现了懒加载:
// 1、类只有在访问到对应的成员才被加载——在这里,只有访问到SingleHolder
// .singleton时才会被加载;
// 2、加载一个类时,其静态内部类不会被同时加载。
private static class SingletonHolder {
private static final Singleton singleton = new Singleton();
}
}
2、饿汉模式:在第一次使用之前就加载好。
1)定义:在第一次使用之前就初始化
2)使用场景:程序启动后需要立即访问
3)实现方式:
a、
/**
* 线程安全
*/
public class Singleton{
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
b、
/**
* 线程安全,防止反射入侵和序列化问题,同时这也是《java effective》中推荐的创建单例模式的方式
*/
public enum Singleton {
// 1、通过枚举是如何做到线程安全的?
// 枚举类型经过编译之后会生成public static final Single SINGLETON的字段,
// 并且在静态块中对该字段进行初始化
// JVM在类加载过程中保证静态块在多线程环境下安全地执行
// 2、通过枚举如下做到单例的?
// 枚举类的构造器是私有的
// 3、如何防止反射入侵的?
// 1)通过反射获取构造方法无法获取枚举类的构造方法
// 2) 通过反射实例化反射类型时会判断是否枚举类型,如果是枚举类型会抛出异常
// 以上两点均由JVM保证
// 4、如何防止反序列问题的?
// 执行反序列化时会调用针对枚举类的方法:readEnum,该方法保证了反序列化时不会实例化
// 多个对象。同样,这种方式由JVM提供
SINGLETON {
public void doSomething() {
// doSomething
}
};
public abstract void doSomething ();
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。