一.什么是单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问他的全局访问稳点。
二.单例模式的几种写法
1.饿汉式单例
public class SingletonInstance{
private static SingletonInstance instance=new SingleInstance;
private SingletonInstance(){
}
public static SingletonInstance instance(){
return instance;
}
}
线程安全的饿汉式单例
public class SingletonInstance{
private static SingletonInstance instance=new SingleInstance;
private SingletonInstance(){
}
public static synchronized SingletonInstance instance(){
return instance;
}
}
2.懒汉式单例
public class SingletonInstance{
private static SingletonInstance instance;
private SingletonInstance instance;
public static SingletonInstance instance(){
if(null==instance){
instance = new SingletonInstance();
}
return instance;
}
}
线程安全的懒汉式单例
public class SingletonInstance{
private static SingletonInstance instance;
private SingletonInstance instance;
public static synchronize SingletonInstance instance(){
if(null==instance){
instance = new SingletonInstance();
}
return instance;
}
}
3.双重检测单例(DCL double check lock)
public class SingletonInstance{
private staic SingletonInstance instance;
private SingletonInstance (){
}
public static SingletonInstance(){
if(null==instance){
synchornize(SingletonInstance.class){
if(null==instance){
instance=new SingletonInstance();
}
}
}
return instance;
}
}
4.枚举单列
public enum SingletonInstance{
INSTANCE
}
5.单列管理类创建单例
public class SingletonInstanceManager {
private static Map<String, Object> objMap = new HashMap<>();
public static void registerService(String key,Object instance) {
if(objMap.containsKey(key)) {
objMap.put(key, instance);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
6.通过静态内部类实现单例
public class SingletonInstance{
private SingletonInstance (){
}
private static class Builder{
private static SingletonInstance singletonInstance = new SingletonInstance();
}
public static SingletonInstance instance(){
return Builder.singletonInstance ;
}
}
三.序列化和反射造成的单例失败及解决办法
1.反射造成的单例失败
public class client {
public static void main(String[] args) {
System.out.println("反射前:");
System.out.println(SingletonInstance.instance());
System.out.println(SingletonInstance.instance());
try {
//反射破坏了单例
Class clz=Class.forName("com.seven.singleton.SingletonInstance");
Constructor constru=clz.getDeclaredConstructor();
System.err.println(constru);
constru.setAccessible(true);
SingletonInstance singletonInstance=(SingletonInstance) constru.newInstance();
System.out.println("反射后:");
System.out.println("singleton="+singletonInstance);
singletonInstance.print();
SingletonInstance singletonInstance1=(SingletonInstance) constru.newInstance();
System.out.println("singleton="+singletonInstance1);
singletonInstance1.print();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
我们把控制台的打印展示出来
反射前:
com.seven.singleton.SingletonInstance@15db9742
com.seven.singleton.SingletonInstance@15db9742
反射后:
private com.seven.singleton.SingletonInstance()
singleton=com.seven.singleton.SingletonInstance@6d06d69c
this is a single
singleton=com.seven.singleton.SingletonInstance@7852e922
this is a single
我们发现通过反射后,会有两个不同的对象了,所以我们可以通过在私有构造方法里面加判断,来解除这个问题,
private SingletonInstance (){
if(null!=Builder.singletonInstance){
throw new RuntimeException("单例模式不允许外界使用构造器!");
}
}
private static class Builder{
private static SingletonInstance singletonInstance = new SingletonInstance();
}
public static SingletonInstance instance(){
return Builder.singletonInstance ;
}
public void print() {
System.out.println("this is a single");
}
这样就解决了
2.序列化造成的单例问题
public class client {
public static void main(String[] args) {
System.out.println("反射前:");
System.out.println(SingletonInstance.instance());
System.out.println(SingletonInstance.instance());
FileOutputStream fileOutputStream = null;
ObjectOutputStream objectOutputStream = null;
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try {
fileOutputStream = new FileOutputStream("SingletonInstance.txt");
objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(SingletonInstance.instance());
System.out.println("序列化对象成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
objectOutputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 2.通过刚才生成的序列化对象文件反序列化生成对象
try {
fileInputStream = new FileInputStream("SingletonInstance.txt");
objectInputStream = new ObjectInputStream(fileInputStream);
SingletonInstance singletonInstance3 = (SingletonInstance) objectInputStream.readObject();
System.out.println(singletonInstance3);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们把控制台的打印展示出来
反射前:
com.seven.singleton.SingletonInstance@22f71333
com.seven.singleton.SingletonInstance@22f71333
序列化对象成功!
com.seven.singleton.SingletonInstance@685cb137
我们发现,反序列化之后,单例就失效了,那怎么解决呢
/**
* 反序列化时通过该默认方法返回对象
* @return
*/
private Object readResolve(){
return Builder.singletonInstance;
}
加上这句即可
四.总结
本文介绍了什么是单例模式以及单例模式的写法,并介绍了单例模式在反射和反序列化失效时的原因和解决办法,如果本文对你有用,可以点个赞,如果有不正之处,欢迎留下你的评论,谢谢!