面試官:兄弟,說說Java的static關鍵字吧

讀者乙在上一篇我去系列文章裏留言說,「我盲猜下一篇標題是,‘我去,你居然不知道 static 關鍵字’」。我只能說乙猜對了一半,像我這麼有才華的博主,怎麼可能被讀者猜中了心思呢,必須搞點不同的啊,因此本篇文章的標題你看到了。java

七年前,我從美女不少的蘇州回到美女也很多的洛陽,抱着一幅「從二線城市退居三線城市」的心態,投了很多簡歷,也「約談」了很多面試官,但僅有兩三個令我感到滿意。其中有一位叫老馬,至今還活在個人微信通信錄裏。他當時扔了一個面試題把我砸懵了:「兄弟,說說 Java 的 static 關鍵字吧。git

我那時候二十三歲,正值青春年華,自認爲全部的面試題都能對答如流,結果沒想到啊,被「刁難」了——原來洛陽這塊互聯網的荒漠也有技術專家啊。如今回想起來,臉上不自覺地泛起了羞愧的紅暈:主要是本身當時太菜了。程序員

無論怎麼說,通過多年的努力,我如今的技術功底已經很是紮實了,有能力寫篇文章剖析一下 Java 的 static 關鍵字了——只要能給初學者一些參考,我就以爲很是知足。github

先來個提綱挈領(唉呀媽呀,成語區博主上線了)吧:面試

static 關鍵字可用於變量、方法、代碼塊和內部類,表示某個特定的成員只屬於某個類自己,而不是該類的某個對象。安全

0一、靜態變量

靜態變量也叫類變量,它屬於一個類,而不是這個類的對象。bash

public class Writer {
    private String name;
    private int age;
    public static int countOfWriters;

    public Writer(String name, int age) {
        this.name = name;
        this.age = age;
        countOfWriters++;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
複製代碼

其中,countOfWriters 被稱爲靜態變量,它有別於 name 和 age 這兩個成員變量,由於它前面多了一個修飾符 static微信

這意味着不管這個類被初始化多少次,靜態變量的值都會在全部類的對象中共享。工具

Writer w1 = new Writer("沉默王二",18);
Writer w2 = new Writer("沉默王三",16);

System.out.println(Writer.countOfWriters);
複製代碼

按照上面的邏輯,你應該能推理得出,countOfWriters 的值此時應該爲 2 而不是 1。從內存的角度來看,靜態變量將會存儲在 Java 虛擬機中一個名叫「Metaspace」(元空間,Java 8 以後)的特定池中。this

靜態變量和成員變量有着很大的不一樣,成員變量的值屬於某個對象,不一樣的對象之間,值是不共享的;但靜態變量不是的,它能夠用來統計對象的數量,由於它是共享的。就像上面例子中的 countOfWriters,建立一個對象的時候,它的值爲 1,建立兩個對象的時候,它的值就爲 2。

簡單小結一下:

1)因爲靜態變量屬於一個類,因此不要經過對象引用來訪問,而應該直接經過類名來訪問;

2)不須要初始化類就能夠訪問靜態變量。

public class WriterDemo {
    public static void main(String[] args) {
        System.out.println(Writer.countOfWriters); // 輸出 0
    }
}
複製代碼

0二、靜態方法

靜態方法也叫類方法,它和靜態變量相似,屬於一個類,而不是這個類的對象。

public static void setCountOfWriters(int countOfWriters) {
    Writer.countOfWriters = countOfWriters;
}
複製代碼

setCountOfWriters() 就是一個靜態方法,它由 static 關鍵字修飾。

若是你用過 java.lang.Math 類或者 Apache 的一些工具類(好比說 StringUtils)的話,對靜態方法必定不會感動陌生。

Math 類的幾乎全部方法都是靜態的,能夠直接經過類名來調用,不須要建立類的對象。

簡單小結一下:

1)Java 中的靜態方法在編譯時解析,由於靜態方法不能被重寫(方法重寫發生在運行時階段,爲了多態)。

2)抽象方法不能是靜態的。

3)靜態方法不能使用 this 和 super 關鍵字。

4)成員方法能夠直接訪問其餘成員方法和成員變量。

5)成員方法也能夠直接方法靜態方法和靜態變量。

6)靜態方法能夠訪問全部其餘靜態方法和靜態變量。

7)靜態方法沒法直接訪問成員方法和成員變量。

0三、靜態代碼塊

靜態代碼塊能夠用來初始化靜態變量,儘管靜態方法也能夠在聲明的時候直接初始化,但有些時候,咱們須要多行代碼來完成初始化。

public class StaticBlockDemo {
    public static List<String> writes = new ArrayList<>();

    static {
        writes.add("沉默王二");
        writes.add("沉默王三");
        writes.add("沉默王四");

        System.out.println("第一塊");
    }

    static {
        writes.add("沉默王五");
        writes.add("沉默王六");

        System.out.println("第二塊");
    }
}
複製代碼

writes 是一個靜態的 ArrayList,因此不太可能在聲明的時候完成初始化,所以須要在靜態代碼塊中完成初始化。

簡單小結一下:

1)一個類能夠有多個靜態代碼塊。

2)靜態代碼塊的解析和執行順序和它在類中的位置保持一致。爲了驗證這個結論,能夠在 StaticBlockDemo 類中加入空的 main 方法,執行完的結果以下所示:

第一塊
第二塊
複製代碼

0四、靜態內部類

Java 容許咱們在一個類中聲明一個內部類,它提供了一種使人信服的方式,容許咱們只在一個地方使用一些變量,使代碼更具備條理性和可讀性。

常見的內部類有四種,成員內部類、局部內部類、匿名內部類和靜態內部類,限於篇幅緣由,前三種不在咱們本次文章的討論範圍,之後有機會再細說。

public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
複製代碼

以上這段代碼是否是特別熟悉,對,這就是建立單例的一種方式,第一次加載 Singleton 類時並不會初始化 instance,只有第一次調用 getInstance() 方法時 Java 虛擬機纔開始加載 SingletonHolder 並初始化 instance,這樣不只能確保線程安全也能保證 Singleton 類的惟一性。不過,建立單例更優雅的一種方式是使用枚舉。

簡單小結一下:

1)靜態內部類不能訪問外部類的全部成員變量。

2)靜態內部類能夠訪問外部類的全部靜態變量,包括私有靜態變量。

3)外部類不能聲明爲 static。

學到了吧?學到就是賺到。

我是沉默王二,一枚有趣的程序員。若是以爲文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回覆【666】更有我爲你精心準備的 500G 高清教學視頻(已分門別類)。

本文 GitHub 已經收錄,有大廠面試完整考點,歡迎 Star。

原創不易,莫要白票,請你爲本文點個贊吧,這將是我寫做更多優質文章的最強動力。

相關文章
相關標籤/搜索