理解 Java 程式
| 導航 入門 主題: |
本文介紹了一個可以在控制檯中執行的小型 Java 程式。它計算平面上的兩點之間的距離。您現在不需要理解程式的結構和意義;我們很快就會講到。此外,由於該程式旨在作為簡單的入門介紹,因此它有一些改進空間,並且我們將在本模組後面介紹一些改進措施。但不要操之過急!
此類名為 Distance,因此使用您喜歡的編輯器或 Java IDE,首先建立一個名為 Distance.java 的檔案,然後複製下面的原始碼,將其貼上到檔案中並儲存檔案。
程式碼清單 2.1:Distance.java
public class Distance {
private java.awt.Point point0, point1;
public Distance(int x0, int y0, int x1, int y1) {
point0 = new java.awt.Point(x0, y0);
point1 = new java.awt.Point(x1, y1);
}
public void printDistance() {
System.out.println("Distance between " + point0 + " and " + point1
+ " is " + point0.distance(point1));
}
public static void main(String[] args) {
Distance dist = new Distance(
intValue(args[0]), intValue(args[1]),
intValue(args[2]), intValue(args[3]));
dist.printDistance();
}
private static int intValue(String data) {
return Integer.parseInt(data);
}
}
|
此時,您可能希望檢視原始碼以瞭解您能理解多少。雖然它可能不是最易讀的程式語言,但瞭解其他過程式語言(如 C)或其他面嚮物件語言(如 C++ 或 C#)的人將能夠理解大多數(如果不是全部)示例程式。
儲存檔案後,編譯程式
編譯命令
$ javac Distance.java |
(如果 javac 命令失敗,請檢視 安裝說明。)
要執行程式,您需要提供平面上的兩個點的 x 和 y 座標,並用空格隔開。對於此版本的 Distance,僅支援整數點。命令序列是 java Distance <x0> <y0> <x1> <y1>,用於計算點 (x0, y0) 和 (x1, y1) 之間的距離。
如果您收到 java.lang.NumberFormatException 異常,則某些引數不是數字。如果您收到 java.lang.ArrayIndexOutOfBoundsException 異常,則您沒有提供足夠的數字。 |
以下列出兩個示例
點 (0, 3) 和 (4, 0) 之間距離的輸出
$ java Distance 0 3 4 0 Distance between java.awt.Point[x=0,y=3] and java.awt.Point[x=4,y=0] is 5.0 |
點 (-4, 5) 和 (11, 19) 之間距離的輸出
$ java Distance -4 5 11 19 Distance between java.awt.Point[x=-4,y=5] and java.awt.Point[x=11,y=19] is 20.518284528683193 |
稍後我們將解釋這些奇怪的輸出,並展示如何改進它。
如我們所承諾,我們現在將提供對該 Java 程式的詳細描述。我們將討論程式的語法和結構,以及該結構的意義。
程式碼清單
public class Distance {
private java.awt.Point point0, point1;
public Distance(int x0, int y0, int x1, int y1) {
point0 = new java.awt.Point(x0, y0);
point1 = new java.awt.Point(x1, y1);
}
public void printDistance() {
System.out.println("Distance between " + point0 + " and " + point1
+ " is " + point0.distance(point1));
}
public static void main(String[] args) {
Distance dist = new Distance(
intValue(args[0]), intValue(args[1]),
intValue(args[2]), intValue(args[3]));
dist.printDistance();
}
private static int intValue(String data) {
return Integer.parseInt(data);
}
}
|
- 圖 2.1: 基本 Java 語法。
- 有關 Java 語法元素的進一步介紹,另請參閱 語法。
Java 類的 語法 是用於編碼類的字元、符號及其結構。Java 程式由一系列標記組成。標記有多種型別。例如,有單詞標記,如 class 和 public,它們代表 關鍵字 (以紫色顯示 在上圖中)——Java 中具有保留含義的特殊單詞。其他單詞,如 Distance、point0、x1 和 printDistance,不是關鍵字,而是 識別符號(以灰色顯示)。識別符號在 Java 中有許多不同的用途,但主要用作名稱。Java 還有標記來表示數字,如 1 和 3;這些被稱為 字面量 (以橙色顯示)。字串字面量 (以藍色顯示),如 "Distance between ",由嵌入雙引號中的零個或多個字元組成,而 運算子 (以紅色顯示),如 + 和 =,用於表達基本計算,如加法或字串連線或賦值。還有左大括號和右大括號 ({ 和 }),它們將 塊 括起來。類的主體就是一個這樣的塊。某些標記是標點符號,如句點 .、逗號 , 和分號 ;。您使用 空格(如空格、製表符和換行符)來分隔標記。例如,關鍵字和識別符號之間需要空格:publicstatic 是一個包含十二個字元的單個識別符號,而不是兩個 Java 關鍵字。
public class Distance {
private java.awt.Point point0, point1;
public Distance(int x0, int y0, int x1, int y1) {
point0 = new java.awt.Point(x0, y0);
point1 = new java.awt.Point(x1, y1);
}
public void printDistance() {
System.out.println("Distance between " + point0 + " and " + point1
+ " is " + point0.distance(point1));
}
public static void main(String[] args) {
Distance dist = new Distance(
intValue(args[0]), intValue(args[1]),
intValue(args[2]), intValue(args[3]));
dist.printDistance();
}
private static int intValue(String data) {
return Integer.parseInt(data);
}
}
- 圖 2.2: 宣告和定義。
如上所示,使用一系列標記構建 Java 類下一個構建塊:宣告和定義。類宣告提供類的名稱和可見性。在我們的示例中,public class Distance 是類宣告。它由(在這種情況下)兩個關鍵字組成, 和 public,後面跟著識別符號 classDistance。
這意味著我們正在定義一個名為 Distance 的類。其他類,或者在我們的例子中,命令列,可以透過此名稱引用該類。public 關鍵字是 訪問修飾符,它宣告該類及其成員可以從其他類訪問。class 關鍵字顯然將此宣告標識為一個類。Java 還允許宣告 介面 和 註解。
類聲明後面跟著一個塊(用大括號包圍),它提供類的定義 (在 圖 2.2 中以藍色顯示)。定義是類的實現 - 類成員的宣告和定義。該類恰好包含六個成員,我們將依次解釋。
- 兩個名為
point0和point1的欄位宣告 (以綠色顯示) - 一個建構函式宣告 (以橙色顯示)
- 三個方法宣告 (以紅色顯示)
示例:例項欄位
[edit | edit source]宣告
程式碼段 2.1:宣告。
private java.awt.Point point0, point1;
|
...聲明瞭兩個 例項欄位。例項欄位代表在構造類的例項時分配的命名值。當 Java 程式建立一個 Distance 例項時,該例項將包含 point0 和 point1 的空間。當建立另一個 Distance 物件時,它將包含其自身的 point0 和 point1 值的空間。第一個 Distance 物件中 point0 的值可以獨立於第二個 Distance 物件中 point0 的值而變化。
此宣告包括
private訪問修飾符,
這意味著這些例項欄位對其他類不可見。- 例項欄位的型別。在本例中,型別為
java.awt.Point。
這是java.awt包中的類Point。 - 以逗號分隔的列表中的例項欄位的名稱。
這兩個欄位也可以用兩個獨立但更詳細的宣告來宣告,
程式碼段 2.2:詳細宣告。
private java.awt.Point point0;
private java.awt.Point point1;
|
由於這些欄位的型別是引用型別(即一個引用或可以儲存對物件值的引用的欄位),因此當建立 Distance 例項時,Java 將隱式地將 point0 和 point1 的值初始化為 null。null 值意味著引用值不引用任何物件。特殊的 Java 字面量 null 用於在程式中表示 null 值。雖然可以在宣告中顯式地分配 null 值,如
程式碼段 2.3:宣告和賦值。
private java.awt.Point point0 = null;
private java.awt.Point point1 = null;
|
但這是不必要的,大多數程式設計師會省略這種預設賦值。
示例:建構函式
[edit | edit source]建構函式 是類中的一種特殊方法,用於構造類的例項。建構函式可以執行對物件的初始化,超出 Java VM 自動執行的初始化。例如,Java 將自動將欄位 point0 和 point1 初始化為 null。
程式碼段 2.4:類的建構函式
public Distance(int x0, int y0, int x1, int y1) {
point0 = new java.awt.Point(x0, y0);
point1 = new java.awt.Point(x1, y1);
}
|
上面的建構函式由五個部分組成
- 可選的 訪問修飾符。
在本例中,建構函式被宣告為public - 建構函式名稱,必須與類名稱完全匹配:在本例中為
Distance。 - 建構函式引數。
引數列表是必需的。即使建構函式沒有任何引數,也必須指定空列表()。引數列表宣告每個方法引數的型別和名稱。 - 可選的
throws子句,它宣告建構函式可能引發的 異常。此建構函式沒有宣告任何異常。 - 建構函式體,它是一個 Java 塊(用
{}括起來)。此建構函式的體包含兩個語句。
此建構函式接受四個引數,分別命名為 x0, y0, x1 和 y1。每個引數都需要一個引數型別宣告,在本例中,所有四個引數的型別都是 。引數列表中的引數用逗號分隔。int
此建構函式中的兩個賦值使用 Java 的 new 運算子 分配兩個 java.awt.Point 物件。第一個分配表示第一個點 (x0, y0) 的物件,並將其分配給 point0 例項變數(替換例項變數初始化的 null 值)。第二個語句分配具有 (x1, y1) 的第二個 java.awt.Point 例項,並將其分配給 point1 例項變數。
這是 Distance 類的建構函式。Distance 隱式地從 java.lang.Object 擴充套件。如果程式碼中沒有顯式地編碼,Java 會將對超類建構函式的呼叫作為建構函式的第一個可執行語句插入。上面的建構函式體等效於以下帶有顯式超類建構函式呼叫的體
程式碼段 2.5:超類建構函式。
{
super();
point0 = new java.awt.Point(x0, y0);
point1 = new java.awt.Point(x1, y1);
}
|
雖然該類確實可以透過其他方式實現,例如僅儲存兩個點的座標並計算距離為 ,但該類使用現有的 java.awt.Point 類。此選擇與該類的抽象定義相匹配:列印平面上的兩點之間的距離。我們利用了 Java 平臺中已實現的現有行為,而不是重新實現它。我們將在後面看到如何使程式更靈活,而不會增加太多複雜性,因為我們在這裡選擇使用物件抽象。但是,關鍵點是該類使用資訊隱藏。也就是說,如何類儲存其狀態或如何計算距離是隱藏的。我們可以更改此實現,而不會改變客戶使用和呼叫該類的方式。
示例:方法
[edit | edit source]方法 是第三種也是最重要的類成員型別。該類包含三個定義 Distance 類行為的 方法:printDistance()、main() 和 intValue()
printDistance() 方法
[edit | edit source]printDistance() 方法將兩點之間的距離列印到標準輸出(通常是控制檯)。
程式碼部分 2.6:printDistance() 方法。
public void printDistance() {
System.out.println("Distance between " + point0
+ " and " + point1
+ " is " + point0.distance(point1));
}
|
這個例項方法在隱式Distance物件的上下文中執行。例項欄位引用point0 和point1 引用該隱式物件的例項欄位。您也可以使用特殊變數this 來顯式引用當前物件。在例項方法中,Java 將名稱this 繫結到執行該方法的物件,並且this 的型別是當前類的型別。printDistance 方法的主體也可以編碼為
程式碼部分 2.7:當前類的顯式例項。
System.out.println("Distance between " + this.point0
+ " and " + this.point1
+ " is " + this.point0.distance(this.point1));
|
以使例項欄位引用更明確。
此方法在一個語句中計算距離並列印距離。距離使用point0.distance(point1) 計算;distance() 是java.awt.Point 類的例項方法(point0 和point1 是該類的例項)。該方法對point0 進行操作(在方法執行期間將this 繫結到point0 引用的物件),並接受另一個 Point 作為引數。實際上,它比這稍微複雜一點,但我們稍後會解釋。distance() 方法的結果是雙精度浮點數。
此方法使用語法
程式碼部分 2.8:字串連線。
"Distance between " + this.point0
+ " and " + this.point1
+ " is " + this.point0.distance(this.point1)
|
來構建一個字串,傳遞給System.out.println()。此表示式是一系列字串連線方法,這些方法連線字串或原始型別(如雙精度數)或物件的字串表示形式,並返回一個長字串。例如,此表示式針對點 (0,3) 和 (4,0) 的結果是字串
輸出
"Distance between java.awt.Point[x=0,y=3] and java.awt.Point[x=4,y=0] is 5.0" |
該方法隨後將其列印到System.out。
為了列印,我們呼叫println()。這是一個來自java.io.PrintStream 的例項方法,它是類java.lang.System 中的靜態欄位out 的型別。Java VM 在啟動程式時將System.out 繫結到標準輸出流。
main() 方法
[edit | edit source]main() 方法是 Java 從命令列啟動 Java 程式時呼叫的主入口點。命令
輸出
java Distance 0 3 4 0 |
指示 Java 定位 Distance 類,將四個命令列引數放入一個 String 值陣列中,然後將這些引數傳遞給該類的public static main(String[]) 方法。我們將在稍後介紹陣列。您要從命令列或桌面快捷方式呼叫的任何 Java 類都必須具有此簽名或以下簽名的主方法:public static main(String...)。
程式碼部分 2.9:main() 方法。
public static void main(String[] args) {
Distance dist = new Distance(
intValue(args[0]), intValue(args[1]),
intValue(args[2]), intValue(args[3]));
dist.printDistance();
}
|
main() 方法呼叫最終方法intValue() 四次。intValue() 接受單個字串引數並返回字串中表示的整數值。例如,intValue("3") 將返回整數 3。
進行測試優先程式設計或執行迴歸測試的人會在每個 Java 類中編寫一個 main() 方法,並在每個 Python 模組中編寫一個main() 函式,以執行自動化測試。當一個人直接執行檔案時,main() 方法會執行並執行該檔案的自動化測試。當一個人執行另一個 Java 檔案,該檔案又匯入了許多其他 Java 類時,只執行一個 main() 方法 - 直接執行檔案的 main() 方法。
intValue() 方法
[edit | edit source]intValue() 方法將它的工作委託給Integer.parseInt() 方法。主方法可以直接呼叫Integer.parseInt();intValue() 方法只是使main() 方法更具可讀性。
程式碼部分 2.10:intValue() 方法。
private static int intValue(String data) {
return Integer.parseInt(data);
}
|
此方法是private,因為它與欄位point0 和point1 一樣,是類內部實現的一部分,而不是Distance 類外部程式設計介面的一部分。
靜態方法與例項方法
[edit | edit source]main() 和intValue() 方法都是靜態方法。static 關鍵字告訴編譯器建立一個與該類關聯的單個記憶體空間。每個例項化的個體物件都有自己的私有狀態變數和方法,但使用與編譯器在第一個類物件例項化或建立時建立的單個類物件公用的相同的靜態方法和成員。這意味著該方法在靜態或非物件上下文中執行 - 當各種物件從各種物件執行靜態方法時,沒有隱式單獨的例項可用,並且特殊變數this 不可使用。因此,靜態方法無法直接訪問例項方法或例項欄位(例如printDistance() 或point0)。main() 方法只能透過例項引用(例如dist)呼叫例項方法printDistance() 方法。
資料型別
[edit | edit source]大多數宣告都有一個數據型別。Java 有幾種資料型別類別:引用型別、原始型別、陣列型別以及一種特殊型別 void。
原始型別
[edit | edit source]原始型別 用於表示布林值、字元值和數值。此程式只顯式地使用一種原始型別,,它表示 32 位有符號整數值。該程式還隱式地使用int,這是doublejava.awt.Point 的distance() 方法的返回型別。double 值是 64 位 IEEE 浮點數。main() 方法使用整數值 0、1、2 和 3 來訪問命令列引數的元素。Distance() 建構函式的四個引數也具有型別int。此外,intValue() 方法的返回型別為int。這意味著對該方法的呼叫,例如intValue(args[0]),是一個型別為int 的表示式。這有助於解釋為什麼主方法不能呼叫
程式碼部分 2.11:錯誤型別。
new Distance(args[0], args[1], args[2], args[3]) // This is an error
|
由於args 陣列元素的型別為 String,而我們的建構函式引數必須為int,因此此類呼叫會導致錯誤,因為 Java 不會自動將型別為 String 的值轉換為int 值。
Java 的原始型別是、boolean、byte、char、short、int、long 和float。它們都是 Java 語言關鍵字。double
除了原始型別,Java 還支援引用型別。引用型別是由 Java 類或介面定義的 Java 資料型別。引用型別之所以得名,是因為此類值引用一個物件或包含指向一個物件的引用。這個概念類似於其他語言(如 C)中的指標。
Java 使用引用型別java.lang.String來表示字元資料序列,或字串,通常稱為String。字串字面量(如"Distance between ")是型別為 String 的常量。
該程式使用三種不同的引用型別
- java.lang.String(或簡稱為 String)
- Distance
- java.awt.Point
- 有關更多資訊,請參閱章節:Java 程式設計/類、物件和型別。
Java 支援陣列,它們是聚合型別,具有固定元素型別(可以是任何 Java 型別)和整型大小。該程式只使用一個數組,String[] args。這表示args具有陣列型別,且元素型別為String。Java VM 會構建並初始化傳遞給main方法的陣列。有關如何建立陣列和訪問其大小的更多詳細資訊,請參閱陣列。
陣列元素使用整型索引進行訪問。陣列的第一個元素始終是元素 0。該程式使用索引 0、1、2 和 3 顯式訪問args陣列的前四個元素。該程式沒有執行任何輸入驗證,例如驗證使用者是否向程式傳遞了至少四個引數。我們將在後面進行修復。
void在 Java 中不是型別;它代表型別的缺失。不返回值的方法被宣告為void 方法。
該類定義了兩個 void 方法
程式碼部分 2.12:Void 方法
public static void main(String[] args) { ... }
public void printDistance() { ... }
|
Java 中的空白符用於分隔 Java 原始檔中的標記。空白符在某些地方是必需的,例如在訪問修飾符、型別名稱和識別符號之間,並在其他地方用於提高可讀性。
在 Java 中需要空白符的地方,可以使用一個或多個空白符字元。在 Java 中空白符可選的地方,可以使用零個或多個空白符字元。
Java 空白符由以下內容組成:
- 空格字元
' '(0x20), - 製表符(十六進位制 0x09),
- 換頁符(十六進位制 0x0c),
- 換行符(十六進位制 0x0a)或回車符(十六進位制 0x0d)字元。
換行符是特殊的空白符字元,因為它們還會終止行註釋,而普通空白符不會。
其他 Unicode 空格字元(包括縱向製表符)在 Java 中不允許用作空白符。
檢視static方法intValue
程式碼部分 2.13:方法宣告
private static int intValue(String data) {
return Integer.parseInt(data);
}
|
在private和static之間、static和int之間、int和intValue之間,以及String和data之間都需要空白符。
如果程式碼這樣寫
程式碼部分 2.14:摺疊的程式碼
privatestaticint intValue(String data) {
return Integer.parseInt(data);
}
|
…則意味著完全不同的東西:它聲明瞭一個返回值型別為privatestaticint的方法。這種型別可能不存在,並且該方法不再是靜態的,因此上面的程式碼會導致語義錯誤。
Java 會忽略語句前面的所有空白符。因此,這兩段程式碼對於編譯器來說是相同的
程式碼部分 2.15:縮排的程式碼
public static void main(String[] args) {
Distance dist = new Distance(
intValue(args[0]), intValue(args[1]),
intValue(args[2]), intValue(args[3]));
dist.printDistance();
}
private static int intValue(String data) {
return Integer.parseInt(data);
}
|
程式碼部分 2.16:未縮排的程式碼
public static void main(String[] args) {
Distance dist = new Distance(
intValue(args[0]), intValue(args[1]),
intValue(args[2]), intValue(args[3]));
dist.printDistance();
}
private static int intValue(String data) {
return Integer.parseInt(data);
}
|
但是,第一個的風格(帶空白符)更受歡迎,因為可讀性更高。即使以更高的閱讀速度,方法主體也更容易與頭部區分開。