java的Constructor(構造器)的理解

使用構造器時需要記住:

1.構造器必須與類同名(如果一個源文件中有多個類,那麼構造器必須與公共類同名)

2.每個類可以有一個以上的構造器

3.構造器可以有0個、1個或1個以上的參數

4.構造器沒有返回值

5.構造器總是伴隨着new操作一起調用

6.不添加任何構造器會默認有空的構造器

繼承與構造器

使用super調用父類構造器的語句必須是子類構造器的第一條語句

爲什麼加上super?

Java的構造器並不是函數,所以他並不能被繼承,這在我們extends的時候寫子類的構造器時比較的常見,即使子類構造器參數和父類的完全一樣,我們也要寫super就是因爲這個原因。

如果子類構造器沒有顯式地調用父類的構造器,則將自動調用父類的默認(沒有參數)的構造器。如果父類沒有不帶參數的構造器,並且在子類的構造器中又沒有顯式地調用父類的構造器,則java編譯器將報告錯誤

構造方法和實例方法的區別:
一、主要的區別在於三個方面:修飾符、返回值、命名
1、和實例方法一樣,構造器可以有任何訪問的修飾符,public、private、protected或者沒有修飾符   ,都可以對構造方法進行修飾。不同於實例方法的是構造方法不能有任何非訪問性質的修飾符修飾,例如static、final、synchronized、abstract等都不能修飾構造方法。
解釋:構造方法用於初始化一個實例對象,所以static修飾是沒有任何意義的;多個線程不會同時創建內存地址相同的同一個對象,所以synchronized修飾沒有意義;
構造方法不能被子類繼承,所以final和abstract修飾沒有意義。
2、返回類型是非常重要的,實例方法可以返回任何類型的值或者是無返回值(void),而構造方法是沒有返回類型的,void也不行。
3、至於命名就是構造方法與類名相同,當然了實例方法也可以與類名相同,但是習慣上我們爲實例方法命名的時候通常是小寫的,另一方面也是與構造方法區分開。
而構造方法與類名相同,所以首字母一般大寫。
下面看幾個例子熟悉一下:
public class Sample {  
      
    private int x;  
  
    public Sample() { // 不帶參數的構造方法  
        this(1);  
    }  
      
    public Sample(int x) { //帶參數的構造方法  
        this.x=x;  
    }  
      
    public int Sample(int x) { //不是構造方法  
        return x++;  
    }  
  
}  
上面的例子即使不通過註釋我們也很容易能區分開的,再看下面一個例子
public class Mystery {  
    private String s;  
      
    public void Mystery() { //不是構造方法  
    s = "constructor";  
    }  
      
    void go() {  
    System.out.println(s);  
    }  
      
    public static void main(String[] args) {  
    Mystery m = new Mystery();  
    m.go();  
    }  
}  
程序執行的結果爲null,雖然說Mystery m = new Mystery();調用了Mystery 類的構造方法,但是public void Mystery()並不是構造方法,他只是一個普通的實例方法而已,那該類的構造方法哪去了呢?
二、說到這就得說一下java的默認構造方法
我們知道,java語言中規定每個類至少要有一個構造方法,爲了保證這一點,當用戶沒有給java類定義明確的構造方法的時候,java爲我們提供了一個默認的構造方法,這個構造方法沒有參數,修飾符是public並且方法體爲空。
其實默認的構造方法還分爲兩種,一種就是剛剛說過的隱藏的構造方法,另一種就是顯示定義的默認構造方法.
如果一個類中定義了一個或者多個構造方法,並且每一個構造方法都是帶有參數形式的,那麼這個類就沒有默認的構造方法,看下面的例子。
public class Sample1{}  
  
public class Sample2{  
    public Sample2(int a){System.out.println("My Constructor");}  
}  
  
public class Sample3{  
    public Sample3(){System.out.println("My Default Constructor");}  
}  
上面的三個類中Sample1有一個隱式的默認構造方法,下列語句Sample1 s1=new Sample()合法;
Sample2沒有默認的構造方法,下列語句Sample2 s2=new Sample2()不合法,執行會編譯錯誤
Sample3有一個顯示的默認構造方法,所以以下語句Sample3  s3=new Sample3();合法。
三、實例方法和構造方法中this、super的使用.
"this"的用法
實例方法中可以使用this關鍵字,它指向正在執行方法的類的實例對象,當然static方法中是不可以使用this對象的,因爲靜態方法不屬於類的實例對象;而構造方法中同樣可以使用this關鍵字,構造器中的this是指向同一個對象中不同參數的另一個構造器。讓我們來看下面的一段代碼:
public class Platypus {  
    String name;  
  
    Platypus(String input) {  
        name = input;  
    }  
  
    Platypus() {  
        this("John/Mary Doe");  
    }  
  
    public static void main(String args[]) {  
        Platypus p1 = new Platypus("digger");  
        Platypus p2 = new Platypus();  
        System.out.println(p1.name + "----" + p2.name);  
    }  
}  
上面的代碼中 類有兩個構造器,第一個構造器給類的成員name賦值,第二個構造器調用第一個構造器給類的成員name一個初始值Jonn/Mary Doe
所以程序執行結果:digger----John/Mary Doe
需要注意的兩個地方是:
1、構造方法中通過this關鍵字調用其他構造方法時,那麼這句代碼必須放在第一行,否則會編譯錯誤。
2、構造方法中只能通過this調用一次其他的構造方法。
 
"super"的用法:
實例方法和構造方法中的super關鍵字都用於去指向父類,實例方法中的super關鍵字是去調用父類當中的某個方法,看下面的代碼:
class getBirthInfo {  
    void getBirthInfo() {  
        System.out.println("born alive.");  
    }  
}  
  
class Platypus1 extends getBirthInfo  
{  
    void getBirthInfo() {  
           System.out.println("hatch from eggs");  
           System.out.println("a mammal normally is ");  
           super.getBirthInfo();  
      }  
}  
  
public class test1 {  
    public static void main(String[] args) {  
        Platypus1 p1=new Platypus1();  
        p1.getBirthInfo();  
    }  
}  
上面的例子使用super.getBirthInfo();調用了它的父類的void getBirthInfo()方法。
構造器中使用super關鍵字調用父類中的構造器,看下面的代碼:
class getBirthInfo {  
    getBirthInfo(){  
        System.out.println("auto");  
    }  
    void aa() {  
        System.out.println("born alive.");  
    }  
}  
  
class Platypus1 extends getBirthInfo  
{  
      Platypus1() {  
        super();  
        System.out.println("hatch from eggs");  
        System.out.println("a mammal normally is ");  
      }  
}  
  
public class test1 {  
    public static void main(String[] args) {  
        Platypus1 p1=new Platypus1();  
    }  
}  
執行了代碼我們就會看到構造器中的super調用了父類的構造方法。
 
類的繼承機制使得子類可以調用父類的功能,下面介紹類在繼承關係的初始化順序問題
 
請看實例1:
class SuperClass   
{   
    SuperClass()   
    {   
        System.out.println("SuperClass constructor");   
    }   
}   
public class SubClass extends SuperClass {  
    SubClass()   
    {   
        System.out.println("SubClass constructor");   
    }   
    public static void main(String[] args) {  
        SubClass sub = new SubClass();   
    }   
}   
執行結果:SuperClass constructor
     SubClass constructor
代碼中我們只實例化子類一個對象,但從執行結果上看程序一開始並不是運行子類的構造方法,而是先執行父類的默認構造方法,然後再執行子類的構造方法.所以我們在實例化子類對象時,程序會先調用父類的默認構造方法,然後再執行子類的構造方法。
 
再看實例2:
class SuperClass   
{   
    SuperClass(String str)   
    {   
    System.out.println("Super with a string.");   
    }   
}   
public class SubClass extends SuperClass   
{   
    SubClass(String str)   
    {   
    System.out.println("Sub with a string.");   
    }   
      
    public static void main(String[] args)   
    {   
    SubClass sub = new SubClass("sub");   
    }   
}   
此程序在JDK下不能編譯成功,因爲我們在實例化子類對象的時候會先調用其父類默認的構造方法,但是它的父類沒有默認的構造方法,所以不能編譯成功。
解決辦法:
1、在父類中加一個顯示的默認構造方法
2、在子類的構造方法中加一句super(str)並且必須在構造器的第一句。
兩個辦法都可以解決程序編譯的問題,但是執行結果是不一樣的.
第一種執行結果爲:Sub with a string.
第二種執行結果爲:Super with a string. 
                       Sub with a string.     第二種方法即使父類中有顯示的默認構造方法也不會被調用。
 
再看實例三:
class One   
{   
    One(String str)   
    {   
    System.out.println(str);   
    }   
}   
class Two   
{   
    One one_1 = new One("one-1");   
    One one_2 = new One("one-2");   
    One one_3 = new One("one-3");   
    Two(String str)   
    {   
    System.out.println(str);   
    }   
}   
public class Test   
{   
    public static void main(String[] args)   
    {   
    System.out.println("Test main() start");   
    Two two = new Two("two");   
    }   
}  
執行結果:
Test main() start
one-1
one-2
one-3
two
我們在main方法中實例了一個Two的對象,但是程序在實例Two對象時並沒有先調用Two的構造方法,而是先初始化Two類的成員變量,Two類中有三個成員變量,他們都是One類的對象,所以要依次執行One類的構造方法,然後再初始化Two類的對象。
既在實例化類的對象時,類中的成員變量會首先進行初始化,如果其中的成員變量有對象,那麼它們也會按照順序執行初始化工作。在所有類成員初始化完成後,才調用對象所在類的構造方法創建對象。構造方法作用就是初始化。 
 
再看實例四:
class One   
    {   
    One(String str)   
    {   
    System.out.println(str);   
    }   
}   
class Two   
{   
    One one_1 = new One("one-1");   
    One one_2 = new One("one-2");   
    static One one_3 = new One("one-3");   
    Two(String str)   
    {   
    System.out.println(str);   
    }   
}   
public class Test   
{   
    public static void main(String[] args)   
    {   
    System.out.println("Test main() start");   
    Two two_1 = new Two("two-1");   
    System.out.println("------------");   
    Two two_2 = new Two("two-2");   
    }   
}   
執行結果:
Test main() start
one-3
one-1
one-2
two-1
------------
one-1
one-2
two-2
結論:如果一個類中有靜態對象,那麼他會在非靜態對象初始化前進行初始化,但只初始化一次。而非靜態對象每次調用時都要初始化。
 
再看實例五:
class One   
{   
    One(String str)   
    {   
    System.out.println(str);   
    }   
}   
class Two   
{   
    One one_1 = new One("one-1");   
    One one_2 = new One("one-2");   
    static One one_3 = new One("one-3");   
    Two(String str)   
    {   
    System.out.println(str);   
    }   
}   
public class Test   
{   
    static Two two_3 = new Two("two-3");   
    public static void main(String[] args)   
    {   
    System.out.println("Test main() start");   
    Two two_1 = new Two("two-1");   
    System.out.println("------------");   
    Two two_2 = new Two("two-2");   
    }   
}  
執行結果:
one-3
one-1
one-2
two-3
Test main() start
one-1
one-2
two-1
------------
one-1
one-2
two-2
結論:程序中主類的靜態變量會在main()方法執行前初始化。結果中只輸出了一次one-3,這也說明:如果一個類中有靜態對象,那麼它會在非靜態對象前初始化,但只初始化一次。非靜態對象每次調用時都要初始化。 
 
總結初始化順序:
  1.主類的靜態成員首先初始化。 
  2.主類的父類的構造方法被調用。 
  3.主類的非靜態對象(變量)初始化。 
  4.調用主類的構造方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章