Java基礎教程(10)--類
一.宣告類
你已經見過了以如下方式定義的類:
class MyClass {
// field, constructor, and method declarations
}
上面是宣告類的最基本的語法。可以在宣告類時提供更多的資訊,例如它繼承的父類,或實現的介面等,例如:
class MyClass extends MySuperClass implements MyInterface {
// field, constructor, and method declarations
}
這意味著MyClass類繼承自MySuperClass類並是實現了MyInterface介面。
還可以在開頭加上修飾符,例如public、private、final等。有關這些修飾符的作用將在下文中進行詳細的介紹。一般來說,類宣告由以下幾部分組成:
- 修飾符。例如public、private以及其他將會在後面見到的修飾符;
- 類名。按照慣例使用首字母大寫的駝峰命名法。
- 繼承的父類。使用關鍵字extend宣告,一個類只能直接繼承一個父類。
- 一個或多個實現的介面。使用關鍵字implements宣告,多個介面之間使用逗號隔開。
- 類體。定義在一對大括號中。
二.域的宣告
Bicycle類使用以下幾行程式碼宣告它的域:
public int cadence;
public int gear;
public int speed;
域的宣告由以下幾部分組成:
- 修飾符,例如private、public、static、final等;
- 域的型別;
- 域的名稱。
許可權修飾符
許可權修飾符用來控制其他類對一個成員的訪問許可權。現在我們只討論public和private,其他許可權修飾符將在後面的文章中介紹。
- public:可以從所有類訪問該欄位;
- private:只能在自己的類中訪問。
本著封裝的精神,應該儘可能地將域的訪問許可權設為private,這意味這隻能在自己的類中訪問它的值。但是,其他類仍有訪問它的需要,可以通過新增訪問域的方法來供其他類訪問,就像下面這樣:
public class Bicycle { private int cadence; private int gear; private int speed; public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed; } public int getCadence() { return cadence; } public void setCadence(int newValue) { cadence = newValue; } public int getGear() { return gear; } public void setGear(int newValue) { gear = newValue; } public int getSpeed() { return speed; } public void applyBrake(int decrement) { speed -= decrement; } public void speedUp(int increment) { speed += increment; } }
三.定義方法
下面定義了一個方法:
public int add(int a, int b) {
return a + b;
}
方法定義中必需的是返回值型別,方法名,一對小括號,以及大括號和方法體。更一般地來說,方法的宣告由六個部分組成:
- 修飾符——例如public、private、static等;
- 返回值型別——方法返回的資料的型別。若方法無返回值則為void;
- 方法名稱——和變數的命名規則相同,但慣例有一點不同;
- 引數列表——位於小括號中,用逗號隔開。方法也可以沒有引數;
- 方法丟擲的異常——有關這部分的內容將在後續教程中進行介紹;
- 方法體——位於大括號中,方法的具體程式碼。
簽名:方法的簽名由方法名稱和引數列表組成。簽名用來在一個類中唯一地標識一個方法,也就是說,一個類中不能存在兩個簽名相同的方法。上面的add方法的簽名為add(int a, int b)。
方法的名稱可以是任何合法的識別符號。但是按照慣例,方法名的第一個單詞應該是動詞並且小寫它的首字母。例如run、compareTo等。
方法的過載
過載是指一個類中的兩個或多個方法具有相同的名稱但具有不同的引數列表。Java支援方法的過載,並且可以通過方法的簽名區分它們。
假設有一個計算器類Calculator。為不同型別的運算元的加法設計不同的名字是一件很麻煩的事,例如addInt、addDouble、addFloat等。在Java中,可以為這些方法使用相同的名稱,只要它們的引數列表是不同的。例如:
public class Calculator {
public static add(int a, int b) {return a + b;}
public static add(double a, double b) {return a + b;}
...
}
不能宣告名稱和引數列表都相同的方法,因為編譯器無法區分它們。編譯器在區分方法時只考慮簽名,所以名稱和引數列表相同,但返回值不同的兩個方法並不算過載,編譯器將會給出錯誤。
四.構造器
構造器(也稱構造方法)是將類例項化為物件是自動呼叫的方法。但與普通方法不同的是,它的名字與類名相同且沒有返回值。例如,Bicycle類有一個構造方法:
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
在建立物件時,new運算子將會呼叫構造方法:
Bicycle myBike = new Bicycle(30, 0, 8);
一個類可以有多個構造方法,只要它們的引數列表不同。還可以有無參構造方法:
public Bicycle() {
gear = 1;
cadence = 10;
speed = 0;
}
如果沒有提供構造方法,編譯器將會自動提供一個無參構造方法,這個無參構造方法會自動呼叫父類的無參構造方法。如果父類沒有無參構造方法,編譯器將會給出錯誤。因此在不提供構造方法時要保證父類有無參構造方法。如果你的類沒有明確的父類,那麼它有一個隱含的父類Object,這個類有一個無參構造方法。
可以在構造方法中手動呼叫父類的構造方法,有關這部分的內容將會在介面和繼承的教程中介紹。還可以在構造方法的宣告中使用訪問修飾符來控制哪些類可以呼叫建構函式。如果某個類不能呼叫本類的構造方法,則無法在該類內部直接建立本類物件。
五.向方法或構造器傳遞引數
方法的引數分為形式引數和實際引數。形式引數(簡稱形參)是方法宣告中的引數列表,實際引數(簡稱實參)是指在呼叫方法時傳遞的具體的值。呼叫方法時,實際引數的順序和型別必須與形式引數完全匹配。例如之前定義的add方法:
int add(int a, int b) {
return a + b;
}
下面的語句呼叫了這個方法並把返回值賦值給變數sum,2和3就是呼叫方法時傳遞的實參:
int sum = add(2, 3);
還可以傳遞任意數量的引數。例如讓add方法可以計算任意數量的整數的和:
int add(int... a) {
int sum = 0;
for(int i = 0; i < a.length; i++) {
sum += a[i];
}
return sum;
}
可以看到,在方法內部,這個引數被視為一個數組,可以通過陣列的訪問形式去訪問每一個引數。需要注意的是,引數列表中只能有一個這樣的引數,並且它必須放在最後一個位置上。
形參的名稱如果與類的某個域的名稱相同,那麼在這個方法內,這個形參將會遮蔽與它同名的域。可以使用this關鍵字來區分同名的形參和域,有關this關鍵字的內容將會在後面的文章中討論。
基本資料型別引數
基本資料型別的引數按值傳遞到方法中。這意味著對引數值的任何更改都僅存在於方法的範圍內。方法返回時,引數消失,對它們的任何更改都將丟失。下面是一個例子:
public class PassPrimitiveByValue {
public static void main(String[] args) {
int a = 1;
increment(a);
System.out.println(a);
}
public static void increment(int x) {
x = x + 1;
}
}
這個程式會輸出1而不是2。x只是接收了a的值,對x的操作與a完全沒有關係,所以函式結束後,a的值還是1。
引用型別引數
引用型別的引數將引用傳遞到方法中。對形參的操作就相當於對實參進行同樣的操作。例如:
public class PassPrimitiveByReference {
public static void main(String[] args) {
Foo foo = new Foo(1);
changeBar(foo);
System.out.println(foo.getBar());
}
public static void changeBar(Foo f) {
f.setBar(2);
}
}
class Foo {
private int bar;
public Foo(int bar) {this.bar = bar;}
public int getBar() {return bar;}
public void setBar(int bar) {this.bar = bar;}
}
這個程式會輸出2。因為形參f和實參foo引用了同一個物件。