android的單例模式學(xué)習(xí)
1 餓漢單例模式
public class AppUtils_0 {
private static final AppUtils_0 APP_UTILS_0 = new AppUtils_0();
private AppUtils_0() {
Log.e("text123", "AppUtils_0: " );
}
public static AppUtils_0 getInstance() {
return APP_UTILS_0;
}
}
優(yōu)點(diǎn) :
類在被加載的時(shí)候,單件模式實(shí)例就已經(jīng)被創(chuàng)建彩掐。
如果單件模式實(shí)例在系統(tǒng)中經(jīng)常會被用到,餓漢式是一個(gè)不錯(cuò)的選擇宦棺。
缺點(diǎn) :
類在被加載的時(shí)候,單件模式實(shí)例就已經(jīng)被創(chuàng)建,會占用資源
如果此類用的不是很頻繁就虧了
2.0 懶漢模式
public class AppUtils_1 {
private static AppUtils_1 APP_UTILS_1;
public static synchronized AppUtils_1 getInstance () {
if (APP_UTILS_1 == null) {
APP_UTILS_1 = new AppUtils_1();
}
return APP_UTILS_1;
}
}
優(yōu)點(diǎn) :
這樣寫的單例模式只有在使用的時(shí)候才會被實(shí)例化,在一定程度上節(jié)約了資源
缺點(diǎn) :
第一次加載的時(shí)候需要及時(shí)進(jìn)行實(shí)例化,反應(yīng)稍慢,最大的問題是每次調(diào)用getInstance 的時(shí)候都會同步
這樣就會造成不必要的同步開銷,所以這樣的寫法不建議使用.
2.1 懶漢模式 (改進(jìn)版)
public class AppUtils_1_1 {
private static AppUtils_1_1 APP_UTILS_1_1;
public static AppUtils_1_1 getInstance () {
if (APP_UTILS_1_1 == null) {
synchronized (AppUtils_1_1.class){
if (APP_UTILS_1_1 == null) {
APP_UTILS_1_1 = new AppUtils_1_1();
}
}
}
return APP_UTILS_1_1;
}
}
對比
相對對于上一個(gè)模式這個(gè)模式在getInstance方法中對instance進(jìn)行了兩次判空
第一次判斷是為了避免不必要的同步 第二次就是為了判斷在null的情況下創(chuàng)建實(shí)例
這樣就避免了反復(fù)同步也達(dá)到了單例的要求.
隱患
這個(gè)寫法大概做了3件事情
1 給AppUtils 的實(shí)例分配內(nèi)存
2 調(diào)用AppUtils的構(gòu)造函數(shù),初始化成員字段
3 將APP_UTILS_1_1對象指向分配的內(nèi)存空間
但是 java的編譯器允許處理器亂序執(zhí)行以及Cache,
寄存器到內(nèi)存回寫內(nèi)存順序規(guī)定上面的 2 和3 是沒有先后的順序的
就是說 執(zhí)行順序可以是 123 也可以是132
就是在兩個(gè)線程中同時(shí)執(zhí)行g(shù)etInstance()函數(shù) 假設(shè)a線程在執(zhí)行很快 APP_UTILS_1_1就不會是空了
b線程就會直接取走APP_UTILS_1_1在使用就會有問題了
優(yōu)點(diǎn) :
資源利用率高,第一次執(zhí)行g(shù)etInstance是單例對象才會被實(shí)例化
缺點(diǎn) :
第一次加載反應(yīng)稍慢,也由于java內(nèi)存模型的原因偶爾加載失敗
在高并發(fā)環(huán)境下也有一定的缺陷,雖然概率小
3 靜態(tài)內(nèi)部單例模式
public class AppUtils_2 {
private AppUtils_2() {
}
private static class AppUtils_2Holder {
private static final AppUtils_2 UTILS_2 = new AppUtils_2();
}
public static AppUtils_2 getInstance() {
return AppUtils_2Holder.UTILS_2;
}
}
說明 :
當(dāng)?shù)谝淮渭虞dAppUtils_2類是并不會初始化 UTILS_2 只有在第一次調(diào)用AppUtils_2的getInstance
才會初始化 UTILS_2 類 因此第一次調(diào)用getInstance會導(dǎo)致虛擬機(jī)加載AppUtils_2Holder類
這樣的方法不僅保證線程安全也能保證單例對象唯一性同時(shí)也實(shí)現(xiàn)了單例的實(shí)例化
所以推薦使用這個(gè)方法
static在內(nèi)存中是怎么分配的
為什么靜態(tài)修飾后反復(fù)獲取這個(gè)類 就是唯一的一個(gè)呢?在內(nèi)存中有是怎么存放的呢?
堆
Java的堆是一個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū),類的對象從中分配空間帮匾。
這些對象通過new亏拉、newarray、anewarray和multianewarray等指令建立走孽,
它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負(fù)責(zé)的琳状,
堆的優(yōu)勢是可以動態(tài)地分配內(nèi)存大小磕瓷,生存期也不必事先告訴編譯器,
因?yàn)樗窃谶\(yùn)行時(shí)動態(tài)分配內(nèi)存的,Java的垃圾收集器會自動收走這些不再使用的數(shù)據(jù)困食。
但缺點(diǎn)是边翁,由于要在運(yùn)行時(shí)動態(tài)分配內(nèi)存,存取速度較慢.
棧
棧的優(yōu)勢是硕盹,存取速度比堆要快符匾,僅次于寄存器,棧數(shù)據(jù)可以共享瘩例。
但缺點(diǎn)是啊胶,存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性垛贤。棧中主要存放一些基本類型的變量
(int, short, long, byte, float, double, boolean, char)和對象句柄焰坪。
一般的類 在申明的時(shí)候在棧中設(shè)置一個(gè)地址存放的是為null的應(yīng)用
在XX xx = new XX();的時(shí)候會在堆中分配內(nèi)存存放xx 然后堆中的引用指向這個(gè)地址
static
內(nèi)存不止分為堆和棧,還有另外3個(gè)區(qū),
靜態(tài)變量在靜態(tài)存儲區(qū) 所以所有的靜態(tài)變量和靜態(tài)內(nèi)都是在這個(gè)靜態(tài)區(qū)的
一個(gè)類中南吮,一個(gè)static變量只會有一個(gè)內(nèi)存空間琳彩,雖然有多個(gè)類實(shí)例,但這些類實(shí)例中的這個(gè)static變量會共享同一個(gè)內(nèi)存空間.
就是說XX類 有很多new出來的類 xx_0 xx_1; 但是XX類中的static修飾的 變量 在xx_0 或者xx_1 中都是一個(gè)地址
所以這才是單例模式的原理
參考地址 https://www.cnblogs.com/avivahe/p/5747127.html
參考地址 https://blog.csdn.net/qianyayun19921028/article/details/80365126
參考地址 http://www.reibang.com/p/202f6abb229c
q 靜態(tài)內(nèi)部內(nèi) 中也存在靜態(tài)變量和非靜態(tài)變量 這些內(nèi)存有是怎么分配的呢?
a :靜態(tài)內(nèi)部類中的靜態(tài)變量也是在方法區(qū)中的部凑,
這里我們可以把靜態(tài)內(nèi)部類看成一 個(gè)類這個(gè)類本身是在堆區(qū)的(他和類是平級的)靜態(tài)內(nèi)部類的加載和一般的類也是一樣的
q 接口類 和接口函數(shù)是如何分配內(nèi)存的?
a :看接口的調(diào)用他是在有實(shí)例化的情況才能使用函數(shù) 但是他的常量是默認(rèn)用靜態(tài) 修飾的