基礎篇-final, finally, finalize 的區別

要知道三者的區別,首先對三者進行簡單的認識

一、final

首先明確final是java的關鍵字,是一種修飾符,可以修飾類、屬性、方法。

1、修飾類

使用final修飾的類不能被其他類繼承,即抽象類可能永遠和final說bye bye 了。

那麼都有哪些類被final修飾?

常見的就是java.lang.String以及java.lang包下的多數類。個人覺得被final修飾的這些類主要是直白的實現了一些功能而不再需要我們再繼續擴展的一些類。

public final class Boolean
public final class Byte
public final class Character
public static final class Character.UnicodeBlock
public final class Class<T>
public final class Compiler
public final class Double
public final class Integer
public final class StringBuffer
public final class System
...

 

上面可以看到使用final修飾以後無法繼承。

如果確定類不會被繼承可以使用該修飾符修飾,反之謹慎使用吧

2、修飾方法

/**
 * final關鍵字用法
 * @author andim
 *
 */
public class FinalDemo extends Test{
	public void test(){
		method();
	}
	/**
	 * 繼承的重寫不起作用
	 */
	public void method(){
		System.out.println("重寫失敗,錯誤提示:Cannot override the final method from Test");
	}
}

class Test{
	public final void method(){
		
	}
}

 

總結:修飾方法主要爲了防止外部修改其方法,使用場景多在繼承時。

注:類的private方法會隱式地被指定爲final方法。

3、修飾屬性

修飾變量的場景一般較多

對於一個final變量,如果是基本數據類型的變量,則其數值一旦在初始化之後便不能更改;如果是引用類型的變量,則在對其初始化之後便不能再讓其指向另一個對象。(下面的方法中賦值都會提示錯誤)

public class FinalDemo{
	public final int in = 5;
	private Object obj = new Object();
	protected String str;//默認賦值爲null
	public static void main(String[] args) {
		in = 2;
		obj = new Object();
		str = "string";
	}
}

1.類的成員變量中使用final修飾後的區別

public class FinalDemo{
	public static void main(String[] args) {
		String a = "helloworld";
		final String b = "hello";
		String c = "hello";
		String d = b+"world";
		String e = c+"world";
		System.out.println(d == a);
		System.out.println(e == a);
	}
}

打印結果:

true
false
分析原因:

成員變量a賦值後,helloworld值存入常量區。

因爲當final變量是基本數據類型以及String類型時,如果在編譯期間能知道它的確切值,則編譯器會把它當做編譯期常量使用。

所以在構建d時,直接將hello字符串拿到並發現常量池中有該字符串,所以指向其地址。

而c字符串在常量池的新開闢內存地址,所以構建e時是該地址之上的構建,並不指向a的地址空間,所以結果這樣。

不過final修飾的成員變量必須在編譯時就確定,如果不確定則結果也爲false,如給b賦值是使用方法等。

public class FinalDemo{
	String a = "helloworld";
	final String b;
	String c = "hello";
	public FinalDemo(){
		b = "hello";
	}
	public void test(){
		String d = b+"world";
		String e = c+"world";
		System.out.println(d == a);
		System.out.println(e == a);
	}
	public static void main(String[] args) {
		new FinalDemo().test();
	}
}

2.final修飾的引用變量內容是否可變?

public class FinalDemo{
	public static void main(String[] args) {
		final Test test = new Test();
		System.out.println(test.in);
		System.out.println(++test.in);
	}
}

class Test{
	public int in = 0;
}

結果:

0
1
發現是可變的。

3.靜態內部類訪問外部屬性,爲什麼外部屬性必須使用final修飾?

內部類裏面使用外部類的局部變量時,其實就是內部類的對象在使用它,內部類對象生命週期中都可能調用它,而內部類試圖訪問外部方法中的局部變量時,外部方法的局部變量很可能已經不存在了,那麼就得延續其生命,拷貝到內部類中,而拷貝會帶來不一致性,從而需要使用final聲明保證一致性。說白了,內部類會自動拷貝外部變量的引用,爲了避免:1. 外部方法修改引用,而導致內部類得到的引用值不一致 2.內部類修改引用,而導致外部方法的參數值在修改前和修改後不一致。於是就用 final 來讓該引用不可改變。

好了,final的介紹到此爲止,下面看令兩個。

 

 

二、finally

finally是在異常處理時提供finally塊來執行任何清除操作。不管有沒有異常被拋出、捕獲,finally塊都會被執行。try塊中的內容是在無異常時執行到結束。catch塊中的內容,是在try塊內容發生catch所聲明的異常時,跳轉到catch塊中執行。finally塊則是無論異常是否發生,都會執行finally塊的內容,所以在代碼邏輯中有需要無論發生什麼都必須執行的代碼,就可以放在finally塊中。

所以就是說finally只在捕獲異常時使用。

三、finalize

finalize()是在java.lang.Object裏定義的,也就是說每一個對象都有這麼個方法。這個方法在gc啓動,該對象被回收的時候被調用。其實gc可以回收大部分的對象(凡是new出來的對象,gc都能搞定,一般情況下我們又不會用new以外的方式去創建對象),所以一般是不需要程序員去實現finalize的。
特殊情況下,需要程序員實現finalize,當對象被回收的時候釋放一些資源,比如:一個socket鏈接,在對象初始化時創建,整個生命週期內有效,那麼就需要實現finalize,關閉這個鏈接。
使用finalize還需要注意一個事,調用super.finalize();

一個對象的finalize()方法只會被調用一次,而且finalize()被調用不意味着gc會立即回收該對象,所以有可能調用finalize()後,該對象又不需要被回收了,然後到了真正要被回收的時候,因爲前面調用過一次,所以不會調用finalize(),產生問題。
所以,推薦不要使用finalize()方法,它跟析構函數不一樣。

1.object定義
protected void finalize() //定義爲子類可見

2.執行時機不可預知
當一個對象變得不可觸及時,垃圾回收器某個時期會回收此對象。
當回收對象之前會調用finalize方法,這類似於人類臨終之前必須做一件事情:寫遺言。
因爲GC是不確定性的(這跟JVM相關),所以finalize方法的執行具有不可預知性。

3.finalize忽略異常
即finalize代碼中若出現異常,異常會被忽略

4.finalize使用
什麼時候使用?一般來說,finalize被作爲第二種安全網來使用,如FileInputStream類,
當對象回收時,有可能資源爲釋放,所以這裏第二次來確認(那也總比不釋放強吧,雖然具體釋放時機未定)

總結:

以上三個除了單詞長相上相似以外,貌似沒有任何有交集的地方

final是修飾符,finally用來捕獲異常時無論結果到要執行的代碼塊,finalize是方法,調用gc的方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章