CodeGym/Java Blog/Toto sisi/對象創建期間的操作順序
John Squirrels
等級 41
San Francisco

對象創建期間的操作順序

在 Toto sisi 群組發布
個成員
你好!今天的課程將相當……呃……多面 :) 從某種意義上說,我們將涵蓋廣泛的主題,但它們都與對象創建過程相關。 對象創建期間的操作順序 - 1我們會從頭到尾分析一下:構造函數是怎麼調用的,字段(包括靜態字段)是怎麼初始化的,按照什麼順序初始化的等等。文章中討論的一些點我們之前已經觸及到了,大家可以瀏覽一下關於基類構造函數的材料。首先,讓我們回顧一下對像是如何創建的。你很清楚從開發者的角度來看這個過程是怎樣的:他創建了一個類,寫了new,然後一切就緒了:)這裡我們將討論當我們寫的時候計算機和 Java 機器內部發生了什麼,例如:
Cat cat = new Cat();
我們之前已經討論過這個問題,但為了以防萬一,我們會提醒您:
  • 首先,分配存儲對象的內存。
  • 接下來,Java 機器創建對象的引用(在我們的例子中,引用是 Cat cat)。
  • 最後,初始化變量並調用構造函數(我們將更詳細地研究這個過程)。
此外,從關於對像生命週期的課程中,您可能還記得只要至少有一次引用,一個對象就會持續存在。如果沒有剩餘,則該對象成為垃圾收集器的獵物。 對象創建期間的操作順序 - 2前兩點不應引起任何特殊問題。內存分配是一個簡單的過程,只有兩種可能的結果:要么有內存,要么沒有 :) 而創建鏈接並不罕見。但第三點代表的是一整套嚴格按順序執行的操作。我不喜歡死記硬背測試,但是你需要很好地理解這個過程並且你需要記住這個操作順序. 前面幾節課講對象創建過程的時候,你對繼承一無所知,所以有些東西解釋起來有問題。現在你知道了很多,我們終於可以完整地考慮這個問題了:)所以第三點說“最後,變量被初始化,構造函數被調用。 ”但是這一切發生的順序是什麼?為了更好地理解,讓我們創建兩個超級簡單的類——一個父類和一個子類:
public class Vehicle {

   public static int vehicleCounter = 0;

   private String description = "Vehicle";
   public Vehicle() {
   }

   public String getDescription() {
       return description;
   }
}

public class Truck extends Vehicle {

   private static int truckCounter = 0;

   private int yearOfManufacture;
   private String model;
   private int maxSpeed;

   public Truck(int yearOfManufacture, String model, int maxSpeed) {
       this.yearOfManufacture = yearOfManufacture;
       this.model = model;
       this.maxSpeed = maxSpeed;

       Vehicle.vehicleCounter++;
       truckCounter++;
   }
}
該類Truck是卡車的一個實現,其中包含代表其年份、型號和最大速度的字段。現在我們要創建一個這樣的對象:
public class Main {

   public static void main(String[] args) throws IOException {

       Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
   }
}
對於 Java 機器,該過程將如下所示:
  1. 發生的第一件事是類的靜態變量Vehicle被初始化。是的,我說的是Vehicle類,不是Truck。靜態變量在調用構造函數之前初始化,這從父類開始。讓我們嘗試驗證這一點。vehicleCounter我們將類中的字段設置Vehicle為 10 並嘗試在VehicleTruck構造函數中顯示它。

    public class Vehicle {
    
       public static int vehicleCounter = 10;
       private String description = "Vehicle";
    
       public Vehicle() {
           System.out.println(vehicleCounter);
       }
    
       public String getDescription() {
           return description;
       }
    }
    
    public class Truck extends Vehicle {
    
       private static int truckCount = 0;
    
       private int yearOfManufacture;
       private String model;
       private int maxSpeed;
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) {
           System.out.println(vehicleCounter);
           this.yearOfManufacture = yearOfManufacture;
           this.model = model;
           this.maxSpeed = maxSpeed;
    
           Vehicle.vehicleCounter++;
           truckCount++;
       }
    }

    我們特意將 println 語句放在構造函數的最開頭,以確保在顯示Truck時卡車的字段尚未初始化。vehicleCounter

    結果如下:

    10
    10
  2. 父類的靜態變量初始化完成後,再對子類的靜態變量進行初始化。在我們的例子中,這是班級truckCounter的領域Truck

    讓我們做另一個實驗,我們將嘗試在初始化其他字段之前顯示構造函數truckCounter內部的值:Truck

    public class Truck extends Vehicle {
    
       private static int truckCounter = 10;
    
       private int yearOfManufacture;
       private String model;
       private int maxSpeed;
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) {
           System.out.println(truckCounter);
           this.yearOfManufacture = yearOfManufacture;
           this.model = model;
           this.maxSpeed = maxSpeed;
    
           Vehicle.vehicleCounter++;
           truckCounter++;
       }
    }

    如您所見,在構造函數開始時,值 10已經分配給我們的靜態變量Truck

  3. 現在還不是構造函數的時間!變量初始化繼續。 第三次初始化父類的非靜態變量。 如您所見,繼承使創建對象的過程變得非常複雜,但是您對此無能為力:您只需要記住編程中的一些東西:)

    作為實驗,我們可以descriptionVehicle類中為變量賦一些初始值,然後在構造函數中更改它。

    public class Vehicle {
    
       public static int vehicleCounter = 10;
    
       private String description = "Initial value of the description field";
    
       public Vehicle() {
           System.out.println(description);
           description = "Vehicle";
           System.out.println(description);
       }
    
       public String getDescription() {
           return description;
       }
    }

    讓我們運行 main()創建卡車的方法:

    public class Main {
    
       public static void main(String[] args) throws IOException {
    
           Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
       }
    }

    我們得到以下結果:

    Initial value of the description field
    Vehicle

    這證明在Vehicle構造函數開始時,該description字段已經被賦值。

  4. 最後,是構造函數的時間了! 更準確地說,是基類構造函數的時候了。它在對象創建過程的第四步被調用。

    這也很容易驗證。讓我們嘗試向控制台輸出兩行:一行在Vehicle基類構造函數中,第二行在Truck構造函數中。我們需要確信Vehicle首先顯示裡面的行:

    public Vehicle() {
    
       System.out.println("Hello from the Vehicle constructor!");
    }
    
    public Truck(int yearOfManufacture, String model, int maxSpeed) {
    
       System.out.println("Hello from the Truck constructor!");
       this.yearOfManufacture = yearOfManufacture;
       this.model = model;
       this.maxSpeed = maxSpeed;
    
       Vehicle.vehicleCounter++;
       truckCounter++;
    }

    我們將運行我們的main()方法並查看結果:

    Hello from the Vehicle constructor!
    Hello from the Truck constructor!

    出色的。這意味著我們沒有弄錯 :) 讓我們繼續。

  5. 現在是初始化子類(即我們的類)的非靜態字段的Truck時候了。被實例化的類中的字段直到第五步才被初始化!令人驚訝,但確實如此 :) 同樣,我們將做一個簡單的檢查——就像父類一樣:我們將一些初始值賦給變量,maxSpeed並在Truck構造函數中檢查該值是否在構造函數啟動之前分配:

    public class Truck extends Vehicle {
    
       private static int truckCounter = 10;
    
       private int yearOfManufacture;
       private String model;
       private int maxSpeed = 150;
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) {
    
           System.out.println("Initial value of maxSpeed = " + this.maxSpeed);
           this.yearOfManufacture = yearOfManufacture;
           this.model = model;
           this.maxSpeed = maxSpeed;
    
           Vehicle.vehicleCounter++;
           truckCounter++;
       }
    }

    控制台輸出:

    Initial value of maxSpeed = 150

    可以看到,  構造函數啟動Truck ,maxSpeed 已經等於150了!

  6. Truck調用子類的構造函數。

    只有在這一點上,最後,我們正在實例化的類的構造函數才會被調用!

    只有在第六步中,這些字段才會被賦予我們作為參數傳遞給卡車的值。

    如您所見,“構建”一輛卡車,即對象創建過程,並不簡單。但我們似乎已將其分解為最小的部分 :)

對象創建期間的操作順序 - 3 為什麼理解這個過程如此重要?想像一下,如果您不知道“幕後”到底發生了什麼,那麼創建一個普通對象的結果會是多麼出乎意料:)現在是時候返回課程並完成一些任務了!祝你好運,很快見到你!:)
留言
  • 受歡迎
你必須登入才能留言
此頁面尚無留言