Java關鍵詞final解讀

来源:https://www.cnblogs.com/knowledgeispower/archive/2022/12/19/16992932.html
-Advertisement-
Play Games

1 final基本用法 final:“這是無法改變的" final可以修飾:變數、參數、方法、類 1.1 final修飾變數 修飾變數(變數、局部變數),當變數類型為: 基本類型,一旦被賦值,該值不能被改變。 引用類型,一旦引用被初始化指向一個對象,就不能指向別的對象,但對象內容可以被修改 數據類型 ...


目錄

1 final基本用法

final:“這是無法改變的"
final可以修飾:變數、參數、方法、類

1.1 final修飾變數

修飾變數(變數、局部變數),當變數類型為:

  • 基本類型,一旦被賦值,該值不能被改變。
  • 引用類型,一旦引用被初始化指向一個對象,就不能指向別的對象,但對象內容可以被修改
  • 數據類型:數組也是引用類型

分析以下代碼:

import java.util.Random;
class Value {
    int i; // Package access
    public Value(int i) { this.i = i; }
}
public class FinalData {
    private static Random rand = new Random(47);
    private String id;
    public FinalData(String id) { this.id = id; }
    //編譯時常量
    private final int valueOne = 9;
    private static final int VALUE_TWO = 99;
    public static final int VALUE_THREE = 39;
    //非編譯時常量
    private final int i4 = rand.nextInt(20);
    static final int INT_5 = rand.nextInt(20);
    private Value v1 = new Value(11);
    private final Value v2 = new Value(22);
    private static final Value VAL_3 = new Value(33);
    // Arrays:
    private final int[] a = { 1, 2, 3, 4, 5, 6 };
    public String toString() {
        return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
    }

    public static void main(String[] args) {
        FinalData fd1 = new FinalData("fd1");
        //! fd1.valueOne++; // Error: can’t change value
        fd1.v2.i++; // OK:引用指向的對象內容可變
        fd1.v1 = new Value(9); // OK :非final,引用可變
        for(int i = 0; i < fd1.a.length; i++)
            fd1.a[i]++; // Object isn’t constant!
        //! fd1.v2 = new Value(0); // Error: final引用不可變
        //! fd1.VAL_3 = new Value(1); //Error: final引用不可變
        //! fd1.a = new int[3];
        System.out.println(fd1);
        System.out.println("Creating new FinalData");
        FinalData fd2 = new FinalData("fd2");
        System.out.println(fd1);
        System.out.println(fd2);
    }
}
/* 運行結果:
fd1: i4 = 15, INT_5 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18
fd2: i4 = 13, INT_5 = 18
*///:~

說明:

  1. valuOne和VALUE_TWO:都是編譯期常量,無重大區別。
  2. VAL_THREE:典型的對常量定義的方式:定義為public,則可以被用於包之外;定義為static,則強調只有一份;定義為final,則說明它是一個常量。註意這種類型常量的命名方式(大寫和下劃線)
  3. i4和INT_ 5:final變數不代表編譯時可知它的值,可以在運行時初始化值。例如在運行時使用隨機生成的數值來初始化
  4. v1、v2、VAL_3 說明final引用的特征
  5. 特別註意:INT_5:不可以通過創建第二個FinalData對象而加以改變。因為它是static的,在裝載時已被初始化,而不是每次創建新對象時都初始化

1.2 final修飾方法參數

參數:遵循final修飾變數的約束條件,不能在方法中修改它的值或者指向別的對象

 private void finalParam(final Map param){
    param = new HashMap();//報錯
    param.put("","");//不報錯
 }

1.3 final修飾方法

使用final方法的原因:確保在繼承中使方法行為保持不變,並且不會被覆蓋(設計考慮)。

  • final修飾的方法不可以重寫(重寫發生在父類與之類)
  • final修飾的方法可以重載(同一個類)

以下代碼可以正確運行:

public class FinalExampleParent {
    public final void test() {
    }
    public final void test(String str) {
    }
}

final和private:

類中所有的private方法都隱式地指定為final的。由於其它類無法取用private方法,因此無法覆蓋它。可以對private方法添加final修飾,但沒意義。

1.4 final修飾類

當類定義為final時,表示該類不可繼承
final類的所有方法都是隱式為final,因為無法覆蓋它們

1.5 空白final

定義:被聲明為final但又未給定初值的域
用途:提供了更大的靈活性:一個類中的final域就可以做到根據對象而有所不同,卻又保持其恆定不變的特性。

class Poppet {
    private int i;
    Poppet(int ii) { i = ii; }
}
public class BlankFinal {
    private final int i = 0; // Initialized final
    private final int j; // Blank final
    private final Poppet p; // Blank final reference
    //空final構造器中初始化
    public BlankFinal() {
        j = 1; // Initialize blank final
        p = new Poppet(1); // Initialize blank final reference
    }
    public BlankFinal(int x) {
        j = x; // Initialize blank final
        p = new Poppet(x); // Initialize blank final reference
    }
    public static void main(String[] args) {
    //空final域在不同情形下賦予不一樣的初值
        new BlankFinal();
        new BlankFinal(47);
    }
}

說明:

  1. 必須在域的定義處或者每個構造器中對final賦值,這正是fnal域在使用前總是被初始化的原因所在。
  2. 一個類中的final域可以根據對象而有所不同,卻又保持其不變的特性。

1.6 static final

  1. 同時是static final 的欄位占據一段不能改變的存儲空間,它必須在定義的時候進行賦值,否則編譯器將不予通過【即使在構造函數中初始化也不行】。
  2. static修飾的欄位並不屬於一個對象,而是屬於這個類的。【對一個類創建多個對象,其static final 修飾的變數其實是指向同一個值】

2 jvm角度理解final不可變性

一、Javac編譯器
final變數的不變性由Javac編譯時來保證:(只能在編譯期而不能在運行期中檢查)

javac編譯時,進入數據及控制流分析階段時,Flow.flow()會涉及以下檢查:檢查final變數是否有多次賦值,空白final變數是否在構造函數中進行過初始化。

這裡參考:javac final變數未賦值檢測講解

二、JVM類載入
final類的不可變性由jvm進行類載入的校驗階段來保證

JVM類載入的校驗階段中,對元數據驗證時,包含final語義校驗:
1. 這個類的父類是否繼承了不允許被繼承的類(被final修飾的類)
2. 類中的欄位、方法是否與父類產生矛盾(例如覆蓋了父類的final欄位,或者出現不符合規則的方法重載,例如方法參數都一致,但返回值類型卻不同等)

3 final多線程下可見性

定義:被final修飾的欄位在構造器中一旦被初始化完成,並且構造器沒有把“this”的引用傳遞出去,那麼在其他線程中就能看見final欄位的值。
如代碼所示,變數i與j都具備可見性,它們無須同步就能被其他線程正確訪問。

public static final int i;
public final int j;
static {
    i = 0;
    // 省略後續動作 
}
{
    // 選擇在構造函數中初始化
    j = 0;
    // 省略後續動作
}

解讀:
final欄位如果聲明時賦值,因為只能賦值一次,因此即便存在併發,也能確保只有唯一值
如果在構造函數中賦值,在無引用溢出下,構造函數是線程安全的,因此final欄位也是線程安全

4 final域重排序規則

這方面內容待研究,或者參考:final域重排序規則

5 面試常見問題

5.1 所有的final修飾的欄位都是編譯期常量嗎?

不是
編譯期常量指的就是程式在編譯時就能確定這個常量的具體值
非編譯期常量就是程式在運行時才能確定常量的值 (運行時常量)

public class Test {
    //編譯期常量
    final int i = 1;
    final static int J = 1;
    //非編譯期常量
    Random r = new Random();
    final int k = r.nextInt();
}

k的值由隨機數對象決定,所以不是所有的final修飾的欄位都是編譯期常量,只是k的值在被初始化後無法被更改。

5.2 final類型的類如何拓展?

設計模式中最重要的兩種關係,一種是繼承/實現,另外一種是組合關係。所以當遇到不能用繼承的,應該考慮用組合:

class MyString{
    private String innerString;
    // ...init & other methods
    // 支持老的方法
    public int length(){
        return innerString.length(); // 通過innerString調用老的方法
    }
    // 添加新方法
    public String toMyString(){
        //...
    }
}

5.3 如何理解private所修飾的方法是隱式的final?

類中所有的private方法都隱式地指定為final,因為其它類無法調用private方法,因此無法覆蓋它。可以對private方法添加final修飾,但沒意義

參考書籍:《Thinking in Java》 《深入理解java虛擬機》


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、ES2015中有四種相等演算法1. 抽象(非嚴格)相等比較。(==)2. 嚴格相等比較。( )3. 同值。(Object.is())4. 同值零。二、JavaScript提供三種不同的值比較操作1. 嚴格相等比較,使用 比較符號。(在兩者進行比較時,不會執行類型轉換)2. 抽象相等比較,使用 == ...
  • 假設,我們有這樣一張 Gif 圖: 利用 CSS,我們嘗試來搞一些事情。 圖片的 Glitch Art 風 在這篇文章中 --CSS 故障藝術,我們介紹了利用混合模式製作一種暈眩感覺的視覺效果。有點類似於抖音的 LOGO。 像是這樣: 假設,我們有這樣一張圖: 只需要一個標簽即可 <div clas ...
  • 案例介紹 歡迎來到我的小院,我是霍大俠,恭喜你今天又要進步一點點了!我們來用JavaScript編程實戰案例,做一個表情評價程式。用戶打星進行評價,表情會根據具體星星數量發生變化。 案例演示 點擊星星可以進行滿意程度評價,星星數量變換表情也會隨之變換。 源碼學習 進入核心代碼學習,我們先來看HTML ...
  • 2015 年 HTTP/2 標準發表後,大多數主流瀏覽器也於當年年底支持該標準。此後,憑藉著多路復用、頭部壓縮、伺服器推送等優勢,HTTP/2 得到了越來越多開發者的青睞。不知不覺的 HTTP 已經發展到了第三代,鵝廠也緊跟技術潮流,很多項目也在逐漸使用 HTTP/3。本文基於興趣部落接入 HTTP... ...
  • 1)寫用例 TestCase 2)執行用例 1:TestSuite存儲用例,2:TestLoader找用例,存儲用例,存放指定的TestSuite 3)對比實際結果/期望結果,判定用例是否通過#斷言Assert 4)出局測試報告TextTestRunner 1.測試類 import requests ...
  • 1.get/post介紹 get和post是最常用的http/htttps請求方式,一定要掌握好二者各自特點以及區別。 2.get/post特點 get請求 請求參數在請求地址後面,提交的數據量較小,安全性較差,不建議用來提交敏感信息(地址欄中會顯示,並且有可能被保存請求地址)。 post請求 請求 ...
  • 家居網購項目實現04 以下皆為部分代碼,詳見 https://github.com/liyuelian/furniture_mall.git 10.功能09-後臺管理 刪除家居 10.1需求分析/圖解 需求如下: 管理員進入到家居管理頁面 點擊刪除家居連接,彈出確認視窗,確認刪除,取消放棄 10.2 ...
  • 訓練模型過程中,經常需要追蹤一些性能指標的變化情況,以便瞭解模型的實時動態,例如:回歸任務中的MSE、分類任務中的Accuracy、生成對抗網路中的圖片、網路模型結構可視化…… 除了追蹤外,我們還希望能夠將這些指標以動態圖表的形式可視化顯示出來。 TensorFlow的附加工具Tensorboar... ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...