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構造方法,所有編譯器會說構造方法不存在。
解決方法:
- 在B的構造方法中,加入super(3);
- 在A中加入一個不帶引數的構造方法,A(){}
- 去掉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的方法