<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 6.8 `final`關鍵字 由于語境(應用環境)不同,`final`關鍵字的含義可能會稍微產生一些差異。但它最一般的意思就是聲明“這個東西不能改變”。之所以要禁止改變,可能是考慮到兩方面的因素:設計或效率。由于這兩個原因頗有些區別,所以也許會造成`final`關鍵字的誤用。 在接下去的小節里,我們將討論`final`關鍵字的三種應用場合:數據、方法以及類。 ## 6.8.1 `final`數據 許多程序設計語言都有自己的辦法告訴編譯器某個數據是“常數”。常數主要應用于下述兩個方面: (1) 編譯期常數,它永遠不會改變 (2) 在運行期初始化的一個值,我們不希望它發生變化 對于編譯期的常數,編譯器(程序)可將常數值“封裝”到需要的計算過程里。也就是說,計算可在編譯期間提前執行,從而節省運行時的一些開銷。在Java中,這些形式的常數必須屬于基本數據類型(Primitives),而且要用`final`關鍵字進行表達。在對這樣的一個常數進行定義的時候,必須給出一個值。 無論`static`還是`final`字段,都只能存儲一個數據,而且不得改變。 若隨同對象引用使用`final`,而不是基本數據類型,它的含義就稍微讓人有點兒迷糊了。對于基本數據類型,`final`會將值變成一個常數;但對于對象引用,`final`會將引用變成一個常數。進行聲明時,必須將引用初始化到一個具體的對象。而且永遠不能將引用變成指向另一個對象。然而,對象本身是可以修改的。Java對此未提供任何手段,可將一個對象直接變成一個常數(但是,我們可自己編寫一個類,使其中的對象具有“常數”效果)。這一限制也適用于數組,它也屬于對象。 下面是演示`final`字段用法的一個例子: ``` //: FinalData.java // The effect of final on fields class Value { int i = 1; } public class FinalData { // Can be compile-time constants final int i1 = 9; static final int I2 = 99; // Typical public constant: public static final int I3 = 39; // Cannot be compile-time constants: final int i4 = (int)(Math.random()*20); static final int i5 = (int)(Math.random()*20); Value v1 = new Value(); final Value v2 = new Value(); static final Value v3 = new Value(); //! final Value v4; // Pre-Java 1.1 Error: // no initializer // Arrays: final int[] a = { 1, 2, 3, 4, 5, 6 }; public void print(String id) { System.out.println( id + ": " + "i4 = " + i4 + ", i5 = " + i5); } public static void main(String[] args) { FinalData fd1 = new FinalData(); //! fd1.i1++; // Error: can't change value fd1.v2.i++; // Object isn't constant! fd1.v1 = new Value(); // OK -- not final for(int i = 0; i < fd1.a.length; i++) fd1.a[i]++; // Object isn't constant! //! fd1.v2 = new Value(); // Error: Can't //! fd1.v3 = new Value(); // change handle //! fd1.a = new int[3]; fd1.print("fd1"); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData(); fd1.print("fd1"); fd2.print("fd2"); } } ///:~ ``` 由于`i1`和`I2`都是具有`final`屬性的基本數據類型,并含有編譯期的值,所以它們除了能作為編譯期的常數使用外,在任何導入方式中也不會出現任何不同。`I3`是我們體驗此類常數定義時更典型的一種方式:`public`表示它們可在包外使用;`Static`強調它們只有一個;而`final`表明它是一個常數。注意對于含有固定初始化值(即編譯期常數)的`fianl static`基本數據類型,它們的名字根據規則要全部采用大寫。也要注意`i5`在編譯期間是未知的,所以它沒有大寫。 不能由于某樣東西的屬性是`final`,就認定它的值能在編譯時期知道。`i4`和`i5`向大家證明了這一點。它們在運行期間使用隨機生成的數字。例子的這一部分也向大家揭示出將`final`值設為`static`和非`static`之間的差異。只有當值在運行期間初始化的前提下,這種差異才會揭示出來。因為編譯期間的值被編譯器認為是相同的。這種差異可從輸出結果中看出: ``` fd1: i4 = 15, i5 = 9 Creating new FinalData fd1: i4 = 15, i5 = 9 fd2: i4 = 10, i5 = 9 ``` 注意對于`fd1`和`fd2`來說,`i4`的值是唯一的,但`i5`的值不會由于創建了另一個`FinalData`對象而發生改變。那是因為它的屬性是`static`,而且在載入時初始化,而非每創建一個對象時初始化。 從`v1`到`v4`的變量向我們揭示出`final`引用的含義。正如大家在`main()`中看到的那樣,并不能認為由于`v2`屬于`final`,所以就不能再改變它的值。然而,我們確實不能再將`v2`綁定到一個新對象,因為它的屬性是`final`。這便是`final`對于一個引用的確切含義。我們會發現同樣的含義亦適用于數組,后者只不過是另一種類型的引用而已。將引用變成`final`看起來似乎不如將基本數據類型變成`final`那么有用。 (2) 空白`final` Java 1.1允許我們創建“空白`final`”,它們屬于一些特殊的字段。盡管被聲明成`final`,但卻未得到一個初始值。無論在哪種情況下,空白`final`都必須在實際使用前得到正確的初始化。而且編譯器會主動保證這一規定得以貫徹。然而,對于`final`關鍵字的各種應用,空白`final`具有最大的靈活性。舉個例子來說,位于類內部的一個`final`字段現在對每個對象都可以有所不同,同時依然保持其“不變”的本質。下面列出一個例子: ``` //: BlankFinal.java // "Blank" final data members class Poppet { } class BlankFinal { final int i = 0; // Initialized final final int j; // Blank final final Poppet p; // Blank final handle // Blank finals MUST be initialized // in the constructor: BlankFinal() { j = 1; // Initialize blank final p = new Poppet(); } BlankFinal(int x) { j = x; // Initialize blank final p = new Poppet(); } public static void main(String[] args) { BlankFinal bf = new BlankFinal(); } } ///:~ ``` 現在強行要求我們對`final`進行賦值處理——要么在定義字段時使用一個表達式,要么在每個構造器中。這樣就可以確保`final`字段在使用前獲得正確的初始化。 (3) `final`參數 Java 1.1允許我們將參數設成`final`屬性,方法是在參數列表中對它們進行適當的聲明。這意味著在一個方法的內部,我們不能改變參數引用指向的東西。如下所示: ``` //: FinalArguments.java // Using "final" with method arguments class Gizmo { public void spin() {} } public class FinalArguments { void with(final Gizmo g) { //! g = new Gizmo(); // Illegal -- g is final g.spin(); } void without(Gizmo g) { g = new Gizmo(); // OK -- g not final g.spin(); } // void f(final int i) { i++; } // Can't change // You can only read from a final primitive: int g(final int i) { return i + 1; } public static void main(String[] args) { FinalArguments bf = new FinalArguments(); bf.without(null); bf.with(null); } } ///:~ ``` 注意此時仍然能為`final`參數分配一個`null`(空)引用,同時編譯器不會捕獲它。這與我們對非`final`參數采取的操作是一樣的。 方法`f()`和`g()`向我們展示出基本類型的參數為`final`時會發生什么情況:我們只能讀取參數,不可改變它。 ## 6.8.2 `final`方法 之所以要使用`final`方法,可能是出于對兩方面理由的考慮。第一個是為方法“上鎖”,防止任何繼承類改變它的本來含義。設計程序時,若希望一個方法的行為在繼承期間保持不變,而且不可被覆蓋或改寫,就可以采取這種做法。 采用`final`方法的第二個理由是程序執行的效率。將一個方法設成`final`后,編譯器就可以把對那個方法的所有調用都置入“嵌入”調用里。只要編譯器發現一個`final`方法調用,就會(根據它自己的判斷)忽略為執行方法調用機制而采取的常規代碼插入方法(將參數壓入棧;跳至方法代碼并執行它;跳回來;清除棧參數;最后對返回值進行處理)。相反,它會用方法主體內實際代碼的一個副本來替換方法調用。這樣做可避免方法調用時的系統開銷。當然,若方法體積太大,那么程序也會變得雍腫,可能受到到不到嵌入代碼所帶來的任何性能提升。因為任何提升都被花在方法內部的時間抵消了。Java編譯器能自動偵測這些情況,并頗為“明智”地決定是否嵌入一個`final`方法。然而,最好還是不要完全相信編譯器能正確地作出所有判斷。通常,只有在方法的代碼量非常少,或者想明確禁止方法被覆蓋的時候,才應考慮將一個方法設為`final`。 類內所有`private`方法都自動成為`final`。由于我們不能訪問一個`private`方法,所以它絕對不會被其他方法覆蓋(若強行這樣做,編譯器會給出錯誤提示)。可為一個`private`方法添加`final`指示符,但卻不能為那個方法提供任何額外的含義。 ## 6.8.3 `final`類 如果說整個類都是`final`(在它的定義前冠以`final`關鍵字),就表明自己不希望從這個類繼承,或者不允許其他任何人采取這種操作。換言之,出于這樣或那樣的原因,我們的類肯定不需要進行任何改變;或者出于安全方面的理由,我們不希望進行子類化(子類處理)。 除此以外,我們或許還考慮到執行效率的問題,并想確保涉及這個類各對象的所有行動都要盡可能地有效。如下所示: ``` //: Jurassic.java // Making an entire class final class SmallBrain {} final class Dinosaur { int i = 7; int j = 1; SmallBrain x = new SmallBrain(); void f() {} } //! class Further extends Dinosaur {} // error: Cannot extend final class 'Dinosaur' public class Jurassic { public static void main(String[] args) { Dinosaur n = new Dinosaur(); n.f(); n.i = 40; n.j++; } } ///:~ ``` 注意數據成員既可以是`final`,也可以不是,取決于我們具體選擇。應用于`final`的規則同樣適用于數據成員,無論類是否被定義成`final`。將類定義成`final`后,結果只是禁止進行繼承——沒有更多的限制。然而,由于它禁止了繼承,所以一個`final`類中的所有方法都默認為`final`。因為此時再也無法覆蓋它們。所以與我們將一個方法明確聲明為`final`一樣,編譯器此時有相同的效率選擇。 可為`final`類內的一個方法添加`final`指示符,但這樣做沒有任何意義。 ## 6.8.4 `final`的注意事項 設計一個類時,往往需要考慮是否將一個方法設為`final`。可能會覺得使用自己的類時執行效率非常重要,沒有人想覆蓋自己的方法。這種想法在某些時候是正確的。 但要慎重作出自己的假定。通常,我們很難預測一個類以后會以什么樣的形式復用或重復利用。常規用途的類尤其如此。若將一個方法定義成`final`,就可能杜絕了在其他程序員的項目中對自己的類進行繼承的途徑,因為我們根本沒有想到它會象那樣使用。 標準Java庫是闡述這一觀點的最好例子。其中特別常用的一個類是`Vector`。如果我們考慮代碼的執行效率,就會發現只有不把任何方法設為`final`,才能使其發揮更大的作用。我們很容易就會想到自己應繼承和覆蓋如此有用的一個類,但它的設計者卻否定了我們的想法。但我們至少可以用兩個理由來反駁他們。首先,`Stack`(棧)是從`Vector`繼承來的,亦即`Stack`“是”一個`Vector`,這種說法是不確切的。其次,對于`Vector`許多重要的方法,如`addElement()`以及`elementAt()`等,它們都變成了`synchronized`(同步的)。正如在第14章要講到的那樣,這會造成顯著的性能開銷,可能會把final提供的性能改善抵銷得一干二凈。因此,程序員不得不猜測到底應該在哪里進行優化。在標準庫里居然采用了如此笨拙的設計,真不敢想象會在程序員里引發什么樣的情緒。 另一個值得注意的是`Hashtable`(散列表),它是另一個重要的標準類。該類沒有采用任何`final`方法。正如我們在本書其他地方提到的那樣,顯然一些類的設計人員與其他設計人員有著全然不同的素質(注意比較`Hashtable`極短的方法名與`Vector`的方法名)。對類庫的用戶來說,這顯然是不應該如此輕易就能看出的。一個產品的設計變得不一致后,會加大用戶的工作量。這也從另一個側面強調了代碼設計與檢查時需要很強的責任心。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看