一、Java中final修飾符既可以修飾類、方法,也可以修飾變量,基本規則如下:
1)用final修飾的類不能被擴展,也就是說不可能有子類;
2)用final修飾的方法不能被替換或隱藏:
①使用final修飾的實例方法在其所屬類的子類中不能被替換(overridden);
②使用final修飾的靜態方法在其所屬類的子類中不能被重定義(redefined)而隱藏(hidden);
3)用final修飾的變量最多只能賦值一次,在賦值方式上不同類型的變量或稍有不同:
①靜態變量必須明確賦值一次(不能只使用類型缺省值);作為類成員的靜態變量,賦值可以在其聲明
中通過初始化表達式完成,也可以在靜態初始化塊中進行;作為接口成員的靜態變量,賦值只能在其
聲明中通過初始化表達式完成;
②實例變量同樣必須明確賦值一次(不能只使用類型缺省值);賦值可以在其聲明中通過初始化表達式
完成,也可以在實例初始化塊或構造器中進行;
③方法參數變量在方法被調用時創建,同時被初始化為對應實參值,終止於方法體(body)結束,在此
期間其值不能改變;
④構造器參數變量在構造器被調用(通過實例創建表達式或顯示的構造器調用)時創建,同時被初始化
為對應實參值,終止於構造器體結束,在此期間其值不能改變;
⑤異常處理器參數變量在有異常被try語句的catch子句捕捉到時創建,同時被初始化為實際的異常對象
,終止於catch語句塊結束,在此期間其值不能改變;
⑥局部變量在其值被訪問之前必須被明確賦值;
示例一:
interface IncludeFinalInterface{
//接口中定義的變量都是public static final的
int staticVar = 2009; //作為接口成員的變量,其賦值只能在聲明中完成
}
class IncludeFinal{
static final int staticVar;
final int instanceVar;
IncludeFinal(){
instanceVar = 9; //實例變量的賦值還可以在構造器中進行
}
IncludeFinal(final boolean sameAsStatic){
instanceVar = sameAsStatic ? staticVar : 9;
// sameAsStatic = false;//不能編譯,final修飾的構造器參數變量在構造器體中不能被再次賦值
}
static final void showStaticVar(){
System.out.println("IncludeFinal.staticVar = " + staticVar);
}
final void showInstanceVar(){
System.out.println("IncludeFinal.instatnceVar = " + instanceVar);
}
void show(final boolean showStatic){
if( showStatic )
showStaticVar();
else
showInstanceVar();
// showStatic = false; //不能編譯,final修飾的方法參數變量在方法體中不能被再次賦值
}
static void demoExceptionHandlerParameter(){
try{
System.out.println(10/0);
}
catch(final ArithmeticException ae){
ae.printStackTrace();
// ae = null;//不能編譯,final修飾的異常處理器參數變量在catch語句塊中不能被再次賦值
}
}
static void showLocalVar(){
final int localVar;
localVar = 209; //局部變量在其值被訪問之前必須被明確賦值,
//當然在其聲明中同時指定一個初始化值似乎是一個更好的選擇
System.out.println("LocalVar = " + localVar);
}
static{ //除在聲明中同時初始化外,作為類成員的靜態變量,其賦值還可以在靜態初始化塊中進行
staticVar = 2009;
}
// { //實例變量的賦值還可以在實例初始化塊中進行
// instanceVar = 9;
// }
}
public class FinalTest extends IncludeFinal {
// static void showStaticVar(){ //編譯錯誤,不能重定義父類中用final修飾的靜態方法
// System.out.println("FinalTest.staticVar = " + staticVar);
// }
// void showInstanceVar(){ //編譯錯誤,不能替換父類中用final修飾的實例方法
// System.out.println("FinalTest.instatnceVar = " + instanceVar);
// }
public static void main(String[] args){
showStaticVar();
demoExceptionHandlerParameter();
showLocalVar();
new IncludeFinal(false).show(false);
}
}
運行結果:
IncludeFinal.staticVar = 2009
java.lang.ArithmeticException: / by zero
at IncludeFinal.demoExceptionHandlerParameter(FinalTest.java:32)
at FinalTest.main(FinalTest.java:61)
LocalVar = 209
IncludeFinal.instatnceVar = 9
二、關於final變量的進一步說明:
1)定義:blank final變量是其聲明中不包含初始化表達式的final變量。
2)對於引用類型變量,final修飾符表示一旦賦值該變量就始終指向堆中同一個對象,不可改變,但是其
所指對象本身(其狀態)是可以改變的;不象C++中的const,在Java中沒有辦法僅通過一個final就可
聲明一個對象的不變性(immutability)。
3)常變量(constant variable):
①定義:常變量是用編譯時常量表達式初始化的帶有final修飾符的基本類型或字符串類型變量;
②無論靜態變量還是實例變量,如果它是常變量,則其引用在編譯時會被解析成該常變量所表示的值,
在class文件中並不存在任何對常變量域的引用;也正是基於此,當在源代碼中修改某個常變量域的
初始值並重新編譯后,該改動並不為其他class文件可見,除非對他們也重新編譯。
示例二:
public class ConstantVariable {
static final int staticVar = 10 + 8; //靜態變量staticVar是一個常變量
final int instanceVar = 100 + 8; //實例變量instanceVar也是一個常變量
//靜態變量notConstantVariable不是常變量,因為其初始化表達式不是編譯時常量表達式
static final int notConstantVariable = (int)(Math.random() * 10);
}
public class Test {
public static void main(String[] args){
//對類ConstantVariable中常變量staticVar的引用
System.out.println("staticVar = " + ConstantVariable.staticVar);
//對類ConstantVariable中常變量instanceVar的引用
System.out.println("instanceVar = " + new ConstantVariable().instanceVar);
//對類ConstantVariable中靜態變量notConstantVariable的引用
System.out.println("notConstantVariable = " + ConstantVariable.notConstantVariable);
}
}
運行結果:
staticVar = 18
instanceVar = 108
notConstantVariable = 2
Test.class文件的反編譯結果:
import java.io.PrintStream;
public class Test
{ //從該文件很容易驗證,當在ConstantVariable.java中修改常變量的值並重新編譯后,類Test的運行結果不會受任何影
//響,除非把Test.java文件也重新編譯一遍
public Test(){}
public static void main(String args[]){
//對常變量ConstantVariable.staticVar的引用在編譯后直接被其值“18”替換
System.out.println("staticVar = 18");
(new ConstantVariable()).getClass();
//對類ConstantVariable中常變量instanceVar的引用在編譯后直接被其值“108”替換
System.out.println((new StringBuilder("instanceVar = ")).append(108).toString());
//ConstantVariable.notConstantVariable不是常變量,所以其引用得以保持
System.out.println(
(new StringBuilder("notConstantVariable = ")).
append(ConstantVariable.notConstantVariable).
toString()
);
}
}
4)用final修飾的參數變量,雖然並不為所有人喜歡,但它的確為編譯器提供了附加的信息,一方面可以
增強其錯誤檢測能力,另一方面也有助於其對代碼的優化。
5)final修飾符與其他修飾符共同出現時,書寫順序可參考下面一行:
Annotation public protected private abstract static final synchronized native transient volatile strictfp