在前面的示例中,main方法都被標記爲static修飾符。我們來具體討論一下這個修飾符的含義。
靜態域
如果將域定義爲static,每個類中只有一個這樣的域。而每一個對象對於所有的實例域卻都有自己的一份拷貝。例如,假定需要給每一個僱員賦予唯一的標識碼。這裏給Employee類添加一個實例域id和一個靜態域nextId:
class Employee {
private static int nextId = 1;
private int id;
...
}
現在,每一個僱員對象都有一個自己的id域,但這個類的所有實例都將共享一個nextId域。換句話說,如果有1000個Employee類的對象,則有1000個實例域id。但是,只有一個靜態域nextId。即使沒有一個僱員對象,靜態域nextId也存在。它屬於類,而不屬於任何獨立的對象。
下面實現一個簡單的方法:
public void setId() {
id = nextId;
nextId++;
}
假定爲harry設定僱員標識碼:
harry.setId()
harry的id域被設置爲靜態域nextId當前的值,並且靜態域nextId的值加1:
harry.id = Employee.nextId;
Employee.nextId++;
靜態常量
靜態變量使用的比較少,但靜態常量卻使用的比較多。例如,在Math類中定義了一個靜態產量:
public class Math {
public static final double PI = 3.14159265358979323946;
}
在程序中,可以採用Math.PI的形式獲得這個常量。
如果關鍵字static被省略,PI就變成了Math類的一個實例域。需要通過Math類的對象訪問PI,並且每一個Math對象都有它自己的一份PI拷貝。
另一個多次使用的靜態常量是System.out。它在System類中聲明:
public class System {
public static final PrintStream out = ....;
}
之前說過,由於每個類對象都可以對共有域進行修改,所以,最好不要將域設計爲public。然而,共有常量(即final域)卻沒問題。因爲out被聲明爲final,所以,不允許再將其他打印流賦給它:
System.out = new PrintStream(...); // 這是錯誤的,因爲out是final的,不能被修改。
靜態方法
靜態方法是一種不能向對象實施操作的方法。例如,Math類的pow方法就是一個靜態方法。表達式
Math.pow(x, a);
計算x的a次冪,不使用任何Math對象。換句話說,沒有隱式的參數。
可以認爲靜態方法是沒有this參數的方法。
Employee類的靜態方法不能訪問Id實例域,因爲它不能操作對象。但是,靜態方法可以訪問自身類的靜態域。如:
public static int getNextId() {
return nextId;
}
可以通過類名調用這個方法:
int n = Employee.getNextId();
這個方法可以省略關鍵字static嗎?答案是肯定的。但是,需要通過Employee類對象的引用調用這個方法。
在下面兩種情況下使用靜態方法:
- 一個方法不需要訪問對象狀態,其所需參數都是通過顯示參數提供(如:Math.pow)。
- 一個方法只需要訪問類的靜態域(如:Employee.getNextId)。
工廠方法
靜態方法還有另外一個常見的用途。類似LocalDate和NumberFormat的類使用靜態工廠方法(factory method)來構造對象。NumberFormat類如下使用工廠方法生成不同風格的格式化對象:
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
double x = 0.1;
System.out.println(currencyFormatter.format(x)); // $0.10
System.out.println(percentFormatter.format(x)); // 10%
爲什麼NumberFormat類不利用構造器完成這些操作呢?這主要有兩個原因:
- 無法命名構造器。構造器的名字必須與類名相同。但是,這裏希望得到的貨幣實例和百分比實例採用不同的名字。
- 當使用構造器時,無法改變所構造的對象類型。而Factory方法將返回一個DecimalFormat類對象,這是NumberFormat類的子類(後續詳細講解)。
main方法
需要注意,不需要使用對象調用靜態方法。例如,不需要構造Math類就可以調用Math.pow。
同理,main方法也是一個靜態方法。
public class Application {
public static void main(String[] args) {
...
}
}
main方法不對任何對象進行操作。事實上,在啓動程序時還沒有任何一個對象。靜態的main方法將執行並創建程序所需要的對象。
單元測試
每一個類可以有一個main方法。這是一個常用語對類進行單元測試的技巧。例如,可以在Employee類總添加一個main方法:
class Employee {
...
public static void main(String[] args) {
Employee e = new Employee("Romeo", 50000, 2003, 3, 31);
e.raiseSalary(10);
System.out.println(e.getName() + " " + e.getSalary());
}
}
如果想要獨立地測試Employee類,只需要執行
java Employee
如果Employee類是一個更大型程序的一部分,就可以執行大型程序的入口類:
java Application
Employee類的main方法永遠不會執行。
捐贈
若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。