一.什麼是單例模式(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;
}
加上這句即可
四.總結
本文介紹了什麼是單例模式以及單例模式的寫法,並介紹了單例模式在反射和反序列化失效時的原因和解決辦法,如果本文對你有用,可以點個贊,如果有不正之處,歡迎留下你的評論,謝謝!