国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

JAVA中的final關(guān)鍵字用法實(shí)例詳解

2019-11-26 14:46:15
字體:
供稿:網(wǎng)友

本文實(shí)例講述了JAVA中的final關(guān)鍵字用法。分享給大家供大家參考,具體如下:

根據(jù)上下文環(huán)境,java的關(guān)鍵字final也存在著細(xì)微的區(qū)別,但通常指的是“這是無法改變的。”不想改變的理由有兩種:一種是效率,另一種是設(shè)計。由于兩個原因相差很遠(yuǎn),所以關(guān)鍵子final可能被誤用。

接下來介紹一下使用到final的三中情況:數(shù)據(jù),方法,類

final數(shù)據(jù)

許多編程語言都有某種方法,來向編譯器告知一塊數(shù)據(jù)是恒定不變的。有時數(shù)據(jù)的恒定不變是很有用的,例如:

1. 一個編譯時恒定不變的常量
2. 一個在運(yùn)行時初始化,而你不希望它被改變。

對于編譯期常量的這種情況,編譯器可以將該常量值代入任何可能用到它的計算式中,也就是說,可以在編譯期就執(zhí)行計算式,這減輕了一些運(yùn)行時的負(fù)擔(dān)。在java中,這類常量必須是基本類型,并且以final表示。在對這個常量定義時,必須進(jìn)行賦值。

一個即是static又是final的域只占一段不能改變的存儲空間。

當(dāng)final應(yīng)用于對象引用時,而不是基本類型時,其含義有些讓人疑惑。對基本類型使用final不能改變的是他的數(shù)值。而對于對象引用,不能改變的是他的引用,而對象本身是可以修改的。一旦一個final引用被初始化指向一個對象,這個引用將不能在指向其他對象。java并未提供對任何對象恒定不變的支持。這一限制也通用適用于數(shù)組,它也是對象。例如:

package finalPackage;import java.util.*;class Value {  int i;  public Value(int i) {    this.i = i;  }}/** * final數(shù)據(jù)常量 * @author Administrator * 對基本類型使用final不能改變的是它的數(shù)值。 * 而對于對象引用,不能改變的是他的引用,而對象本身是可以修改的。 * 一旦一個final引用被初始化指向一個對象,這個引用將不能在指向其他對象。  * 注意,根據(jù)慣例,即是static又是final的域(即編譯器常量)將用大寫表示,并用下劃分割個單詞。 */public class FinalData {  private static Random rand = new Random(47);  private String id;  public FinalData(String id) {    this.id = id;  }  // 編譯時常量 Can be compile-time constants:  private final int valueOne = 9;  private static final int VALUE_TWO = 99;  // 典型的公共常量 Typical public constant:  public static final int VALUE_THREE = 39;  // 運(yùn)行時常量 Cannot be compile-time constants:  private final int i4 = rand.nextInt(20);  static final int INT_5 = rand.nextInt(20);  private Value v1 = new Value(11);  private final Value v2 = new Value(22);  private static final Value VAL_3 = new Value(33);  // 數(shù)組 Arrays:  private final int[] a = { 1, 2, 3, 4, 5, 6 };  public String toString() {    return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;  }  public static void main(String[] args) {    FinalData fd1 = new FinalData("fd1");    // ! fd1.valueOne++; // Error: can't change value    fd1.v2.i++; // Object isn't constant!    fd1.v1 = new Value(9); // OK -- not final    for (int i = 0; i < fd1.a.length; i++)      fd1.a[i]++; // Object isn't constant!    // ! fd1.v2 = new Value(0); // Error: Can't    // ! fd1.VAL_3 = new Value(1); // change reference    // ! fd1.a = new int[3];    System.out.println(fd1);    System.out.println("Creating new FinalData");    FinalData fd2 = new FinalData("fd2");    System.out.println(fd1);    System.out.println(fd2);  }  /**   * 輸出結(jié)果:   * fd1: i4 = 15, INT_5 = 18   * Creating new FinalData   * fd1: i4 = 15, INT_5 = 18   * fd2: i4 = 13, INT_5 = 18   */}

由于valueOne和VALUE_TWO都是帶有編譯時數(shù)值的final基本類型,所以它們二者均可以用作編譯期常量,并且沒有重大區(qū)別。VALUE_THREE是一種更加典型的對常量進(jìn)行定義的方式:定義為public,可以被任何人訪問;定義為static,則強(qiáng)調(diào)只有一份;定義為final,這說明它是個常量。請注意帶有恒定初始值的final static基本類型全用大寫字母命名,并且字母與字母之間用下劃線隔開。

我們不能因為某些數(shù)據(jù)是final的就認(rèn)為在編譯時可以知道它的值。在運(yùn)行時使用隨機(jī)數(shù)來初始化i4和INT_5的值說明了這一點(diǎn)。實(shí)例中fd1和fd2中i4的值是唯一的,每次都會被初始化為15,13。INT_5的值是不可以通過創(chuàng)建第二個FinalData對象加以改變的。這是因為他是static的,在裝載類時(也就是第一次創(chuàng)建這個類對象時)已經(jīng)被初始化,而不是每次創(chuàng)建都初始化。

java也許生成"空白final",所謂空白final是指被聲明為final但又未給初值的域。無論什么情況下編譯器都會保證final域在使用前初始化。但空白final在final的使用上提供了很大的靈活性,為此,一個final域可以根據(jù)某些對象有所不同,卻又保持恒定不變的特性。下面的事例說明了一點(diǎn):

package finalPackage;class Poppet {  private int i;  Poppet(int ii) {    i = ii;  }  public int getI() {    return i;  }  public void setI(int i) {    this.i = i;  }}/** * 空白final * @author Administrator * 所謂空白final是指被聲明為final但又未給初值的域。無論什么情況下編譯器都會保證final域在使用前初始化。 */public class BlankFinal {  private final int i = 0; // Initialized final  private final int j; // Blank final  private final Poppet p; // Blank final reference  // Blank finals MUST be initialized in the constructor:  public BlankFinal() {    j = 1; // Initialize blank final    p = new Poppet(1); // Initialize blank final reference  }  public BlankFinal(int x) {    j = x; // Initialize blank final    p = new Poppet(x); // Initialize blank final reference  }  public static void main(String[] args) {    BlankFinal b1=new BlankFinal();    BlankFinal b2=new BlankFinal(47);    System.out.println("b1.j="+b1.j+"/t/t b1.p.i="+b1.p.getI());    System.out.println("b2.j="+b2.j+"/t/t b2.p.i="+b2.p.getI());  }  /**   * 輸出結(jié)果:   * b1.j=1     b1.p.i=1   * b2.j=47     b2.p.i=47   */} 

final參數(shù)

java中也許將參數(shù)列表中的參數(shù)以聲明的方式聲指明為final。這意味著你無發(fā)改變參數(shù)所指向的對象。例如:

package finalPackage;class Gizmo {  public void spin(String temp) {    System.out.println(temp+" Method call Gizmo.spin()");  }}/** * final參數(shù) * @author Administrator * 如果將參數(shù)列表中的參數(shù)指明為final,這意味著你無發(fā)改變參數(shù)所指向的對象的引用。 */public class FinalArguments {  void with(final Gizmo g) {    // ! g = new Gizmo(); // Illegal -- g is final  }  void without(Gizmo g) {    g = new Gizmo(); // OK -- g not final    g.spin("without");  }  // 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);    System.out.println("bf.g(10)="+bf.g(10));  }  /**   * 輸出結(jié)果:   * withoutMethod call Gizmo.spin()   * bf.g(10)=11   */} 

使用final方法有兩個原因。第一個原因是把方法鎖定,以防止任何繼承它的類修改它的含義。這是出于設(shè)計的考慮:想要確保在繼承中使用的方法保持不變,并且不會被覆蓋。

過去建議使用final方法的第二個原因是效率。在java的早期實(shí)現(xiàn)中,如果將一個方法指明為final,就是同意編譯器將針對該方法的所有調(diào)用都轉(zhuǎn)為內(nèi)嵌調(diào)用。當(dāng)編譯器發(fā)現(xiàn)一個final方法調(diào)用命令時,它會根據(jù)自己的謹(jǐn)慎判斷,跳過插入程序代碼這種正常的調(diào)用方式而執(zhí)行方法調(diào)用機(jī)制(將參數(shù)壓入棧,跳至方法代碼處執(zhí)行,然后跳回并清理棧中的參數(shù),處理返回值),并且以方法體中的實(shí)際代碼的副本來代替方法調(diào)用。這將消除方法調(diào)用的開銷。當(dāng)然,如果一個方法很大,你的程序代碼會膨脹,因而可能看不到內(nèi)嵌所帶來的性能上的提高,因為所帶來的性能會花費(fèi)于方法內(nèi)的時間量而被縮減。

在最近的java版本中,虛擬機(jī)(特別是hotspot技術(shù))可以探測到這些情況,并優(yōu)化去掉這些效率反而降低的額外的內(nèi)嵌調(diào)用,因此不再需要使用final方法來進(jìn)行優(yōu)化了。事實(shí)上,這種做法正逐漸受到勸阻。在使用java se5/6時,應(yīng)該讓編譯器和JVM去處理效率問題,只有在想明確禁止覆蓋式,才將方法設(shè)置為final的。

final和private關(guān)鍵字

類中的所有private方法都是隱式的制定為final的。由于你無法訪問private方法你也就無法覆蓋它。可以對private方法添加final修飾詞,但這毫無意義。例如:

package finalPackage;/** * final和private關(guān)鍵字  *  * 類中的所有private方法都是隱式的制定為final的。 * 由于你無法訪問private方法,所以你也就無法覆蓋它。 * 可以對private方法添加final修飾詞,但這毫無意義。 */class WithFinals {  // Identical to "private" alone:  private final void f() {    System.out.println("WithFinals.f()");  }  // Also automatically "final":  private void g() {    System.out.println("WithFinals.g()");  }}class OverridingPrivate extends WithFinals {  private final void f() {    System.out.println("OverridingPrivate.f()");  }  private void g() {    System.out.println("OverridingPrivate.g()");  }}class OverridingPrivate2 extends OverridingPrivate {  public final void f() {    System.out.println("OverridingPrivate2.f()");  }  public void g() {    System.out.println("OverridingPrivate2.g()");  }}public class OverideFinal {  public static void main(String[] args) {    WithFinals w1 = new WithFinals();    // ! w1.f(); //Error,無法訪問私有方法    // ! w1.g(); //Error,無法訪問私有方法    OverridingPrivate w2 = new OverridingPrivate();    // ! w2.f(); //Error,無法訪問私有方法    // ! w2.g(); //Error,無法訪問私有方法    OverridingPrivate2 w3 = new OverridingPrivate2();    w3.f();    w3.g();  }  /**   * 輸出結(jié)果:   * OverridingPrivate2.f()   * OverridingPrivate2.g()   */}

"覆蓋"只有在某方法是基類接口的一部分時才會發(fā)生。即,必須將一個對象向上轉(zhuǎn)型為它的基類并調(diào)用相同的方法。如果某方法是private的,它就不是基類接口的一部分。它僅是一些隱藏于類中的程序代碼,如果一個基類中存在某個private方法,在派生類中以相同的名稱創(chuàng)建一個public,protected或包訪問權(quán)限方法的話,該方法只不過是與基類中的方法有相同的名稱而已,并沒有覆蓋基類方法。由于private方法無法觸及而且能有效隱藏,所以除了把它看成是因為它所歸屬的類的組織結(jié)構(gòu)的原因而存在外,其他任何事物都不需要考慮它。

final 類

當(dāng)將類定義為final時,就表明了你不打算繼承該類,而且也不許別人這樣做。換句話說,出于某種考慮,你對該類的設(shè)計永不需要做任何變動,或者出于安全的考慮,你不希望他有子類。例如:

package finalPackage;class SmallBrain {}final class Dinosaur {  int i = 7;  int j = 1;  SmallBrain x = new SmallBrain();  void f() {    System.out.println("Dinosaur.f()");  }}// ! class Further extends Dinosaur {}// error: Cannot extend final class 'Dinosaur'/** * final 類 *  * final類中的屬性可以選擇是否定義為final * final類中的方法都隱式的制定為final方法,因此你無法覆蓋他們 */public class Jurassic {  public static void main(String[] args) {    Dinosaur n = new Dinosaur();    n.f();    n.i = 40;    n.j++;    System.out.println("n.i="+n.i);    System.out.println("n.j="+n.j);  }  /**   * 輸出結(jié)果為:   * Dinosaur.f()   * n.i=40   * n.j=2   */}

請注意,final類的域可以根據(jù)個人的意愿選擇是或不是final。不論類是否被定義為final,相同的規(guī)則同樣適用于定義為final的域。然而,由于final是無法繼承的,所以被final修飾的類中的方法都隱式的制定為fianl,因為你無法覆蓋他們。在fianl類中可以給方法添加final,但這不會產(chǎn)生任何意義。

結(jié)論:

根據(jù)程序上下文環(huán)境,Java關(guān)鍵字final有“這是無法改變的”或者“終態(tài)的”含義,它可以修飾非抽象類、非抽象類成員方法和變量。你可能出于兩種理解而需要阻止改變:設(shè)計或效率。
final類不能被繼承,沒有子類,final類中的方法默認(rèn)是final的。
final方法不能被子類的方法覆蓋,但可以被繼承。
final成員變量表示常量,只能被賦值一次,賦值后值不再改變。
final不能用于修飾構(gòu)造方法。
注意:父類的private成員方法是不能被子類方法覆蓋的,因此private類型的方法默認(rèn)是final類型的。

希望本文所述對大家Java程序設(shè)計有所幫助。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 元阳县| 连江县| 思南县| 宁强县| 文成县| 连平县| 阿城市| 神木县| 绩溪县| 莆田市| 象山县| 永城市| 遵义县| 叙永县| 嘉禾县| 安阳县| 潮州市| 高平市| 秦安县| 什邡市| 句容市| 运城市| 军事| 北海市| 罗源县| 郴州市| 许昌市| 长汀县| 蒙山县| 绥宁县| 名山县| 策勒县| 临沧市| 镇远县| 闽侯县| 库车县| 博客| 台北县| 招远市| 仪陇县| 淮安市|