1. 程式人生 > 其它 >Java中的final變數、final方法和final類

Java中的final變數、final方法和final類

Java中的final變數、final方法和final類

final變數

final關鍵字可用於變數宣告,一旦該變數被設定,就不可以再改變該變數的值。通常,由final定義的變數為常量。例如,在類中定義PI值,可以使用如下語句:

final double PI=3.14;

在Java中定義全域性常量,通常使用public static final修飾,這樣的常量只能在定義是被賦值。

public static final double PI_VAULE = 3.14;

規範:被定義為final的常量定義時需要使用大寫字母命名,並且中間使用下劃線進行連線。

常量示例:

import java.util.Random;
 
class Test
{
	int i = 0;
}
 
/**
 * 常量示例
 * 
 * @author pan_junbiao
 *
 */
public class FinalData
{
	static Random rand = new Random();
	private final int VALUE_1 = 9; // 宣告一個final常量
	private static final int VALUE_2 = 10; // 宣告一個final、static常量
	private final Test test = new Test(); // 宣告一個final引用
	private Test test2 = new Test(); // 宣告一個不是final的引用
	private final int[] a = { 1, 2, 3, 4, 5, 6 }; // 宣告一個定義為final的陣列
	private final int i4 = rand.nextInt(20);
	private static final int i5 = rand.nextInt(20);
 
	public String toString()
	{
		return "i4值:" + i4 + " i5值:" + i5 + " ";
	}
 
	public static void main(String[] args)
	{
		FinalData data = new FinalData();
 
		// 報錯:不能改變定義為final的常量值
		// data.VALUE_1 = 8;
 
		// 報錯:不能改變定義為final的常量值
		// data.VALUE_2 = 9;
 
		// 報錯:不能將定義為final的引用指向其他引用
		// data.test = new Test();
 
		// 正確: 可以對指定為final的引用中的成員變數賦值
		data.test.i = 1;
 
		// 正確: 可以將沒有定義為final的引用指向其他引用
		data.test2 = new Test();
 
		// 報錯:不能對定義為final的陣列賦值
		// int b[] = { 7, 8, 9 };
		// data.a = b;
 
		// 但是final的陣列中的每一項內容是可以改變的
		for (int i = 0; i < data.a.length; i++)
		{
			data.a[i] = 9;
		}
 
		System.out.println(data);
		System.out.println("data2");
		System.out.println(new FinalData());
	}
}

執行結果:

i4值:5 i5值:8
data2
i4值:4 i5值:8

從上述執行結果中可以發現i5的值是相同的。

全域性常量:

我們知道一個被定義為final的物件引用只能指向唯一一個物件,不可以將它再指向其它物件,但是一個物件的值卻是可以改變的,那麼為了使一個常量真正做到不可更改,可以將常量宣告為static final。

示例:在專案中建立FinalStaticData類,在該類中建立Random類的物件,在主方法中分別輸出類中定義的final變數a1與a2。

import static java.lang.System.out;
 
import java.util.Random;
 
/**
 * FinalStaticData類
 * 
 * @author pan_junbiao
 *
 */
public class FinalStaticData
{
	private static Random rand = new Random(); // 例項化一個Random類物件
	// 隨機產生0~10之間的隨機數賦予定義為final的a1
	private final int a1 = rand.nextInt(10);
	// 隨機產生0~10之間的隨機數賦予定義為static final的a2
	private static final int a2 = rand.nextInt(10);
 
	public static void main(String[] args)
	{
		FinalStaticData fdata = new FinalStaticData(); // 例項化一個物件
		// 呼叫定義為final的a1
		out.println("重新例項化物件呼叫a1的值:" + fdata.a1);
		// 呼叫定義為static final的a2
		out.println("重新例項化物件呼叫a2的值:" + fdata.a2);
		// 例項化另外一個物件
		FinalStaticData fdata2 = new FinalStaticData();
		out.println("重新例項化物件呼叫a1的值:" + fdata2.a1);
		out.println("重新例項化物件呼叫a2的值:" + fdata2.a2);
	}
}

執行結果:

重新例項化物件呼叫a1的值:6
重新例項化物件呼叫a1的值:8
重新例項化物件呼叫a1的值:1
重新例項化物件呼叫a1的值:8

從本示例執行結果中可以看出,定義為final的常量不是恆定不變的,將隨機數賦予定義為final的常量,可以做到每次執行程式時改變a1的值。但是a2與a1不同,由於它被宣告為static final形式,所以在記憶體中為a2開闢了一個恆定不變的區域,當再次例項化一個FinalStaticData物件時,仍然指向a2這塊記憶體區域,所以a2的值儲存不變。a2是在裝載時被初始化,而不是每次建立新物件時被初始化;而a1會重新例項化物件時被更改。

最後總結一下在程式中final資料可以出現的位置,如下程式。

/**
 * 總結一下在程式中final資料可以出現的位置
 * 
 * @author pan_junbiao
 *
 */
public class FinalDataTest
{
	// final成員變數不可更改
	final int VALUE_ONE = 6;
 
	// 在宣告final成員變數時沒有賦值,稱為空白final
	final int BLANK_FINALVAULE;
 
	// 在構造方法中為空白final賦值
	public FinalDataTest()
	{
		BLANK_FINALVAULE = 8;
	}
 
	// 設定final引數,不可以改變引數x的值
	int doIt(final int x)
	{
		return x + 1;
	}
 
	// 區域性變數定義為final,不可以改變i的值
	void doSomething()
	{
		final int i = 7;
	}
}

final方法

首先,我們應該瞭解定義為final的方法不能被重寫。

將方法定義為final型別可以防止任何子類修改該類的定義與實現方式,同時定義為final的方法執行效率要高於非final方法。在修飾許可權中曾經提到過private修飾符,如果一個父類的某個方法被設定為private修飾符,子類將無法訪問該方法,自然無法覆蓋該方法,所以一個定義為private的方法隱式被指定為final型別,這樣無須將一個定義為private的方法再定義為final型別。

語法:

private final void test()
{
}

final類

定義為final的類不能被繼承。

如果希望一個類不允許任何類繼承,並且不允許其他人對這個類有任何改動,可以將這個類設定為final形式。

final類的語法如下:

final 類名{}

如果將某個類設定為final形式,則類中的所有方法都被隱式設定為final形式,但是final類中的成員變數可以被定義為final或非final形式。

示例:在專案中建立FinalClass類,在類中定義doit()方法和變數a,實現在主方法中操作變數a自增。

/**
 * 定義final類
 * 
 * @author pan_junbiao
 *
 */
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); // 結果:4
	}
}

總結

下面總結了一些使用final關鍵字的好處:

(1)final關鍵字提高了效能。JVM和Java應用都會快取final變數。

(2)final變數可以安全的在多執行緒環境下進行共享,而不需要額外的同步開銷。

(3)使用final關鍵字,JVM會對方法、變數及類進行優化。

不可變類:

建立不可變類要使用final關鍵字。不可變類是指它的物件一旦被建立了就不能被更改了。String是不可變類的代表。不可變類有很多好處,譬如它們的物件是隻讀的,可以在多執行緒環境下安全的共享,不用額外的同步開銷等等。

關於final的重要知識點:

(1)final關鍵字可以用於成員變數、本地變數、方法以及類。

(2)final成員變數必須在宣告的時候初始化或者在構造器中初始化,否則就會報編譯錯誤。

(3) 你不能夠對final變數再次賦值。

(4)本地變數必須在宣告時賦值。

(5)在匿名類中所有變數都必須是final變數。

(6)final方法不能被重寫。

(7)final類不能被繼承。

(8)final關鍵字不同於finally關鍵字,後者用於異常處理。

(9)final關鍵字容易與finalize()方法搞混,後者是在Object類中定義的方法,是在垃圾回收之前被JVM呼叫的方法。

(10)介面中宣告的所有變數本身是final的。

(11)final和abstract這兩個關鍵字是反相關的,final類就不可能是abstract的。

(12)final方法在編譯階段繫結,稱為靜態繫結(static binding)。

(13)沒有在宣告時初始化final變數的稱為空白final變數(blank final variable),它們必須在構造器中初始化,或者呼叫this()初始化。不這麼做的話,編譯器會報錯“final變數(變數名)需要進行初始化”。

(14)將類、方法、變數宣告為final能夠提高效能,這樣JVM就有機會進行估計,然後優化。

(15)按照Java程式碼慣例,final變數就是常量,而且通常常量名要大寫。