在java中,final可以用來修飾類、方法和變量。final修飾類,表示該類是無法被任何其他類繼承的,意味著此類在一個繼承樹中是一個葉子類,并且此類的設(shè)計已被認(rèn)為很完美而不需要進行修改或擴展。final修飾類中的方法,表示該類是無法被任何其他類繼承的,不可以被重寫;也就是把該方法鎖定了,以防止繼承類對其進行更改。final修飾類中的變量,表示該變量一旦被初始化便不可改變。
程序員必備接口測試調(diào)試工具:立即使用
Apipost = Postman + Swagger + Mock + Jmeter
Api設(shè)計、調(diào)試、文檔、自動化測試工具
后端、前端、測試,同時在線協(xié)作,內(nèi)容實時同步
本教程操作環(huán)境:windows7系統(tǒng)、java8版、DELL G3電腦。
final關(guān)鍵字是什么?
1、final可以用來修飾的結(jié)構(gòu):類、方法、變量
2、final用來修飾一個類:此類不能被其它類繼承。
當(dāng)我們需要讓一個類永遠不被繼承,此時就可以用final修飾,但要注意:final類中所有的成員方法都會隱式的定義為final方法。
比如:String類、System類、StringBuffer類
3、final 用來修飾方法 :表明此方法不可以被重寫
- 作用
(1) 把方法鎖定,以防止繼承類對其進行更改。
(2) 效率,在早期的java版本中,會將final方法轉(zhuǎn)為內(nèi)嵌調(diào)用。但若方法過于龐大,可能在性能上不會有多大提升。因此在最近版本中,不需要final方法進行這些優(yōu)化了。
final方法意味著“最后的、最終的”含義,即此方法不能被重寫。
-
比如:Object類中的getClass( )
4、final 用來修飾變量 ,此時變量就相當(dāng)于常量
-
final用來修飾屬性:可以考慮賦值的位置有:顯式初始化、代碼塊中初始化、構(gòu)造器中初始化
-
final修飾局部變量:尤其是使用final修飾形參時,表明此形參是一個常量。當(dāng)我們調(diào)用此方法時,給常量形參賦一個實參,一旦賦值之后,就只能在方法體內(nèi)使用此形參的值,不能重新進行賦值。
-
如果final修飾一個引用類型時,則在對其初始化之后便不能再讓其指向其他對象了或者說他的地址不能發(fā)生變化了(因為引用的值是一個地址,final要求值,即地址的值不發(fā)生變化),但該引用所指向的對象的內(nèi)容是可以發(fā)生變化的。本質(zhì)上是一回事。
5、使用 final 關(guān)鍵字聲明類、變量和方法需要注意以下幾點:
-
final 用在變量的前面表示變量的值不可以改變,此時該變量可以被稱為常量。
-
final 用在方法的前面表示方法不可以被重寫(子類中如果創(chuàng)建了一個與父類中相同名稱、相同返回值類型、相同參數(shù)列表的方法,只是方法體中的實現(xiàn)不同,以實現(xiàn)不同于父類的功能,這種方式被稱為方法重寫,又稱為方法覆蓋。這里了解即可,教程后面我們會詳細講解)。
-
final 用在類的前面表示該類不能有子類,即該類不可以被繼承。
final 修飾變量
final 修飾的變量即成為常量,只能賦值一次,但是 final 所修飾局部變量和成員變量有所不同。
-
final 修飾的局部變量必須使用之前被賦值一次才能使用。
-
final 修飾的成員變量在聲明時沒有賦值的叫“空白 final 變量”。空白 final 變量必須在構(gòu)造方法或靜態(tài)代碼塊中初始化。
注意:final 修飾的變量不能被賦值這種說法是錯誤的,嚴(yán)格的說法是,final 修飾的變量不可被改變,一旦獲得了初始值,該 final 變量的值就不能被重新賦值。
public class FinalDemo { void doSomething() { // 沒有在聲明的同時賦值 final int e; // 只能賦值一次 e = 100; System.out.print(e); // 聲明的同時賦值 final int f = 200; } // 實例常量 final int a = 5; // 直接賦值 final int b; // 空白final變量 // 靜態(tài)常量 final static int c = 12;// 直接賦值 final static int d; // 空白final變量 // 靜態(tài)代碼塊 static { // 初始化靜態(tài)變量 d = 32; } // 構(gòu)造方法 FinalDemo() { // 初始化實例變量 b = 3; // 第二次賦值,會發(fā)生編譯錯誤 // b = 4; } }
上述代碼第 4 行和第 6 行是聲明局部常量,其中第 4 行只是聲明沒有賦值,但必須在使用之前賦值(見代碼第 6 行),其實局部常量最好在聲明的同時初始化。代碼第 13、14、16 和 17 行都聲明成員常量。代碼第 13 和 14 行是實例常量,如果是空白 final 變量(見代碼第 14 行),則需要在構(gòu)造方法中初始化(見代碼第 27 行)。代碼第 16 和 17 行是靜態(tài)常量,如果是空白 final 變量(見代碼第 17 行),則需要在靜態(tài)代碼塊中初始化(見代碼第 21 行)。
另外,無論是那種常量只能賦值一次,見代碼第 29 行為 b 常量賦值,因為之前 b 已經(jīng)賦值過一次,因此這里會發(fā)生編譯錯誤。
final 修飾基本類型變量和引用類型變量的區(qū)別
當(dāng)使用 final 修飾基本類型變量時,不能對基本類型變量重新賦值,因此基本類型變量不能被改變。 但對于引用類型變量而言,它保存的僅僅是一個引用,final 只保證這個引用類型變量所引用的地址不會改變,即一直引用同一個對象,但這個對象完全可以發(fā)生改變。
下面程序示范了 final 修飾數(shù)組和 Person 對象的情形。
import java.util.Arrays; class Person { private int age; public Person() { } // 有參數(shù)的構(gòu)造器 public Person(int age) { this.age = age; } // 省略age的setter和getter方法 // age 的 setter 和 getter 方法 } public class FinalReferenceTest { public static void main(String[] args) { // final修飾數(shù)組變量,iArr是一個引用變量 final int[] iArr = { 5, 6, 12, 9 }; System.out.println(Arrays.toString(iArr)); // 對數(shù)組元素進行排序,合法 Arrays.sort(iArr); System.out.println(Arrays.toString(iArr)); // 對數(shù)組元素賦值,合法 iArr[2] = -8; System.out.println(Arrays.toString(iArr)); // 下面語句對iArr重新賦值,非法 // iArr = null; // final修飾Person變量,p是一個引用變量 final Person p = new Person(45); // 改變Person對象的age實例變量,合法 p.setAge(23); System.out.println(p.getAge()); // 下面語句對P重新賦值,非法 // p = null; } }
從上面程序中可以看出,使用 final 修飾的引用類型變量不能被重新賦值,但可以改變引用類型變量所引用對象的內(nèi)容。例如上面 iArr 變量所引用的數(shù)組對象,final 修飾后的 iArr 變量不能被重新賦值,但 iArr 所引用數(shù)組的數(shù)組元素可以被改變。與此類似的是,p 變量也使用了 final 修飾,表明 p 變量不能被重新賦值,但 p 變量所引用 Person 對象的成員變量的值可以被改變。
注意:在使用 final 聲明變量時,要求全部的字母大寫,如 SEX,這點在開發(fā)中是非常重要的。
如果一個程序中的變量使用 public static final 聲明,則此變量將稱為全局變量,如下面的代碼:
public static final String SEX= "女";
final修飾方法
final 修飾的方法不可被重寫,如果出于某些原因,不希望子類重寫父類的某個方法,則可以使用 final 修飾該方法。
Java 提供的 Object 類里就有一個 final 方法 getClass(),因為 Java 不希望任何類重寫這個方法,所以使用 final 把這個方法密封起來。但對于該類提供的 toString() 和 equals() 方法,都允許子類重寫,因此沒有使用 final 修飾它們。
下面程序試圖重寫 final 方法,將會引發(fā)編譯錯誤。
public class FinalMethodTest { public final void test() { } } class Sub extends FinalMethodTest { // 下面方法定義將出現(xiàn)編譯錯誤,不能重寫final方法 public void test() { } }
上面程序中父類是 FinalMethodTest,該類里定義的 test() 方法是一個 final 方法,如果其子類試圖重寫該方法,將會引發(fā)編譯錯誤。
對于一個 private 方法,因為它僅在當(dāng)前類中可見,其子類無法訪問該方法,所以子類無法重寫該方法——如果子類中定義一個與父類 private 方法有相同方法名、相同形參列表、相同返回值類型的方法,也不是方法重寫,只是重新定義了一個新方法。因此,即使使用 final 修飾一個 private 訪問權(quán)限的方法,依然可以在其子類中定義與該方法具有相同方法名、相同形參列表、相同返回值類型的方法。
下面程序示范了如何在子類中“重寫”父類的 private final 方法。
public class PrivateFinalMethodTest { private final void test() { } } class Sub extends PrivateFinalMethodTest { // 下面的方法定義不會出現(xiàn)問題 public void test() { } }
上面程序沒有任何問題,雖然子類和父類同樣包含了同名的 void test() 方法,但子類并不是重寫父類的方法,因此即使父類的 void test() 方法使用了 final 修飾,子類中依然可以定義 void test() 方法。
final 修飾的方法僅僅是不能被重寫,并不是不能被重載,因此下面程序完全沒有問題。
public class FinalOverload { // final 修飾的方法只是不能被重寫,完全可以被重載 public final void test(){} public final void test(String arg){} }
final修飾類
final 修飾的類不能被繼承。當(dāng)子類繼承父類時,將可以訪問到父類內(nèi)部數(shù)據(jù),并可通過重寫父類方法來改變父類方法的實現(xiàn)細節(jié),這可能導(dǎo)致一些不安全的因素。為了保證某個類不可被繼承,則可以使用 final 修飾這個類。
下面代碼示范了 final 修飾的類不可被繼承。
final class SuperClass { } class SubClass extends SuperClass { //編譯錯誤 }
因為 SuperClass 類是一個 final 類,而 SubClass 試圖繼承 SuperClass 類,這將會引起編譯錯誤。
final 修飾符使用總結(jié)
1. final 修飾類中的變量
表示該變量一旦被初始化便不可改變,這里不可改變的意思對基本類型變量來說是其值不可變,而對對象引用類型變量來說其引用不可再變。其初始化可以在兩個地方:一是其定義處,也就是說在 final 變量定義時直接給其賦值;二是在構(gòu)造方法中。這兩個地方只能選其一,要么在定義時給值,要么在構(gòu)造方法中給值,不能同時既在定義時賦值,又在構(gòu)造方法中賦予另外的值。
2. final 修飾類中的方法
說明這種方法提供的功能已經(jīng)滿足當(dāng)前要求,不需要進行擴展,并且也不允許任何從此類繼承的類來重寫這種方法,但是繼承仍然可以繼承這個方法,也就是說可以直接使用。在聲明類中,一個 final 方法只被實現(xiàn)一次。
3. final 修飾類
表示該類是無法被任何其他類繼承的,意味著此類在一個繼承樹中是一個葉子類,并且此類的設(shè)計已被認(rèn)為很完美而不需要進行修改或擴展。
對于 final 類中的成員,可以定義其為 final,也可以不是 final。而對于方法,由于所屬類為 final 的關(guān)系,自然也就成了 final 型。也可以明確地給 final 類中的方法加上一個 final,這顯然沒有意義。
推薦教程:《java教程》