c++模板类静态成员变量_一文讲透父子类中静态变量,成员变量初始化顺序原理...
推薦:
- 從面試到入職到離職,我在B站工作的30天時光
- 愛了愛了,Alibaba頂級MySQL調優手冊到手,加薪妥了
- 愛了愛了,Spring Cloud Alibaba內部微服務架構筆記真的太牛了
本文主要是想分析一下靜態變量和成員變量的初始化順序,以及如果存在父類和子類關系時,這些變量的順序又是如何呢?本文將一一進行分析。
類加載初始化和實例對象初始化
首先我們要區分一個概念,那就是變量初始化也可以分為兩大類, 一類是Java虛擬機中的類加載機制內有一個初始化,而我們實例化一個對象(即通過new關鍵字)時也有一個對象實例的初始化。
首先我們要知道,一個類理論上不考慮內存影響的話可以被實例化無數次,所以我們想要實例化一個對象,那么就必須要有這個類,所有類加載一定在對象實例化前面。
而靜態變量是在類加載階段就會進行初始化的(如果想詳細了解Java虛擬機的類加載機制,可以點擊這里),故而如果在同一個類中,那么靜態變量的初始化一定在成員變量的初始化前面
靜態變量和成員變量區別
我們先來看下面兩句簡單的代碼:
package com.zwx.coreJava;public class InitVariable { public static int m = 1;//靜態變量(類變量) private int n = 2;//成員變量(實例變量) public static void main(String[] args) { System.out.println(InitVariable.m);//直接通過類訪問變量 InitVariable initVariable = new InitVariable();//創建一個實例對象 System.out.println(initVariable.n);//需要通過實例對象才能訪問成員變量 }}其中m就是靜態變量,也稱之為類變量,類加載之后,存儲在方法區內,可以直接通過:類名.變量名進行調用。如:對象.m。
其中n就是成員變量,也稱之為實例變量,屬于對象實例,需要通過new一個對象之后才能引用。如:new 對象().n。成員變量的生命周期是和實例對象一致的,當實例對象被回收之后,對應的成員變量也消失了。
變量加載順序
加下來就讓我們一起結合例子驗證一下變量的加載順序
靜態變量和成員變量加載順序
首先我們建立一個父類,然后僅以父類為例子驗證下靜態變量和成員變量的加載順序:
package com.zwx.coreJava;public class SuperInitVariable { static String superStaticStr = "I'm Super Static Str1"; String superInstanceStr = "I'm Super Instance Str1"; static { superStaticStr = "I'm Super Static Str2"; } static { superStaticStr = "I'm Super Static Str3"; } public SuperInitVariable() { superInstanceStr = "I'm Super Instance Str2"; }}然后新建一個測試類:
package com.zwx.coreJava;public class TestVariableOrder { public static void main(String[] args) { System.out.println(SuperInitVariable.superStaticStr); SuperInitVariable superInitVariable = new SuperInitVariable(); System.out.println(superInitVariable.superInstanceStr); }}輸出結果:
I'm Super Static Str3I'm Super Instance Str2很明顯,第一句話還沒有實例化對象就可以輸出靜態變量,所以靜態變量優先級最高,靜態代碼塊也是一樣會加載,多個代碼塊之間按先后順序加載。
第二句成員變量先初始化了變量,再執行了構造器。所以可以得出如下結論:
- 1、靜態變量優先級最先被初始化,多個靜態代碼塊按代碼先后順序進行加載
- 2、成員變量先賦值,再執行構造函數
父類和子類變量加載順序
將上面的父類簡單改造一下:
package com.zwx.coreJava;public class SuperInitVariable { static String superStaticStr = "I'm Super Static Str1"; String superInstanceStr = "I'm Super Instance Str1"; static { superStaticStr = "I'm Super Static Str2"; System.out.println("Super Static:" + superStaticStr); } public SuperInitVariable() { superInstanceStr = "I'm Super Instance Str2"; System.out.println("Super Construct:" + superStaticStr); }}然后再新建一個子類,繼承上面的SuperInitVariable:
package com.zwx.coreJava;public class SubInitVariable extends SuperInitVariable { static String subStaticStr = "I'm Sub Static Str1"; String subInstanceStr = "I'm Sub Instance Str1"; static { subStaticStr = "I'm Sub Static Str2"; System.out.println("Sub Static:" + subStaticStr); } public SubInitVariable() { subInstanceStr = "I'm Sub Instance Str1"; System.out.println("Sub Construct:" + subInstanceStr); }}然后在測試類中執行以下語句:
package com.zwx.coreJava;public class TestVariableOrder { public static void main(String[] args) { System.out.println(SubInitVariable.subStaticStr); }}輸出如下結果:
Super Static:I'm Super Static Str2Sub Static:I'm Sub Static Str2I'm Sub Static Str2可以很明顯看到加載順序為:先加載父類靜態變量,再加載子類靜態變量
接下來再改造下測試類,我們去實例化一個SubInitVariable對象實例:
輸出結果如下:
Super Static:I'm Super Static Str2Sub Static:I'm Sub Static Str2Super Construct:I'm Super Static Str2Sub Construct:I'm Sub Instance Str1可以很明顯的得出如下結論:
- 1、初始化父類靜態變量。
- 2、初始化子類靜態變量。
- 3、初始化父類成員變量。
- 4、加載父類構造器。
- 5、初始化子類成員變量。
- 6、加載子類構造器。
或者說可以分的更細致一點,可以總結為如下:
- 1、初始化父類靜態變量。
- 2、初始化父類靜態代碼塊。
- 3、初始化子類靜態變量。
- 4、初始化子類靜態代碼塊。
- 5、初始化父類成員變量。
- 6、加載父類構造器。
- 7、初始化子類成員變量。
- 8、加載子類構造器。
引用類加載順序
上面的例子中變量都是String類型的,那么假如變量是引用類型呢?又會怎么樣?
我們再來看個例子。新建兩個類,其中一個類引用另一個類:
上面例子中看到,我們只是持有了另一個對象的引用,不做任何初始化動作,這時候是否會去加載引用類呢?我們新建一個測試類測試一下:
package com.zwx.coreJava;public class TestVariableOrder { public static void main(String[] args) { //測試引用變量和數組 System.out.println(InitReferenceVariable.staticObj); InitReferenceVariable initReferenceVariable = new InitReferenceVariable(); System.out.println(initReferenceVariable.obj); }}輸出如下結果:
static code:nullnullconstruct code:nullnull可以看到,引用類ReferenceObj沒有被實例化也沒有被加載。
那么我們把上面的變量進行初始化修改一下:
再次運行測試類,輸出如下結果:
I'm reference Static StrI'm reference Construct Strstatic code:com.zwx.coreJava.ReferenceObj@4b1210eecom.zwx.coreJava.ReferenceObj@4b1210eeI'm reference Construct Strconstruct code:com.zwx.coreJava.ReferenceObj@4d7e1886可以看到:I’m reference Static Str只輸出了1次,而I’m reference Construct Str輸出了2次,這是因為同一個類只會被加載1次,但是我們new了兩次,也就是實例化了2次,所以構造函數會執行2次。
根據這個結果我們可以得出如下結論:
- 1、一個類假如只是引用另一個類而沒有被實例化,那么不會觸發引用類的類加載和實例化
- 1、一個類假如引用另一個類并且實例化了引用類,那么會優先加載引用類和實例化引用類
數組引用類加載順序
把類改造如下:
package com.zwx.coreJava;public class InitReferenceVariable { static ReferenceObj[] arr = new ReferenceObj[10];}然后在測試類中輸出如下語句:
System.out.println("輸出數組:" + InitReferenceVariable.arr);輸出結果如下:
輸出數組:[Lcom.zwx.coreJava.ReferenceObj;@4b1210ee可以看到只會輸出這一句話,而不會觸發ReferenceObj的類加載和初始化。
可以得出如下結論:
如果將引用類作為數組對象,那么創建數組的時候不會觸發數組對象的類加載和初始化
總結
本文從父類,子類,引用類,以及數組引用類的角度全面分析了什么時候會觸發一個類的加載,什么時候會觸發一個類的實例化。而一個類在加載的時候就會初始化靜態變量,一個類在實例化的時候就會觸發成員變量的初始化。本文涉及到類加載時并沒有特別深入的介紹,有興趣的,可以看下本人下的JVM虛擬機系列2中講述的類加載機制,相信看完之后會有更加深刻的理解和體會。
來源:http://002ii.cn/?T0vKxr
總結
以上是生活随笔為你收集整理的c++模板类静态成员变量_一文讲透父子类中静态变量,成员变量初始化顺序原理...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【软件工程】重要知识点
- 下一篇: C++ virtual笔试