日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

final的8个小细节,听说只有高手才知道!你知道几个?

發(fā)布時(shí)間:2025/3/11 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 final的8个小细节,听说只有高手才知道!你知道几个? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

final關(guān)鍵字是一個(gè)常用的關(guān)鍵字,可以修飾變量、方法、類(lèi),用來(lái)表示它修飾的類(lèi)、方法和變量不可改變,下面就聊一下使用 final 關(guān)鍵字的一些小細(xì)節(jié)。

細(xì)節(jié)一、final 修飾類(lèi)成員變量和實(shí)例成員變量的賦值時(shí)機(jī)

對(duì)于類(lèi)變量:

  • 聲明變量的時(shí)候直接賦初始值

  • 在靜態(tài)代碼塊中給類(lèi)變量賦初始值

  • 如下代碼所示:

    public?class?FinalTest?{??? //a變量直接賦值private?final?static??int?a?=?1;private?final?static??int?b;//b變量通過(guò)靜態(tài)代碼塊賦值static?{b=2;} }

    對(duì)于實(shí)例變量:

  • 在聲明變量的時(shí)候直接賦值

  • 在非靜態(tài)代碼塊中賦值

  • 在構(gòu)造器中賦初始化值

  • 如下代碼所示:

    public?class?FinalTest?{//c變量在在聲明時(shí)直接賦值private?final??int?c?=1;private?final??int?d;private?final??int?e;//d變量在非靜態(tài)代碼塊中賦值{d=2;}//e變量在構(gòu)造器中賦值FinalTest(){e=3;} }

    細(xì)節(jié)二、當(dāng) final 修飾的成員變量未對(duì)它進(jìn)行初始化時(shí),會(huì)出現(xiàn)錯(cuò)誤嗎?

    答:會(huì)出現(xiàn)錯(cuò)誤。因?yàn)?java 語(yǔ)法規(guī)定,final 修飾的成員變量必須由程序員顯示的初始化,系統(tǒng)不會(huì)對(duì)變量進(jìn)行隱式的初始化。

    如下圖所示,未初始化變量就會(huì)出現(xiàn)編譯錯(cuò)誤:

    細(xì)節(jié)三、final 修飾基本類(lèi)型變量和引用類(lèi)型變量的區(qū)別

    如果 fianl 修飾的是一個(gè)基本數(shù)據(jù)類(lèi)型的數(shù)據(jù),一旦賦值后就不能再次更改。

    那么 final 修飾的是引用數(shù)據(jù)類(lèi)型呢?這個(gè)引用的變量能夠改變嗎?

    看下面的代碼:

    public?class?FinalTest?{//在聲明final實(shí)例成員變量時(shí)進(jìn)行賦值private?final?static?Student?student?=?new?Student(50,?"Java");public?static?void?main(String[]?args)?{//對(duì)final引用數(shù)據(jù)類(lèi)型student進(jìn)行更改student.age?=?100;System.out.println(student.toString());}static?class?Student?{private?int?age;private?String?name;public?Student(int?age,?String?name)?{this.age?=?age;this.name?=?name;}@Overridepublic?String?toString()?{return?"Student{"?+"age="?+?age?+",?name='"?+?name?+?'\''?+'}';}} }//下面是打印結(jié)果 Student{age=100,?name='Java'}

    從打印結(jié)果可以看到:引用數(shù)據(jù)類(lèi)型變量 student 的 age 屬性修改成 100,是可以修改成功的。

    結(jié)論:

  • 當(dāng) final 修飾基本數(shù)據(jù)類(lèi)型變量時(shí),不能對(duì)基本數(shù)據(jù)類(lèi)型變量重新賦值,因此基本數(shù)據(jù)類(lèi)型變量不能被改變。

  • 對(duì)于引用類(lèi)型變量而言,它僅僅保存的是一個(gè)引用,final 只保證這個(gè)引用類(lèi)型變量所引用的地址不會(huì)發(fā)生改變,即一直引用這個(gè)對(duì)象,但這個(gè)對(duì)象里面的屬性是可以改變的。

  • 細(xì)節(jié)四、final 修飾局部變量的場(chǎng)景

    fianl 局部變量由程序員進(jìn)行顯示的初始化,如果 final 局部變量進(jìn)行初始化之后就不能再次進(jìn)行更改。

    如果 final 變量未進(jìn)行初始化,可以進(jìn)行賦值,并且只能進(jìn)行一次賦值,一旦賦值之后再次賦值就會(huì)出錯(cuò)。

    下面的代碼演示 final 修飾局部變量的情況:

    細(xì)節(jié)五、final 修飾方法會(huì)對(duì)重載有影響嗎?重寫(xiě)呢?

    對(duì)于重載:final 修飾方法后是可以重載的

    如下代碼:

    public?class?FinalTest?{public?final?void?test(){}//重載方法不會(huì)出現(xiàn)問(wèn)題public?final?void?test(String?test){} }

    對(duì)于重寫(xiě):當(dāng)父類(lèi)的方法被 final 修飾的時(shí)候,子類(lèi)不能重寫(xiě)父類(lèi)的該方法

    如上代碼所示,可以看到會(huì)出現(xiàn) cannot override ,overridden method is final 的編譯錯(cuò)誤提示

    細(xì)節(jié)六、final 修飾類(lèi)的場(chǎng)景

    當(dāng)用final修飾一個(gè)類(lèi)時(shí),表明這個(gè)類(lèi)不能被繼承。也就是說(shuō),如果一個(gè)類(lèi)你永遠(yuǎn)不會(huì)讓他被繼承,就可以用 final 進(jìn)行修飾。

    final 類(lèi)中的成員變量可以根據(jù)需要設(shè)為 final,但是要注意 final 類(lèi)中的所有成員方法都會(huì)被隱式地指定為 final 方法。

    細(xì)節(jié)七、寫(xiě) final 域的重排序規(guī)則,你知道嗎?

    這個(gè)規(guī)則是指禁止對(duì) final 域的寫(xiě)重排序到構(gòu)造函數(shù)之外,這個(gè)規(guī)則的實(shí)現(xiàn)主要包含了兩個(gè)方面:

  • JMM 禁止編譯器把 final 域的寫(xiě)重排序 到 構(gòu)造函數(shù) 之外

  • 編譯器會(huì)在 final 域?qū)懼?#xff0c;構(gòu)造函數(shù) return 之前,插入一個(gè) StoreStore 屏障。這個(gè)屏障可以禁止處理器把 final 域的寫(xiě)重排序到構(gòu)造函數(shù)之外

  • 給舉個(gè)例子,要不太抽象了,先看一段代碼

    public?class?FinalTest{private?int?a;??//普通域private?final?int?b;?//final域private?static?FinalTest?finalTest;public?FinalTest()?{a?=?1;?//?1.?寫(xiě)普通域b?=?2;?//?2.?寫(xiě)final域}public?static?void?writer()?{finalTest?=?new?FinalTest();}public?static?void?reader()?{FinalTest?demo?=?finalTest;?//?3.讀對(duì)象引用int?a?=?demo.a;????//4.讀普通域int?b?=?demo.b;????//5.讀final域} }

    假設(shè)線程 A 在執(zhí)行 writer()方法,線程 B 執(zhí)行 reader()方法。

    由于變量 a 和變量 b 之間沒(méi)有依賴性,所以就有可能會(huì)出現(xiàn)下圖所示的重排序

    由于普通變量 a 可能會(huì)被重排序到構(gòu)造函數(shù)之外,所以線程 B 就有可能讀到的是普通變量 a 初始化之前的值(零值),這樣就可能出現(xiàn)錯(cuò)誤。

    而 final 域變量 b,根據(jù)重排序規(guī)則,會(huì)禁止 final 修飾的變量 b 重排序到構(gòu)造函數(shù)之外,從而 b 能夠正確賦值,線程 B 就能夠讀到 final 域變量 b初始化后的值。

    結(jié)論:寫(xiě) final 域的重排序規(guī)則可以確保在對(duì)象引用為任意線程可見(jiàn)之前,對(duì)象的 final 域已經(jīng)被正確初始化過(guò)了,而普通域就不具有這個(gè)保障。

    細(xì)節(jié)八:讀 final 域的重排序規(guī)則,你知道嗎?

    這個(gè)規(guī)則是指在一個(gè)線程中,初次讀對(duì)象引用和初次讀該對(duì)象包含的 final 域,JMM 會(huì)禁止這兩個(gè)操作的重排序。

    還是上面那段代碼

    public?class?FinalTest{private?int?a;??//普通域private?final?int?b;?//final域private?static?FinalTest?finalTest;public?FinalTest()?{a?=?1;?//?1.?寫(xiě)普通域b?=?2;?//?2.?寫(xiě)final域}public?static?void?writer()?{finalTest?=?new?FinalTest();}public?static?void?reader()?{FinalTest?demo?=?finalTest;?//?3.讀對(duì)象引用int?a?=?demo.a;????//4.讀普通域int?b?=?demo.b;????//5.讀final域} }

    假設(shè)線程 A 在執(zhí)行 writer()方法,線程 B 執(zhí)行 reader()方法。

    線程 B 可能就會(huì)出現(xiàn)下圖所示的重排序

    可以看到,由于讀對(duì)象的普通域被重排序到了讀對(duì)象引用的前面,就會(huì)出現(xiàn)線程 B 還未讀到對(duì)象引用就在讀取該對(duì)象的普通域變量,這顯然是錯(cuò)誤的操作。而 final 域的讀操作就“限定”了在讀 final 域變量前已經(jīng)讀到了該對(duì)象的引用,從而就可以避免這種情況。

    結(jié)論:讀 final 域的重排序規(guī)則可以確保在讀一個(gè)對(duì)象的 final 域之前,一定會(huì)先讀包含這個(gè) final 域的對(duì)象的引用。

    結(jié)束

    今天給大家總結(jié)了一下使用 final 關(guān)鍵字容易忽視的一些小細(xì)節(jié),看完希望你能有所收獲。

    往期推薦

    Java中的Switch都支持String了,為什么不支持long?


    對(duì)象復(fù)制的7種方法,還是Spring的最好用!


    分布式ID生成的9種方法,特好用!


    關(guān)注我,每天陪你進(jìn)步一點(diǎn)點(diǎn)!

    總結(jié)

    以上是生活随笔為你收集整理的final的8个小细节,听说只有高手才知道!你知道几个?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。