1. 程式人生 > >Java - 物件的初始化

Java - 物件的初始化

  • Java中的初始化

    • java會盡力保證所有的變數在使用前都能夠得到恰當的初始化,避免出現一堆null導致報錯

      • 因此當變數是 基本型別 時,Java都會給他初始化一個初值

      • 但如果變數是 物件型別 時,因為Java無法知道這個自定義的型別要給什麼初始化的值,因此如果沒有初始化的話,還是會得到一個null值

      • 初始化的順序 : 先執行宣告變數時的初始化 -> 再執行構造器方法

      class Leaf {
          int level;
      }
      ​
      class MyTest {
          //可以在此處宣告成員變數時,就給一個初始值
          //或是也可以寫在構造器裡,兩種辦法都可以
          int i = 100; 
          int x;
          //也可以在宣告時,直接初始化物件型別
          Leaf leaf = new Leaf(); 
          Leaf leaf2;
          
          MyTest(){
              i = 10;
              
              //輸出 10,此i被初始化了兩次,第一次被設為100,第二次被設為10
              System.out.println(i); 
        
              //輸出 0,此x則是因為被Java初始化了一次,所以值才會是0
              System.out.println(x); 
      
              //輸出
      [email protected]
      (hashCode) //因為此物件型別的變數在宣告時被初始化過了,所以有實際指到某的對像        System.out.println(leaf); //輸出 0        System.out.println(leaf.level);       //輸出 null,因為Java不知道給這個物件設什麼初值,所以不會初始化物件型別的變數  System.out.println(leaf2); //丟擲異常 NullPointerException        System.out.println(leaf2.level);   } } ​ public class Main {    public static void main(String[] args) {        MyTest myTest = new MyTest();   } }
    • 靜態資料的初始化

      • 靜態資料static和類裡平常的成員變數一樣,也是java會進行基本的初始化(基本型別給值,物件型別給null),然而因為無論建立多少物件,靜態變數都只佔用一份儲存資料,所以如果想在宣告靜態變數之外進行初始化,需要使用靜態塊來初始化

        • 靜態塊只有在必要時刻才會進行,並不是程式啟動就會執行,只有在此類第一個物件被建立、或者第一次訪問靜態變數時,靜態塊才會被執行,所有的靜態變數就會被初始化,此後,所有靜態物件不會再次被初始化,因為所有此類的物件中,只會有一份靜態變數,而他們也只能初始化一次

        • 由下面的例子可以看到,當靜態變數i

          被訪問時,靜態塊就會被執行了,所有的靜態變數ij都會被初始化,因此當後面又在new一個物件時,靜態塊並不會再次被初始化,因為整份類的所有物件中,靜態變數只有一份

        • 使用static {}會建立一個靜態塊,而使用{}則是建立普通塊,這兩個方法塊差別很大,很坑,要特別注意

        class MyTest {
            static int i = 10;
            static int j = 20;
        ​
            //定義一個靜態塊,靜態變數的初始化放在這裡,或是在宣告變數時初始化
            static {
                System.out.println("靜態塊初始化");
                i = 100;
            }
            
            //要注意,這種前面沒有加 static 關鍵字的區塊,不是靜態塊
            //是每建立一次物件都會執行的普通塊,並且會在構造器前執行,很坑
            {
                System.out.println("普通塊初始化");
            }
            
            MyTest() {
                System.out.println("constructor");
            }
        }
        ​
        public class Main {
            public static void main(String[] args) {
                System.out.println("呼叫靜態變數 i");
                System.out.println(MyTest.i);
        ​
                System.out.println("new物件開始");
                MyTest myTest = new MyTest();
                MyTest myTest2 = new MyTest();
                System.out.println("new物件結束");
                
                System.out.println("呼叫靜態變數 j");
                System.out.println(MyTest.j);
            }
        }
        呼叫靜態變數 i
        靜態塊執行
        100
        new物件開始
        普通塊初始化
        constructor
        普通塊初始化
        constructor
        new物件結束
        呼叫靜態變數 j
        20
    • 初始化的順序

      • 建立一個物件時,初始化的順序

        • 靜態變數宣告時初始化 -> 靜態塊初始化 -> 普通的變數宣告時初始化 -> 普通塊初始化 -> 構造器初始化

      • 訪問一個靜態變數時,初始化的順序

        • 靜態變數宣告時初始化 -> 靜態塊初始化

  • 有繼承關係時的初始化

    • 把握一個重點,就是靜態塊永遠優先於普通塊+構造器,然後才是Parent優先於Child

      • 建立一個Child物件時,初始化的順序

        • Parent靜態塊 -> Child靜態塊 -> Parent普通塊 + Parent構造器 -> Child普通塊 + Child構造器

      • 訪問一個靜態變數時,初始化的順序

        • 如果是訪問Parnet的靜態變數,那就是隻初始化 Parent的靜態塊

        • 但如果訪問了Child自己宣告的靜態變數,則會初始化 Parent靜態塊 + Child靜態塊

      class Parent {
          public static int x = 10;
      ​
          static {
              System.out.println("parent 靜態塊");
          }
      ​
          {
              System.out.println("parent 普通塊");
          }
      ​
          Parent() {
              System.out.println("parent constructor");
          }
      }
      ​
      class Child extends Parent {
          public static int y = 20;
      ​
          static {
              System.out.println("child 靜態塊");
          }
      ​
          {
              System.out.println("child 普通塊");
          }
      ​
          Child() {
              super();
              System.out.println("child constructor");
          }
      }
      ​
      public class Main {
          public static void main1(String[] args) {
              //main1 直接new一個Child物件
              Child child = new Child();
          }
          
          public static void main2(String[] args) {
              //main2 呼叫Parent的靜態變數
              System.out.println("parent x: " + Child.x);
          }
          
          public static void main3(String[] args) {
              //main3 呼叫Child的靜態變數
              System.out.println("child y: " + Child.y);
          }
      }
      //main1結果 直接new一個Child物件
      parent 靜態塊
      child 靜態塊
      parent 普通塊
      parent constructor
      child 普通塊
      child constructor
      ​
      //main2結果 呼叫Parent的靜態變數
      parent 靜態塊
      parent x: 10
          
      //main3結果 呼叫Child的靜態變數
      parent 靜態塊
      child 靜態塊
      child y: 20