在標題爲面向對象的編程概念課程中對面向對象概念的介紹以自行車課爲例,以賽車,山地自行車和雙人自行車爲子類,下面是可能實現Bicycle
類的示例代碼,爲你提供類聲明的概述,本課程的後續部分將逐步備份和解釋類聲明,目前,不要關心細節。編程
public class Bicycle { // the Bicycle class has // three fields public int cadence; public int gear; public int speed; // the Bicycle class has // one constructor public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed; } // the Bicycle class has // four methods public void setCadence(int newValue) { cadence = newValue; } public void setGear(int newValue) { gear = newValue; } public void applyBrake(int decrement) { speed -= decrement; } public void speedUp(int increment) { speed += increment; } }
做爲Bicycle
的子類的MountainBike
類的類聲明可能以下所示:segmentfault
public class MountainBike extends Bicycle { // the MountainBike subclass has // one field public int seatHeight; // the MountainBike subclass has // one constructor public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; } // the MountainBike subclass has // one method public void setHeight(int newValue) { seatHeight = newValue; } }
MountainBike
繼承了Bicycle
的全部字段和方法,並增長了seatHeight
和設置它的方法(山地自行車有座位,能夠根據地形要求上下移動)。數組
你已經看到如下列方式定義的類:app
class MyClass { // field, constructor, and // method declarations }
這是一個類聲明,類主體(大括號之間的區域)包含爲從類建立的對象的生命週期提供的全部代碼:用於初始化新對象的構造函數,提供類及其對象狀態的字段的聲明,以及實現類及其對象行爲的方法。編程語言
前面的類聲明是最小的,它僅包含類聲明所需的那些組件,你能夠在類聲明的開頭提供有關該類的更多信息,例如其超類的名稱,是否實現任何接口等等,例如:ide
class MyClass extends MySuperClass implements YourInterface { // field, constructor, and // method declarations }
表示MyClass
是MySuperClass
的子類,它實現了YourInterface
接口。函數
你也能夠在最開始添加public
或private
等修飾符 — 這樣你就能夠看到類聲明的開頭行可能變得很是複雜,public
和private
修飾符決定了其餘類能夠訪問MyClass
的內容,本課程稍後將對此進行討論。關於接口和繼承的課程將解釋如何以及爲何在類聲明中使用extends
和implements
關鍵字,目前你不須要擔憂這些額外的。this
一般,類聲明能夠按順序包含這些組件:rest
public
、private
以及稍後你將遇到的許多其餘修飾符。extends
開頭,一個類只能extend
(子類)一個父類。implements
,一個類能夠implement
多個接口。{}
。有幾種變量:code
Bicycle
類使用如下代碼行來定義其字段:
public int cadence; public int gear; public int speed;
字段聲明按順序由三個部分組成:
public
或private
。Bicycle
的字段被命名爲cadence
、gear
和speed
,而且都是整數數據類型(int),public
關鍵字將這些字段標識爲公共成員,可由任何能夠訪問該類的對象訪問。
使用的第一個(最左側)修飾符容許你控制哪些其餘類能夠訪問成員字段,目前,只考慮public
和private
,其餘訪問修飾符將在後面討論。
public
修飾符 — 能夠從全部類訪問該字段。private
修飾符 — 該字段只能在其本身的類中訪問。本着封裝的精神,將字段設爲private
是很常見的,這意味着它們只能從Bicycle
類直接訪問,可是,咱們仍然須要訪問這些值,這能夠經過添加爲咱們獲取字段值的公共方法間接完成:
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; } }
全部變量都必須具備類型,你可使用原始類型,如int
、float
、boolean
等,或者你可使用引用類型,例如字符串、數組或對象。
全部變量(不管是字段、局部變量仍是參數)都遵循「語言基礎」課程「變量命名」中介紹的相同命名規則和約定。
在本課程中,請注意相同的命名規則和約定用於方法和類名稱,除了:
如下是典型方法聲明的示例:
public double calculateAnswer(double wingSpan, int numberOfEngines, double length, double grossTons) { //do the calculation here }
方法聲明中惟一必需的元素是方法的返回類型、名稱、一對圓括號()
和大括號之間的主體{}
。
更通常地,方法聲明有六個組件,順序以下:
public
、private
和其餘你將在稍後瞭解的內容。void
。()
,若是沒有參數,則必須使用空括號。修飾符、返回類型和參數將在本課程的後面部分討論,異常將在後面的課程中討論。
定義:方法聲明的兩個組件包括方法簽名 — 方法的名稱和參數類型。
上面聲明的方法的簽名是:
calculateAnswer(double, int, double, double)
雖然方法名稱能夠是任何合法標識符,但代碼約定限制方法名稱,按照慣例,方法名稱應該是小寫的動詞或以小寫的動詞開頭的多單詞名稱,後跟形容詞、名詞等。在多單詞名稱中,第二個和後面每一個單詞的第一個字母應該大寫,這裏有些例子:
run runFast getBackground getFinalData compareTo setX isEmpty
一般,方法在其類中具備惟一名稱,可是,因爲方法重載,方法可能與其餘方法具備相同的名稱。
Java編程語言支持重載方法,Java能夠區分具備不一樣方法簽名的方法,這意味着若是類中的方法具備不一樣的參數列表,則它們能夠具備相同的名稱(有一些條件,將在標題爲「接口和繼承」的課程中討論)。
假設你有一個類可使用書法來繪製各類類型的數據(字符串、整數等),而且包含繪製每種數據類型的方法,爲每一個方法使用新名稱很麻煩 — 例如,drawString
、drawInteger
、drawFloat
等等。在Java編程語言中,你能夠對全部繪圖方法使用相同的名稱,可是爲每一個方法傳遞不一樣的參數列表,所以,數據繪圖類可能會聲明四個名爲draw
的方法,每一個方法都有一個不一樣的參數列表。
public class DataArtist { ... public void draw(String s) { ... } public void draw(int i) { ... } public void draw(double f) { ... } public void draw(int i, double f) { ... } }
重載方法由傳遞給方法的參數的數量和類型區分,在代碼示例中,draw(String s)
和draw(int i)
是不一樣且惟一的方法,由於它們須要不一樣的參數類型。
你不能聲明具備相同名稱和相同數量和類型的參數的多個方法,由於編譯器沒法區分它們。
在區分方法時編譯器不考慮返回類型,所以即便它們具備不一樣的返回類型,也不能聲明具備相同簽名的兩個方法。
注意:應謹慎使用重載方法,由於它們會使代碼的可讀性下降。
類包含被調用以從類藍圖建立對象的構造函數,構造函數聲明看起來像方法聲明 — 除了它們使用類的名稱而且沒有返回類型,例如,Bicycle
有一個構造函數:
public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed; }
要建立一個名爲myBike
的新Bicycle
對象,new
運算符將調用構造函數:
Bicycle myBike = new Bicycle(30, 0, 8);
new Bicycle(30, 0, 8)
爲對象建立內存空間並初始化其字段。
雖然Bicycle
只有一個構造函數,但它可能有其餘構造函數,包括一個無參構造函數:
public Bicycle() { gear = 1; cadence = 10; speed = 0; }
Bicycle yourBike = new Bicycle();
調用無參構造函數來建立一個名爲yourBike
的新Bicycle
對象。
兩個構造函數均可以在Bicycle
中聲明,由於它們具備不一樣的參數列表,與方法同樣,Java平臺根據列表中的參數數量及其類型來區分構造函數。你不能爲相同類編寫兩個具備相同參數數量和類型的構造函數,由於平臺沒法區分它們,這樣作會致使編譯時錯誤。
你沒必要爲你的類提供任何構造函數,但在執行此操做時必須當心,編譯器自動爲沒有構造函數的任何類提供無參數的默認構造函數,此默認構造函數將調用超類的無參數構造函數,在這種狀況下,若是超類沒有無參數構造函數,編譯器將會報錯,所以你必須驗證它是否有,若是你的類沒有顯式的超類,那麼它有一個隱式的超類Object,它有一個無參數的構造函數。
你能夠本身使用超類構造函數,本課開頭的MountainBike
類就是這樣作的,稍後將在有關接口和繼承的課程中對此進行討論。
你能夠在構造函數的聲明中使用訪問修飾符來控制哪些其餘類能夠調用構造函數。
注意:若是另外一個類不能調用MyClass
構造函數,則沒法直接建立MyClass
對象。
方法或構造函數的聲明聲明該方法或構造函數的參數的數量和類型,例如,如下是根據貸款金額、利率、貸款期限(期數)和貸款的將來價值計算住房貸款的每個月付款的方法:
public double computePayment( double loanAmt, double rate, double futureValue, int numPeriods) { double interest = rate / 100.0; double partial1 = Math.pow((1 + interest), - numPeriods); double denominator = (1 - partial1) / interest; double answer = (-loanAmt / denominator) - ((futureValue * partial1) / denominator); return answer; }
此方法有四個參數:貸款金額、利率、將來價值和期數,前三個是雙精度浮點數,第四個是整數,參數在方法體中使用,而且在運行時將採用傳入的參數的值。
注意:參數是指方法聲明中的變量列表,參數是調用方法時傳遞的實際值,調用方法時,使用的參數必須與聲明參數的類型和順序匹配。
你能夠將任何數據類型用於方法或構造函數的參數,這包括原始數據類型,如在computePayment
方法中看到的雙精度數、浮點數和整數,以及引用數據類型,如對象和數組。
這是一個接受數組做爲參數的方法示例,在此示例中,該方法建立一個新的Polygon
對象,並從Point
對象數組中初始化它(假設Point
是一個表示x,y座標的類):
public Polygon polygonFrom(Point[] corners) { // method body goes here }
注意:若是要將方法傳遞給方法,請使用
lambda
表達式或方法引用。
你可使用名爲可變參數的構造將任意數量的值傳遞給方法,當你不知道將多少特定類型的參數傳遞給該方法時,你可使用可變參數,這是手動建立數組的快捷方式(前一種方法可使用可變參數而不是數組)。
要使用可變參數,你經過省略號跟隨最後一個參數的類型(三個點,...
),而後是空格和參數名稱,而後可使用任何數量的參數調用該方法,包括無參數。
public Polygon polygonFrom(Point... corners) { int numberOfSides = corners.length; double squareOfSide1, lengthOfSide1; squareOfSide1 = (corners[1].x - corners[0].x) * (corners[1].x - corners[0].x) + (corners[1].y - corners[0].y) * (corners[1].y - corners[0].y); lengthOfSide1 = Math.sqrt(squareOfSide1); // more method body code follows that creates and returns a // polygon connecting the Points }
你能夠看到,在方法內部,corners
被視爲數組,可使用數組或參數序列調用該方法,在任何一種狀況下,方法體中的代碼都會將參數視爲數組。
你最多見的是使用打印方法的可變參數,例如,這個printf
方法:
public PrintStream printf(String format, Object... args)
容許你打印任意數量的對象,它能夠像這樣調用:
System.out.printf("%s: %d, %s%n", name, idnum, address);
或者像這樣:
System.out.printf("%s: %d, %s, %s, %s%n", name, idnum, address, phone, email);
或者還有不一樣數量的參數。
向方法或構造函數聲明參數時,爲該參數提供名稱,此名稱在方法體內用於引用傳入的參數。
參數的名稱在其範圍內必須是惟一的,它不能與同一方法或構造函數的另外一個參數的名稱相同,也不能是方法或構造函數中的局部變量的名稱。
參數能夠與類的某個字段具備相同的名稱,若是是這種狀況,則稱該參數遮蔽該字段,遮蔽字段可能使你的代碼難以閱讀,而且一般僅在設置特定字段的構造函數和方法中使用,例如,考慮如下Circle
類及其setOrigin
方法:
public class Circle { private int x, y, radius; public void setOrigin(int x, int y) { ... } }
Circle
類有三個字段:x
,y
和radius
,setOrigin
方法有兩個參數,每一個參數與其中一個字段具備相同的名稱,每一個方法參數都會影響共享其名稱的字段,所以,在方法體內使用簡單名稱x或y是指參數,而不是字段。要訪問該字段,你必須使用限定名稱,這將在本課程後面的「使用this
關鍵字」一節中討論。
原始參數(如int
或double
)按值傳遞給方法,這意味着對參數值的任何更改都僅存在於方法的範圍內,方法返回時,參數消失,對它們的任何更改都將丟失,這是一個例子:
public class PassPrimitiveByValue { public static void main(String[] args) { int x = 3; // invoke passMethod() with // x as argument passMethod(x); // print x to see if its // value has changed System.out.println("After invoking passMethod, x = " + x); } // change parameter in passMethod() public static void passMethod(int p) { p = 10; } }
運行此程序時,輸出爲:
After invoking passMethod, x = 3
引用數據類型參數(如對象)也按值傳遞給方法,這意味着當方法返回時,傳入的引用仍然引用與之前相同的對象,可是,若是對象的字段的值具備適當的訪問級別,則能夠在該方法中更改它們的值。
例如,考慮任意類中移動Circle
對象的方法:
public void moveCircle(Circle circle, int deltaX, int deltaY) { // code to move origin of circle to x+deltaX, y+deltaY circle.setX(circle.getX() + deltaX); circle.setY(circle.getY() + deltaY); // code to assign a new reference to circle circle = new Circle(0, 0); }
使用這些參數調用該方法:
moveCircle(myCircle, 23, 56)
在方法內部,circle
最初引用的是myCircle
,該方法將circle
引用的對象(即myCircle
)的x和y座標分別改變23和56,方法返回時,這些更改將保持不變。而後circle
被賦予新的Circle
對象的引用,其中x = y = 0
,可是,這種從新分配沒有永久性,由於引用是按值傳遞的,不能更改,在該方法中,circle
指向的對象已更改,可是,當方法返回時,myCircle
仍然引用與調用方法以前相同的Circle
對象。