靜態代碼塊、構造代碼塊、構造器

導讀

今天在作公司的項目,即統計實例化對象的存活個數,例如如下的代碼java

package com.zbystudy;

/**
 * Created By zby on 14:27 2019/4/12
 */
public class StaticFiled {

    public static int count = 0;

    private final int objCount = count++;

    public void printObjCount() {
        System.out.println("生成對象的個數爲: " + objCount);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new StaticFiled().printObjCount();
        }
    }
}

輸出如圖所示:this

靜態屬性直接作加法運算

在靜態代碼塊中加法

可是,我若是把代碼修改爲這樣的:spa

public class StaticFiled {

   public static int count = 0;
    
   static {
       count++;
   }

    private final int objCount = count;

    。。。。。。
}

在靜態代碼塊中作加法

你會發現,其只輸出1,爲何會這樣呢?會在下文講解。code

在構造代碼塊中加法

咱們再把代碼修改爲這樣的,結果又會不同:對象

public class StaticFiled {

    public static int count = 0;

    {
        count++;
    }
    
    private final int objCount = count;

    。。。。。。
}

其輸出結果是這樣的:get

在代碼塊中加減

你會發現,其跳過了0,而直接從1輸出,哈哈,很奇怪,對吧?我也覺着很奇怪,不過,會在下文做詳細介紹。it

概念

構造器

概念

假想咱們沒有構造器,可是咱們還須要建立對象。由於,java是經過消息來請求其餘對象。要想請求對象,勢必向該對象發送一條消息,換句話說,能夠把消息當作某個特定對象的方法的調用。於是,對象仍是很重要的。若是咱們手動去寫一個方法,即初始化建立對象的方法,這勢必會很是繁瑣的。class

於是,咱們就想到了java自帶的構造器,其就是爲了初始化對象的。請求


構造代碼塊

概念

定義在類的成員位置上,使用"{}"括起來的代碼。構造代碼塊會在每次類被調用,或者被實例化時就會被執行。其優於用以實例化對象的構造器,如代碼所示:程序

/**
 * Created By zby on 16:49 2019/4/12
 */
public class Child{

    private String name;

    public Child(String name) {
        super(name);
        this.name = name;
    }

    public Child(){
        System.out.println("子類Child類的構造器");
    }

    {
        System.out.println("子類Child類的   第一個  構造代碼塊");
    }

    {
        System.out.println("子類Child類的  第二個  構造代碼塊");
    }

    public static void main(String[] args) {
        new Child();
    }
}

其輸出結果如圖所示:

代碼塊和構造器

你會發現,程序先執行構造代碼塊,而後再執行構造器,也就是說,構造代碼塊的優先級比構造器的優先級高。

這也解決了咱們上面的問題,爲何程序的輸出會跳過0,直接從1開始輸出呢?由於,咱們首次實例化StaticFiled對象以前,構造代碼塊就已執行了一遍,此時的count是1,而不是0了。

同時,你也會發現,構造代碼塊的執行自己也是有前後順序的,先寫的先輸出,後寫的後輸出。

父類中的構造代碼塊

可是,若是父類中有構造代碼塊,子類輸出又是什麼樣的呢?這樣,咱們定義一個父類,如代碼所示:

public class Parent {

    private String name;

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

    public String getName() {
        return name;
    }
    
    public Parent() {
        System.out.println("父類Parent類的構造器");
    }

    {
        System.out.println("父類Parent類的  第一個  構造代碼塊");
    }

    {
        System.out.println("父類Parent類的  第一個  構造代碼塊");
    }
}

咱們再次執行Child類,觀察此時的輸出結果:

父類構造代碼塊和子類代碼塊的關係

你會神奇的發現,首先輸出父類的構造代碼塊和構造器,再次輸出子類的構造代碼塊和構造器。

於是,咱們能夠得出兩個結論:

  • 在實例化子類對象時,會執行父類中全部未加載的構造代碼塊和與子類相同的構造器。

與子類相同的構造器是什麼意思?也就是說,若是咱們把Child類中main方法,修改成這樣的: public static void main(String[] args){new Child("hhhh");},其輸出結果是這樣的:

子類相同的構造器的結果

  • 父級和子類構造代碼塊和構造器之間的優先級:父級構造代碼塊 --》 父級構造器 --》 子類構造代碼塊 --》 子類構造器

靜態代碼塊

概念

靜態代碼塊只執行一次,是在某個特定類第一次建立對象的時候執行,此後再也不執行該靜態代碼塊,如代碼所示:

/**
 * Created By zby on 16:49 2019/4/12
 */
public class Child extends Parent {

    private String name;

    public Child() {
//        System.out.println("子類Child類的構造器");
    }

    static {
        System.out.println("子類Child類的靜態代碼塊");
    }

    public static void main(String[] args) {
        for (int i=0;i<10;i++){
            new Child();
        }
    }
}

咱們建立了10個子類對象,但只輸出一次靜態代碼塊中的數據,結果如圖所示:

靜態代碼塊的輸出結果

這也解決了上面的問題,爲何咱們在靜態代碼塊中執行這個指令: static {count++;} ,其輸出結果始終是 1 的緣由了

父類靜態代碼塊

可是若是父類中存在靜態代碼塊,子類的輸出又是什麼樣的呢?

在Parent類中添加代碼: static {System.out.println("父類Parent類的靜態代碼塊");}

在Child類中添加代碼: static {System.out.println("子類Child類的靜態代碼塊");}

咱們執行子類的代碼,獲得的截圖:

父類靜態

由上圖,咱們得出告終論:

首先執行父類中的靜態代碼塊,再執行子類的靜態代碼塊。

三者綜合應用

父類中存在靜態代碼塊、構造代碼塊、構造器,子類中也存在靜態代碼塊、構造代碼塊、構造器,那麼,輸出結果是什麼樣的呢?

輸出結果如圖說示:

三者綜合應用的輸出結果

咱們得出了這個結論三者的優先級:

父類靜態代碼塊 --》 子類靜態代碼塊 --》父類構造代碼塊 --》 父類的構造器 --》子類構造代碼塊 --》子類構造器

總結

咱們只有明白了父類靜態代碼塊、子類靜態代碼塊、父類構造代碼塊、父類的構造器、子類構造代碼塊、子類構造器的關係,才能作更多的事情。就像是蓋房子,地基打得越牢固,蓋的房子越穩定。不然,蓋得越高,危險性越大。

相關文章
相關標籤/搜索