Java總結

weixin_34402408發表於2016-05-12

字串

  • java字串使用+進行拼接時,編譯器會通過StringBuilder進行優化。但是在迴圈體中使用+=的時候會建立多個StringBuilder,可以在迴圈體外顯式使用StringBuilder,可以使得程式效率更高
  • Buffer是執行緒安全的,所以在單執行緒的情況先執行效率低於Builder

單例的寫法

單例一般有兩種寫法,也將其稱為惡漢式和懶漢式,我這裡說的是在沒有 lazy load 等考慮的情況下
沒有帶引數的寫法:

public class Singleton {
    public static final Singleton INSTANCE = new Singleton();

    private Singleton(){}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

帶引數的寫法:

public class Singleton {
    private volatile static Singleton sInstance;
    private static final Object LOCK = new Object();

    private Singleton(Param param){
        // do some init
    }

    public static Singleton getInstance(Param param) {
        if (sInstance == null) {
            synchronized (LOCK) {
                if (sInstance == null) {
                    sInstance = new Singleton(param);
                }
            }
        }
        return sInstance;
    }
}

關於為何需要雙重判空網上有很多介紹的文章,這裡不再多說,需要注意的是需要使用volatile防止重排序問題

this逃逸

this逃逸主要指在構造方法沒有執行完畢的情況下,其他執行緒獲取到了這個物件的引用。需要小心在構造方法中不要使用一些非同步手段將this物件釋出出去。

併發 多執行緒相關

  • 使用 synchronized 關鍵字同步物件的時候應該在使用物件的地方都使用 synchronized來包裹,否則沒有同步效果
  • 將不可變的物件用 final 來修飾可以減少很多同步的問題。final並不表示物件的內容不會變化,只是說不能再賦值。相當於一個指標指向的地址確定了,但是內容沒有限制
  • 正確的中斷執行緒:
class MyThread extends Thread {

    public void run() {
        try {
            doSomeThing();
        } catch (InterruptedException e) {
            doCancel();
        }
    }

    public void cancel() {
        interrupt();
    }
}

還有一種方法是通過在 cancel 方法中設定標誌位來取消執行緒也是可以的,注意將標誌位設定為 volatile 並且正確的處理中斷

  • 執行緒池的設定:執行緒池有三種,計算密集執行緒池、IO執行緒池,newThread(考慮到有可能希望某個任務及早執行而不是線上程池中排隊)。其中計算密集型執行緒池的大小建議為$N(cpu核數)+1$,具體參見 《Java併發程式設計實戰》 8.2 設定執行緒池大小
  • 執行緒如果在處理不可打斷的任務,應該在出現中斷的時候將中斷狀態儲存起來,然後在任務執行完了之後,再響應中斷
class MyThread extends Thread {
    private volatile boolean interrupted = false;
    public void run() {
        try {
            while (true) {
                try {
                    doSomeThing();
                } catch (InterruptedException e) {
                    interrupted = true;
                }
            }
        } finally {
            if (interrupted)
                Thread.currentThread().interrupted();
        }
    }
    public void cancel() {
        interrupted = true;
    }
}
  • Thread#join() 方法可以讓當前執行緒等待目標執行緒執行結束,如果目標執行緒已經結束,則執行當前執行緒後面的程式碼,否則必須等待目標執行緒結束之後(如果設定了超時時間,超時和目標執行緒結束兩個條件滿足一個即可)能執行後面的程式碼。使用join()方法解決定時任務提前結束引起的一些問題,具體參見 《Java併發程式設計實戰》 7.1.4 示例:計時執行

static

  • 使用private static編譯器會優先考慮將方法內聯
  • 無論例項化幾次或者呼叫幾次方法,static程式碼段都執行一次...比如,你建立了類的兩個例項,但是隻有建立第一個例項的時候static程式碼段才被執行.建立第二個的時候將不執行...使用類名呼叫靜態方法也同理
  • 在例項化一個物件的時候也被呼叫.而且先於建構函式被呼叫.也就是說,我們建立一個物件,那麼首先先呼叫static程式碼段,然後再呼叫建構函式

強引用、弱引用、軟引用、虛引用

強引用

強引用是使用最普遍的引用。當記憶體空間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足的問題

Object o=new Object();   //  強引用

軟引用

如果一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體。

String str=new String("abc");                                     // 強引用
SoftReference<String> softRef=new SoftReference<String>(str);     // 軟引用

弱引用

弱引用與軟引用的區別在於:只具有弱引用的物件擁有更短暫的生命週期。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現那些只具有弱引用的物件。

String str=new String("abc");    
WeakReference<String> abcWeakRef = new WeakReference<String>(str);
str=null;

虛引用

虛引用顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定物件的生命週期。如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
幾種引用方式在垃圾回收時候的區別

838203-bb23002828f15f25.jpg

838203-b9a6ae8afbe96ba1.jpg

Java中父類與子類中靜態程式碼段、非靜態程式碼段和構造方法的執行過程

執行流程為:
父類靜態程式碼段->子類靜態程式碼段->父類非靜態程式碼段->父類構造方法->子類非靜態程式碼段->父類構造方法
在子類的構造方法執行前必然會執行父類的構造方法,如果指明瞭super(params),則執行父類中對應的構造方法,否則執行預設沒有引數的構造方法
參考:

  1. http://blog.csdn.net/mazhimazh/article/details/19752475