天天看點

java final 詳解

簡介

final是java的關鍵字,可以聲明成員變量、方法、類以及本地變量,它所表示的是“這部分是無法修改的”。不想被改變的原因有兩個:效率、設計。

final作用于方法

final 修飾方法,則表明該方法不能被重寫(override),是以對于 final 方法使用的第一個原因是針對設計的,進行方法鎖定,以防止任何子類來對它的修改.

final 方法, 在某些情況下可以對執行效率産生幫助.對于被修飾為final的方法,在編譯器期的時候,有可能會進行

内聯(inline)

優化.

内聯調用:

是編譯器為程式做的一種優化操作.虛拟機不再執行正常的方法調用(參數壓棧,跳轉到方法處執行,再調回,處理棧參數,處理傳回值),而是直接将方法展開,以方法體中的實際代碼替代原來的方法調用。這樣減少了方法調用的開銷。

  1. 如果方法被多次調用,或者内聯的方法将會被多次拷貝,會相應的增加記憶體占用. 這是一種空間置換時間的一個政策.
  2. 如果方法體代碼量過大,拷貝的次數過多,那麼将反而達不到優化的目的.
  3. 對于final方法是否進行内聯,由編譯器決定,并不是所有的final方法都會被内聯.
  4. 編譯器進行内聯優化,并不隻針對final方法, 如單行實作的方法也可能被内聯.

final作用于類

如果某個類用 final 修改,表明該類是最終類,它不希望也不允許其他來繼承它。在程式設計中處于安全或者其他原因,我們不允許該類存在任何變化,也不希望它有子類,這個時候就可以使用 final 來修飾該類了.

final修飾的類,其成員方法也會自動加上final修飾,而成員變量不受影響.

final作用于變量

final修飾變量分為兩種情況, 一種是作用于基本資料類型;一種是作用于引用類型.

  1. 作用于基本資料類型

表示該變量的值不能被修改,在使用

javap -v

反彙編後,可以發現它被标注為

ConstantValue

static final java.lang.String sfs;
    descriptor: Ljava/lang/String;
    flags: ACC_STATIC, ACC_FINAL
    ConstantValue: String xxx
           
  1. 作用在引用類型

表示該對象的引用不能被更改.即該對象初始化後,不能在對其指派為其他引用. 但是其引用的對象内容可以被更改.

final 對用于成員變量(Filed)在并發中作用

final的記憶體語義 : 隻要對象是正确構造的(被構造對象的引用在構造函數中沒有“逸出”),那麼不需要使用同步(指lock和volatile的使用)就可以保證任意線程都能看到這個final域在構造函數中被初始化之後的值。

final域的重排序規則

  1. 在構造函數内對一個final域的寫入,與随後把這個被構造對象的引用指派給一個引用變量,這兩個操作之間不能重排序。

JMM禁止編譯器把final域的寫重排序到構造函數之外.

編譯器會在final域的寫之後,構造函數return之前,插入一個StoreStore屏障。這個屏障禁止處理器把final域的寫重排序到構造函數之外。

  1. 初次讀取一個包含final域的對象的引用,與随後初次讀這個final域,這兩個操作之間不能重排序。

在一個線程中,初次讀對象引用與初次讀該對象包含的final域,JMM禁止處理器重排序這兩個操作(注意,這個規則僅僅針對處理器)。

編譯器會在讀final域操作的前面插入一個LoadLoad屏障。

構造函數"逸出" : 在構造函數内部,這個被構造對象的引用為其他線程所見.如構造函數中将this指派給成員變量.

public class FinalReference {
    final  int            i;
    static FinalReference obj;

    public FinalReference() {
        i = 1;                  // 1. 寫final域
        obj = this;             // 2. this引用在此"逸出"
    }

    public static void writer() {
        new FinalReference();
    }

    public static void reader() {
        if (obj != null) {      // 3.
            int temp = obj.i;   // 4.
        }
    }
}
           

構造函數"逸出",将不能保證final語義.

在構造函數傳回前,被構造對象的引用不能為其他線程所見,因為此時的final域可能還沒有被初始化。

引用

  1. java并發程式設計的藝術
  2. Final of Java,這一篇差不多了
上一篇: 切圖技巧