Java 類的實例變量初始化的過程 靜態塊、非靜態塊、構造函數的加載順序

先看一道Java面試題:java

 

 1 public class Baset {
 2     private String baseName = "base";
 3     // 構造方法
 4     public Baset() {
 5         callName();
 6     }
 7     // 成員方法
 8     public void callName() {
 9         // TODO Auto-generated method stub
10         System.out.println("basename:" + baseName);
11     }
12   //靜態內部類
13     static class Sub extends Baset {//static必須寫在開頭
14         // 靜態字段
15         private String baseName = "sub";
16         public Sub() {
17             callName();
18         }
19         // 重寫父類的方法
20         public void callName() {
21             System.out.println("subname:" + baseName);
22         }
23     }
24 
25     public static void main(String[] args) {
26         Baset base = new Sub();
27     }
28 }

 

求這段程序的輸出。面試

解答此題關鍵在於理解和掌握類的加載過程以及子類繼承父類後,重寫方法的調用問題:函數

1、從程序的執行順序去解答:spa

1.編譯;當這個類被編譯通知後,會在相應的目錄下生成兩個.class 文件。一個是 Base.class,另一個就是Base$Sub.class。這個時候類加載器將這兩個.class  文件加載到內存code

二、Base base= new Sub():blog

  聲明父類變量base對子類的引用,JAVA類加載器將Base,Sub類加載到JVM(Java虛擬機);繼承

三、JVM爲Base,Sub 的的成員開闢內存空間內存

  此時,Base 和Sub類中的值爲null;虛擬機

四、new Sub();it

  這個時候會調用Sub類的隱式構造方法,

     Sub的構造方法本質爲:

     public Sub(){

  super();//  調用父類的構造方法必須在構造方法中的第一行,爲何呢?這是由於在一些程序的升級中,要兼容舊版本的一些功能,父類即原先的一些初始化信息也要保證  //被執行到,而後執行當前

  baseName = "sub";//子類字段初始化

      }

    new Sub()執行到super()這行代碼也就是跑到父類中去執行了,咱們跳轉到父類中的無參構造方法中執行,最後執行Sub()中的baseName = "sub"

五、public Base() ;

      父類無參構造方法的本質爲:

  public Base(){

  baseName= "base";//父類字段初始化

  callName();

    }

  即將父類的baseName賦值爲「base」,賦值後調用callName();

六、callName()方法在子類中被重寫,所以調用子類的callName(),子類的callName方法執行,打印輸出的是子類的baseName 字段的值,而這個時候子類的構造函數中字段的賦值還未執行。

七、父類的構造函數執行完畢,這個時候又回到子類當中,從super()的下一行繼續執行,這個時候才爲子類字段baseName 分配好存儲空間,隨後爲其賦值:

 可見,在baseName = "sub"執行前,子類的callName()已經執行,因此子類的baseName爲默認值狀態null;

 、另寫兩個程序,進行更具體的分析

 一、第一個程序:

 1 public class InitialOrderTest {
 2 //    變量
 3     public String field = "變量";
 4 //  靜態變量
 5     public  static  String staticField="靜態變量";
 6 //父類靜態方法    
 7     public static void Order(){
 8         System.out.print("父類靜態方法");
 9         System.out.println("staticField:"+staticField);
10     }
11 
12 //  靜態初始代碼塊
13     static{
14         System.out.println("靜態初始化塊");
15            System.out.println("staticField:"+staticField);
16        }
17 //   初始化代碼塊  
18       {
19           System.out.println("初始化代碼塊");
20           System.out.println("field:"+field);
21        }
22 //   構造函數
23    public InitialOrderTest(){
24        System.out.println("構造器");
25        
26    }
27  
28    public static void main(String[] args) {
29        System.out.println("-----[[-------");
30        System.out.println(InitialOrderTest.staticField);
31        InitialOrderTest.Order();
32        System.out.println("------]]------");
33        InitialOrderTest i = new InitialOrderTest();
34        System.out.println("-----[[-------");
35        System.out.println(InitialOrderTest.staticField);
36        InitialOrderTest.Order();
37        System.out.println("------]]------");
38       
39    }
40 }

執行結果爲:

   第一個程序總結:

     1)、java中的塊分爲靜態塊(static{})和非靜態塊({}),這兩種的執行是有區別的:

        非靜態塊的執行時間是:在執行構造函數以前。   靜態塊的執行時間是:class文件加載時執行。

        static類型的屬性也是在類加載時執行的。

    2)可見Java類的實例變量初始化的過程:

        static類型的成員屬性執行,靜態塊(static{})按順序執行,而後非靜態成員變量初始化,非靜態代碼塊({})執行,最後執行構造方法。

       static類型與static塊按前後順序執行。

---------------------------------------------------------------------------------------------------------------------------------------------------

  二、第二個程序:

   

 1 public class BaseTest {
 2     // 父類變量
 3     private String baseName = "base";
 4     // 父類靜態變量
 5     private static String staticField = "父類靜態變量";
 6     // 父類靜態方法
 7     public static void Order() {
 8         System.out.println("父類靜態方法-");
 9         System.out.println("staticField:" + staticField);
10     }
11     // 父類靜態初始代碼塊
12     static {
13         System.out.println("父類靜態初始化代碼塊-");
14         System.out.println("staticField:" + staticField);
15     }
16     // 初始化代碼塊
17     {
18         System.out.println("父類非靜態初始化代碼塊-");
19         System.out.println("baseName:" + baseName);
20     }
21     // 構造函數
22     public BaseTest() {
23         System.out.println("父類構造方法");
24         callName();
25     }
26     // 成員方法
27     public void callName() {
28         System.out.println("父類callName方法-");
29         System.out.println("baseName:" + baseName);
30     }
31 
32     // 靜態內部類
33     static class Sub extends BaseTest {
34         // 子類變量
35         private String baseName = "sub";
36         // 子類 靜態變量
37         private static String staticField = "子類靜態變量";
38 
39         // 子類靜態方法
40         public static void Order() {
41             System.out.println("子類靜態方法-");
42             System.out.println("staticField:" + staticField);
43         }
44 
45         // 子類靜態初始化代碼塊
46         static {
47             System.out.println("子類靜態初始化代碼塊-");
48             System.out.println("staticField:" + staticField);
49         }
50         // 子類非靜態初始化代碼塊
51         {
52             System.out.println("子類非靜態初始化代碼塊-");
53             System.out.println("baseName:" + baseName);
54         }
55 
56         public Sub() {
57             System.out.println("子類構造方法");
58             callName();
59         }
60 
61         public void callName() {
62             System.out.println("子類重寫父類callName方法-");
63             System.out.println("baseName:" + baseName);
64         }
65     }
66 
67     public static void main(String[] args) {
68         
69         BaseTest b = new Sub();
70 
71         System.out.println("-----[[-------");
72         Sub.Order();
73         System.out.println(Sub.staticField);
74         System.out.println(BaseTest.staticField);
75         BaseTest.Order();
76         System.out.println("------]]------");
77 
78     }
79 }

 

  執行結果:

  

第二個程序總結:

     1)可見Java初始化的順序:   

 1  1 父類靜態代碼塊-static{ }
 2  2 父類靜態變量初始化
 3  3 子類靜態代碼塊-static{ }
 4  4 子類靜態變量初始化
 5  5 父類非靜態代碼塊-{}
 6  6 父類非靜態變量初始化
 7  7 父類構造方法
 8  8 子類非靜態代碼塊-{}
 9  9 子類非靜態變量初始化
10 10 子類構造方法
相關文章
相關標籤/搜索