final:
final可以讓你控制你的成員、方法或者是一個類是否可被覆寫或繼承等功能,這些特點使final在Java中擁有了一個不可或缺的地位,也是學習Java時必須要知道和掌握的關鍵字之一。
final成員
當你在類中定義變數時,在其前面加上final關鍵字,那便是說,這個變數一旦被初始化便不可改變,這裡不可改變的意思對基本類型來說是其值不可變,而對於物件變數來說其引用不可再變。其初始化可以在兩個地方,一是其定義處,二是在建構函式中,兩者只能選其一。
下面程式很簡單的示範了final的常規用法:
public class Test{
final int t = 1; // 在定義時給值
// 或者(兩者只能選其一)
final int t;
public Test(){
t = 3; // 構造時給值
}
}
還有一種用法是定義方法中的參數為final,對於基本類型的變數,這樣做並沒有什麼實際意義,因為基本類型的變數在調用方法時是傳值的,也就是說你可以在方法中更改這個參數變數而不會影響到調用語句,然而對於物件變數,卻顯得很實用,因為物件變數在傳遞時是傳遞其引用,這樣你在方法中對物件變數的修改也會影響到調用語句中的物件變數,當你在方法中不需要改變作為參數的物件變數時,明確使用final進行聲明,會防止你無意的修改而影響到調用方法。
另外方法中的內部類在用到方法中的參變數時,此參變也必須聲明為final才可使用,如下代碼所示:
public class Test{
void print(final String str){
class InnerTest{
InnerTest (){
System.out.println(str);
}
}
InnerTest it=new InnerTest ();
}
public static void main(String[] args){
Test test=new Test();
test.print("Hello word!!!");
}
}
final方法
將方法聲明為final那有兩個原因,第一就是說明你已經知道這個方法提供的功能已經滿足你要求,不需要進行擴充,並且也不允許任何從此類繼承的類來覆寫這個方法,但是繼承仍然可以繼承這個方法,也就是說可以直接使用。第二就是允許編譯器將所有對此方法的調用轉化為inline(行內)調用的機制,它會使你在調用final方法時,直接將方法主體插入到調用處,而不是進行例行的方法調用,例如儲存斷點,壓棧等,這樣可能會使你的程式效率有所提高,然而當你的方法主體非常龐大時,或你在多處調用此方法,那麼你的調用主體代碼便會迅速膨脹,可能反而會影響效率,所以你要慎用final進行方法定義。
final類
當你將final用於類身上時,你就需要仔細考慮,因為一個final類是無法被任何人繼承的,那也就意味著此類在一個繼承樹中是一個葉子類,並且此類的設計已被認為很完美而不需要進行修改或擴充。對於final類中的成員,你可以定義其為final,也可以不是final。而對於方法,由於所屬類為final的關係,自然也就成了final型的。你也可以明確的給final類中的方法加上一個final,但這顯然沒有意義。
finally:
finally 關鍵字是對 Java 異常處理模型的最佳補充。 finally 結構使代碼總會執行,而不管有無異常發生。使用 finally 可以維護對象的內部狀態,並可以清理非記憶體資源。如果沒有 finally,您的代碼就會很費解。例如,下面的代碼說明,在不使用 finally 的情況下您如何編寫代碼來釋放非記憶體資源:
public void writeFile(String filePath, String fileName, String args)
throws IOException
{
FileWriter fw = new FileWriter(filePath + fileName);
try {
fw.write(args);
} catch (IOException e) {
//1
fw.close();
throw e;
}
//2
fw.close();
}
這段代碼建立了一個FileWriter object,並調用 write 方法。在退出該方法之前,您必須關閉FileWriter object,以避免資源漏洞。為了完成這一任務,我們在 //2 處調用 close,它是該方法的最後一條語句。但是,如果 try 塊中發生一個異常會怎麼樣呢?在這種情況下,//2 處的 close 調用永遠不會發生。因此,您必須捕獲這個異常,並在重新發出這個異常之前在 //1 處插入對 close 的另一個調用。這樣就可以確保在退出該方法之前關閉FileWriter object。這樣編寫代碼既麻煩又易於出錯,但在沒有 finally 的情況下這是必不可少的。有了 finally,前面的代碼就可以重寫為以下的形式:
public void writeFile(String filePath, String fileName, String args)
throws IOException
{
FileWriter fw = new FileWriter(filePath + fileName);
try {
fw.write(args);
} catch (IOException e) {
throw e;
} finally {
fw.close();
}
}
finally 塊確保 close 方法總被執行,而不管 try 塊內是否發出異常。因此,可以確保在退出該方法之前總會調用 close 方法。這樣您就可以確信FileWriter object被關閉並且您沒有泄漏資源。
finalize:
根據Java語言規範,JVM保證調用finalize函數之前,這個對象是不可達的,但是JVM不保證這個函數一定會被調用。另外,規範還保證finalize函數最多運行一次。
通常,finalize用於一些不容易控制、並且非常重要資源的釋放,例如一些I/O的操作,資料的串連。這些資源的釋放對整個應用程式是非常關鍵的。在這種情況下,程式員應該以通過程式本身管理(包括釋放)這些資源為主,以finalize函數釋放資源方式為輔,形成一種雙保險的管理機制,而不應該僅僅依靠finalize來釋放資源。
final 關鍵字 和 static 用法
一、final
根據程式上下文環境,Java關鍵字final有“這是無法改變的”或者“終態的”含義,它可以修飾非抽象類別、非抽象類別成員方法和變數。你可能出於兩種理解而需要阻止改變:設計或效率。
final類不能被繼承,沒有子類,final類中的方法預設是final的。
final方法不能被子類的方法覆蓋,但可以被繼承。
final成員變數表示常量,只能被賦值一次,賦值後值不再改變。
final不能用於修飾構造方法。
注意:父類的private成員方法是不能被子類方法覆蓋的,因此private類型的方法預設是final類型的。
1、final類
final類不能被繼承,因此final類的成員方法沒有機會被覆蓋,預設都是final的。在設計類時候,如果這個類不需要有子類,類的實現
細節不允許改變,並且確信這個類不會載被擴充,那麼就設計為final類。
2、final方法
如果一個類不允許其子類覆蓋某個方法,則可以把這個方法聲明為final方法。
使用final方法的原因有二:
第一、把方法鎖定,防止任何繼承類修改它的意義和實現。
第二、高效。編譯器在遇到調用final方法時候會轉入內嵌機制,大大提高執行效率。
例如:
| public class Test1 { public static void main(String[] args) { // TODO 自動產生方法存根 } public void f1() { System.out.println("f1"); } //無法被子類覆蓋的方法 public final void f2() { System.out.println("f2"); } public void f3() { System.out.println("f3"); } private void f4() { System.out.println("f4"); } } public class Test2 extends Test1 { public void f1(){ System.out.println("Test1父類方法f1被覆蓋!"); } public static void main(String[] args) { Test2 t=new Test2(); t.f1(); t.f2(); //調用從父類繼承過來的final方法 t.f3(); //調用從父類繼承過來的方法 //t.f4(); //調用失敗,無法從父類繼承獲得 } } |
3、final變數(常量)
用final修飾的成員變數表示常量,值一旦給定就無法改變!
final修飾的變數有三種:靜態變數、執行個體變數和局部變數,分別表示三種類型的常量。
從下面的例子中可以看出,一旦給final變數初值後,值就不能再改變了。
另外,final變數定義的時候,可以先聲明,而不給初值,這中變數也稱為final空白,無論什麼情況,編譯器都確保空白final在使用之前必須被初始化。但是,final空白在final關鍵字final的使用上提供了更大的靈活性,為此,一個類中的final資料成員就可以實現依對象而有所不同,卻有保持其恒定不變的特徵。
| package org.leizhimin; public class Test3 { private final String S="final執行個體變數S"; private final int A=100; public final int B=90; public static final int C=80; private static final int D=70; public final int E; //final空白,必須在初始化對象的時候賦初值 public Test3(int x){ E=x; } /** * @param args */ public static void main(String[] args) { Test3 t=new Test3(2); //t.A=101; //出錯,final變數的值一旦給定就無法改變 //t.B=91; //出錯,final變數的值一旦給定就無法改變 //t.C=81; //出錯,final變數的值一旦給定就無法改變 //t.D=71; //出錯,final變數的值一旦給定就無法改變 System.out.println(t.A); System.out.println(t.B); System.out.println(t.C); //不推薦用對象方式訪問靜態欄位 System.out.println(t.D); //不推薦用對象方式訪問靜態欄位 System.out.println(Test3.C); System.out.println(Test3.D); //System.out.println(Test3.E); //出錯,因為E為final空白,依據不同對象值有所不同. System.out.println(t.E); Test3 t1=new Test3(3); System.out.println(t1.E); //final空白變數E依據對象的不同而不同 } private void test(){ System.out.println(new Test3(1).A); System.out.println(Test3.C); System.out.println(Test3.D); } public void test2(){ final int a; //final空白,在需要的時候才賦值 final int b=4; //局部常量--final用於局部變數的情形 final int c; //final空白,一直沒有給賦值. a=3; //a=4; 出錯,已經給賦過值了. //b=2; 出錯,已經給賦過值了. } } |
4、final參數
當函數參數為final類型時,你可以讀取使用該參數,但是無法改變該參數的值。
public class Test4 { public static void main(String[] args) { new Test4().f1(2); } public void f1(final int i){ //i++; //i是final類型的,值不允許改變的. System.out.print(i); } } |
二、static
static表示“全域”或者“靜態”的意思,用來修飾成員變數和成員方法,也可以形成靜態static代碼塊,但是Java語言中沒有全域變數的概念。
被static修飾的成員變數和成員方法獨立於該類的任何對象。也就是說,它不依賴類特定的執行個體,被類的所有執行個體共用。只要這個類被載入, Java虛擬機器就能根據類名在運行時資料區的方法區內定找到他們。因此,static對象可以在它的任何對象建立之前訪問,無需引用任何對象。
用public修飾的static成員變數和成員方法本質是全域變數和全域方法,當聲明它類的對象市,不產生static變數的副本,而是類的所有執行個體共用同一個static變數。
static 變數前可以有private修飾,表示這個變數可以在類的靜態代碼塊中,或者類的其他靜態成員方法中使用(當然也可以在非靜態成員方法中使用--廢話),但是不能在其他類中通過類名來直接引用,這一點很重要。實際上你需要搞明白,private是存取權限限定,static表示不要執行個體化就可以使用,這樣就容易理解多了。static前面加上其它存取權限關鍵字的效果也以此類推。
static修飾的成員變數和成員方法習慣上稱為靜態變數和靜態方法,可以直接通過類名來訪問,訪問文法為:
類名.靜態方法名(參數列表...)
類名.靜態變數名
用static修飾的代碼塊表示靜態代碼塊,當Java虛擬機器(JVM)載入類時,就會執行該代碼塊(用處非常大,呵呵)。
1、static變數
按照是否靜態對類成員變數進行分類可分兩種:一種是被static修飾的變數,叫靜態變數或類變數;另一種是沒有被static修飾的變數,叫執行個體變數。兩者的區別是:
對於靜態變數在記憶體中只有一個拷貝(節省記憶體),JVM只為靜態分配一次記憶體,在載入類的過程中完成靜態變數的記憶體配置,可用類名直接存取(方便),當然也可以通過對象來訪問(但是這是不推薦的)。
對於執行個體變數,沒建立一個執行個體,就會為執行個體變數分配一次記憶體,執行個體變數可以在記憶體中有多個拷貝,互不影響(靈活)。
2、靜態方法
靜態方法可以直接通過類名調用,任何的執行個體也都可以調用,因此靜態方法中不能用this和super關鍵字,不能直接存取所屬類的執行個體變數和執行個體方法(就是不帶static的成員變數和成員成員方法),只能訪問所屬類的靜態成員變數和成員方法。因為執行個體成員與特定的對象關聯!這個需要去理解,想明白其中的道理,不是記憶!!!
因為static方法獨立於任何執行個體,因此static方法必須被實現,而不能是抽象的abstract。
3、static代碼塊
static 代碼塊也叫靜態代碼塊,是在類中獨立於類成員的static語句塊,可以有多個,位置可以隨便放,它不在任何的方法體內,JVM載入類時會執行這些靜態代碼塊,如果static代碼塊有多個,JVM將按照它們在類中出現的先後順序依次執行它們,每個代碼塊只會被執行一次。例如:
public class Test5 { private static int a; private int b; static{ Test5.a=3; System.out.println(a); Test5 t=new Test5(); t.f(); t.b=1000; System.out.println(t.b); } static{ Test5.a=4; System.out.println(a); } public static void main(String[] args) { // TODO 自動產生方法存根 } static{ Test5.a=5; System.out.println(a); } public void f(){ System.out.println("hhahhahah"); } } |
運行結果:
3
hhahhahah
1000
4
5
利用靜態代碼塊可以對一些static變數進行賦值,最後再看一眼這些例子,都一個static的main方法,這樣JVM在運行main方法的時候可以直接調用而不用建立執行個體。
4、static和final一塊用表示什麼
static final用來修飾成員變數和成員方法,可簡單理解為“全域常量”!
對於變數,表示一旦給值就不可修改,並且通過類名可以訪問。
對於方法,表示不可覆蓋,並且可以通過類名直接存取。