二十三種設計模式之單例模式
核心作用
保證一個類隻有一個執行個體,并且對外提供一個通路該執行個體的全局通路點。
優點
- 由于單例模式隻生成一個執行個體,減少系統性能開銷
- 單例模式可以在系統設定全局通路點,優化環境共享資源通路
常用應用場景
- windows 系統的任務管理器、資源回收筒;
- 項目中讀取配置檔案的類,一般配置檔案隻需要讀取一次,是以隻需要一個讀取配置檔案的類;
- 網站的計數器
- 應用程式中日志應用,一般都是單例模式實作,由于共享的日志檔案一直處于打開狀态,隻能有一個執行個體操作,否則不好追加
- 資料庫的連接配接池設計
- 作業系統的檔案系統
- 在 Spring 中,每個 bean 預設是單例,這樣做的優點是 Spring 容器很好的管理
- 在 Servlet 程式設計中,每個 Servlet 也是單例
- 在 SpringMVC 中,控制器對象也是單例
步驟
- 私有化構造方法
- 提供私有的靜态的對象的執行個體屬性
- 提供對外通路的靜态方法
五種單例模式
- 餓漢式(線程安全,調用效率高,但是不能延時加載)
- 懶漢式(線程安全,調用效率不高,但是可以延時加載)
- 雙重檢測鎖式(由于 JVM 底層内部模型原因,偶爾會出現問題,不建議使用)
- 靜态内部類式(線程安全,調用效率高,可以延時加載)
- 枚舉單例(線程安全,調用效率高,不能延時加載,實作簡單。由于 JVM 從根本上提供保障,避免通過反射和反序列化的漏洞)
餓漢式
package singleton;
/**
* 餓漢式單例模式
* 類加載時,立即加載這個對象。加載類是,天然的線程安全
*
* @author SIMBA1949
* @date 2019/6/5 21:20
*/
public class Singleton01 {
private static Singleton01 instance = new Singleton01();
private Singleton01() {
}
public static Singleton01 getInstance(){
return instance;
}
}
懶漢式
package singleton;
/**
* 懶漢式單例模式
* 延時加載,資源使用率高,并發情況下 synchronized 效率低
*
* @author SIMBA1949
* @date 2019/6/5 21:39
*/
public class Singleton02 {
private static Singleton02 instance;
private Singleton02() {
}
public static synchronized Singleton02 getInstance(){
if (null == instance){
instance = new Singleton02();
}
return instance;
}
}
雙重檢測鎖單例模式
package singleton;
/**
* 雙重檢測鎖單例模式
*
* @author SIMBA1949
* @date 2019/6/5 21:44
*/
public class Singleton03 {
private static Singleton03 instance;
private Singleton03() {
}
public static Singleton03 getInstance(){
if (null == instance){
synchronized (Singleton03.class){
if (null == instance){
instance = new Singleton03();
}
}
}
return instance;
}
}
靜态内部類單例模式
package singleton;
/**
* 靜态内部類單例模式
* 類初始化的時候不會将内部類一起初始化,隻有調用的時候才會加載靜态内部類,加載類時線程時安全的
* INSTANCE 是 static final 修飾的,保證記憶體中隻有一個執行個體存在
* 兼備并發高效調用和延時加載的優勢
*
* @author SIMBA1949
* @date 2019/6/5 21:54
*/
public class Singleton04 {
private Singleton04() {
}
private static class Singleton04Inter{
public static final Singleton04 INSTANCE = new Singleton04();
}
public static Singleton04 getInstance(){
return Singleton04Inter.INSTANCE;
}
}
枚舉單例模式
package singleton;
/**
* @author SIMBA1949
* @date 2019/6/5 22:01
*/
public enum Singleton05 {
/**
* 這個枚舉元素 INSTANCE 本身就是單例對象
*/
INSTANCE
}
避免反射建立多個對象
package singleton;
/**
* 避免反射建立多個對象,在構造方法中判斷如果執行個體屬性不為 null 時,抛出異常即可
*
* @author SIMBA1949
* @date 2019/6/5 22:08
*/
public class Singleton064Reflect {
private static Singleton064Reflect instance = new Singleton064Reflect();
private Singleton064Reflect() {
if (null != instance){
throw new RuntimeException();
}
}
public static Singleton064Reflect getInstance(){
return instance;
}
}
避免反序列化建立多個對象
package singleton;
import java.io.*;
/**
* 添加 readResolve() 方法可以避免反序列化建立多個對象
*
* @author SIMBA1949
* @date 2019/6/5 22:12
*/
public class Singleton074Serializable implements Serializable{
private static final long serialVersionUID = -3093202203157151493L;
private static Singleton074Serializable instance = new Singleton074Serializable();
private Singleton074Serializable() {
}
public static Singleton074Serializable getInstance(){
return instance;
}
/**
* 添加 readResolve() 方法可以避免反序列化建立多個對象
* @return
*/
private Object readResolve(){
return instance;
}
}
class DeserializableTest{
public static void main(String[] args) throws IOException, ClassNotFoundException {
Singleton074Serializable instance = Singleton074Serializable.getInstance();
// 序列化
FileOutputStream fis = new FileOutputStream(new File("T:/s.java"));
ObjectOutputStream oos = new ObjectOutputStream(fis);
oos.writeObject(instance);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("T:/s.java")));
Singleton074Serializable deInstance = (Singleton074Serializable) ois.readObject();
System.out.println(instance == deInstance);
}
}