亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

JAVA面試題解惑系列(四)——final、finally和

系統 1746 0

好東西分享
final、finally和finalize的區別是什么?

這是一道再經典不過的面試題了,我們在各個公司的面試題中幾乎都能看到它的身影。final、finally和finalize雖然長得像孿生三兄弟一樣,但是它們的含義和用法卻是大相徑庭。這一次我們就一起來回顧一下這方面的知識。

final關鍵字

我們首先來說說final。它可以用于以下四個地方:

  1. 定義變量,包括靜態的和非靜態的。
  2. 定義方法的參數。
  3. 定義方法。
  4. 定義類。


我們依次來回顧一下每種情況下final的作用。首先來看第一種情況,如果final修飾的是一個基本類型,就表示這個變量被賦予的值是不可變的,即它是個常量;如果final修飾的是一個對象,就表示這個變量被賦予的引用是不可變的,這里需要提醒大家注意的是,不可改變的只是這個變量所保存的引用,并不是這個引用所指向的對象。在第二種情況下,final的含義與第一種情況相同。實際上對于前兩種情況,有一種更貼切的表述final的含義的描述,那就是,如果一個變量或方法參數被final修飾,就表示它只能被賦值一次,但是JAVA虛擬機為變量設定的默認值不記作一次賦值。

被final修飾的變量必須被初始化。初始化的方式有以下幾種:

  1. 在定義的時候初始化。
  2. final變量可以在初始化塊中初始化,不可以在靜態初始化塊中初始化。
  3. 靜態final變量可以在靜態初始化塊中初始化,不可以在初始化塊中初始化。
  4. final變量還可以在類的構造器中初始化,但是靜態final變量不可以。


通過下面的代碼可以驗證以上的觀點:

Java代碼 復制代碼
  1. public ? class ?FinalTest?{ ??
  2. ???? //?在定義時初始化 ??
  3. ???? public ? final ? int ?A?=? 10 ; ??
  4. ??
  5. ???? public ? final ? int ?B; ??
  6. ???? //?在初始化塊中初始化 ??
  7. ????{ ??
  8. ????????B?=? 20 ; ??
  9. ????} ??
  10. ??
  11. ???? //?非靜態final變量不能在靜態初始化塊中初始化 ??
  12. ???? //?public?final?int?C; ??
  13. ???? //?static?{ ??
  14. ???? //?C?=?30; ??
  15. ???? //?} ??
  16. ??
  17. ???? //?靜態常量,在定義時初始化 ??
  18. ???? public ? static ? final ? int ?STATIC_D?=? 40 ; ??
  19. ??
  20. ???? public ? static ? final ? int ?STATIC_E; ??
  21. ???? //?靜態常量,在靜態初始化塊中初始化 ??
  22. ???? static ?{ ??
  23. ????????STATIC_E?=? 50 ; ??
  24. ????} ??
  25. ??
  26. ???? //?靜態變量不能在初始化塊中初始化 ??
  27. ???? //?public?static?final?int?STATIC_F; ??
  28. ???? //?{ ??
  29. ???? //?STATIC_F?=?60; ??
  30. ???? //?} ??
  31. ??
  32. ???? public ? final ? int ?G; ??
  33. ??
  34. ???? //?靜態final變量不可以在構造器中初始化 ??
  35. ???? //?public?static?final?int?STATIC_H; ??
  36. ??
  37. ???? //?在構造器中初始化 ??
  38. ???? public ?FinalTest()?{ ??
  39. ????????G?=? 70 ; ??
  40. ???????? //?靜態final變量不可以在構造器中初始化 ??
  41. ???????? //?STATIC_H?=?80; ??
  42. ??
  43. ???????? //?給final的變量第二次賦值時,編譯會報錯 ??
  44. ???????? //?A?=?99; ??
  45. ???????? //?STATIC_D?=?99; ??
  46. ????} ??
  47. ??
  48. ???? //?final變量未被初始化,編譯時就會報錯 ??
  49. ???? //?public?final?int?I; ??
  50. ??
  51. ???? //?靜態final變量未被初始化,編譯時就會報錯 ??
  52. ???? //?public?static?final?int?STATIC_J; ??
  53. }??
    public class FinalTest {
	// 在定義時初始化
	public final int A = 10;

	public final int B;
	// 在初始化塊中初始化
	{
		B = 20;
	}

	// 非靜態final變量不能在靜態初始化塊中初始化
	// public final int C;
	// static {
	// C = 30;
	// }

	// 靜態常量,在定義時初始化
	public static final int STATIC_D = 40;

	public static final int STATIC_E;
	// 靜態常量,在靜態初始化塊中初始化
	static {
		STATIC_E = 50;
	}

	// 靜態變量不能在初始化塊中初始化
	// public static final int STATIC_F;
	// {
	// STATIC_F = 60;
	// }

	public final int G;

	// 靜態final變量不可以在構造器中初始化
	// public static final int STATIC_H;

	// 在構造器中初始化
	public FinalTest() {
		G = 70;
		// 靜態final變量不可以在構造器中初始化
		// STATIC_H = 80;

		// 給final的變量第二次賦值時,編譯會報錯
		// A = 99;
		// STATIC_D = 99;
	}

	// final變量未被初始化,編譯時就會報錯
	// public final int I;

	// 靜態final變量未被初始化,編譯時就會報錯
	// public static final int STATIC_J;
}

  


我們運行上面的代碼之后出了可以發現final變量(常量)和靜態final變量(靜態常量)未被初始化時,編譯會報錯。

用final修飾的變量(常量)比非final的變量(普通變量)擁有更高的效率,因此我們在實際編程中應該盡可能多的用常量來代替普通變量,這也是一個很好的編程習慣。

當final用來定義一個方法時,會有什么效果呢?正如大家所知,它表示這個方法不可以被子類重寫,但是它這不影響它被子類繼承。我們寫段代碼來驗證一下:

Java代碼 復制代碼
  1. class ?ParentClass?{ ??
  2. ???? public ? final ? void ?TestFinal()?{ ??
  3. ????????System.out.println( "父類--這是一個final方法" ); ??
  4. ????} ??
  5. } ??
  6. ??
  7. public ? class ?SubClass? extends ?ParentClass?{ ??
  8. ???? /** ?
  9. ?????*?子類無法重寫(override)父類的final方法,否則編譯時會報錯 ?
  10. ?????*/ ??
  11. ???? //?public?void?TestFinal()?{ ??
  12. ???? //?System.out.println("子類--重寫final方法"); ??
  13. ???? //?} ??
  14. ???? ??
  15. ???? public ? static ? void ?main(String[]?args)?{ ??
  16. ????????SubClass?sc?=? new ?SubClass(); ??
  17. ????????sc.TestFinal(); ??
  18. ????} ??
  19. }??
    class ParentClass {
	public final void TestFinal() {
		System.out.println("父類--這是一個final方法");
	}
}

public class SubClass extends ParentClass {
	/**
	 * 子類無法重寫(override)父類的final方法,否則編譯時會報錯
	 */
	// public void TestFinal() {
	// System.out.println("子類--重寫final方法");
	// }
	
	public static void main(String[] args) {
		SubClass sc = new SubClass();
		sc.TestFinal();
	}
}

  


這里需要特殊說明的是,具有private訪問權限的方法也可以增加final修飾,但是由于子類無法繼承private方法,因此也無法重寫它。編譯器在處理private方法時,是按照final方法來對待的,這樣可以提高該方法被調用時的效率。不過子類仍然可以定義同父類中的private方法具有同樣結構的方法,但是這并不會產生重寫的效果,而且它們之間也不存在必然聯系。

最后我們再來回顧一下final用于類的情況。這個大家應該也很熟悉了,因為我們最常用的String類就是final的。由于final類不允許被繼承,編譯器在處理時把它的所有方法都當作final的,因此final類比普通類擁有更高的效率。而由關鍵字abstract定義的抽象類含有必須由繼承自它的子類重載實現的抽象方法,因此無法同時用final和abstract來修飾同一個類。同樣的道理,final也不能用來修飾接口。final的類的所有方法都不能被重寫,但這并不表示final的類的屬性(變量)值也是不可改變的,要想做到final類的屬性值不可改變,必須給它增加final修飾,請看下面的例子:

Java代碼 復制代碼
  1. public ? final ? class ?FinalTest?{ ??
  2. ??
  3. ???? int ?i?=? 10 ; ??
  4. ??
  5. ???? public ? static ? void ?main(String[]?args)?{ ??
  6. ????????FinalTest?ft?=? new ?FinalTest(); ??
  7. ????????ft.i?=? 99 ; ??
  8. ????????System.out.println(ft.i); ??
  9. ????} ??
  10. }??
    public final class FinalTest {

	int i = 10;

	public static void main(String[] args) {
		FinalTest ft = new FinalTest();
		ft.i = 99;
		System.out.println(ft.i);
	}
}

  


運行上面的代碼試試看,結果是99,而不是初始化時的10。

finally語句

接下來我們一起回顧一下finally的用法。這個就比較簡單了,它只能用在try/catch語句中,并且附帶著一個語句塊,表示這段語句最終總是被執行。請看下面的代碼:

Java代碼 復制代碼
  1. public ? final ? class ?FinallyTest?{ ??
  2. ???? public ? static ? void ?main(String[]?args)?{ ??
  3. ???????? try ?{ ??
  4. ???????????? throw ? new ?NullPointerException(); ??
  5. ????????}? catch ?(NullPointerException?e)?{ ??
  6. ????????????System.out.println( "程序拋出了異常" ); ??
  7. ????????}? finally ?{ ??
  8. ????????????System.out.println( "執行了finally語句塊" ); ??
  9. ????????} ??
  10. ????} ??
  11. }??
    public final class FinallyTest {
	public static void main(String[] args) {
		try {
			throw new NullPointerException();
		} catch (NullPointerException e) {
			System.out.println("程序拋出了異常");
		} finally {
			System.out.println("執行了finally語句塊");
		}
	}
}

  


運行結果說明了finally的作用:

  1. 程序拋出了異常
  2. 執行了finally語句塊


請大家注意,捕獲程序拋出的異常之后,既不加處理,也不繼續向上拋出異常,并不是良好的編程習慣,它掩蓋了程序執行中發生的錯誤,這里只是方便演示,請不要學習。

那么,有沒有一種情況使finally語句塊得不到執行呢?大家可能想到了return、continue、break這三個可以打亂代碼順序執行語句的規律。那我們就來試試看,這三個語句是否能影響finally語句塊的執行:

Java代碼 復制代碼
  1. public ? final ? class ?FinallyTest?{ ??
  2. ??
  3. ???? //?測試return語句 ??
  4. ???? public ?ReturnClass?testReturn()?{ ??
  5. ???????? try ?{ ??
  6. ???????????? return ? new ?ReturnClass(); ??
  7. ????????}? catch ?(Exception?e)?{ ??
  8. ????????????e.printStackTrace(); ??
  9. ????????}? finally ?{ ??
  10. ????????????System.out.println( "執行了finally語句" ); ??
  11. ????????} ??
  12. ???????? return ? null ; ??
  13. ????} ??
  14. ??
  15. ???? //?測試continue語句 ??
  16. ???? public ? void ?testContinue()?{ ??
  17. ???????? for ?( int ?i?=? 0 ;?i?<? 3 ;?i++)?{ ??
  18. ???????????? try ?{ ??
  19. ????????????????System.out.println(i); ??
  20. ???????????????? if ?(i?==? 1 )?{ ??
  21. ???????????????????? continue ; ??
  22. ????????????????} ??
  23. ????????????}? catch ?(Exception?e)?{ ??
  24. ????????????????e.printStackTrace(); ??
  25. ????????????}? finally ?{ ??
  26. ????????????????System.out.println( "執行了finally語句" ); ??
  27. ????????????} ??
  28. ????????} ??
  29. ????} ??
  30. ??
  31. ???? //?測試break語句 ??
  32. ???? public ? void ?testBreak()?{ ??
  33. ???????? for ?( int ?i?=? 0 ;?i?<? 3 ;?i++)?{ ??
  34. ???????????? try ?{ ??
  35. ????????????????System.out.println(i); ??
  36. ???????????????? if ?(i?==? 1 )?{ ??
  37. ???????????????????? break ; ??
  38. ????????????????} ??
  39. ????????????}? catch ?(Exception?e)?{ ??
  40. ????????????????e.printStackTrace(); ??
  41. ????????????}? finally ?{ ??
  42. ????????????????System.out.println( "執行了finally語句" ); ??
  43. ????????????} ??
  44. ????????} ??
  45. ????} ??
  46. ??
  47. ???? public ? static ? void ?main(String[]?args)?{ ??
  48. ????????FinallyTest?ft?=? new ?FinallyTest(); ??
  49. ???????? //?測試return語句 ??
  50. ????????ft.testReturn(); ??
  51. ????????System.out.println(); ??
  52. ???????? //?測試continue語句 ??
  53. ????????ft.testContinue(); ??
  54. ????????System.out.println(); ??
  55. ???????? //?測試break語句 ??
  56. ????????ft.testBreak(); ??
  57. ????} ??
  58. } ??
  59. ??
  60. class ?ReturnClass?{ ??
  61. ???? public ?ReturnClass()?{ ??
  62. ????????System.out.println( "執行了return語句" ); ??
  63. ????} ??
  64. }??
    public final class FinallyTest {

	// 測試return語句
	public ReturnClass testReturn() {
		try {
			return new ReturnClass();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("執行了finally語句");
		}
		return null;
	}

	// 測試continue語句
	public void testContinue() {
		for (int i = 0; i < 3; i++) {
			try {
				System.out.println(i);
				if (i == 1) {
					continue;
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				System.out.println("執行了finally語句");
			}
		}
	}

	// 測試break語句
	public void testBreak() {
		for (int i = 0; i < 3; i++) {
			try {
				System.out.println(i);
				if (i == 1) {
					break;
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				System.out.println("執行了finally語句");
			}
		}
	}

	public static void main(String[] args) {
		FinallyTest ft = new FinallyTest();
		// 測試return語句
		ft.testReturn();
		System.out.println();
		// 測試continue語句
		ft.testContinue();
		System.out.println();
		// 測試break語句
		ft.testBreak();
	}
}

class ReturnClass {
	public ReturnClass() {
		System.out.println("執行了return語句");
	}
}

  


上面這段代碼的運行結果如下:

  1. 執行了return語句
  2. 執行了finally語句
  3. 0
  4. 執行了finally語句
  5. 1
  6. 執行了finally語句
  7. 2
  8. 執行了finally語句
  9. 0
  10. 執行了finally語句
  11. 1
  12. 執行了finally語句


很明顯,return、continue和break都沒能阻止finally語句塊的執行。從輸出的結果來看,return語句似乎在finally語句塊之前執行了,事實真的如此嗎?我們來想想看,return語句的作用是什么呢?是退出當前的方法,并將值或對象返回。如果finally語句塊是在return語句之后執行的,那么return語句被執行后就已經退出當前方法了,finally語句塊又如何能被執行呢?因此,正確的執行順序應該是這樣的:編譯器在編譯return new ReturnClass();時,將它分成了兩個步驟,new ReturnClass()和return,前一個創建對象的語句是在finally語句塊之前被執行的,而后一個return語句是在finally語句塊之后執行的,也就是說finally語句塊是在程序退出方法之前被執行的。同樣,finally語句塊是在循環被跳過(continue)和中斷(break)之前被執行的。

finalize方法

最后,我們再來看看finalize,它是一個方法,屬于java.lang.Object類,它的定義如下:

Java代碼 復制代碼
  1. protected ? void ?finalize()? throws ?Throwable?{?}??
    protected void finalize() throws Throwable { }

  


眾所周知,finalize()方法是GC(garbage collector)運行機制的一部分,關于GC的知識我們將在后續的章節中來回顧。

在此我們只說說finalize()方法的作用是什么呢?

finalize()方法是在GC清理它所從屬的對象時被調用的,如果執行它的過程中拋出了無法捕獲的異常(uncaught exception),GC將終止對改對象的清理,并且該異常會被忽略;直到下一次GC開始清理這個對象時,它的finalize()會被再次調用。

請看下面的示例:

Java代碼 復制代碼
  1. public ? final ? class ?FinallyTest?{ ??
  2. ???? //?重寫finalize()方法 ??
  3. ???? protected ? void ?finalize()? throws ?Throwable?{ ??
  4. ????????System.out.println( "執行了finalize()方法" ); ??
  5. ????} ??
  6. ??
  7. ???? public ? static ? void ?main(String[]?args)?{ ??
  8. ????????FinallyTest?ft?=? new ?FinallyTest(); ??
  9. ????????ft?=? null ; ??
  10. ????????System.gc(); ??
  11. ????} ??
  12. }??
    public final class FinallyTest {
	// 重寫finalize()方法
	protected void finalize() throws Throwable {
		System.out.println("執行了finalize()方法");
	}

	public static void main(String[] args) {
		FinallyTest ft = new FinallyTest();
		ft = null;
		System.gc();
	}
}

  


運行結果如下:

  • 執行了finalize()方法


程序調用了java.lang.System類的gc()方法,引起GC的執行,GC在清理ft對象時調用了它的finalize()方法,因此才有了上面的輸出結果。調用System.gc()等同于調用下面這行代碼:

Java代碼 復制代碼
  1. Runtime.getRuntime().gc();??
    Runtime.getRuntime().gc();

  


調用它們的作用只是建議垃圾收集器(GC)啟動,清理無用的對象釋放內存空間,但是GC的啟動并不是一定的,這由JAVA虛擬機來決定。直到JAVA虛擬機停止運行,有些對象的finalize()可能都沒有被運行過,那么怎樣保證所有對象的這個方法在JAVA虛擬機停止運行之前一定被調用呢?答案是我們可以調用System類的另一個方法:

Java代碼 復制代碼
  1. public ? static ? void ?runFinalizersOnExit( boolean ?value)?{ ??
  2. ???? //other?code ??
  3. }??
    public static void runFinalizersOnExit(boolean value) {
	//other code
}

  


給這個方法傳入true就可以保證對象的finalize()方法在JAVA虛擬機停止運行前一定被運行了,不過遺憾的是這個方法是不安全的,它會導致有用的對象finalize()被誤調用,因此已經不被贊成使用了。

由于finalize()屬于Object類,因此所有類都有這個方法,Object的任意子類都可以重寫(override)該方法,在其中釋放系統資源或者做其它的清理工作,如關閉輸入輸出流。

通過以上知識的回顧,我想大家對于final、finally、finalize的用法區別已經很清楚了。

?

向原作者致敬,轉自:

作者:臧圩人(zangweiren)
網址: http://zangweiren.iteye.com

JAVA面試題解惑系列(四)——final、finally和finalize的區別


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 91久久精品日日躁夜夜躁欧美 | 狠狠色丁香婷婷综合久久片 | 日本a级精品一区二区三区 日本a毛片在线播放 | 国产精品久久久久激情影院 | 久久金8天国 | 自拍偷自拍亚洲精品被多人伦好爽 | 亚洲伦理中文字幕 | 久久伊人一区二区三区四区 | 亚洲人成影院在线高清 | 四虎永久在线免费观看 | 九九热这里只有 | 最新国产午夜精品视频成人 | 久久久www成人免费精品 | 老司机午夜免费视频 | 91精品久久久 | 亚洲精品一区二区三区四区手机版 | 全黄一级裸片视频免费 | 国产精品久久久久久久牛牛 | 免费观看四虎精品成人 | 天天干视频在线 | 最新国产精品亚洲二区 | 欧美在线一级片 | 亚洲精品久久99久久一 | 婷婷 色天使 | 国产一区精品 | 四虎国产精品永久地址51 | 香蕉网站男人网站 | 中文字幕天天躁夜夜狠狠综合 | 久久涩综合 | 青草青青在线观看免费视频 | 国产91页| 欧美 亚洲 中文字幕 | 免费欧美一级片 | 日韩av成人 | 91在线视频免费观看 | 精品91| 色 综合 欧美 亚洲 国产 | 我想看一级毛片免费的 | 亚洲va久久久噜噜噜久久男同 | 久久99精品国产麻豆宅宅 | 美女一级大黄录像一片 |