Java中的關(guān)鍵字之final詳解
一、final關(guān)鍵字概述
final有最終的、不可變更的意思,它可用于修飾類、變量和方法。用于表示它修飾的類、方法和變量不可改變。
總結(jié)起來(lái)是如下三點(diǎn):
- final修飾變量——表示該變量一旦獲得了初始值就不可被改變,即不能夠被重新賦值。final即可以修飾成員變量(包括類變量和實(shí)例變量),也可以修飾局部變量形參。
- final修飾方法——final修飾的方法不可被重寫(xiě)。
- final修飾類——final修飾的類不可以被繼承。
二、final修飾變量
1、final修飾成員變量
Java語(yǔ)法規(guī)定:final修飾的成員變量必須由程序員顯示地指定初始值。
final修飾的類變量、實(shí)例變量能指定初始值的地方如下:
- 類變量:必須在靜態(tài)初始化塊中指定初始值或聲明該類變量時(shí)指定初始值,而且只能在這兩個(gè)地方的其中之一指定。
- 實(shí)例變量:必須在普通初始化塊、聲明該實(shí)例變量或構(gòu)造器中指定初始值,而且只能在三個(gè)地方的其中之一指定。
若成員變量的值未在上面任何一處指定初始值,那么這些成員變量的值將一直是系統(tǒng)默認(rèn)分配的0、'\u0000'、false或null,這些成員變量也就完全失去了存在的意義。
final修飾成員變量的例子如下:
public class FinalVariable {
// 定義成員變量時(shí)指定默認(rèn)值
final int a = 2;
final String str;
final int b;
final static float c;
// 在普通初始化塊中,給沒(méi)有指定默認(rèn)值的實(shí)例變量指定初始值
{
str = "final";
// 聲明實(shí)例變量a時(shí)已經(jīng)指定了默認(rèn)值,因此不能為a重新賦值
// a = 66;
}
// 在靜態(tài)初始化塊中,給沒(méi)有指定默認(rèn)值的類變量指定初始值
static {
c = 6.6f;
}
// 在構(gòu)造器中,可對(duì)沒(méi)有指定默認(rèn)值,有沒(méi)有在普通初始化中
// 指定初始值的實(shí)例變量指定初始值
public FinalVariable() {
b = 8;
// 已在普通初始化塊中為str指定了初始值,因此在構(gòu)造器中不能對(duì)str重新賦值
// str = "change";
}
public void change() {
// 不能再普通方法中為final修飾的成員變量指定初始值,也不能重賦值
// c = 8.8f;
}
}2、final修飾局部變量
系統(tǒng)不會(huì)對(duì)局部變量進(jìn)行初始化,局部變量必須被顯示初始化。因此使用final修飾局部變量時(shí),即可以在定義時(shí)指定默認(rèn)值,也可以不指定默認(rèn)值。如果在定義時(shí)沒(méi)有指定默認(rèn)值,則可以在后面代碼中對(duì)該final變量賦初始值,但只能一次,不能重復(fù)賦值;如果在定義時(shí)已經(jīng)指定默認(rèn)值,則后面代碼中不能再對(duì)該變量賦值。
final修飾局部變量的例子如下:
public class FinalLocalVariable {
// 不能對(duì)final修飾的形參賦值
public void test(final int a) {
// 因?yàn)樾螀⒃谡{(diào)用該方法時(shí),已經(jīng)由系統(tǒng)根據(jù)傳入的參數(shù)來(lái)完成初始化,
// 因此使用final修飾的形參不能被賦值。
// a = 6;
}
public static void main(String[] args) {
// 定義final局部變量時(shí)指定默認(rèn)值,則str變量無(wú)法重新賦值
final String str = "final";
// 定義final局部變量時(shí)沒(méi)有指定默認(rèn)值,則該局部變量可被賦值一次
final int b;
b = 8;
}
}3、final修飾基本類型變量和引用類型變量的區(qū)別
當(dāng)使用final修飾基本類型變量時(shí),不能對(duì)基本類型變量重新賦值,因此基本類型變量不能被改變。但對(duì)于引用類型變量而言,它保存的僅僅是一個(gè)引用,final只保證這個(gè)引用變量所引用的地址不會(huì)改變,即一直引用同一個(gè)對(duì)象,但可以改變引用類型變量所引用的對(duì)象的內(nèi)容。
下面以final修飾數(shù)組和Person對(duì)象舉例:
import lombok.Data;
import java.util.Arrays;
@Data
class Person {
private int age;
public Person(int age) {
this.age = age;
}
}
public class FinalReference {
public static void main(String[] args) {
// final修飾數(shù)組變量,iArr是一個(gè)引用變量
final int[] iArr = {5, 6, 12, 9};
System.out.println(Arrays.toString(iArr));
// 對(duì)數(shù)組元素進(jìn)行排序
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
// 對(duì)數(shù)組元素賦值
iArr[2] = -8;
System.out.println(Arrays.toString(iArr));
// 不能對(duì)iArr重新賦值
// iArr = null;
// final修飾Person變量,p是一個(gè)引用變量
final Person p = new Person(45);
// 改變Person對(duì)象的age實(shí)例變量
p.setAge(23);
System.out.println(p.getAge());
// 不能對(duì)p重新賦值
// p = null;
}
}4、final變量的“宏替換”
final修飾符的一個(gè)重要用途就是定義“宏變量”。對(duì)一個(gè)final變量來(lái)說(shuō),不管它是類變量、實(shí)例變量還是局部變量,只要該變量滿足三個(gè)條件,這個(gè)final變量就不再是一個(gè)變量,而是相當(dāng)于一個(gè)直接量。
- 使用final修飾符修飾;
- 在定義該final變量時(shí)指定了初始值(該初始值可以是直接量,也可以是基本的算數(shù)表達(dá)式或字符串連接運(yùn)算,不可以是普通變量或調(diào)用方法);
- 該初始值可以在編譯時(shí)就被確定下來(lái)。
滿足以上三個(gè)條件的final變量本質(zhì)上就是一個(gè)“宏變量”,編譯器會(huì)把程序中所有用到該變量的地方直接替換成該變量的值。
下面看下final變量作為“宏變量”的例子:

上面代碼a是final修飾的局部變量,并且在定義時(shí)指定了初始值5,對(duì)于程序來(lái)說(shuō),變量a其實(shí)根本不存在,實(shí)際上編譯器在編譯階段,將System.out.println(a);轉(zhuǎn)換為System.out.println(5);。
final變量的b和str1在定義分別通過(guò)基本的算數(shù)表達(dá)式和字符串連接運(yùn)算指定了初始值。因此b和str1是“宏變量”。由于str1是“宏變量”,它將被直接替換成“final宏變量888”,str1將指向的是字符串常量池中的“final宏變量888”。因此第一個(gè)判斷為true。而str2需要調(diào)用String類的方法,因此編譯器無(wú)法在編譯時(shí)確定str2的值,str2不會(huì)被當(dāng)成“宏變量”處理。因此第二個(gè)判斷為false。
s2變量引用的字符串可以在編譯時(shí)就確定下來(lái),因此時(shí)直接引用常量池中已有的“final宏變量”。因此第三個(gè)判斷相等。對(duì)于s3而言,它的值由st1和st2進(jìn)行連接運(yùn)算后得到。由于st1、st2只是兩個(gè)普通變量,編譯期不會(huì)執(zhí)行“宏替換”,因此編譯器無(wú)法在編譯時(shí)確定s3的值,也就無(wú)法讓s3指向字符串常量池中緩存的“final宏變量”,所以第4個(gè)判斷為false。而s4是由st3和st4進(jìn)行連接運(yùn)算后得到,st3和st4是兩個(gè)宏變量,因此可以執(zhí)行“宏替換”,編譯器可以編譯階段確定s4的值,使其指向字符串常量池中緩存的“final宏變量”,所以第5個(gè)判斷為true。實(shí)際上s2和s4是等價(jià)的。
三、final修飾方法
final修飾的方法不可被重寫(xiě),如果出于某些原因,不希望子類重寫(xiě)父類某個(gè)方法,則可以使用final修飾該方法。
Java提供的Object類里就有一個(gè)final方法:getClass(),因?yàn)镴ava不希望任何類重寫(xiě)這個(gè)方法,所以使用final把這個(gè)方法密封起來(lái)。但對(duì)于該類提供的toStirng()和equals()方法,都允許子類重寫(xiě),因此沒(méi)有使用final修飾他們。
四、final修飾類
final修飾的類不可以有子類,例如java.lang.Math類就是一個(gè)final類,他不可以有子類。
當(dāng)子類繼承父類時(shí),將可以訪問(wèn)到父類內(nèi)部數(shù)據(jù),并可以通過(guò)重寫(xiě)父類方法來(lái)改變父類方法的實(shí)現(xiàn)細(xì)節(jié),這可能導(dǎo)致一些不安全的因素。為了保證某個(gè)類不可被繼承,則可以使用final修飾這個(gè)類。
到此這篇關(guān)于Java中的關(guān)鍵字之final詳解的文章就介紹到這了,更多相關(guān)final關(guān)鍵字內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
maven模塊化開(kāi)發(fā)部署實(shí)現(xiàn)方案
有些用戶有定制化需求,需要添加新的模塊功能,因此需要平臺(tái)主體功能迭代的同時(shí),非主體功能和定制化功能插件化,本文給大家介紹maven模塊化開(kāi)發(fā)部署實(shí)現(xiàn)方案,感興趣的朋友一起看看吧2024-01-01
Java優(yōu)選算法之位運(yùn)算實(shí)戰(zhàn)例子
這篇文章主要介紹了Java優(yōu)選算法之位運(yùn)算的相關(guān)資料,位運(yùn)算基礎(chǔ)包括左移、右移、取反、按位與、按位或、異或等操作,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-11-11
JavaWeb實(shí)現(xiàn)用戶登錄與注冊(cè)功能(服務(wù)器)
這篇文章主要介紹了JavaWeb實(shí)現(xiàn)用戶登錄與注冊(cè)功能,服務(wù)器部分的關(guān)鍵代碼實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
非常適合新手學(xué)生的Java線程池優(yōu)化升級(jí)版
作者是一個(gè)來(lái)自河源的大三在校生,以下筆記都是作者自學(xué)之路的一些淺薄經(jīng)驗(yàn),如有錯(cuò)誤請(qǐng)指正,將來(lái)會(huì)不斷的完善筆記,幫助更多的Java愛(ài)好者入門2022-03-03
Spring Security使用Lambda DSL配置流程詳解
Spring Security 5.2 對(duì) Lambda DSL 語(yǔ)法的增強(qiáng),允許使用lambda配置HttpSecurity、ServerHttpSecurity,重要提醒,之前的配置方法仍然有效。lambda的添加旨在提供更大的靈活性,但是用法是可選的。讓我們看一下HttpSecurity的lambda配置與以前的配置樣式相比2023-02-02
Java數(shù)字格式類(NumberFormat類和DecimalFormat類)用法詳解
NumberFormat類是Java提供的一個(gè)格式化數(shù)字的類,可以將一串?dāng)?shù)字轉(zhuǎn)化成自己想要的數(shù)據(jù)格式,也可以將字符串轉(zhuǎn)化成數(shù)值,下面這篇文章主要給大家介紹了關(guān)于Java數(shù)字格式類(NumberFormat類和DecimalFormat類)用法的相關(guān)資料,需要的朋友可以參考下2022-07-07

