Java筆記03:構造方法

前言

編程中有兩個安全性問題很常見:初始化清理。C語言中很多的bug都是因爲程序員忘記初始化導致的。清理則是另一個重要的問題,常常會有人在程序中使用了一些資源(特別是內存空間)而忘記回收。
Java引入了構造器機制,每個類都有一個特殊的構造方法,當創建這個類的對象時,構造方法就會自動被調用。另外Java還使用了垃圾收集器(Garbage Collector, GC)去自動回收不再被使用的對象所佔的資源。

構造方法

對於一個有很多不同字段(field)的類,如果直接要去將每個字段都初始化,像下面這樣:

Person ming = new Person();
ming.name = "xiaoming"
ming.age = 20
ming.score = 9

或者爲了安全性考慮,類常常會將一些字段以private修飾符修飾,這樣的字段只能通過類內部的方法去訪問,不能直接賦值,你可能想着建一個init()方法,但是這樣每次創建實例都要去記得調用一遍。
Java實現了一類特殊的構造方法,創建類實例時就會自動調用,爲了解決以下兩個問題:

  • 構造方法名可能和其它方法名衝突
  • Java如何知道哪個方法是構造方法並去調用

構造方法要遵循和類名稱同名的命名規範。

public class Main {
    public static void main(String[] args) {
        Person p = new Person("Xiao Ming", 15);
        System.out.println(p.getName());
        System.out.println(p.getAge());
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }
}

Person類的定義中,有着一個同名的Person方法,這個方法會在new的時候被調用:

Person p = new Person("Xiao Ming", 15);

構造方法可以沒有參數(事實上如果你沒有自己定義構造方法,解釋器會幫你加上一個空的構造方法),也不需要返回值,但是不需要void修飾符。

如果一個類的字段本身已經初始化過:

class Person {
    private String name = "xiao ming";
    private int age = 9;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

那麼在創建對象後哪個纔是這個字段的值呢?在創建實例時,會先對字段進行初始化,然後再調用構造方法。

方法重載(overload)

在同一個類中,可以有很多同名的方法,但是參數不同。我們把這稱爲方法重載。方法重載是很有用的,對於有時候有些功能類似的方法我們可以給它們相同的名字,但是使用不同參數,這樣只要通過不同的調用就能達到不同的目的。

將人類語言細微的差別映射到編程語言中會產生一個問題。通常,相同的詞可以表達多種不同的含義——它們被"重載"了。特別是當含義的差別很小時,這會更加有用。你會說"清洗襯衫"、"清洗車"和"清洗狗"。而如果硬要這麼說就會顯得很愚蠢:"以洗襯衫的方式洗襯衫"、"以洗車的方式洗車"和"以洗狗的方式洗狗",因爲聽衆根本不需要區分行爲的動作。大多數人類語言都具有"冗餘"性,所以即使漏掉幾個詞,你也能明白含義。你不需要對每個概念都使用不同的詞彙——可以從上下文推斷出含義。

對於構造方法,同樣也可以重載:

class Person {
    String name;
    Person() {
        this.name = "No one";
    }
    Person(String name) {
        this.name = name;
    }
}

現在我們有兩種方式來創建對象:

Person ming = new Person();
Person hong = new Person("xiao hong");

打印這個兩個對象的name,一個使用了無參數構造函數賦值的默認字符串,一個則使用了我們指定的名字。

要注意重載方法要有不同的參數或者不同順序的參數來讓編譯器知道你使用的具體是哪個方法。

this關鍵字

this是一個特殊的關鍵字,它永遠表示當前實例本身。

class Person {
    String name;
    Person() {
        name = "No one";
    }
}

像上面這個寫法是可行的,但是當構造方法變成下面這樣:

class Person {
    String name;
    Person(String name) {
        name = name;
        // this.name = name; 纔是正確寫法
    }
}

構造函數獲取的形式參數名爲name,這與我們類自身的字段同名了,對於這種命名有衝突的情況,就要使用this關鍵字了。this表示實例本身,this.name也就表示它自身的name字段了。

當你在一個類中寫了多個構造方法,有時你想在一個構造方法中調用另一個構造方法來避免代碼重複。就可以用this關鍵字實現這樣的調用。

class Person {
    String name;
    int age;
    Person() {
        this("xiao ming", 12);
    }
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

如上圖,我們在無參數的構造方法裏調用了另一個有參數的構造方法,並賦予了默認值。

注意:this的用途很多,但是不能用在static修飾的靜態方法裏。

static關鍵字

static關鍵字用來表示靜態字段或靜態方法。

public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        Person hong = new Person();
        System.out.println(ming.name);
        hong.name = "xiao hong";
        System.out.println(ming.name);
    }
}

class Person {
    static String name = "xiao ming";
}

我們將Person類的name字段定義爲靜態,運行上面的程序你會發現,當你修改了hong.name,打印出的ming.name也被改變了。
對於一個類的靜態字段,不論你創建出多少個對象,它們都共享同一個值。你創建一百個Person實例,它們的name字段也只佔一個內存空間。
甚至於你不實例化對象,也是一樣能訪問這個字段的,直接通過類訪問就行了:

System.out.println(Person.name);

我們再來看看靜態方法

public class Main {
    public static void main(String[] args) {
        Person.getName();

    }
}

class Person {
    static void getName() {
        System.out.println("xiao ming");
    }
}

對於靜態方法,我們根本沒有實例化Person類,而是直接通過這個類就去調用這個方法了。
對於爲什麼main()這個入口方法一定要用static來修飾我想你應該已經有了答案。
靜態方法裏不能調用非靜態方法,但是反過來是可以的。因爲非靜態方法必須要創建對象之後通過對象來使用。

掃碼關注:
二維碼

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