寫一篇文章容易嗎?太不容易了,首先,需要一個安靜的環境,這一點就非常不容易,很多小伙伴的辦公室都是開放式的,非常吵,況且上班時間寫的話,領導就不高興了;只能抽時間寫,其次,環境有了,還要有一顆安靜的心,如果心里裝著其他揮之不去的事,那就糟糕了,呆坐著電腦前一整天也不會有結果,
我十分佩服一些同行,他們寫萬字長文,這在我看來,幾乎不太可能完成,因為我要日更,一萬字的長文,如果走原創的話,至少需要一周時間,甚至一個月的時間,
就如小伙伴們看到的,我寫的文章大致都能在五分鐘內閱讀完,并且能夠保證小伙伴們在閱讀完學到或者溫習到一些知識,這就是我的風格,通俗易懂,輕松幽默,
好了,又一篇我去系列的文章它來了:你竟然還不會用 final 關鍵字,
已經晚上 9 點半了,我還沒有下班,因為要和小王一塊修復一個 bug,我訂了一份至尊披薩,和小王吃得津津有味的時候,他突然問了我一個問題:“老大,能給我詳細地說說 final 關鍵字嗎,總感覺對這個關鍵字的認知不夠全面,”
一下子我的火氣就來了,盡管小王問的態度很謙遜,很卑微,但我還是忍不住破口大罵:“我擦,小王,你丫的竟然不會用 final,我當初是怎么面試你進來的!”

發火歸發火,我這個人還是有原則的,等十點半回到家后,我決定為小王專門寫一篇文章,好好地講一講 final 關鍵字,也希望給更多的小伙伴一些幫助,
盡管繼承可以讓我們重用現有代碼,但有時處于某些原因,我們確實需要對可擴展性進行限制,final 關鍵字可以幫助我們做到這一點,
01、final 類
如果一個類使用了 final 關鍵字修飾,那么它就無法被繼承,如果小伙伴們細心觀察的話,Java 就有不少 final 類,比如說最常見的 String 類,
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {}
為什么 String 類要設計成 final 的呢?原因大致有以下三個:
- 為了實作字串常量池
- 為了執行緒安全
- 為了 HashCode 的不可變性
更詳細的原因,可以查看我之前寫的一篇文章,
任何嘗試從 final 類繼承的行為將會引發編譯錯誤,為了驗證這一點,我們來看下面這個例子,Writer 類是 final 的,
public final class Writer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
嘗試去繼承它,編譯器會提示以下錯誤,Writer 類是 final 的,無法繼承,

不過,類是 final 的,并不意味著該類的物件是不可變的,
Writer writer = new Writer();
writer.setName("沉默王二");
System.out.println(writer.getName()); // 沉默王二
Writer 的 name 欄位的默認值是 null,但可以通過 settter 方法將其更改為“沉默王二”,也就是說,如果一個類只是 final 的,那么它并不是不可變的全部條件,
如果,你想了解不可變類的全部真相,請查看我之前寫的文章這次要說不明白immutable類,我就怎么地,突然發現,寫系列文章真的妙啊,很多相關性的概念全部涉及到了,我真服了自己了,
把一個類設計成 final 的,有其安全方面的考慮,但不應該故意為之,因為把一個類定義成 final 的,意味著它沒辦法繼承,假如這個類的一些方法存在一些問題的話,我們就無法通過重寫的方式去修復它,

02、final 方法
被 final 修飾的方法不能被重寫,如果我們在設計一個類的時候,認為某些方法不應該被重寫,就應該把它設計成 final 的,
Thread 類就是一個例子,它本身不是 final 的,這意味著我們可以擴展它,但它的 isAlive() 方法是 final 的:
public class Thread implements Runnable {
public final native boolean isAlive();
}
需要注意的是,該方法是一個本地(native)方法,用于確認執行緒是否處于活躍狀態,而本地方法是由作業系統決定的,因此重寫該方法并不容易實作,
Actor 類有一個 final 方法 show():
public class Actor {
public final void show() {
}
}
當我們想要重寫該方法的話,就會出現編譯錯誤:

如果一個類中的某些方法要被其他方法呼叫,則應考慮事被呼叫的方法稱為 final 方法,否則,重寫該方法會影響到呼叫方法的使用,
一個類是 final 的,和一個類不是 final,但它所有的方法都是 final 的,考慮一下,它們之間有什么區別?
我能想到的一點,就是前者不能被繼承,也就是說方法無法被重寫;后者呢,可以被繼承,然后追加一些非 final 的方法,沒毛病吧?看把我聰明的,

03、final 變數
被 final 修飾的變數無法重新賦值,換句話說,final 變數一旦初始化,就無法更改,之前被一個小伙伴問過,什么是 effective final,什么是 final,這一點,我在之前的文章也有闡述過,所以這里再貼一下地址:
http://www.itwanger.com/java/2020/02/14/java-final-effectively.html
1)final 修飾的基本資料型別
來宣告一個 final 修飾的 int 型別的變數:
final int age = 18;
嘗試將它修改為 30,結果編譯器生氣了:

2)final 修飾的參考型別
現在有一個普通的類 Pig,它有一個欄位 name:
public class Pig {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在測驗類中宣告一個 final 修飾的 Pig 物件:
final Pig pig = new Pig();
如果嘗試將 pig 重新賦值的話,編譯器同樣會生氣:

但我們仍然可以去修改 Pig 的欄位值:
final Pig pig = new Pig();
pig.setName("特立獨行");
System.out.println(pig.getName()); // 特立獨行
3)final 修飾的欄位
final 修飾的欄位可以分為兩種,一種是 static 的,另外一種是沒有 static 的,就像下面這樣:
public class Pig {
private final int age = 1;
public static final double PRICE = 36.5;
}
非 static 的 final 欄位必須有一個默認值,否則編譯器將會提醒沒有初始化:

static 的 final 欄位也叫常量,它的名字應該為大寫,可以在宣告的時候初始化,也可以通過 static [代碼塊初始化](),
4) final 修飾的引數
final 關鍵字還可以修飾引數,它意味著引數在方法體內不能被再修改:
public class ArgFinalTest {
public void arg(final int age) {
}
public void arg1(final String name) {
}
}
如果嘗試去修改它的話,編譯器會提示以下錯誤:

04、總結
親愛的讀者朋友,我應該說得很全面了吧?我想小王看到了這篇文章后一定會感謝我的良苦用心的,他畢竟是個積極好學的好同事啊,
如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回復「并發」更有一份阿里大牛重寫的 Java 并發編程實戰,從此再也不用擔心面試官在這方面的刁難了,
本文已收錄 GitHub,傳送門~ ,里面更有大廠面試完整考點,歡迎 Star,
我是沉默王二,一枚有顏值卻靠才華茍且的程式員,關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,嘻嘻,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/165911.html
標籤:Java
上一篇:構建自己的jar包上傳至Mvaen中央倉庫和版本更新
下一篇:SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis 實作分庫分表
