Java對象初始化

自動初始化(默認值)

一個類的全部基本數據成員都會獲得初始化,運行下面的例子能夠查看這些默認值:ide

class Default{
    boolean t;
    char c;
    byte b;
    short s;
    int i;
    long l;
    float f;
    double d;
    public void show() {
        System.out.println("基本類型   初始化值\n"+
                        "boolean<----->" + t +"\n" +
                        "char<----->" + c +"\n" +
                        "byte<----->" + b + "\n" +
                        "short<----->" + s + "\n" +
                        "int<----->" + i + "\n" +
                        "long<----->" + l + "\n" +
                        "float<----->" + f + "\n" +
                        "double<----->" + d + "\n"
        );

    }
}
public class InitValue {
    public static void main(String[] args) {
        Default d = new Default();
        d.show();
    }
}

【運行結果】:函數

基本類型   初始化值
boolean<----->false
char<----->
byte<----->0
short<----->0
int<----->0
long<----->0
float<----->0.0
double<----->0.0

其中,char類型的默認值爲空(null)。編碼

對於非基本數據類型而言,對象的句柄也會被初始化:spa

class Person {
    private String name;
    // setter
}
class Default {
    Person p;
    public void show() {
        System.out.println("Person<----->" + p);
    }
}
public class InitValue {
    public static void main(String[] args) {
        Default d = new Default();
        d.show();
    }
}

【運行結果】:code

Person<----->null

可見,句柄初始化值爲null。這就是說,若是沒有爲p指定初始化值就調用相似於p.setName的方法,就會出現異常。對象

規定初始化

若是須要本身爲變量賦一個初始值,能夠在定義變量的同時賦值。blog

class Default{
    boolean t = true;
    char c = 'A';
    byte b = 47;
    short s = 0xff;
    int i = 24;
    long l = 999;
    float f = 1.2f;
    double d = 1.732;
    public void show() {
        System.out.println(
                        "boolean<----->" + t +"\n" +
                        "char<----->" + c +"\n" +
                        "byte<----->" + b + "\n" +
                        "short<----->" + s + "\n" +
                        "int<----->" + i + "\n" +
                        "long<----->" + l + "\n" +
                        "float<----->" + f + "\n" +
                        "double<----->" + d + "\n"
        );

    }
}
public class InitValue {
    public static void main(String[] args) {
        Default d = new Default();
        d.show();
    }
}

甚至能夠經過一個方法來進行初始化;繼承

class Person {
    int i = set();
    //...
}

這些方法也可使用自變量:ci

class Person {
    int i;
    int j = set(i);
    //...
}

構建器初始化

構建器進行初始化的優勢是能夠在運行期決定初始化值。例如:it

class Person {
    int age;
    Person() {
        age = 89;
    }
}

age首先會初始化爲0,而後變成89。對於全部基本類型以及對象的句柄,這種狀況都是成立的。

初始化順序

在一個類裏,初始化的順序是由變量在類內的定義順序決定的。即便變量定義大量遍及於方法定義的中間,那麼變量仍然會在調用任何方法(包括構造函數)以前獲得初始化。例如:

class Pet {
    Pet(int age) {
        System.out.println("Pet(" + age + ")");
    }
}

class Person {
    Pet t1 = new Pet(1);

    Person() {
        System.out.println("---Person()---");
        t3 = new Pet(33);
    }

    Pet t2 = new Pet(2);
    void show() {
        System.out.println("show----running");
    }
    Pet t3 = new Pet(3);
}


public class OrderOfInitialization {
    public static void main(String[] args) {
        Person p = new Person();
        p.show();
    }
}

【運行結果】:

Pet(1)
Pet(2)
Pet(3)
---Person()---
Pet(33)
show----running

上例中,雖然t一、t二、t3的定義遍及於類中,可是初始化的前後順序是由t一、t二、t3的定義順序決定的(本身動手調換t一、t二、t3看看結果),且初始化優先於構建器執行,當調用Person的構建器時,t3從新初始化。

靜態數據的初始化

若是數據是靜態的(static),一樣的過程也會執行。若屬於基本類型,並且未對其進行初始化,就會自動得到本身的標準基本類型初始值;若它是指向一個對象的句柄,除非建立一個對象同它鏈接起來,不然獲得一個空值(null)。若是在定義時初始化,採起的方式與非靜態值是不一樣的,這是由於static只有一個存儲區域。例如:

class Bowl {
    Bowl(int marker) {
        System.out.println("Bowl(" + marker + ")");
    }
    void f(int marker) {
        System.out.println("f(" + marker + ")");
    }
}

class Table {
    static Bowl b1 = new Bowl(1);
    Table() {
        System.out.println("Table()");
        b2.f(1);
    }
    void f2(int marker) {
        System.out.println("f2(" + marker + ")");
    }
    static Bowl b2 = new Bowl(2);
}

class Cupboard {
    Bowl b3 = new Bowl(3);
    static Bowl b4 = new Bowl(4);
    Cupboard() {
        System.out.println("Cupboard()");
        b4.f(2);
    }
    void f3 (int marker) {
        System.out.println("f3(" + marker + ")");
    }
    static Bowl b5 = new Bowl(5);
}

public class StaticInitialization {
    public static void main(String[] args) {
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        t2.f2(1);
        t3.f3(1);
    }
    static Table t2 = new Table();
    static Cupboard t3 = new Cupboard();
}

【運行結果】:

Bowl(1)
Bowl(2)
Table()
f(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f(2)
f2(1)
f3(1)

靜態代碼塊

Java容許將其餘static初始化工做劃分到類內一個特殊的代碼塊中,這種代碼塊的形式爲static關鍵字,後面跟着一個方法主體,稱爲靜態代碼塊。靜態代碼塊只有在第一次生成那個類的對象首次訪問屬於那個類的static成員時執行。例如:

class Person {
    Person(int age) {
        System.out.println("Person(" + age + ")");
    }
    void f(int age) {
        System.out.println("f(" + age + ")");
    }
}

class Persons {
    static Person p1;
    static Person p2;
    static {
        p1 = new Person(1);
        p2 = new Person(2);
    }
    Persons() {
        System.out.println("Persons()");
    }
}

public class ExplicitStatic {
    public static void main(String[] args) {
        System.out.println("Inside main()");
        Persons.p1.f(18);//1
    }
    static Persons x = new Persons();//2
    static Persons y = new Persons();//2
}

在標記爲1的行內訪問static對象p1的時候,或在行1被註釋而行2未被註釋是,用於Persons的static初始化模塊就會運行。若1和2都被註釋掉,則用於Persons的靜態代碼塊不會執行。

靜態屬性和靜態代碼塊執行的前後順序

class Person {
    Person(int age) {
        System.out.println("Person("+age+")");
    }
}
class Persons {
    static Person p = new Person(2); // 1
    static {
        p = new Person(3);
    }
    static Person p = new Person(2); // 2

}
public class CompStaticInit {
    public static void main(String[] args) {

    }
    static Persons x = new Persons();
}

根據註釋1保留2,註釋2保留1的結果分析可知,靜態屬性和靜態代碼塊的執行順序取決於編碼的順序。誰在前面就先執行誰。

非靜態屬性的初始化

class Animal {
    Animal(int age) {
        System.out.println("Animal(" + age + ")");
    }
    void f(int age) {
        System.out.println("f(" + age + ")");
    }
}
public class NotStaticInit {
    Animal a1;
    Animal a2;
    {
        a1 = new Animal(1);
        a2 = new Animal(2);
        System.out.println("a1 & a2 initialized");
    }
    NotStaticInit() {
        System.out.println("NotStaticInit");
    }
    public static void main(String[] args) {
        System.out.println("Inside main()");
        NotStaticInit x = new NotStaticInit();
    }
}

相似於靜態代碼塊,匿名代碼塊與非靜態屬性的初始化順序取決於編碼順序

繼承中的對象初始化過程

class Insect {
    int i = 1;
    int j;

    Insect() {
        prt("i = " + i + ", j = " + j);
        j = 2;
    }

    static int x1 = prt("static Insect.x1 initialized");

    static int prt(String s) {
        System.out.println(s);
        return 3;
    }
}

public class Beetle extends Insect {
    int k = prt("Beeklt.k initialized");

    Beetle() {
        prt("k = " + k);
        prt("j = " + j);
    }

    static int x2 = prt("static Bootle.x2 initialized");
    static int prt(String s) {
        System.out.println(s);
        return 4;
    }

    public static void main(String[] args) {
        prt("Beetle constructor");
        Beetle b = new Beetle();
    }
}

【運行結果】:

static Insect.x1 initialized
static Bootle.x2 initialized
Beetle constructor
i = 1, j = 0
Beeklt.k initialized
k = 4
j = 2

對Beetle運行Java時,發生的第一件事情是裝載程序到外面找到那個類。在裝載過程當中,裝載程序發現一個基礎類,因此隨之將其載入。不管是否生成基礎類的對象,這一過程都將執行。若是基礎類含有另外一個基礎類,則另外一個基礎類隨即也會載入,以此類推。接下來就在根基礎類中執行static初始化,再在下一個衍生類中執行,以此類推。這是由於衍生類的初始化可能要依賴於對基礎類成員的初始化。

當類都裝載完畢,就能建立對象。首先,這個對象中的全部基本數據類型都會設置成爲他們的默認值,對象句柄設爲null。而後執行基礎類的構建器。這種狀況是自動完成的(衍生類的構造函數中默認調用了super(),也能夠經過super指定基類的構建器)。基礎類構建器完成後,衍生類實例變量就會按原本的順序獲得初始化,而後執行構建器的剩餘的主體部分。

總結對象建立的過程:

  • 靜態只在類加載的時候執行且只執行一次;
  • 非靜態只有在實例化的時候執行,每次建立對象都執行;
  • 靜態在非靜態以前執行,基類靜態優先於衍生類靜態執行;
  • 靜態屬性和靜態代碼塊的執行屬性取決於它們在類中的位置,誰在前先執行誰;
  • 非靜態屬性和構造塊的執行順序取決於它們在類中的位置,誰在前執行誰。
相關文章
相關標籤/搜索