1. 程式人生 > >Java筆記丨18 物件構造與初始化

Java筆記丨18 物件構造與初始化

構造方法

物件都有構造方法

如果沒有,編譯器加一個default構造方法

思考

抽象類有沒有構造方法?(有)

 

呼叫本類或父類的構造方法

this呼叫本類的其他構造方法

super呼叫直接父類的構造方法

this或super要放在第一條語句,且只能夠有一條

如果沒有this及super,則編譯器自動加上super(),即呼叫直接父類不帶引數的構造方法

因為必須令所有父類的構造方法都得到呼叫,否則整個物件的構建就可能不正確。

示例:ConstructCallThisAndSuper.java

class ConstructCallThisAndSuper

{

       public static void main(String[] args){

              Person p = new Graduate();

       }

}



class Person

{

       String name;

       int age;

       Person(){}

       Person( String name, int age ){

              this.name=name; this.age=age;

              System.out.println("In Person(String,int)");

       }

}



class Student extends Person

{

       String school;

       Student(){

              this( null, 0, null );

              System.out.println("In Student()");

       }

       Student( String name, int age, String school ){

              super( name, age );

              this.school = school;

              System.out.println("In Student(String,int,String)");

       }

}



class Graduate extends Student

{

       String teacher="";

       Graduate(){

              //super();

              System.out.println("In Graduate()");

       }

}

結果:

In Person(String,int)

In Student(String,int,String)

In Student()

In Graduate()

 

一個問題

Class A{

A(int a){}

}

Class B extends A{

B(String s){}//編譯不能通過

}

編譯器會自動呼叫B(String s){super();}出錯

因為這裡沒寫this也沒寫super,所以它呼叫super(),而A中又沒有不帶引數的構造方法。A中已經有一個構造方法,系統不會新增default構造方法,所有編譯器會說構造方法不存在。

解決方法:

  1. 在B的構造方法中,加入super(3);
  2. 在A中加入一個不帶引數的構造方法,A(){}
  3. 去掉A中全部的構造方法,則編譯器會自動加入一個不帶引數的構造方法,稱為預設的構造方法

 

建立物件時的初始化

p=new Person(){{age=18;name=”李明”;}}

這樣可以針對沒有相應建構函式,但又要賦值。雙括號可以同時對欄位進行賦值

注意雙括號

 

例項初始化和靜態初始化

例項初始化

在類中直接寫

{語句…}

例項初始化,先於構造方法{}中的語句執行

靜態初始化

static{語句…}

靜態初始化,在第一次使用這個類時要執行

其執行的具體時機是不確定的,但總是先於例項的初始化

示例:InitialTest.java

class InitialTest

{

       public static void main(String[] args)

       {

              new InitialTest2(6);

       }

       int n=10;  //step2

       {

              n++;

              System.out.println("InitialTest..."+n);

       }

       static int x;

       static

       {

              x++;

              System.out.println("static..." +x);

       }

      

}



class InitialTest2 extends InitialTest{

       //普通的構造方法

InitialTest2(int a){

              this.a=a;

              System.out.println("this.a=" + a );

       }

       //這以下的部分會先於上面的構造方法執行

//變數的宣告

       int a;

       //例項初始化

       {

              System.out.println("InitialTest2..."+this.a);

       }

       //靜態初始化,在類載入時就會執行

       static

       {

              x++;

              System.out.println("static2..." +x);

       }

}

結果:

static...1

static2...2

InitialTest...11

InitialTest2...0

this.a=6

 

構造方法的執行過程

1.呼叫本類或父類的構造方法,直至最高一層(Object)

2.按照宣告順序執行欄位的初始化賦值

3.執行建構函式中的各語句

簡單地說:

先父類構造,在本類成員賦值,最後執行構造方法中的語句

示例:JavaPConstructor.java

class JavaPConstructor

{

       int a=2000;                   //step2

       JavaPConstructor(){

              this.a=3000;          //step3

       }

}

step1:呼叫Object中的方法

 

示例:ConstructSequence.java

class ConstructSequence {

       public static void main(String[] args){

              Person p = new Student("李明", 18, "北大");

       }

}

class Person{

       String name="未命名";  //step 2

       int age=-1;

       Person( String name, int age ){

              super(); //step 1 不加super()系統也會自動新增

              //step 3

              System.out.println( "開始構造Person(),此時this.name="+this.name+",this.age="+this.age );

              this.name=name; this.age=age;

              System.out.println( "Person()構造完成,此時this.name="+this.name+",this.age="+this.age );

       }

}

class Student extends Person{

       String school="未定學校"; //step2

       Student( String name, int age, String school ){

              super( name, age );  //step 1

              //step 3

              System.out.println( "開始構造Student(),此時this.name="+this.name+",this.age="+this.age+",this.school="+this.school );

              this.school = school;

              System.out.println( "Student()構造完成,此時this.name="+this.name+",this.age="+this.age+",this.school="+this.school );

       }

}

結果:

開始構造Person(),此時this.name=未命名,this.age=-1

Person()構造完成,此時this.name=李明,this.age=18

開始構造Student(),此時this.name=李明,this.age=18,this.school=未定學校

Student()構造完成,此時this.name=李明,this.age=18,this.school=北大

 

一個問題

構造方法內部呼叫別的方法

如果這個方法時虛方法,結果會如何?

從語法上來說這是合法的,但是有時會造成事實上的不合理

示例:ConstructorInvokeVirtual.java

class ConstructorInvokeVirtual

{

       public static void main(String[] args){

              Person p = new Student("Li Ming", 18, "PKU");

       }

}



class Person

{

       String name="未命名";

       int age=-1;

       Person( String name, int age ){

              this.name=name; this.age=age;

              sayHello();

       }

       void sayHello(){

              System.out.println( "A Person, name: " + name + ", age: "+ age );

       }

}



class Student extends Person

{

       String school="未定學校";

       Student( String name, int age, String school ){

              super( name, age );

              this.school = school;

       }

       void sayHello(){

              System.out.println( "A Student, name:" + name + ", age: "+ age + ", school: " + school );

       }

}

結果:

A Student, name:Li Ming, age: 18, school: null

在本例中,在建構函式中呼叫了一個動態繫結的方法sayHello(),這時會使用那個方法被覆蓋的定義,而這時物件未完全建立好,所以School還沒有賦值

因此,可能的話,在構建器中避免呼叫任何方法,用盡可能簡單的方法使物件進入就緒狀態

唯一能夠安全呼叫的是具有final的方法