1. 什么是单例模式
单例模式指的是在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存。
2. 如何实现单例模式
1. 饿汉模式
所谓饿汉模式就是立即加载,一般情况下再调用getInstancef方法之前就已经产生了实例,也就是在类加载的时候已经产生了。这种模式的缺点很明显,就是占用资源,当单例类很大的时候,其实我们是想使用的时候再产生实例。因此这种方式适合占用资源少,在初始化的时候就会被用到的类。
/**
* 单例模式:饿汉模式
*/
public class SingletonHungary {
private static SingletonHungary singletonHungary = new SingletonHungary();
private SingletonHungary() {
}
public static SingletonHungary getInstance() {
return singletonHungary;
}
}
2. 枚举式饿汉模式
/**
* 饿汉模式:枚举
*/
public enum SingletonEnumHungary {
INSTANCE;
public void someThing() {
System.out.println("枚举方法实现单例模式");
}
}
2. 懒汉模式
不带锁懒汉模式
/**
* 设计模式:懒汉模式
*/
public class SingletonLazy {
private static SingletonLazy singletonLazy = null;
private SingletonLazy() {
}
public static SingletonLazy getInstance() {
if (null == singletonLazy) {
singletonLazy = new SingletonLazy();
}
return singletonLazy;
}
}
带锁的懒汉模式
/**
* 加上锁的懒汉模式
*/
public class SingletonLazyThread {
private static SingletonLazyThread singletonLazyThread;
private SingletonLazyThread() {
}
public static SingletonLazyThread getInstance() {
try {
if (null == singletonLazyThread) {
Thread.sleep(1000);
synchronized (SingletonLazyThread.class) {
if (null == singletonLazyThread) {
singletonLazyThread = new SingletonLazyThread();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return singletonLazyThread;
}
}
3. 静态内部类
/**
* 静态内部类
*/
public class SingletonStaticInner {
public SingletonStaticInner() {
}
private static class SingletonInner {
private static SingletonStaticInner singletonStaticInner = new SingletonStaticInner();
}
public static SingletonStaticInner getInstance() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
return SingletonInner.singletonStaticInner;
}
}
可以看到使用这种方式我们没有显式的进行任何同步操作,那他是如何保证线程安全呢?和饿汉模式一样,是靠JVM保证类的静态成员只能被加载一次的特点,这样就从JVM层面保证了只会有一个实例对象。那么问题来了,这种方式和饿汉模式又有什么区别呢?不也是立即加载么?实则不然,加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
可以说这种方式是实现单例模式的最优解。
4. 静态代码块
这里提供了静态代码块实现单例模式。这种方式和第一种类似,也是一种饿汉模式。
/**
* 懒汉模式:静态代码块
*/
public class SingletonStaticBlock {
private static SingletonStaticBlock singletonStaticBlock;
static {
singletonStaticBlock = new SingletonStaticBlock();
}
public static SingletonStaticBlock getInstance() {
return singletonStaticBlock;
}
}
5. 序列化与反序列化
/**
* 懒汉模式:序列化和反序列化
* 使用匿名内部类实现单例模式,在遇见序列化和反序列化的场景,得到的不是同一个实例
* 解决这个问题是在序列化的时候使用readResolve方法,即去掉注释的部分
*/
public class SingletonStaticInnerSerialize implements Serializable {
private static final long serialVersionUID = 1L;
private static class InnerClass {
private static SingletonStaticInnerSerialize innerSerialize = new SingletonStaticInnerSerialize();
}
public static SingletonStaticInnerSerialize getInstance() {
return InnerClass.innerSerialize;
}
protected Object readResolve() {
System.out.println("调用了readResolve方法");
return InnerClass.innerSerialize;
}
}
测试代码:
/**
* 测试静态内部类
*/
public class TestSingletonStaticInnerSerializable {
public static void main(String[] args) {
try {
SingletonStaticInnerSerialize serialize = SingletonStaticInnerSerialize.getInstance();
System.out.println(serialize.hashCode());
// 序列化
FileOutputStream fo = new FileOutputStream("tem");
ObjectOutputStream oos = new ObjectOutputStream(fo);
oos.writeObject(serialize);
oos.close();
fo.close();
// 反序列化
FileInputStream fi = new FileInputStream("tem");
ObjectInputStream ois = new ObjectInputStream(fi);
SingletonStaticInnerSerialize serialize1 = (SingletonStaticInnerSerialize) ois.readObject();
ois.close();
fi.close();
System.out.println(serialize1.hashCode());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
可以看到:
865113938
1078694789
结果表明的确是两个不同的对象实例,违背了单例模式,那么如何解决这个问题呢?解决办法就是在反序列化中使用readResolve()方法,将上面的注释代码去掉,再次运行:
865113938
调用了readResolve方法
865113938
问题来了,readResolve()方法到底是何方神圣,其实当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象了, 单例规则也就得到了保证。readResolve()的出现允许程序员自行控制通过反序列化得到的对象。
转载地址:https://blog.youkuaiyun.com/u014672511/article/details/79774847