編寫一個Singleton示例
單例設計模式,即某個類在整個系統中只能有一個實例對象可以被獲取和使用的代碼模式。
例如:代表JVM運行環境的RunTime類。
1、要點
-
1)、某個類只能有一個實例。
- 實現:構造器私有化(外面不能隨意去創建它)。
-
2)、它必須自行創建這個類。
- 實現:含有一個該類的靜態變量來保存這個這個唯一實例。
-
3)、它必須向整個系統提供這個實例。
- 實現:①、直接暴露。 ②、提供getter方法。
-
4)、為了強調這個類是個單例,我們可以用final修飾。
如果 xx.properties放在src(類路徑)下,那么我們可以通過prop.load( Singleton.class.getClassLoader().getResourceAsStream("xx.properties"))加載資源。
2、常見的單例形式
-
餓漢式:直接創建對象,不存在線程安全問題
- 直接實例化餓漢式
- 枚舉式(推薦)
- 靜態代碼塊餓漢式
-
懶漢式
- 線程不安全
- 線程安全
- 靜態內部類(推薦)
第一種:直接實例化餓漢式(簡單直接,不存在線程安全問題)
package org.example.singleton;
/**
* 餓漢式:直接創建實例對象,不管是否需要這個對象都會創建實例。
*/
public class Singleton1 {
public static final Singleton1 INSTANCE = new Singleton1();
private Singleton1(){
}
}
第二種:枚舉式(最簡潔,跟上面一樣效果還更簡潔了,推薦)
package org.example.singleton;
/**
* 枚舉類型:表示該類型對象是有限的幾個
*我們限定一個,那么就是單例了
*/
public enum Singleton2 {
INSTANCE
}
第三種:靜態代碼塊餓漢式(適合復雜實例化)
package org.example.singleton;
import java.io.IOException;
import java.util.Properties;
/**
* 餓漢式:直接創建實例對象,不管是否需要這個對象都會創建實例。
*/
public class Singleton3 {
public static final Singleton3 INSTANCE;
private String info;
static {
Properties prop = new Properties();
try {
prop.load(Singleton3.class.getClassLoader().getResourceAsStream("xx.properties"));
} catch (IOException e) {
throw new RuntimeException(e);
}
INSTANCE = new Singleton3(prop.getProperty("info"));
}
private Singleton3(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
}
報錯:
原因:
maven項目的默認classpath問題:classpath的路徑指 src/main/resources
解決:
創建src/main/resources目錄,然后把xx.properties移動進去即可。
第四種:懶漢式(只適合單線程,多線程會有安全性問題)
package org.example.singleton;
import java.io.IOException;
import java.util.Properties;
/**
* 懶漢式:延遲創建實例。
*/
public class Singleton4 {
private static Singleton4 instance;
private Singleton4() {
}
public Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
測試:
第五種:雙檢查
package org.example.singleton;
public class Singleton5 {
private static Singleton5 instance;
private Singleton5() {
}
public static Singleton5 getInstance() {
if (instance == null) {
synchronized (Singleton5.class) {
if (instance == null) {
instance = new Singleton5();
}
}
}
return instance;
}
}
為了安全,加上synchronized (Singleton5.class)同步監視器已經解決了,外面加上一層if判斷是為了性能問題,如果有實例了就不等待鎖了。
第六種:靜態內部類
package org.example.singleton;
/**
* 靜態內部類:我們就不用一個靜態變量去保存這個實例了,我們用一個內部類去存儲實例。
* 延遲創建實例:在當內部類被加載和初始化的時候,才創建INSTANCE實例對象。
* 因為內部類是在加載和初始化時創建實例,所以是線程安全的。
*/
public class Singleton6 {
private static class Nested{
private static final Singleton6 INSTANCE = new Singleton6();
}
private Singleton6() {
}
public static Singleton6 getInstance() {
return Nested.INSTANCE;
}
}