Java基礎(chǔ) - final

一功蜓、為什么要使用final?

final指的是"這是無(wú)法改變的"。不想改變可能出于兩種理由:設(shè)計(jì)或效率童社。

要具體得講著隆,那就必須看它的使用了,使用它能得到了什么樣的功能效果灭抑,就是它的作用了抵代。

可能是用到final的情況有三種:數(shù)據(jù)、方法和類(lèi)案腺。

1. 修飾數(shù)據(jù)

許多編程語(yǔ)言都有某種方法康吵,來(lái)向編譯器告知一塊數(shù)據(jù)是恒定不變的。

Java用final修飾這些數(shù)據(jù)同辣,就可以達(dá)到這種效果惭载。

我們知道,Java有兩種數(shù)據(jù)類(lèi)型:原始類(lèi)型(Primitive Types, 也經(jīng)常翻譯為原生類(lèi)型或者基本類(lèi)型)和引用類(lèi)型(Reference Types)棒妨。

final關(guān)鍵字修飾基本類(lèi)型和引用類(lèi)型會(huì)有些區(qū)別含长,這里分別進(jìn)行說(shuō)明。

修飾基本類(lèi)型

Java基本類(lèi)型包含數(shù)字類(lèi)型(整數(shù)類(lèi)型和浮點(diǎn)類(lèi)型)和布爾類(lèi)型纷纫。

數(shù)字類(lèi)型:byte, short, int, long, float, double
布爾類(lèi)型:boolean

設(shè)計(jì)

假設(shè)我們現(xiàn)在有這樣的一個(gè)需求:我們?cè)陬?lèi)中定義一個(gè)整型的屬性來(lái)代表某個(gè)邏輯執(zhí)行的次數(shù)田弥,這個(gè)次數(shù)是固定不變的,不希望它被改變商叹。
來(lái)看一下:

int count = 10;

我們?nèi)绻沁@樣簡(jiǎn)單的定義一個(gè)變量剖笙,可以達(dá)到這樣的效果嗎弥咪?答案是不行。我們可以對(duì)這個(gè)count進(jìn)行重新復(fù)制酷勺。實(shí)現(xiàn)上面所說(shuō)的需求扳躬,我們就可以用到final關(guān)鍵字來(lái)修飾這個(gè)表示次數(shù)的變量來(lái)告訴編譯器我們定義的是一個(gè)編譯時(shí)常量,在編譯時(shí)就已經(jīng)確定了击胜,運(yùn)行時(shí)是不能被改變的役纹。

對(duì)final常量重新賦值.png

效率

我們來(lái)看一下下面的代碼:

public class Final2 {

    private final int a = 2;
    private int b = 3;

    public int calc1() {
        return a * 2;
    }

    public int cacl2() {
        return b * 2;
    }

}

這里我們定義了兩個(gè)整型的變量a和b促脉,分別是final和非final的。
下面我們通過(guò)字節(jié)碼來(lái)進(jìn)行分析:

Classfile /Users/rocky/Desktop/Final2.class
  Last modified Jan 26, 2019; size 388 bytes
  MD5 checksum b032ca9258400a7c793c542102d63391
  Compiled from "Final2.java"
public class Final2
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #4.#21         // Final2.a:I
   #3 = Fieldref           #4.#22         // Final2.b:I
   #4 = Class              #23            // Final2
   #5 = Class              #24            // java/lang/Object
   #6 = Utf8               a
   #7 = Utf8               I
   #8 = Utf8               ConstantValue
   #9 = Integer            2
  #10 = Utf8               b
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               calc1
  #16 = Utf8               ()I
  #17 = Utf8               cacl2
  #18 = Utf8               SourceFile
  #19 = Utf8               Final2.java
  #20 = NameAndType        #11:#12        // "<init>":()V
  #21 = NameAndType        #6:#7          // a:I
  #22 = NameAndType        #10:#7         // b:I
  #23 = Utf8               Final2
  #24 = Utf8               java/lang/Object
{
  public Final2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_2
         6: putfield      #2                  // Field a:I
         9: aload_0
        10: iconst_3
        11: putfield      #3                  // Field b:I
        14: return
      LineNumberTable:
        line 1: 0
        line 3: 4
        line 4: 9

  public int calc1();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_4
         1: ireturn
      LineNumberTable:
        line 7: 0

  public int cacl2();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: getfield      #3                  // Field b:I
         4: iconst_2
         5: imul
         6: ireturn
      LineNumberTable:
        line 11: 0
}
SourceFile: "Final2.java"

對(duì)于calc2方法,關(guān)鍵字節(jié)碼指令如下:

0: aload_0
1: getfield      #3                  // Field b:I
4: iconst_2
5: imul
6: ireturn

解讀:
第一步:通過(guò)getfield訪問(wèn)類(lèi)字段b;
第二步:將常量2加載到操作數(shù)棧;
第三步:執(zhí)行乘法運(yùn)算指令imul;
第四步:返回結(jié)果下硕。

再來(lái)看看calc1方法,關(guān)鍵字節(jié)碼指令如下:

0: iconst_4
1: ireturn

解讀:
第一步:將常量4加載到操作數(shù)棧中霜幼;
第二步:返回結(jié)果誉尖。

這里4表示的就是a*2的結(jié)果,沒(méi)有看到乘法運(yùn)算指令琢感。也就是說(shuō)對(duì)于包含有final常量的運(yùn)算表達(dá)式,在編譯器的時(shí)候編譯就已經(jīng)幫我們進(jìn)行了運(yùn)算烘挫。

總結(jié):對(duì)于編譯期常量這種情況柬甥,編譯器可以將常量值代入任何可能用到它的計(jì)算式中,也就是說(shuō)卤橄,可以在編譯時(shí)執(zhí)行計(jì)算式臂外,這減輕了一些運(yùn)行時(shí)的負(fù)擔(dān)。

修飾引用類(lèi)型

上面修飾基本類(lèi)型辜膝,表示變量的數(shù)值恒定不變漾肮,但是如果修飾的是引用類(lèi)型,表示的是引用恒定不變忱辅,一旦引用被初始化指向一個(gè)對(duì)象谭溉,就無(wú)法再把它改為執(zhí)行另一個(gè)對(duì)象。然后损搬,對(duì)象其自身卻是可以被修改的柜与。

比如說(shuō)a變量通過(guò)final修飾指向了一個(gè)對(duì)象b,用指針的概念來(lái)看就是這個(gè)a指向了b這個(gè)對(duì)象所在的內(nèi)存地址颅悉,a這個(gè)指向是不能被修改了迁匠,但是b這個(gè)對(duì)象內(nèi)存空間里面的數(shù)據(jù)是可以被修改的驹溃。

實(shí)例圖說(shuō)明:


修改final修飾的引用變量.png

2.修飾方法

用final修飾方法豌鹤,主要是為了把方法鎖定搂鲫,以防任何繼承類(lèi)修改它的含義。這是出于設(shè)計(jì)的考慮拐辽。

《Java編程思想》中提到使用final修改方法提高效率的問(wèn)題擦酌。同時(shí)也提到最近虛擬機(jī)不需要使用final修飾方法這樣的方式來(lái)進(jìn)行優(yōu)化。

final修飾方法.png

3.修飾類(lèi)

當(dāng)用final修飾類(lèi)睁搭,從設(shè)計(jì)的角度上來(lái)看笼平,出于某種考慮,你對(duì)該類(lèi)的設(shè)計(jì)永不需要做任何變動(dòng)锌唾,或者處于安全的考慮 夺英,你不希望它有子類(lèi)。

繼承final修飾的類(lèi).png

二、空白final

Java允許生成"空白final"载萌,所謂空白final是指被聲明為final但又未給定初值的域。

但是要注意:無(wú)論什么情況可缚,編譯器都確闭啵空白final在使用前必須被初始化知给。

我們可以將final域的初始化放置到構(gòu)造函數(shù)中描姚,這樣的話可以做到根據(jù)對(duì)象而有所不同轩勘,卻又保持其恒定不變的特性怯邪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市澄步,隨后出現(xiàn)的幾起案子和泌,更是在濱河造成了極大的恐慌,老刑警劉巖梯皿,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件东羹,死亡現(xiàn)場(chǎng)離奇詭異忠烛,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)垒拢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)火惊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人尸疆,你說(shuō)我怎么就攤上這事惶岭。” “怎么了症革?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵噪矛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我艇挨,道長(zhǎng)缩滨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任苞冯,我火速辦了婚禮鸠删,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巧娱。我一直安慰自己烘贴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布老翘。 她就那樣靜靜地躺著锻离,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卫键。 梳的紋絲不亂的頭發(fā)上虱朵,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天碴犬,我揣著相機(jī)與錄音,去河邊找鬼服协。 笑死,一個(gè)胖子當(dāng)著我的面吹牛窘游,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼喘批,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼铣揉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起敌厘,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤俱两,失蹤者是張志新(化名)和其女友劉穎曹步,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體尿孔,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筹麸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年物赶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片块差。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡憨闰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出轧坎,到底是詐尸還是另有隱情泽示,我是刑警寧澤蜜氨,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布捎泻,位于F島的核電站笆豁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏闯狱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘦陈。 院中可真熱鬧,春花似錦媒抠、人聲如沸咏花。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)棚菊。三九已至,卻和暖如春检碗,著一層夾襖步出監(jiān)牢的瞬間码邻,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工怕犁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人戈轿。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓阵子,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親智蝠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奈梳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容