­

Java 對象的初始化過程_上

  • 2020 年 3 月 11 日
  • 筆記

前言

本文主要以白話的形式 『簡單』 的描述在 java 中 new 對象的過程,之所以說是 『簡單』 的描述是因為,在本文中不會講述底層的載入過程。

示例

首先認識幾個知識點:

  • 靜態程式碼塊 它會在類初始化的時候執行一次,僅能初始化類變數, 即被static修飾的成員變數,如果有多個靜態程式碼塊時,會按照靜態程式碼塊的編寫順序執行。實際上編譯後多個靜態程式碼塊中的程式碼,會按編寫時的順序整合到一個靜態程式碼塊。
  • 構造程式碼塊 實際上源程式碼在被編譯後,構造程式碼塊中的程式碼就會被複制移動到構造方法程式碼的前面,也就會隨著構造方法的運行而運行。如果存有多個構造方法時,將會在每個構造方法的程式碼之前都放置一遍 構造程式碼塊中的程式碼 。所以也可以認為,構造程式碼塊將在構造方法執行之前執行,如果存在有多個構造程式碼塊時,那麼就會按照構造程式碼塊的編寫順序執行。由於構造程式碼塊的程式碼是放到構造方法中執行的,所以作用也是初始化類實例變數。適用場景:
    • 一個類,它不管創建多少個實例對象,都需要執行相同的初始化程式碼。
    • 你的類有n個構造方法,而每個構造方法都需要執行相同的初始化程式碼。
  • 構造方法 構造方法將會在類實例化時執行,也就是在被 new 時執行,需要注意的是,構造方法可以存在多個,如果你沒有顯示聲明,一個沒寫的話,那編譯器就會幫你加一個默認的構造方法。構造方法可以初始化類成員變數。

知道以上三個知識點後,那麼就先創建一個Person類,觀察一下

Person.java

package cn.ttext.test.init;    public class Person {        private String name;      private int age;      private String sex;          static {          System.out.println("靜態程式碼塊1");      }        {          System.out.println("構造程式碼塊1");      }        static {          System.out.println("靜態程式碼塊2");      }        {          System.out.println("構造程式碼塊2");      }        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;      }        public String getSex() {          return sex;      }        public void setSex(String sex) {          this.sex = sex;      }  }

對其編譯,使用IDEA的反編譯功能查看class文件內容

Person.class

//  // Source code recreated from a .class file by IntelliJ IDEA  // (powered by Fernflower decompiler)  //    package cn.ttext.test.init;    public class Person {      private String name;      private int age;      private String sex;        public Person() {          System.out.println("構造程式碼塊1");          System.out.println("構造程式碼塊2");      }        public String getName() {          return this.name;      }        public void setName(String name) {          this.name = name;      }        public int getAge() {          return this.age;      }        public void setAge(int age) {          this.age = age;      }        public String getSex() {          return this.sex;      }        public void setSex(String sex) {          this.sex = sex;      }        static {          System.out.println("靜態程式碼塊1");          System.out.println("靜態程式碼塊2");      }  }

首先觀察到,編譯後的程式碼,和咱寫的程式碼有點區別:

  1. 編譯器自動幫我曾加了一個默認的,空參數的構造函數。
  2. 編譯器刪除了構造程式碼塊,並將被刪除的構造程式碼塊中的程式碼,有序的放置到了構造方法中。

下面再在Person類中,顯示聲明多個構造函數。

Person.java

package cn.ttext.test.init;    public class Person {        private String name;      private int age;      private String sex;          static {          System.out.println("靜態程式碼塊1");      }        {          System.out.println("構造程式碼塊1");      }        public Person(String name) {          this.name = name;      }        public Person(String name, int age) {          this.name = name;          this.age = age;      }        public Person(String name, int age, String sex) {          this.name = name;          this.age = age;          this.sex = sex;      }        static {          System.out.println("靜態程式碼塊2");      }        {          System.out.println("構造程式碼塊2");      }        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;      }        public String getSex() {          return sex;      }        public void setSex(String sex) {          this.sex = sex;      }  }

查看編譯後的內容

Person.class

//  // Source code recreated from a .class file by IntelliJ IDEA  // (powered by Fernflower decompiler)  //    package cn.ttext.test.init;    public class Person {      private String name;      private int age;      private String sex;        public Person(String name) {          System.out.println("構造程式碼塊1");          System.out.println("構造程式碼塊2");          this.name = name;      }        public Person(String name, int age) {          System.out.println("構造程式碼塊1");          System.out.println("構造程式碼塊2");          this.name = name;          this.age = age;      }        public Person(String name, int age, String sex) {          System.out.println("構造程式碼塊1");          System.out.println("構造程式碼塊2");          this.name = name;          this.age = age;          this.sex = sex;      }        public String getName() {          return this.name;      }        public void setName(String name) {          this.name = name;      }        public int getAge() {          return this.age;      }        public void setAge(int age) {          this.age = age;      }        public String getSex() {          return this.sex;      }        public void setSex(String sex) {          this.sex = sex;      }        static {          System.out.println("靜態程式碼塊1");          System.out.println("靜態程式碼塊2");      }  }

看到編譯後的結果說明幾個問題:

  1. 顯示聲明構造函數後,編譯器將不會幫我們創建默認的構造函數。
  2. 編譯器刪除了構造程式碼塊,並將被刪除的構造程式碼塊中的程式碼,複製移動到每一個構造函數程式碼的最前面
  3. 編譯器會將多個靜態程式碼塊中的程式碼,整合到一個靜態程式碼塊執行。

那現在看一下創建Person對象的控制台輸出

Main.java

package cn.ttext.test.init;    public class Main {        public static void main(String[] args) {          new Person("張三",18,"女");          System.out.println();          new Person("李四",18,"男");      }    }

因為靜態程式碼塊只會隨著類的載入而運行,所以第二次創建對象時,靜態程式碼塊沒有運行。 結合上邊的知識點看,是不是更明白了呢。


轉發請註明本文鏈接。