十七、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 的,無法繼承。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SZjVDOzkjZhNjYxEGOxUTOjN2N0QDM3YjMmRWO3QDMl9CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
不過,類是 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.html1)final 修飾的基本資料類型
來聲明一個 final 修飾的 int 類型的變量:
final int age = 18;
嘗試将它修改為 30,結果編譯器生氣了:
2)final 修飾的引用類型
現在有一個普通的類 Pig,它有一個字段 name:
public class Pig {
private String name;
在測試類中聲明一個 final 修飾的 Pig 對象:
final Pig pig = new Pig();
1
如果嘗試将 pig 重新指派的話,編譯器同樣會生氣:
但我們仍然可以去修改 Pig 的字段值:
final Pig pig = new Pig();
pig.setName("特立獨行");
System.out.println(pig.getName()); // 特立獨行
3)final 修飾的字段
final 修飾的字段可以分為兩種,一種是 static 的,另外一種是沒有 static 的,就像下面這樣:
private final int age = 1;
public static final double PRICE = 36.5;
非 static 的 final 字段必須有一個預設值,否則編譯器将會提醒沒有初始化:
static 的 final 字段也叫常量,它的名字應該為大寫,可以在聲明的時候初始化,也可以通過 static 代碼塊初始化。
final 修飾的參數
final 關鍵字還可以修飾參數,它意味着參數在方法體内不能被再修改:
public class ArgFinalTest {
public void arg(final int age) {
public void arg1(final String name) {
如果嘗試去修改它的話,編譯器會提示以下錯誤:
。。。。。。
後續還會繼續更新,但有些小夥伴可能就忍不住了,這份小白手冊有沒有 PDF 版可以白嫖啊,那必須得有啊,直接「沉默王二」公衆号背景回複「小白」就可以了,不要手軟,覺得不錯的,請多多分享——贈人玫瑰,手有餘香哦。