寫一篇文章容易嗎?太不容易了,首先,須要一個安靜的環境,這一點就很是不容易。不少小夥伴的辦公室都是開放式的,很是吵,何況上班時間寫的話,領導就不高興了;只能抽時間寫。其次,環境有了,還要有一顆安靜的心,若是內心裝着其餘揮之不去的事,那就糟糕了,呆坐着電腦前一成天也不會有結果。html
我十分佩服一些同行,他們寫萬字長文,這在我看來,幾乎不太可能完成。由於我要日更,一萬字的長文,若是走原創的話,至少須要一週時間,甚至一個月的時間。java
就如小夥伴們看到的,我寫的文章大體都能在五分鐘內閱讀完,而且可以保證小夥伴們在閱讀完學到或者溫習到一些知識。這就是個人風格,通俗易懂,輕鬆幽默。git
好了,又一篇我去系列的文章它來了:你居然還不會用 final 關鍵字。程序員
已經晚上 9 點半了,我尚未下班,由於要和小王一塊修復一個 bug。我訂了一份至尊披薩,和小王吃得津津有味的時候,他忽然問了我一個問題:「老大,能給我詳細地說說 final 關鍵字嗎,總感受對這個關鍵字的認知不夠全面。」github
一會兒個人火氣就來了,儘管小王問的態度很謙遜,很卑微,但我仍是忍不住破口大罵:「我擦,小王,你丫的居然不會用 final,我當初是怎麼面試你進來的!」面試
發火歸發火,我這我的仍是有原則的,等十點半回到家後,我決定爲小王專門寫一篇文章,好好地講一講 final 關鍵字,也但願給更多的小夥伴一些幫助。編程
儘管繼承可讓咱們重用現有代碼,但有時處於某些緣由,咱們確實須要對可擴展性進行限制,final 關鍵字能夠幫助咱們作到這一點。安全
若是一個類使用了 final 關鍵字修飾,那麼它就沒法被繼承。若是小夥伴們細心觀察的話,Java 就有很多 final 類,好比說最多見的 String 類。微信
public final class String implements java.io.Serializable, Comparable<String>, CharSequence, Constable, ConstantDesc {}
複製代碼
爲何 String 類要設計成 final 的呢?緣由大體有如下三個:併發
更詳細的緣由,能夠查看我以前寫的一篇文章。
任未嘗試從 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 的,意味着它沒辦法繼承,假如這個類的一些方法存在一些問題的話,咱們就沒法經過重寫的方式去修復它。
被 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 的方法。沒毛病吧?看把我聰明的。
被 final 修飾的變量沒法從新賦值。換句話說,final 變量一旦初始化,就沒法更改。以前被一個小夥伴問過,什麼是 effective final,什麼是 final,這一點,我在以前的文章也有闡述過,因此這裏再貼一下地址:
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 代碼塊初始化。
final 關鍵字還能夠修飾參數,它意味着參數在方法體內不能被再修改:
public class ArgFinalTest {
public void arg(final int age) {
}
public void arg1(final String name) {
}
}
複製代碼
若是嘗試去修改它的話,編譯器會提示如下錯誤:
親愛的讀者朋友,我應該說得很全面了吧?我想小王看到了這篇文章後必定會感謝個人良苦用心的,他畢竟是個積極好學的好同事啊。
若是以爲文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回覆「併發」更有一份阿里大牛重寫的 Java 併發編程實戰,今後不再用擔憂面試官在這方面的刁難了。
本文已收錄 GitHub,傳送門~ ,裏面更有大廠面試完整考點,歡迎 Star。
我是沉默王二,一枚有顏值卻靠才華苟且的程序員。關注便可提高學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,嘻嘻。