都2019年最后一天了,面試的時(shí)候不要還只會(huì)說(shuō)餓漢式实胸、懶漢式他嫡,顯得你很沒競(jìng)爭(zhēng)力
單例模式特點(diǎn)
- 構(gòu)造方法私有化
- 通過(guò)一個(gè)靜態(tài)方法去獲取其實(shí)例
- 在全局中對(duì)象有且只有一個(gè)番官,多線程中需要考慮到線程安全
- 在反序列化時(shí),不要被重新創(chuàng)建新的對(duì)象
面試場(chǎng)景重現(xiàn)
面試官:說(shuō)下單例模式吧
你:?jiǎn)卫J椒譃轲I漢式钢属、懶漢式徘熔、DCL……balabala
面試官:還有其他的寫法嗎?
你:額署咽,我平時(shí)用的比較多的就是這幾個(gè)(我之前面試都是這樣回答的啊近顷,怎么換到現(xiàn)在不行了?宁否?窒升?what?慕匠?饱须?)
單例模式的幾種寫法
1、餓漢式
public class Singleton{
// 不管你要不要台谊,我先給你準(zhǔn)備好
private static Singleton INSTANCE= new Singleton();
// 構(gòu)造方法私有化
private Singleton(){}
// 通過(guò)一個(gè)靜態(tài)方法去獲取其實(shí)例
public static Singleton getInstance(){
return INSTANCE;
}
}
很明顯上面寫的單例蓉媳,滿足了單例模式的兩個(gè)特點(diǎn),第一個(gè)構(gòu)造方法私有化锅铅,第二通過(guò)一個(gè)靜態(tài)方法去獲取其實(shí)例酪呻。餓漢式不管你要不要,我都給你初始化好盐须,有時(shí)候我只想在調(diào)用的時(shí)候才初始化怎么辦呢玩荠?那就往下面看吧~
2、懶漢式
public class Singleton{
private static Singleton INSTANCE = null;
// 構(gòu)造方法私有化
private Singleton(){}
// 通過(guò)一個(gè)靜態(tài)方法去獲取其實(shí)例
public static synchronized Singleton getInstance(){
if(null == INSTANCE){
instance = new Singleton();
}
return INSTANCE;
}
}
懶漢式相比餓漢式做了改變贼邓,在調(diào)用的時(shí)候再去進(jìn)行初始化阶冈,且方法上加上了synchronized關(guān)鍵字以示同步,雖然解決了餓漢式的問題塑径,但每次調(diào)用getInstance()方法的時(shí)候女坑,都會(huì)產(chǎn)生同步開銷,一兩個(gè)還好统舀,如果是好多個(gè)呢匆骗?那還是有必要優(yōu)化一下的
3、Double Check Lock(DCL)
public class Singleton {
// 加上了volatile關(guān)鍵字(禁止指令重排序)
private static volatile Singleton INSTANCE = null;
// 構(gòu)造方法私有化
private Singleton(){}
// 通過(guò)一個(gè)靜態(tài)方法去獲取其實(shí)例
public static Singleton getInstance() {
if (null == INSTANCE ) { // Single Checked
synchronized (Singleton.class) {
if (null == INSTANCE ) { // Double checked
INSTANCE = new Singleton();
}
}
}
return INSTANCE ;
}
}
什么是指令重排序绑咱?volatile是什么绰筛?寫太多不想看
4、靜態(tài)內(nèi)部類
public class Singleton{
private static class SingletonHolder{
public static Singleton INSTANCE = new Singleton();
}
// 構(gòu)造方法私有化
private Singleton(){}
// 通過(guò)一個(gè)靜態(tài)方法去獲取其實(shí)例
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
和餓漢式一樣描融,利用類加載機(jī)制來(lái)保證只創(chuàng)建一個(gè)實(shí)例,但是通過(guò)靜態(tài)內(nèi)部類的方式去創(chuàng)建的衡蚂,只要應(yīng)用中不使用內(nèi)部類窿克,JVM就不會(huì)去加載這個(gè)單例類骏庸,也就不會(huì)創(chuàng)建單例對(duì)象,從而實(shí)現(xiàn)懶漢式的延遲加載年叮。也就是說(shuō)可以把靜態(tài)內(nèi)部類單例理解成是餓漢式和懶漢式的結(jié)合具被。但是,它有個(gè)致命的弱點(diǎn)只损,如果你的這個(gè)單例需要傳context一姿,它就沒辦法滿足你了。
5跃惫、枚舉
class Resource{
}
public enum SomeThing {
INSTANCE;
private Resource instance;
private SomeThing() {
instance = new Resource();
}
public Resource getInstance() {
return instance;
}
}
說(shuō)實(shí)話叮叹,枚舉的單例寫法,我是沒寫過(guò)爆存。你們呢?不研究了蛉顽,任性……
6、容器
public class SingletonManager {
private static Map<String,Object> map=new HashMap<String, Object>();
private SingletonManager(){}
public static void registerService(String key,Object instance){
if (!map.containsKey(key)){
map.put(key,instance);
}
}
public static Object getService(String key){
return map.get(key);
}
}
這我也沒用過(guò)先较,但面試時(shí)候要會(huì)說(shuō)吧携冤,看代碼就知道了,是單例的統(tǒng)一管理類闲勺,通過(guò)一個(gè)HashMap容器去裝曾棕。使用時(shí)通過(guò)key來(lái)獲取對(duì)應(yīng)類型的對(duì)象,這種方式使得我們可以管理多種類型的單例菜循,并且在使用時(shí)可以通過(guò)統(tǒng)一的接口進(jìn)行操作翘地。
你以為完了嗎?
餓漢式债朵、懶漢式子眶、DCL、靜態(tài)內(nèi)部類的單例寫法都有可能會(huì)出現(xiàn)一個(gè)問題序芦,那就是反序列化的時(shí)候臭杰,會(huì)被重新創(chuàng)建對(duì)象。那么問題來(lái)了谚中,怎么解決呢渴杆?
public class Singleton implements Serializable {
……
private Object readResolve() throws ObjectStreamException {
// instead of the object we're on,
// return the class variable INSTANCE
return INSTANCE;
}
}
要想寫readResolve()這個(gè)方法,請(qǐng)記得讓你的單例實(shí)現(xiàn)Serializable接口哦~