編輯:關於Android編程
單例模式算是比較常用的模式,在Java中如果想要一個JVM中只存在某個類的一個實例,就需要使用到單例模式,而只存在一個實例的需求一般是因為:
1,對象實例比較大和復雜,創建開銷很大。
2,只需要一個實例來維護整個功能的流程與交互。
例如Android中的電話應用啟動時,對於單卡單待的電話,只創建一個Phone對象,用來管理RIL,CallTracker,ServiceStateTracker等對象,手機中不存在第二個Phone對象去和RILC通信。
單例的類圖很簡單,看起來也很簡單,只需要一個類只能得到一個實例即可,但是我覺得單例比其他創建型的模式要復雜的多。
如果想要創建一個類Singleton。正常來講只需要new Singleton()即可,但是如果想Singleton只存在一個實例,則不能采用這種方法來創建,因為每一次new都會產生一個新的實例:
1,為了不能使用new創建,就要把構造函數變成private的;
2, 為了只有一個實例,就需要Singleton本身去維護這個實例,於是類中需要定義一個Singleton instance,顯然它應該是private的,因為它是類的實例,不是對象的實例,所以它還應該是靜態的;
3,因為不能new,為了能夠有方法得到Singleton的實例,就得通過一個靜態方法返回實例,比如public static Singleton getInstance(),在內部如果instance是null的則新建,否則返回instance即可,<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPsu1wcvV4sO0tuDG5Mq10OjSqtei0uK1xLarzve7ucrHwvm24LXEo6y4+b7dyc/D5rXEt9bO9qOs0tG+rcTcubu0tL2os/bSu7j21+688rWltaXA/cSjyr3By6GjPC9wPg0KPGgxIGlkPQ=="普通餓漢單例模式">普通餓漢單例模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
// Nothing
}
public static Singleton getInstance(){
return instance;
}
public void say(){
System.out.println("I am singleton");
}
}
餓漢單例會在類裝載時就實例化。好處就是由於classloder機制,保證當一個類被加載的時候,這個類的加載是線程互斥的,而餓漢單例的靜態instance直接新建一個實例,在加載的時候就能線程安全的獲得實例。從而避免了線程安全問題。
劣勢就是實例在裝載的時候就會浪費資源和時間去實例化。雖然大多數時候都是在調用getInstance時才會裝載,不過也沒法保證是否有其他方法會使用到instance而導致新建實例。
這個Singleton單例類在被加載的時候就會創建實例,為了讓它在使用的時候才創建對象所以把它設為null。讓它在getInstace的時候再新建實例,也就是惰性加載。這裡也叫懶漢式單例。
public class lazySingleton {
private static lazySingleton instance = null;
private lazySingleton() {
// TODO Auto-generated constructor stub
}
public static lazySingleton getInstace() {
if (instance==null){
instance =new lazySingleton();
}
return instance;
}
public void say(){
System.out.println("I am lazySingleton");
}
}
前面的懶漢例子用在單線程中不會出現問題,但是如果用在單線程中就會出現問題。如果AB兩個線程都通過getInstace去獲取單例的實例,因為沒法保證getInstace方法會在一個線程中一直執行完再執行另一個線程,如果兩個線程都判定instance位null,則有可能都會進入new語句新建實例。
為了保證同一時刻只有一個線程能夠執行getInstance,就需要對方法加鎖,或者在方法內部使用對象鎖,但是注意要把鎖放在if(instance == null) 判斷的外面,否則還是可能出現同時判定true的情況。
public class SyncSingleton {
private static SyncSingleton instance = null;
private SyncSingleton() {
// TODO Auto-generated constructor stub
}
public static synchronized SyncSingleton getInstance(){
if (instance == null){
instance = new SyncSingleton();
}
return instance;
}
}
或者
public class SyncSingleton {
private static SyncSingleton instance = null;
private static final Object classLock = SyncSingleton.class;
private SyncSingleton(){}
public SyncSingleton getInstance(){
synchronized(classLock){
if(instance == null)
instance = new SyncSingleton();
return instance;
}
}
}
上面的問題解決了多線程的問題,但是也會帶來性能的問題,因為每次調用getInstance,都會進行同步,但實際上,如果instance實例已經建立,那直接返回instance實例就好,這裡是不用加鎖的,只有新建實例的情況才需要同步鎖,但是前面也說到了,新建的時候要把鎖放在if(instance == null) 判斷的外面,否則還是可能出現同時判定true的情況。所以就有了下面的雙重檢查的單例模式
public class EffectiveSingleton {
private volatile static EffectiveSingleton instance = null;
private EffectiveSingleton() {
// TODO Auto-generated constructor stub
}
public static EffectiveSingleton getInstance() {
if (instance == null) { // 首先判斷是否已經創建實例,如果已經創建,直接返回,效率高
synchronized (EffectiveSingleton.class) {// 如果沒有創建,然後再同步,並在同步塊內再初始化。注意要再次判斷是否已實例化
if (instance == null) {
instance = new EffectiveSingleton();
}
}
}
return instance;
}
}
這樣當實例初始化已經完成的情況,每次getInstance直接返回即可,不再需要同步鎖。
當第一次調用getInstance時,則通過synchronized塊包裹的代碼部分保證不會多次調用新建實例。
第二個if (instance == null) 條件判斷是為了第一個if (instance == null) 是給已經存在實例的情況用的,AB線程還是有可能都通過第一個條件判斷的。這就需要在同步塊內,一定要有一個條件判斷。
雙重檢查加鎖單例模式很好地解決了加鎖單例的性能問題。
靜態內部類也叫嵌套類,用這個名字給他定義是更加形象的。意思是說內部類和外部類的關系只是層次嵌套關系,所以只是在創建類文件的時候類文件名是如下形式:outer$inner.java,在使用方面完全和兩個普通類一樣。
在餓漢單例的基礎上,把instance = new Singleton()外用一個叫做SingletonHolder 的靜態內部類包裝一下。
這樣即使在Singleton類加載的時候,也不會導致靜態內部類SingletonHolder 的加載,只有在使用到SingletonHolder 的時候,才會加載。
同時由於類的加載的機制的互斥性,保證創建實例時候的線程安全性。
class Singleton {
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
private Singleton() {
}
public static final Singleton getInstance() {
return SingletonHolder.instance;
}
}
通過PhoneFactory創建Phone對象的例子,截取部分如下:
public class PhoneFactory {
static private Phone sProxyPhone = null;
......
public static void makeDefaultPhone(Context context) {
synchronized(Phone.class) {
if (!sMadeDefaults) {
...
sProxyPhone = new PhoneProxy(new GSMPhone(context,
sCommandsInterface, sPhoneNotifier));
...
sMadeDefaults = true;
}
}
}
可以看出這個例子符合上面的加鎖單例模式,雖然不是采用的雙重判斷的方式來增加效率,但是因為PhoneFactory的makeDefaultPhone基本沒有多線程使用情況,只有在Phone應用啟動的情況下調用一起。
並且PhoneFactory通過makeDefaultPhone來創建實例,但是卻使用getDefaultPhone來獲取實例,也就不存在實例已經存在的情況下,還進入同步塊進行判斷的情況。
ListView是Android開發中比較常用的一個組件,它以列表的形式展示信息,並能根據信息的長度自適應顯示。比如說我們手機裡的通訊錄就用到了ListView顯示聯系人
現在大家很少用短信和飛信發信息了,自從微信出現後,微信可以說已經慢慢替代了短信,飛信,QQ等通信方式,很多朋友都選擇使用微信和朋友們溝通和交流,在節假日的時
Widget引入 我們可以把Widget理解成放置在桌面上的小組件(掛件),有了Widget,我們可以很方便地直接在桌面上進行各種操作,例如播放音樂。 
想成為Android的傑出開發工程師,不懂得Android的設計規則怎麼可以,Android4.0問世後谷歌公司為Android程序員規范了一系列的設計原則,不要再盲目的