Java的final變數、final方法和final類
1、final變數
final關鍵字可用於變數宣告,一旦該變數被設定,就不可以再改變該變數的值。通常,由final定義的變數為常量。
例如,在類中定義PI值,可以使用如下語句:
final double PI=3.14;
當在程式中使用到PI這個常量時,它的值就是3.14,如果在程式中再次對定義為final的常量賦值,編譯器將不會接受。
final關鍵字定義的常量必須在宣告時對其進行賦值操作。final除了可以修飾基本資料型別的常量,還可以修飾物件引用。由於陣列也可以被看作一個物件來引用,所以final可以修飾陣列。一旦一個物件引用被修飾為final後,它只能恆定指向一個物件,無法將其改變以指向另一個物件。一個既是static又是final的欄位只佔據一段不能改變的儲存空間。
例,在專案的com.lzw包中建立FinalData類,在該類中建立Test內部類,並定義各種型別的final變數。
package com.lzw; import java.util.Random; public class FinalData { static Random rand=new Random(); private final int VALUE_1=9; private static final int VALUE_2=10; private final Test test=new Test(); private Test test2=new Test(); private final int[]a= {1,2,3,4,5}; private final int i4=rand.nextInt(20); private static final int i5=rand.nextInt(20); public String toString() { return i4+""+i5+""; } public static void main(String[] args) { FinalData data=new FinalData(); /* data.test=new Test(); data.VALUE_1++; data.VALUE_2++;*/ data.test2=new Test(); /*for(int i=0;i<data.a.length;i++) { a[i]=9; }*/ System.out.println(data.i4); System.out.println(data.i5); System.out.println(data); } } class Test{ int i=0; }
被定義為final的常量定義時需要使用大寫字母命名,並且中間使用下劃線進行連線,這是Java中的編碼規則。同時,定義為final的資料無論是常量、物件引用還是陣列,在主函式中都不可以被改變。
一個被定義為final物件的引用只能指向唯一一個物件,不可以將它再指向其他物件,但是一個物件本身的值卻是可以改變的,那麼為了使一個常量真正做到不可被修改,可以將常量宣告為static final。
package com.lzw; import java.util.Random; public class FinalStaticData { private static Random rand=new Random();//例項化一個Random物件 private final int a1=rand.nextInt(10); private static final int a2=rand.nextInt(10); public static void main(String[] args) { FinalStaticData fdata=new FinalStaticData(); System.out.println("重新例項化物件呼叫a1的值"+fdata.a1); System.out.println("重新例項化物件呼叫a2的值"+fdata.a2); FinalStaticData fData=new FinalStaticData(); System.out.println("重新例項化物件呼叫a1的值"+fData.a1); System.out.println("重新例項化物件呼叫a2的值"+fData.a2); } }
執行結果如下:
從執行結果來看,定義為final的常量不是恆定不變的,將隨機數賦予定義為final的常量,可以做到每次執行程式時改變a1的值。但是a2與a1不同,由於它被宣告為static final形式,所以在程式中為a2開闢了一個恆定不變的區域,當再次例項化一個FinalStaticData物件時,仍然指向a2這塊記憶體區域,所以a2的值保持不變。a2是在裝載時被初始化。而不是每次建立新物件時都被初始化;而a1會在重新例項化物件時被修改。
在Java 中定義全域性常量,通常使用public static final來修飾,這樣的常量只能在定義時被賦值。
可以將方法的引數定義為final型別,這預示著無法在方法中更改引數引用所指向的物件。
2、final方法
定義為final的方法不能被重寫。
將方法定義為final型別可以防止子類修改該類的定義與實現方式,同時定義為final的方法的執行效率要高於非final方法。在修飾許可權中曾經提到過private修飾符,如果一個父類的某個方法被設定為private修飾符,子類將無法訪問該方法,自然無法覆蓋該方法,所以一個定義為private的方法隱式被指定為final型別,這樣無需將一個定義為private 的方法再定義為final型別。例如下面的語句:
private final void test(){
...//程式程式碼
}
但是在父類中被定義為private final的方法似乎可以被子類覆蓋??????
package com.lzw;
class Parents{
private final void doit() {
System.out.println("父類.doit()");
}
final void doit2() {
System.out.println("父類.doit2()");
}
public void doit3() {
System.out.println("父類.doit3()");
}
}
class Sub extends Parents{
//在子類中定義一個doit()方法
public final void doit() {
System.out.println("子類.doit()");
}
//final方法不能被覆蓋
/* final void doit2() {
System.out.println("子類.doit2()");
}*/
public void doit3() {
System.out.println("子類.doit3()");
}
}
public class FinalMethod {
public static void main(String[] args) {
Sub s=new Sub();//例項化
s.doit();//呼叫doit()方法
Parents p=s;//執行向上轉型操作
// p.doit();//不能呼叫private操作
p.doit2();
p.doit3();
}
}
執行結果如下:
可以看出,final方法不能被覆蓋。例如,doit2()方法不能在子類中被重寫,但是在父類中定義了一個private final的doit()方法,同時在子類中也定義了一個doit()方法。從表面上來看,子類中的doit()方法覆蓋了父類的doit()方法,但是覆蓋必須滿足一個物件向上轉型為它的基本型別並呼叫相同方法這樣一個條件
3、final類
定義為final的類不能被繼承。
如果希望一個類不允許任何類繼承,並且不允許其他人對這個類進行任何改動,可以將這個類設定為final形式。
final類的語法如下:
final 類名{}
如果將某個類設定為final形式,則類中的所有方法都被隱式設定為final形式,但是final類中的成員變數可以被定義為final或非final形式。
package com.lzw;
public final class FinalClass {
int a=3;
void doit() {
}
public static void main(String[] args) {
FinalClass f=new FinalClass();
f.a++;
System.out.println(f.a);
}
}
執行結果如下: