前言
編程中有兩個安全性問題很常見:初始化與清理。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
來修飾我想你應該已經有了答案。
靜態方法裏不能調用非靜態方法,但是反過來是可以的。因爲非靜態方法必須要創建對象之後通過對象來使用。
掃碼關注: