在 Java 中恋谭,聲明類话浇、變量和方法時(shí),可使用關(guān)鍵字 final 來修飾别威。final 所修飾的數(shù)據(jù)具有“終態(tài)”的特征躯舔,表示“最終的”意思。具體規(guī)定如下:
final 修飾的類不能被繼承省古。
final 修飾的方法不能被子類重寫粥庄。
final 修飾的變量(成員變量或局部變量)即成為常量,只能賦值一次豺妓。
final 修飾的成員變量必須在聲明的同時(shí)賦值惜互,如果在聲明的時(shí)候沒有賦值,那么只有 一次賦值的機(jī)會(huì)琳拭,而且只能在構(gòu)造方法中顯式賦值训堆,然后才能使用。
final 修飾的局部變量可以只聲明不賦值白嘁,然后再進(jìn)行一次性的賦值坑鱼。
final 一般用于修飾那些通用性的功能、實(shí)現(xiàn)方式或取值不能隨意被改變的數(shù)據(jù)絮缅,以避免被誤用鲁沥,例如實(shí)現(xiàn)數(shù)學(xué)三角方法、冪運(yùn)算等功能的方法耕魄,以及數(shù)學(xué)常量π=3.141593画恰、e=2.71828 等。
事實(shí)上吸奴,為確保終態(tài)性允扇,提供了上述方法和常量的 java.lang.Math 類也已被定義為final 的缠局。
需要注意的是,如果將引用類型(任何類的類型)的變量標(biāo)記為 final考润,那么該變量不能指向任何其它對(duì)象狭园。但可以改變對(duì)象的內(nèi)容,因?yàn)橹挥幸帽旧硎?final 的糊治。
如果變量被標(biāo)記為 final妙啃,其結(jié)果是使它成為常數(shù)。想改變 final 變量的值會(huì)導(dǎo)致一個(gè)編譯錯(cuò)誤俊戳。下面是一個(gè)正確定義 final 變量的例子:
public final int MAX_ARRAY_SIZE = 25; // 常量名一般大寫
常量因?yàn)橛?final 修飾揖赴,所以不能被繼承。
請(qǐng)看下面的代碼:
public final class Demo{
public static final int TOTAL_NUMBER = 5;
public int id;
public Demo() {
// 非法抑胎,對(duì)final變量TOTAL_NUMBER進(jìn)行二次賦值了
// 因?yàn)?+TOTAL_NUMBER相當(dāng)于 TOTAL_NUMBER=TOTAL_NUMBER+1
id = ++TOTAL_NUMBER;
}
public static void main(String[] args) {
final Demo t = new Demo();
final int i = 10;
final int j;
j = 20;
j = 30; // 非法燥滑,對(duì)final變量進(jìn)行二次賦值
}
}
final 也可以用來修飾類(放在 class 關(guān)鍵字前面),阻止該類再派生出子類阿逃,例如 Java.lang.String 就是一個(gè) final 類铭拧。這樣做是出于安全原因,因?yàn)橐WC一旦有字符串的引用恃锉,就必須是類 String 的字符串搀菩,而不是某個(gè)其它類的字符串(String 類可能被惡意繼承并篡改)。
方法也可以被 final 修飾破托,被 final 修飾的方法不能被覆蓋肪跋;變量也可以被 final 修飾,被 final 修飾的變量在創(chuàng)建對(duì)象以后就不允許改變它們的值了土砂。一旦將一個(gè)類聲明為 final州既,那么該類包含的方法也將被隱式地聲明為 final,但是變量不是萝映。
被 final 修飾的方法為靜態(tài)綁定吴叶,不會(huì)產(chǎn)生多態(tài)(動(dòng)態(tài)綁定),程序在運(yùn)行時(shí)不需要再檢索方法表序臂,能夠提高代碼的執(zhí)行效率蚌卤。在Java中,被 static 或 private 修飾的方法會(huì)被隱式的聲明為 final奥秆,因?yàn)閯?dòng)態(tài)綁定沒有意義逊彭。
由于動(dòng)態(tài)綁定會(huì)消耗資源并且很多時(shí)候沒有必要,所以有一些程序員認(rèn)為:除非有足夠的理由使用多態(tài)性吭练,否則應(yīng)該將所有的方法都用 final 修飾诫龙。
這樣的認(rèn)識(shí)未免有些偏激析显,因?yàn)?JVM 中的即時(shí)編譯器能夠?qū)崟r(shí)監(jiān)控程序的運(yùn)行信息鲫咽,可以準(zhǔn)確的知道類之間的繼承關(guān)系签赃。如果一個(gè)方法沒有被覆蓋并且很短,編譯器就能夠?qū)λM(jìn)行優(yōu)化處理分尸,這個(gè)過程為稱為內(nèi)聯(lián)(inlining)锦聊。例如,內(nèi)聯(lián)調(diào)用 e.getName() 將被替換為訪問 e.name 變量箩绍。這是一項(xiàng)很有意義的改進(jìn)孔庭,這是由于CPU在處理調(diào)用方法的指令時(shí),使用的分支轉(zhuǎn)移會(huì)擾亂預(yù)取指令的策略材蛛,所以圆到,這被視為不受歡迎的。然而卑吭,如果 getName() 在另外一個(gè)類中被覆蓋芽淡,那么編譯器就無法知道覆蓋的代碼將會(huì)做什么操作,因此也就不能對(duì)它進(jìn)行內(nèi)聯(lián)處理了豆赏。
從去年到現(xiàn)在挣菲,我根據(jù)市場技術(shù)棧的需求,錄制了一套最新的Java精講視頻教程掷邦,如果你現(xiàn)在也在學(xué)習(xí)Java白胀,在入門學(xué)習(xí)Java的過程當(dāng)中缺乏系統(tǒng)的學(xué)習(xí)教程,你可以加我的Java學(xué)習(xí)交流群:641121948抚岗,獲取或杠,群文件還有學(xué)習(xí)手冊(cè),面試題宣蔚,開發(fā)工具廷痘,PDF文檔教程,可以自行下載件已。