當前位置:才華齋>計算機>java語言>

Java中通過final關鍵字面向物件的詳解

java語言 閱讀(2.66W)

在Java中通過final關鍵字來宣告物件具有不變性(immutable),這裡的物件包括變數,方法,類,與C++中的const關鍵字效果類似。

Java中通過final關鍵字面向物件的詳解

immutable指物件在建立之後,狀態無法被改變

可以從三個角度考慮使用final關鍵字:

程式碼本身:不希望final描述的物件所表現的含義被改變 安全:final物件具有隻讀屬性,是執行緒安全的 效率:無法修改final物件本身,對其引用的操作更為高效

final 變數

定義final Object a,則a只能被初始化一次,一旦初始化,a的資料無法修改,若a為引用型別,則不能重新繫結其他物件。

未被初始化的final變數被稱為blank final,若為成員變數,則必須被初始化或在構造器中賦值。

例子:

class Circle { static final double PI = 3.1415926; final int radius = 5; final int xPos; final int yPos; public Circle(int x, int y) { xPos = x; yPos = y; }}

final 方法

定義final method,則該方法無法被過載,方法設計者不希望由於對方法的過載導致其他相關功能出現異常。

例子:

class BaseClass { public final void method() {}}class DerivedClass extends BaseClass { public final void method() {} // 編譯出錯}

需要注意的是,final方法的定義不一定能夠產生inline的效果,因為方法是否inline取決於JVM的策略,而非final關鍵字,通過final的設計提高方法效率是不準確的。

final 類

final class X定義的類X無法被繼承。

在Java中,String類被設計成final,其定義如下

複製程式碼 程式碼如下:

public class final String extends Object implements Serializable, Comparable, CharSequence

為什麼String被設計成final? 一個String類的例項被初始化後,其在堆上的內容無法被改變,String類提供的任何修改String物件的方法都只能夠產生一個新的String物件,大大簡化了對String的操作,是程式碼更易於閱讀和理解; String final是實現String interning(在記憶體中不同的string值只有一份)的必要條件,因為通常程式碼中存在大量的String物件,不同的引用會指向相同的字串空間,若String不為final,則當一個字串空間的內容改變時,所有的引用都需要知道這一情況,這一機制的實現是十分複雜的,無疑會影響效率。String interning能夠節省記憶體空間,同時也節省時間花銷; String只讀,則不必擔心非常重要的內容被篡改。

內部類與final

在一個方法內定義匿名內部類時,內部類只能訪問方法內的final型別變數,使得Java編譯器能夠提前捕獲變數的值,並在內部類儲存一份副本,當方法銷燬時,內部類的記憶體空間依然完整。

例子:

public class Wrapper { public static void main(String[] args) { // Object obj = null; //編譯出錯 final Object obj = null; new Thread(new Runnable() { public void run() { obj = "hello"; } })t(); }}

PS:內部匿名類無法訪問外面的.非 final 的變數的問題

這個聽起來有點拗口,其實我更多的是想說 Java 內部類的一些特性。

之所以會想起這個題目只要是最近在閱讀 JDK 原始碼中關於 HTTP keepalive 的程式碼時,其中一個原始檔 無意中看到下面這段程式碼。

final boolean result[] = {false};ivileged(new ilegedAction() { public Object run() { try { InetAddress a1 = yName(h1); InetAddress a2 = yName(h2); result[0] = ls(a2); } catch (UnknownHostException e) { } catch (SecurityException e) { } return null; }});return result[0];

Java 的匿名內部類無法訪問對應的函式的非 final 變數。要想訪問外部的 local variable, 這個variable 又必須要先定義成 fianl, 但是一定義成 final 就不能在匿名內部類中修改這個變數的值,所以要想匿名內部類返回一些有用的值時不是那麼的容易。這段程式碼使用了一個非常巧妙的方法,這裡使用陣列的方式繞過這個限制,雖然我們無法修改 result 這個變數的引用,但是我們可以修改 result 指向的那個陣列的內容。

只是想記錄一下內部匿名類修改外部變數的一個小技巧。不過既然已經到了這裡,不妨繼續的看看內部類都有哪些特性或者限制吧。

在繼續本文前,我覺得非常有必要的明確下本文中涉及的一些 Java 術語,這些術語不太好翻譯中文,所以我們還是用英文來描述。

// This is classpublic class JavaTerm { // field or member variable private int field; // constructor public JavaTerm() { } // method public void method() { // local variable int localVariable = 0; // local class class LocalClass { public LocalClass() { } } // anonymous class new Runnable() { public void run() { } }; }}

我們今天更多的將關注於 local class 和 anonymous class,它們都屬於 inner class。

Java 允許我們在一個 class 裡面再定義一個 class, 稱為巢狀類(nested class), nested class 又可以分為兩類,一類是 static nested class, 另外一個是 non-static nested class, 又稱為 inner class。inner class 又可以分為 local class 和 anonymous class。

anonymous class 的一些限制

一個 anonymous class 可以訪問包含它的類的類變數(field/member variable) 一個 anonymous class 不能訪問包含它的作用於中的不是 final 的本地變數(local variable) 和 nested class 一樣,anonymous class 中定義的 variable 會覆蓋包含這個內部類的作用域中的同名的 variable 你不能定義靜態的初始化方法 一個 anonymous class 可以有靜態的成員變數。這個成員變數必須是常量(用 final 修飾)。 一個 anonymous class 不可以有建構函式