欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入淺出的講解Java關(guān)鍵字final的作用

 更新時(shí)間:2023年06月06日 11:54:37   作者:alexhilton  
final是Java中非常常見(jiàn)的一個(gè)關(guān)鍵字,可以說(shuō)每天都在使用它,雖然常見(jiàn),但卻也不見(jiàn)得都那么顯而易見(jiàn),今天就來(lái)研究一下final,以加深對(duì)它的理解和更合理的運(yùn)用,需要的朋友可以參考下

final可以用來(lái)干什么

修飾類

當(dāng)一個(gè)類不想被繼承時(shí),就可以用final來(lái)修飾。

修飾方法

當(dāng)一個(gè)方法不想被子類覆寫(Override)時(shí),可以用final來(lái)修飾。另外一方面,把方法用final來(lái)修飾也有一定的性能提升上的幫助,因?yàn)樘摂M機(jī)知道它不會(huì)被覆寫,所以可以以更簡(jiǎn)單的方式來(lái)處理。

private的方法,默認(rèn)都會(huì)被編譯器加上final.

修飾變量

被final修飾的變量只能賦值一次,之后不能再被修改。如:

final int a = 10;
a = 4; // compilation error

需要注意的是,這里說(shuō)的是只能賦值一次,并不意味著,非要在聲明變量時(shí)直接初始化,比如,下面的代碼也是完全合法的:

final int a;
if (foo()) {
    a = 3;
} else {
    a = 4;
}

修飾域變量

域變量也是變量,所以用final來(lái)修飾的第一個(gè)作用就是賦值后,不能再修改變量的值,比如:

final int a = 10;
final Object b = new Object();

對(duì)于基本類型來(lái)說(shuō),就是變量值不能再被修改;對(duì)于引用來(lái)說(shuō),就是不能再讓其指向其他對(duì)象或者null。

但對(duì)于域變量,聲明為final的域變量必須在聲明時(shí)初始化,或者在構(gòu)造方法中初始化,否則會(huì)有編譯錯(cuò)誤。

此外,聲明為final的域變量還有內(nèi)存模型上的語(yǔ)義,下面詳細(xì)說(shuō)

內(nèi)存模型的作用--防止變量從構(gòu)造方法中逸出

這個(gè)主要是針對(duì)被final修飾的域變量,虛擬機(jī)會(huì)有禁止指令重排的保證:

  • 在構(gòu)造方法內(nèi)對(duì)一個(gè)final變量的寫入,與隨后這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這二個(gè)順序不改變,final變量的寫入一定要早于對(duì)象引用的賦值。

什么意思呢?在多線程環(huán)境下,域變量是有可能從構(gòu)造方法中逸出的,也就是說(shuō)線程有可能讀到還沒(méi)有被構(gòu)造方法初始化的域變量的值。比如:

class Foo {
    int a;
    Foo(int v) {
        a = v;
    }
}

如果是在多線程環(huán)境下,一個(gè)線程A在創(chuàng)建Foo的對(duì)象,另一個(gè)線程B在讀對(duì)象的a的值,則B是有可能讀到未正確初始化a的值(默認(rèn)初始值0)。這就是域變量從構(gòu)造方法中逸出。

關(guān)鍵字final可以禁止虛擬機(jī)指令重排,從而保證了構(gòu)造方法執(zhí)行完畢前final修飾的變量一定是初始化過(guò)了的。

匿名內(nèi)部類使用外部變量時(shí)為何要強(qiáng)制使用final修飾

這個(gè)大家肯定都習(xí)以為常了,比如

private void initViews() {
    final int a = 3; // Compilation error if remove final
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (a > 1) {
                // volala
            }
        }
    }
}

那么,有沒(méi)有想過(guò)為什么?而像其他支持完整閉包的語(yǔ)言如JavaScript,Python等,是沒(méi)有這等限制的。究其原因,是Java對(duì)閉包支持不夠完整,或者說(shuō)它并不是像動(dòng)態(tài)語(yǔ)言那樣的完整閉包。對(duì)于匿名內(nèi)部類來(lái)說(shuō),編譯器會(huì)創(chuàng)建一個(gè)命名類(OutClass$1之類的),然后把匿名類所在的能捕獲的變量,以構(gòu)造參數(shù)的形式傳遞給內(nèi)部類使用,這樣一樣,外部的變量與內(nèi)部類看到的變量是不同的,雖然它們的值是相同的,因此,如果再允許外部修改這些變量,或者內(nèi)部類里面修改這些變量,都會(huì)造成數(shù)據(jù)的不一致性(因?yàn)樗鼈兪遣煌淖兞浚?,所以Java強(qiáng)制要求匿名內(nèi)部類訪問(wèn)的外部變量要加上final來(lái)修飾。

對(duì)于其他語(yǔ)言,匿名內(nèi)部類,持有的是外部變量的一個(gè)包裝的引用(wrapper reference),這可以能看不懂,但是理解起來(lái)就是內(nèi)部類能直接訪問(wèn)外部變量,外部與閉包內(nèi)部訪問(wèn)的是同一個(gè)變量,因此外部修改了,內(nèi)部能看到變化,內(nèi)部修改了,外部也能看到變化。

一句話總結(jié)就是,Java內(nèi)部類與外部持有的是值相同的不同的變量;其他支持閉包的語(yǔ)言則持有的是相同的變量。

建議能使用final的地方就加上final修飾

最后來(lái)聊聊,啥時(shí)候應(yīng)該用final呢?孤的建議(以及眾多大師的建議)就是能多用就多用,除非不能用final,否則就用。原因,有這么幾條:

  • 域變量盡可能加上final

    這個(gè)原因比較明確,前面也提到了,在多線程條件下,會(huì)有很大的優(yōu)勢(shì)。盡可能加上final來(lái)修飾域變量,甚至用Immutable Object,可以省去構(gòu)造時(shí)的多線程同步。

    多線程最大的麻煩是狀態(tài)同步,啥是狀態(tài)?其實(shí)就是共享數(shù)據(jù),域變量就是共享數(shù)據(jù),所以,如果共享數(shù)據(jù)都是不可變的(Immutable),那么自然就沒(méi)有了同步上的麻煩。

  • final類和方法能提升性能

    正常的類和方法,虛擬機(jī)需要為了繼承和方法覆寫而做一次準(zhǔn)備,如果加上了final,虛擬機(jī)知道它不會(huì)被繼承或者覆寫,那么就可以做一些優(yōu)化。雖然,這并不顯著,但是還是可以提升一些性能的。

  • final變量能提升可讀性

    無(wú)論是域變量還是本地變量,加上了final修飾,程序的維護(hù)者就知道了,這個(gè)變量的值不會(huì)再改變,這無(wú)疑會(huì)大大增加可讀性。

以上就是深入淺出的講解Java關(guān)鍵字final的作用的詳細(xì)內(nèi)容,更多關(guān)于Java 關(guān)鍵字 final的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論