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 為什麼理解這個過程如此重要?想像一下,如果您不知道“幕後”到底發生了什麼,那麼創建一個普通對象的結果會是多麼出乎意料:)現在是時候返回課程並完成一些任務了!祝你好運,很快見到你!:)
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION