本節介紹依賴於使用對象引用的類的更多方面以及你在前面的對象部分中瞭解到的點運算符。java
方法返回到調用它的代碼。程序員
return
語句。以先發生者爲準。編程
你在方法聲明中聲明方法的返回類型,在方法體內,使用return
語句返回值。segmentfault
聲明爲void
的任何方法都不返回值,它不須要包含return
語句,但它可能會這樣作,在這種狀況下,可使用return
語句分支出控制流程塊並退出該方法,而且能夠像這樣使用:數組
return;
若是你嘗試從聲明爲void
的方法返回值,則會出現編譯器錯誤。app
任何未聲明爲void
的方法都必須包含帶有相應返回值的return
語句,以下所示:編程語言
return returnValue;
返回值的數據類型必須與方法聲明的返回類型匹配,你不能從聲明爲返回布爾值的方法返回一個整數值。函數
在對象部分中討論的Rectangle
類中的getArea()
方法返回一個整數:this
// a method for computing the area of the rectangle public int getArea() { return width * height; }
此方法返回表達式width * height
求值的整數。spa
getArea
方法返回基本類型,方法還能夠返回引用類型,例如,在一個操做Bicycle
對象的程序中,咱們可能有這樣的方法:
public Bicycle seeWhosFastest(Bicycle myBike, Bicycle yourBike, Environment env) { Bicycle fastest; // code to calculate which bike is // faster, given each bike's gear // and cadence and given the // environment (terrain and wind) return fastest; }
若是此部分讓你感到困惑,請跳過它並在完成接口和繼承課程後返回該部分。
當一個方法使用類名做爲其返回類型時,例如whosFastest
,返回對象的類型類必須是返回類型的子類或確切的類。假設你有一個類層次結構,其中ImaginaryNumber
是java.lang.Number
的子類,而java.lang.Number
又是Object
的子類,以下圖所示。
如今假設你有一個聲明爲返回Number
的方法:
public Number returnANumber() { ... }
returnANumber
方法能夠返回ImaginaryNumber
而不是Object
,ImaginaryNumber
是一個Number
,由於它是Number
的子類,可是,Object
不必定是Number
— 它能夠是String
或其餘類型。
你能夠重寫方法並定義它以返回原始子類的方法,以下所示:
public ImaginaryNumber returnANumber() { ... }
這種稱爲協變返回類型的技術意味着容許返回類型在與子類相同的方向上變化。
注意:你還可使用接口名稱做爲返回類型,在這種狀況下,返回的對象必須實現指定的接口。
在實例方法或構造函數中,這是對當前對象的引用 — 正在調用其方法或構造函數的對象,你可使用此方法從實例方法或構造函數中引用當前對象的任何成員。
使用this
關鍵字的最多見緣由是由於字段被方法或構造函數參數遮蔽。
例如,Point
類就是這樣寫的:
public class Point { public int x = 0; public int y = 0; //constructor public Point(int a, int b) { x = a; y = b; } }
但它多是這樣寫的:
public class Point { public int x = 0; public int y = 0; //constructor public Point(int x, int y) { this.x = x; this.y = y; } }
構造函數的每一個參數都會影響對象的一個字段 — 構造函數內部的x
是構造函數的第一個參數的本地副本,要引用Point
字段x
,構造函數必須使用this.x
。
在構造函數中,你還可使用this
關鍵字來調用同一個類中的另外一個構造函數,這樣作稱爲顯式構造函數調用,這是另外一個Rectangle
類,其實現與對象部分中的實現不一樣。
public class Rectangle { private int x, y; private int width, height; public Rectangle() { this(0, 0, 1, 1); } public Rectangle(int width, int height) { this(0, 0, width, height); } public Rectangle(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } ... }
該類包含一組構造函數,每一個構造函數初始化一些或全部矩形的成員變量,構造函數爲任何成員變量提供默認值,其初始值不是由參數提供的。例如,無參數構造函數在座標0,0處建立1x1矩形。雙參數構造函數調用四參數構造函數,傳遞寬度和高度,但始終使用0,0座標,和以前同樣,編譯器根據參數的數量和類型肯定要調用的構造函數。
若是存在,則另外一個構造函數的調用必須是構造函數中的第一行。
訪問級別修飾符肯定其餘類是否可使用特定字段或調用特定方法,訪問控制有兩個級別:
public
或package-private
(沒有顯式修飾符)。public
、private
、protected
或package-private
(無顯式修飾符)。可使用修飾符public
聲明一個類,在這種狀況下,該類對於全部類均可見,若是一個類沒有修飾符(默認,也稱爲包私有),它只在本身的包中可見(包是相關類的命名組 — 你將在後面的課程中瞭解它們)。
在成員級別,你也可使用public
修飾符或無修飾符(package-private
),就像使用頂級類同樣,而且具備相同的含義。對於成員,還有兩個額外的訪問修飾符:private
和protected
,private
修飾符指定只能在其本身的類中訪問該成員,protected
修飾符指定只能在其本身的包中訪問該成員(與package-private
同樣),此外,還能夠在另外一個包中經過其類的子類訪問該成員。
下表顯示了每一個修飾符容許的成員訪問權限。
修飾符 | 類 | 包 | 子類 | 全部 |
---|---|---|---|---|
public |
Y | Y | Y | Y |
protected |
Y | Y | Y | N |
無修飾符 | Y | Y | N | N |
private |
Y | N | N | N |
第一個數據列指示類自己是否能夠訪問由訪問級別定義的成員,如你所見,類始終能夠訪問本身的成員,第二列指示與該類相同的包中的類(無論父子關係)能夠訪問該成員,第三列指示在此包外聲明的該類的子類是否能夠訪問該成員,第四列指示是否全部類均可以訪問該成員。
訪問級別以兩種方式影響你,首先,當你使用來自其餘源的類(例如Java平臺中的類)時,訪問級別將肯定你本身的類可使用的那些類的哪些成員,其次,當你編寫一個類時,你須要肯定每一個成員變量和類中的每一個方法應具備的訪問級別。
讓咱們看一下類的集合,看看訪問級別如何影響可見性,下圖顯示了此示例中的四個類以及它們之間的關係。
下表顯示了Alpha
類的成員對於可應用於它們的每一個訪問修飾符的可見性。
修飾符 | Alpha | Beta | Alphasub | Gamma |
---|---|---|---|---|
public |
Y | Y | Y | Y |
protected |
Y | Y | Y | N |
無修飾符 | Y | Y | N | N |
private |
Y | N | N | N |
選擇訪問級別的提示:
若是其餘程序員使用你的類,你但願確保不會發生濫用錯誤,訪問級別能夠幫助你執行此操做。
private
。public
字段(本教程中的許多示例都使用public
字段,這可能有助於簡明地說明一些要點,但不建議用於生產代碼),public
字段傾向於將你連接到特定實現,並限制你更改代碼的靈活性。在本節中,咱們將討論使用static
關鍵字建立屬於類的字段和方法,而不是類的實例。
當從同一個類藍圖建立許多對象時,它們每一個都有本身不一樣的實例變量副本,在Bicycle
類的狀況下,實例變量是cadence
、gear
和speed
,每一個Bicycle
對象都有這些變量本身的值,這些變量存儲在不一樣的內存位置。
有時,你但願擁有全部對象共有的變量,這是經過static
修飾符完成的,在聲明中具備static
修飾符的字段稱爲靜態字段或類變量,它們與類相關聯,而不是與任何對象相關聯,該類的每一個實例共享一個類變量,該變量位於內存中的一個固定位置,任何對象均可以更改類變量的值,但也能夠在不建立類實例的狀況下操做類變量。
例如,假設你要建立多個Bicycle
對象併爲每一個對象分配一個序列號,從第一個對象開始爲1
,此ID
號對於每一個對象都是惟一的,所以是一個實例變量。同時,你須要一個字段來跟蹤已建立的Bicycle
對象的數量,以便你知道要分配給下一個對象的ID
,這樣的字段與任何單個對象無關,而與整個類有關,爲此,你須要一個類變量numberOfBicycles
,以下所示:
public class Bicycle { private int cadence; private int gear; private int speed; // add an instance variable for the object ID private int id; // add a class variable for the // number of Bicycle objects instantiated private static int numberOfBicycles = 0; ... }
類變量由類名自己引用,如:
Bicycle.numberOfBicycles
這清楚地代表它們是類變量。
注意:你也可使用對象引用來引用靜態字段
myBike.numberOfBicycles
,但這是不鼓勵的,由於它沒有說明它們是類變量。
你可使用Bicycle
構造函數來設置id
實例變量並增長numberOfBicycles
類變量:
public class Bicycle { private int cadence; private int gear; private int speed; private int id; private static int numberOfBicycles = 0; public Bicycle(int startCadence, int startSpeed, int startGear){ gear = startGear; cadence = startCadence; speed = startSpeed; // increment number of Bicycles // and assign ID number id = ++numberOfBicycles; } // new method to return the ID instance variable public int getID() { return id; } ... }
Java編程語言支持靜態方法以及靜態變量,靜態方法在其聲明中具備static
修飾符,應該使用類名調用,而不須要建立類的實例,如:
ClassName.methodName(args)
注意:你也可使用對象引用來引用靜態方法
instanceName.methodName(args)
,但這是不鼓勵的,由於它沒有說明它們是類方法。
靜態方法的常見用途是訪問靜態字段,例如,咱們能夠向Bicycle
類添加一個靜態方法來訪問numberOfBicycles
靜態字段:
public static int getNumberOfBicycles() { return numberOfBicycles; }
並不是全部實例和類變量和方法的組合都是容許的:
this
關鍵字,由於沒有要引用的實例。static
修飾符與final
修飾符結合使用,也用於定義常量,final
修飾符表示此字段的值不能更改。
例如,如下變量聲明定義了一個名爲PI
的常量,其值是pi
的近似值(圓周長與直徑之比):
static final double PI = 3.141592653589793;
以這種方式定義的常量不能從新分配,若是你的程序嘗試這樣作,則它是編譯時錯誤,按照慣例,常量值的名稱拼寫爲大寫字母,若是名稱由多個單詞組成,則單詞由下劃線(_
)分隔。
注意:若是將基本類型或字符串定義爲常量而且該值在編譯時已知,則編譯器會將代碼中的常量名稱替換爲其值,這稱爲編譯時常量。若是外部世界中常量的值發生變化(例如,若是立法規定pi
實際上應該是3.975
),則須要從新編譯使用此常量來獲取當前值的任何類。
在本節中進行了全部修改以後,Bicycle
類如今是:
public class Bicycle { private int cadence; private int gear; private int speed; private int id; private static int numberOfBicycles = 0; public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed; id = ++numberOfBicycles; } public int getID() { return id; } public static int getNumberOfBicycles() { return numberOfBicycles; } 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 class BedAndBreakfast { // initialize to 10 public static int capacity = 10; // initialize to false private boolean full = false; }
當初始化值可用而且初始化能夠放在一行上時,這頗有效,然而,這種形式的初始化因爲其簡單性而具備侷限性,若是初始化須要一些邏輯(例如,錯誤處理或for循環來填充複雜數組),則簡單的賦值是不合適的。實例變量能夠在構造函數中初始化,其中可使用錯誤處理或其餘邏輯,爲了爲類變量提供相同的功能,Java編程語言包括靜態初始化塊。
注意:沒有必要在類定義的開頭聲明字段,儘管這是最多見的作法,只有在使用它們以前才須要聲明和初始化它們。
靜態初始化塊是用大括號{}
括起來的常規代碼塊,前面是static
關鍵字,這是一個例子:
static { // whatever code is needed for initialization goes here }
一個類能夠有任意數量的靜態初始化塊,它們能夠出如今類體中的任何位置,運行時系統保證按照它們在源代碼中出現的順序調用靜態初始化塊。
還有靜態塊的替代方法 — 你能夠編寫私有靜態方法:
class Whatever { public static varType myVar = initializeClassVariable(); private static varType initializeClassVariable() { // initialization code goes here } }
私有靜態方法的優勢是,若是須要從新初始化類變量,能夠在之後重用它們。
一般,你可使用代碼在構造函數中初始化實例變量,使用構造函數初始化實例變量有兩種選擇:初始化塊和final
方法。
實例變量的初始化程序塊看起來就像靜態初始化程序塊,但沒有static
關鍵字:
{ // whatever code is needed for initialization goes here }
Java編譯器將初始化程序塊複製到每一個構造函數中,所以,該方法可用於在多個構造函數之間共享代碼塊。
沒法在子類中重寫final
方法,這在接口和繼承的課程中討論,如下是使用final
方法初始化實例變量的示例:
class Whatever { private varType myVar = initializeInstanceVariable(); protected final varType initializeInstanceVariable() { // initialization code goes here } }
若是子類可能想要重用初始化方法,這尤爲有用,該方法是final
,由於在實例初始化期間調用非final
方法可能會致使問題。
類聲明爲類命名,並將類主體括在大括號之間,類名能夠在前面加上修飾符,類主體包含類的字段、方法和構造函數,類使用字段來包含狀態信息,並使用方法來實現行爲,初始化類的新實例的構造函數使用類的名稱,看起來像沒有返回類型的方法。
你能夠經過相同的方式控制對類和成員的訪問:在聲明中使用諸如public
之類的訪問修飾符。
你能夠經過在成員聲明中使用static
關鍵字來指定類變量或類方法,未聲明爲static
的成員隱式地是實例成員,類變量由類的全部實例共享,能夠經過類名和實例引用來訪問,類的實例獲取每一個實例變量的本身的副本,必須經過實例引用訪問它們。
你可使用new
運算符和構造函數從類建立對象,new
運算符返回對已建立對象的引用,你能夠將引用分配給變量或直接使用它。
能夠經過使用限定名稱來引用可在其聲明的類以外的代碼訪問的實例變量和方法,實例變量的限定名稱以下所示:
objectReference.variableName
方法的限定名稱以下所示:
objectReference.methodName(argumentList)
或者:
objectReference.methodName()
垃圾收集器自動清理未使用的對象,若是程序再也不包含對它的引用,則不使用該對象,你能夠經過將包含引用的變量設置爲null
來顯式刪除引用。