核心:
- 私有化构造器
- 创建静态属性或静态方法
- 保证一个类有且仅有一个实例,并提供一个访问他的全局访问点。
优点:
- 减小了系统开销。当一个对象的产生需要比较多的资源时(如读取配置、产生其他依赖对象),可以在应用启动时直接产生一个单例对象,永久驻留内存。
- 优化共享资源访问
五种单例实现方式:
- 饿汉式(线程安全,调用效率高,不能延时加载)
- 懒汉式(线程安全,调用效率不高,可以延时加载)
- 双重监测锁式(由于jvm底层为了执行效率,会指令重排,可能会出问题,不建议使用)
- 静态内部类式(线程安全,调用效率高,可以延时加载)
- 枚举单例(线程安全,调用效率高,不能延时加载)
单例对象占用资源少,不需要延时加载,枚举好于饿汉式。
单例对象占用资源大,需要延时加载,静态内部类式好于懒汉式。
饿汉式:
public class Singleton01 {
//类加载时直接创建该类的实例
private static Singleton01 instance = new Singleton01();
private Singleton01() {
}
public static Singleton01 getInstance() {
return instance;
}
}
由于jvm特性,加载类时是线程安全的,所以该方式是线程安全的。
在类加载时直接创建了类的实例,所以不能延时加载。
懒汉式:
public class Singleton02 {
private static Singleton02 instance;
private Singleton02() {
}
//加入了synchronized同步块,导致调用效率低
public static synchronized Singleton02 getInstance() {
if(instance!=null) {
instance = new Singleton02();
}
return instance;
}
}
可以在用的时候时候在创建实例,实现了懒加载,但是调用效率低。
双重监测锁式:
public class Singleton03 {
private static Singleton03 instance;
private Singleton03(){
}
public static Singleton03 getInstance() {
if(instance == null) {
Singleton03 sc;
synchronized (Singleton03.class) {
sc = instance;
if(sc == null) {
synchronized (Singleton03.class) {
if(sc==null) {
sc = new Singleton03();
}
}
instance = sc;
}
}
}
return instance;
}
}
静态内部类式:
public class Singleton04 {
private Singleton04() {
}
public static Singleton04 getInstance() {
return SingletonHolder.instance;
}
//在使用的时候才会加载内部类
private static class SingletonHolder{
private static Singleton04 instance = new Singleton04();
}
}
线程安全调用效率高,并且实现了懒加载
枚举:
public enum Singleton05{
//枚举元素本身就是单例对象
INSTANCE;
}
枚举模式避免了反射和反序列化的漏洞。
可以通过Singleton05.INSTANCE,直接获取到他的对象,枚举没有实现懒加载。
避免反序列化和反射创建新的对象(饿汉式为例):
1.避免反射创建新的对象
反射可以通过setAccessible(true)方式,访问被私有化的构造器。
为了避免这种情况发生可以在构造器中抛出一个异常(一般情况不会考虑)
public class Singleton01 {
//类加载时直接创建该类的实例
private static Singleton01 instance = new Singleton01();
private Singleton01() {
if(instance!=null){
throw new RuntimeException();
}
}
public static Singleton01 getInstance() {
return instance;
}
}
2.避免反序列化创建新的对象
只需要添加一个readResolve()方法就可以了,对象在反序列化时会先看有没有该方法,没有的话创建一个新的对象,但是有就会返回这个方法返回的对象
public class Singleton01 implements Serializable{
//类加载时直接创建该类的实例
private static Singleton01 instance = new Singleton01();
private Singleton01() {
}
public static Singleton01 getInstance() {
return instance;
}
private Object readResolve() throws ObjectStreamException{
return instance;
}
}
感谢高淇java300集