1. 程式人生 > >深入理解 Java 中的 final 關鍵字

深入理解 Java 中的 final 關鍵字

final 是Java 中重要關鍵字之一,可以應用於類、方法以及變數上。這篇文章中將講解什麼是 final 關鍵字?將變數、方法和類宣告為 final 代表了什麼?使用 final 的好處是什麼?

final 關鍵字是什麼?

final 在 Java 中是一個保留的關鍵字,可以宣告成員變數、方法、類以及本地變數。一旦你將引用宣告作 final,你將不能改變這個引用了,編譯器會檢查程式碼,如果試圖將變數再次初始化的話,編譯器會報編譯錯誤。

final 變數

凡是對成員變數或者本地變數(在方法中的或者程式碼塊中的變數稱為本地變數)宣告為 final 的都叫作 final 變數。final 變數經常和 static 關鍵字一起使用,作為常量。下面是 final 變數的例子:

public static final String NAME = "wupx";
NAME = new String("wupx"); //invalid compilation error

final 變數是隻讀的。

final 方法

final 也可以宣告方法,Java 裡用 final 修飾符去修飾一個方法的唯一正確用途就是表達:這個方法原本是一個虛方法,現在通過 final 來宣告這個方法不允許在派生類中進一步被覆寫(override)。

Java 中非私有的成員方法預設都是虛方法,而虛方法就可以在派生類中被覆寫。為了保證某個類上的某個虛方法不在派生類中被進一步覆寫,就需要使用 final 修飾符來宣告,讓編譯器(例如 javac)與 JVM 共同檢查並保證這個限制總是成立。

下面引用 R 大 在知乎上的回答來打破“用 final 修飾方法可以讓對這個方法的呼叫變快”的流言:

曾經有一種廣為流傳的說法是用final修飾方法可以讓對這個方法的呼叫變快。這種說法在現代主流的優化JVM上都是不成立的(例如Oracle JDK / OpenJDK HotSpot VM、IBM J9 VM、Azul Systems Zing VM等)。這是因為:能用final修飾的虛方法,其派生類中必然不可能存在對其覆寫的版本,於是可以判定這個虛方法只有一個可能的呼叫目標;而如果此時把這個final修飾符去掉,這些先進的JVM都可以通過“類層次分析”(Class Hierarchy Analysis,CHA)來發現這個方法在派生類中沒有進一步覆寫的版本,於是同樣可以判定這個虛方法只有一個可能的呼叫目標。然後兩者的優化程度會一模一樣,無論是從“不需要通過虛分派而可以直接呼叫目標”(稱為“去虛化”,devirtualization)還是從“便於內聯”的角度看,這兩種情況都是一樣的。

而如果某個類層次結構中原本某個虛方法就存在多個覆寫版本的話,那麼本來也不可能對這個虛方法加上final修飾,所以就算這種情況下去虛化變得困難,鍋也不能讓“因為沒用final修飾”來背。

使用final關鍵字在現代主流的優化JVM上不會提升效能。

下面是 final 方法的例子:

class User{
    public final String getName(){
        return "user:wupx";
    }
}

class Reader extends User{
    @Override
    public final String getName(){
        return "reader wupx"; //compilation error: overridden method is final
    }
}

final 類

使用 final 來修飾的類叫作 final 類,final類通常功能是完整的,它們不能被繼承,Java 中有許多類是 final 的,比如 String, Interger 以及其他包裝類。下面是 final 類的例項:

final class User{

}

class Reader extends User{  //compilation error: cannot inherit from final class

}

記憶體模型中的 final

對於 final 變數,編譯器和處理器都要遵守兩個重排序規則:

  • 建構函式內,對一個 final 變數的寫入,與隨後把這個被構造物件的引用賦值給一個變數,這兩個操作之間不可重排序
  • 首次讀一個包含 final 變數的物件,與隨後首次讀這個 final 變數,這兩個操作之間不可以重排序

實際上這兩個規則也正是針對 final 變數的寫與讀。寫的重排序規則可以保證,在物件引用對任意執行緒可見之前,物件的 final 變數已經正確初始化了,而普通變數則不具有這個保障;讀的重排序規則可以保證,在讀一個物件的 final 變數之前,一定會先讀這個物件的引用。如果讀取到的引用不為空,根據上面的寫規則,說明物件的 final 變數一定以及初始化完畢,從而可以讀到正確的變數值。

如果 final 變數的型別是引用型,那麼建構函式內,對一個 final 引用的物件的成員域的寫入,與隨後在建構函式外把這個被構造物件的引用賦值給一個引用變數,這兩個操作之間不能重排序。

實際上這也是為了保證 final 變數在對其他執行緒可見之前,能夠正確的初始化完成。

final 關鍵字的好處

下面為使用 final 關鍵字的一些好處:

  • final 關鍵字提高了效能,JVM 和 Java 應用都會快取 final 變數
  • final 變數可以安全的在多執行緒環境下進行共享,而不需要額外的同步開銷

總結

  • final 關鍵字可以用於成員變數、本地變數、方法以及類
  • final 成員變數必須在宣告的時候初始化或者在構造器中初始化,否則就彙報編譯錯誤
  • 不能夠對 final 變數再次賦值
  • 本地變數必須在宣告時賦值
  • 在匿名類中所有變數都必須是 final 變數
  • final 方法不能被重寫
  • final 類不能被繼承
  • 介面中宣告的所有變數本身是 final 的
  • final 和 abstract 這兩個關鍵字是反相關的,final 類就不可能是 abstract 的
  • 沒有在宣告時初始化 final 變數的稱為空白 final 變數(blank final variable),它們必須在構造器中初始化,或者呼叫 this() 初始化,不這麼做的話,編譯器會報錯final變數(變數名)需要進行初始化
  • 按照 Java 程式碼慣例,final 變數就是常量,而且通常常量名要大寫
  • 對於集合物件宣告為 final 指的是引用不能被更改

參考

《Java程式設計思想》

https://www.zhihu.com/question/66083114

相關推薦

深入理解Javasuper關鍵字

之前一直以為super和this是相同的,this指代子類當前物件,super指代父類物件。之前與實驗室師兄還談論過這個問題,當時他說的super只是Java中一個關鍵字,與this並不相同。一直有疑惑,覺得下面這篇部落格分析的比較到位。  Java中關鍵字 super

深入理解 Java final 關鍵字

final 是Java 中重要關鍵字之一,可以應用於類、方法以及變數上。這篇文章中將講解什麼是 final 關鍵字?將變數、方法和類宣告為 final 代表了什麼?使用 final 的好處是什麼? final 關鍵字是什麼? final 在 Java 中是一個保留的關鍵字,可以宣告成員變數、方法、類以及本地變

深入理解Java的volatile關鍵字

語言 重新 為什麽 設置 模型 可見性 會有 普通 enter 在再有人問你Java內存模型是什麽,就把這篇文章發給他中我們曾經介紹過,Java語言為了解決並發編程中存在的原子性、可見性和有序性問題,提供了一系列和並發處理相關的關鍵字,比如synchronized、vola

JDK學習---深入理解java的String

test bound test6 -h 很多 lai 靈活性 圖形 會有 本文參考資料: 1、《深入理解jvm虛擬機》 2、《大話數據結構》、《大化設計模式》 3、http://www.cnblogs.com/ITtangtang/p/3976820.html#344102

深入理解 Java的 流 (Stream)

重要 抽象 bool sta 也會 簡單 throws image true 首先,流是什麽? 流是個抽象的概念,是對輸入輸出設備的抽象,Java程序中,對於數據的輸入/輸出操作都是以“流”的方式進行。設備可以是文件,網絡,內存等。 流具有方向性,至於是輸入流還是輸出流則

javafinal關鍵字

成員變量 子類 void color 需要 sta new 根據 基本用法 在Java中,final關鍵字可以用來修飾類、方法和變量(包括成員變量和局部變量)。下面就從這三個方面來了解一下final關鍵字的基本用法。 1.修飾類   當用final修飾一個類時,表明這個類

解析Javafinal關鍵字的各種用法

col 後序 blog str 訪問 人類 依然 fin 可能 首先,我們可以從字面上理解一下final這個英文單詞的中文含義:“最後的,最終的; 決定性的; 不可更改的;”。顯然,final關鍵詞如果用中文來解釋,“不可更改的”更為合適。當你在編寫程序,可能

深入理解Java的逃逸分析

end 代碼 堆內存 解決 永遠 例子 append 解釋器 return 在Java的編譯體系中,一個Java的源代碼文件變成計算機可執行的機器指令的過程中,需要經過兩段編譯,第一段是把.java文件轉換成.class文件。第二段編譯是把.class轉換成機器指令的過程。

深入理解Java的字段與屬性的區別

ring rgs name 私有變量 pub tail 博文 們的 方式 轉載出處 http://blog.csdn.net/chenchunlin526/article/details/69939337 1、Java中的屬性和字段有什麽區別? 答:Java中的屬性(p

深入理解JAVA的NIO

類文件 邏輯 不同的 字節轉換 實現類 讀取 組件 進行 大量 前言: 傳統的 IO 流還是有很多缺陷的,尤其它的阻塞性加上磁盤讀寫本來就慢,會導致 CPU 使用效率大大降低。 所以,jdk 1.4 發布了 NIO 包,NIO 的文件讀寫設計顛覆了傳統 IO 的設計,采用通

深入理解Java的底層阻塞原理及實現

更多 安全 posix pla static events time() 方便 原理 談到阻塞,相信大家都不會陌生了。阻塞的應用場景真的多得不要不要的,比如 生產-消費模式,限流統計等等。什麽 ArrayBlockingQueue、 LinkedBlockingQueue、

深入理解Java的同步靜態方法和synchronized(class)程式碼塊的類鎖 深入理解Java併發synchronized同步化的程式碼塊不是this物件時的操作

一.回顧學習內容  在前面幾篇部落格中我我們已經理解了synchronized物件鎖、物件鎖的重入、synchronized方法塊、synchronized非本物件的程式碼塊,  連結:https://www.cnblogs.com/SAM-CJM/category/1314992.h

深入理解Java的synchronized鎖重入

問題匯入:如果一個執行緒呼叫了一個物件的同步方法,那麼他還能不能在呼叫這個物件的另外一個同步方法呢? 這裡就是synchronized鎖重入問題。 一.synchronized鎖重入  來看下面的程式碼: .這個是三個同步方法的類 public class Syn

深入理解Java停止執行緒

一.停止執行緒會帶來什麼? 對於單執行緒中,停止單執行緒就是直接使用關鍵字return或者break,但是在停止多執行緒時是讓執行緒在完成任務前去開啟另外一條執行緒,必須放棄當前任務,而這個過程是不可預測,所以必須去做好防備。 二.認識停止執行緒的幾個方法  2.1三個被棄用的方法 &n

深入理解Java的fail-fast和fail-safe

什麼是快速失敗(fail-fast)和安全失敗(fail-safe)?它們又和什麼內容有關係。以上兩點就是這篇文章的內容,廢話不多話,正文請慢用。 我們都接觸 HashMap、ArrayList 這些集合類,這些在 java.util 包的集合類就都是快速失敗的;而  java.ut

javafinal關鍵字淺談

簡單隨筆 1. final關鍵字可以修飾類,表示此類不可被繼承,final類裡的成員方法隱飾final,final類裡的成員變數可以根據需要是否定義為final 2. final可以修飾方法,主要原因是防止此方法被子類修改,即子類可以繼承final方法但不能重寫(類的private方法會

深入理解 Java 的 try-with-resource

背景 眾所周知,所有被開啟的系統資源,比如流、檔案或者Socket連線等,都需要被開發者手動關閉,否則隨著程式的不斷執行,資源洩露將會累積成重大的生產事故。 在Java的江湖中,存在著一種名為finally的功夫,它可以保證當你習武走火入魔之時,還可以

深入理解java的介面 (Interface)

概念 我們知道java中是單繼承的,這就有了很多的限制,比如我們需要多繼承的時候但是不能用多繼承,這時候怎麼辦呢?就引入了介面的概念,它彌補了java中單繼承的缺陷,這一點非常的好,如果要約定子類的實現要求並避免單繼承侷限就需要使用介面。 那麼什麼是介面呢?

深入理解javaSE之final關鍵字(終結器)(面試重點)

final關鍵字含義 final是java中保留的關鍵字,可以修飾類、方法、屬性以及變數,一旦引用宣告作final,那麼我們將不能改變這個引用了,那麼如果你嘗試去改變的話,編譯器會報錯。 final變數 什麼叫final變數? final變數就是用fin

深入理解Java的String

一、String類 想要了解一個類,最好的辦法就是看這個類的實現原始碼,來看一下String類的原始碼: public final class String implements java.io.Serializable, Comparable<Stri