Java 之道/簡介
Java 之道
這本書和這門課的目標是教你像計算機科學家一樣思考。我喜歡計算機科學家思考的方式,因為他們結合了數學、工程和自然科學的最佳特徵。像數學家一樣,計算機科學家使用形式語言來表示思想(特別是計算)。像工程師一樣,他們設計事物,將元件組裝成系統並評估備選方案之間的權衡。像科學家一樣,他們觀察複雜系統的行為,形成假設並檢驗預測。
計算機科學家最重要的技能是解決問題。我的意思是能夠提出問題,創造性地思考解決方案,並清晰準確地表達解決方案。事實證明,學習程式設計的過程是練習解決問題能力的絕佳機會。這就是本章被稱為“程式設計之道”的原因。
從一個層面上來說,你將學習程式設計,這本身就是一項有用的技能。從另一個層面上來說,你將使用程式設計作為達成目的的手段。隨著我們的不斷學習,這個目標將變得更加清晰。
你將學習的程式語言是Java,它相對較新(Sun 於 1995 年 5 月釋出了第一個版本)。Java 是高階語言的一個例子;你可能聽說過的其他高階語言包括 C、C++、Pascal 和 FORTRAN。
正如你可能從“高階語言”這個名稱中推斷的那樣,也存在低階語言,有時被稱為機器語言或組合語言。通俗地說,計算機只能執行用低階語言編寫的程式。因此,用高階語言編寫的程式必須在執行之前進行翻譯。這種翻譯需要一些時間,這是高階語言的一個小缺點。
但它的優勢是巨大的。首先,用高階語言程式設計要容易得多;“容易得多”指的是編寫程式所需的時間更少,程式更短更易讀,並且更有可能正確。其次,高階語言是可移植的,這意味著它們可以在不同的計算機上執行,幾乎不需要或根本不需要修改。低階程式只能在一類計算機上執行,必須重新編寫才能在其他計算機上執行。
由於這些優勢,幾乎所有程式都是用高階語言編寫的。低階語言僅用於少數特殊應用程式。
翻譯程式有兩種方法:解釋或編譯。直譯器是一個程式,它讀取高階程式並執行其中的指令。實際上,它逐行翻譯程式,交替讀取行和執行命令。解釋語言的例子包括 BASIC、Perl 和 Python。
編譯器是一個程式,它讀取高階程式並將其全部翻譯成機器語言,然後執行任何命令。通常你會將程式作為單獨的步驟進行編譯,然後在稍後執行編譯後的程式碼。在這種情況下,高階程式被稱為原始碼,翻譯後的程式被稱為可執行檔案。
例如,假設你用 C 語言編寫了一個程式。你可能會使用文字編輯器來編寫程式(文字編輯器是一個簡單的文字處理器)。程式完成後,你可能會將其儲存在名為 program.c 的檔案中,其中 program 是你起的任意名稱,字尾 .c 是一種約定,表示該檔案包含 C 原始碼。
然後,根據你的程式設計環境的型別,你可能會退出文字編輯器並執行編譯器。編譯器將讀取你的原始碼,將其翻譯並建立一個名為 program.o 的新檔案來儲存目的碼,或一個名為 program.exe 的檔案來儲存可執行檔案。
Java 語言不同尋常,因為它既是編譯的又是解釋的。Java 編譯器不會將 Java 程式翻譯成機器語言,而是生成 Java 位元組碼。位元組碼易於(且快速)解釋,就像機器語言一樣,但它也具有可移植性,就像高階語言一樣。因此,可以在一臺機器上編譯 Java 程式,透過網路將位元組碼傳輸到另一臺機器,然後在另一臺機器上解釋位元組碼。這種能力是 Java 相對於許多其他高階語言的優勢之一。
雖然這個過程可能看起來很複雜,但好訊息是,在大多數程式設計環境(有時稱為開發環境)中,這些步驟都是為你自動化的。通常你只需要編寫程式並輸入一個命令來編譯和執行它。另一方面,瞭解後臺發生的步驟是有用的,這樣如果出現問題,你就可以找出問題所在。
程式的原始碼包含一系列指令,這些指令指定如何執行計算。計算可能是數學運算,例如解方程組或求多項式的根,但也可能是符號運算,例如搜尋和替換文件中的文字,或者(奇怪的是)編譯程式。
指令(或命令,或語句)在不同的程式語言中看起來不同,但有一些基本功能幾乎出現在每種語言中。
- 輸入——從鍵盤、檔案或其他裝置獲取資料。
- 輸出——在螢幕上顯示資料或將資料傳送到檔案或其他裝置。
- 數學——執行基本數學運算,例如加法和乘法。
- 測試——檢查某些條件並執行相應的語句序列。
- 重複——重複執行某些操作,通常帶有某種變化。
信不信由你,這基本上就是全部。你用過的每一個程式,無論多麼複雜,都是由看起來或多或少像這些函式的函式組成的。因此,描述程式設計的一種方法是將一個大型複雜任務分解成越來越小的子任務,直到最終這些子任務足夠簡單,可以使用這些簡單函式來執行。
程式設計是一個複雜的過程,由於它是人完成的,因此經常會導致錯誤。由於一些奇特的原因,程式設計錯誤被稱為 bug,而追蹤和糾正它們的過程被稱為除錯。
程式中可能會出現幾種不同型別的錯誤,區分它們有助於更快地追蹤它們。
編譯器只有在程式語法正確的情況下才能翻譯程式;否則,編譯將失敗,你將無法執行程式。語法是指程式的結構以及關於該結構的規則。
例如,在英語中,句子必須以大寫字母開頭,以句號結尾。這句句子包含語法錯誤。這句也一樣
對於大多數讀者來說,幾個語法錯誤不是什麼大問題,這就是為什麼我們能夠閱讀 E. E. Cummings 的詩歌而不會噴出錯誤訊息。
編譯器不會那麼寬容。如果你的程式中存在任何語法錯誤,編譯器會列印錯誤訊息並退出,你將無法執行你的程式。
更糟糕的是,Java 中的語法規則比英語中的多,而你從編譯器獲得的錯誤訊息通常沒有太大幫助。在你程式設計生涯的前幾周,你可能會花費大量時間追蹤語法錯誤。然而,隨著你積累經驗,你會犯更少的錯誤,並能更快地發現它們。
執行時錯誤
[edit | edit source]第二種型別的錯誤是執行時錯誤,之所以這樣稱呼是因為錯誤直到你執行程式才會出現。在 Java 中,執行時錯誤發生在直譯器執行位元組碼時出現問題。
目前的好訊息是,Java 往往是一種安全的語言,這意味著執行時錯誤很少見,尤其是在接下來的幾週中,我們將編寫的簡單程式型別。
在學期的後期,你可能會開始看到更多的執行時錯誤,特別是在我們開始討論物件和引用時(物件章節)。
在 Java 中,執行時錯誤被稱為異常,在大多數環境中,它們以包含有關發生情況和程式在發生時正在執行操作的資訊的視窗或對話方塊的形式出現。此資訊對於除錯很有用。
邏輯錯誤和語義
[edit | edit source]第三種類型的錯誤是邏輯錯誤或語義錯誤。如果你的程式中存在邏輯錯誤,它將成功編譯和執行,從某種意義上說,計算機不會生成任何錯誤訊息,但它不會做正確的事情。它會做其他事情。具體來說,它會按照你指示它做的事情來做。
問題在於,你編寫的程式不是你想要編寫的程式。程式的含義(語義)是錯誤的。識別邏輯錯誤可能很棘手,因為它要求你透過檢視程式的輸出並試圖弄清楚它正在做什麼來反向操作。
實驗性除錯
[edit | edit source]你在這門課中學到的最重要的技能之一就是除錯。雖然除錯可能會讓人沮喪,但除錯是程式設計中最具智力挑戰性、最具挑戰性和最有趣的方面之一。
從某種意義上說,除錯就像偵探工作。你面對著線索,你必須推斷導致你看到的結果的過程和事件。
除錯也像實驗科學。一旦你有了關於哪裡出錯的想法,你就修改你的程式並再次嘗試。如果你的假設是正確的,那麼你可以預測修改的結果,並且你離編寫一個有效的程式更近一步。如果你的假設是錯誤的,你必須想出一個新的假設。正如夏洛克·福爾摩斯指出的那樣,“當你排除了不可能的事情,剩下的,無論多麼不可能,都必須是真相。”(來自阿瑟·柯南·道爾的《四簽名》)。
Holmes, Sherlock Doyle, Arthur Conan
對於有些人來說,程式設計和除錯是一回事。也就是說,程式設計是逐步除錯程式直到它完成你想要的操作的過程。這個想法是,你應該始終從一個執行某些操作的有效程式開始,並進行小的修改,並在進行時除錯它們,這樣你始終擁有一個有效的程式。
例如,Linux 是一個包含數千行程式碼的作業系統,但它最初是一個簡單的程式,Linus Torvalds 用它來探索英特爾 80386 晶片。據拉里·格林菲爾德說,“Linus 的早期專案之一是一個在列印 AAAA 和 BBBB 之間切換的程式。這後來發展成為 Linux”(來自《Linux 使用者指南測試版 1》)。
在後面的章節中,我將對除錯和其他程式設計實踐提出更多建議。
形式語言與自然語言
[edit | edit source]自然語言是人們所說的語言,比如英語、西班牙語和法語。它們不是由人們設計的(儘管人們試圖對它們施加一些秩序);它們是自然演化的。
形式語言是由人們為特定應用而設計的語言。例如,數學家使用的符號是一種形式語言,它特別擅長表示數字和符號之間的關係。化學家使用形式語言來表示分子的化學結構。同樣,程式語言是旨在表達計算的形式語言。
當你閱讀英語中的句子或形式語言中的語句時,你必須弄清楚句子的結構是什麼(儘管在自然語言中你是下意識地做到的)。這個過程被稱為解析。
例如,當你聽到“另一隻鞋掉了”這句話時,你明白“另一隻鞋”是主語,“掉了”是謂語。一旦你解析了一個句子,你就可以弄清楚它的意思,即句子的語義。假設你知道什麼是鞋子,以及掉下來的意思,你將理解這句話的一般含義。
雖然形式語言和自然語言有很多共同點(標記、結構、語法和語義等),但也存在很多差異
- 歧義 自然語言充滿了歧義,人們透過使用語境線索和其他資訊來處理歧義。形式語言被設計成幾乎或完全沒有歧義,這意味著任何語句都只有一個意思,無論語境如何。
- 冗餘 為了彌補歧義並減少誤解,自然語言運用了大量的冗餘。因此,它們往往很冗長。形式語言的冗餘度較低,更簡潔。
- 字面意義 自然語言充滿了習語和比喻。如果我說“另一隻鞋掉了”,可能根本沒有鞋子,也沒有什麼掉下來。形式語言的意思就是它們說的意思。
從小就說自然語言的人(每個人)往往很難適應形式語言。在某些方面,形式語言和自然語言之間的差異就像詩歌和散文之間的差異,但更甚。
- 詩歌 詞語的使用既是為了它們的音韻,也是為了它們的意思,而整首詩一起創造了一種效果或情感反應。歧義不僅很常見,而且往往是故意的。
- 散文 詞語的字面意思更重要,結構的意義也更大。散文比詩歌更容易分析,但仍然經常存在歧義。
- 程式 計算機程式的含義是明確的和字面的,可以透過對標記和結構的分析完全理解。
閱讀程式的提示
[edit | edit source]以下是一些閱讀程式(和其他形式語言)的建議。首先,請記住,形式語言比自然語言密集得多,因此閱讀它們需要更長的時間。此外,結構非常重要,因此通常不建議從上到下、從左到右閱讀。相反,學會在腦海中解析程式,識別標記並解釋結構。最後,請記住,細節很重要。像拼寫錯誤和標點符號錯誤這樣的小事情,你可以在自然語言中忍耐,但在形式語言中卻會有很大影響。
第一個程式
[edit | edit source]你好,世界!
傳統上,人們在新的語言中編寫的第一個程式被稱為你好,世界。因為它只做一件事情,就是列印你好,世界。在 Java 中,這個程式看起來像這樣
class Hello
{
// main: generate some simple output
public static void main(String[] args) {
System.out.println("Hello, world.");
}
}
有時,程式語言的質量是根據你好,世界。程式的簡單性來判斷的。按照這個標準,Java 表現不佳。即使是最簡單的 Java 程式也包含許多難以向初學者解釋的功能。現在我們將忽略其中很多,但我將解釋其中的一些。
所有程式都由類定義組成,類定義具有以下形式
類
[edit | edit source] class CLASSNAME
{
public static void main(String[] args) {
STATEMENTS
}
}
這裡 CLASSNAME 表示你編造的任意名稱。示例中的類名稱是 Hello。
main
[edit | edit source]在第二行程式碼中,你可以先忽略 `public static void` 這幾個詞,但請注意 `main` 這個詞。`main` 是一個特殊的名稱,它指示程式開始執行的位置。當程式執行時,它會從 `main` 中的第一條語句開始執行,並依次執行,直到到達最後一條語句,然後退出。
在 `main` 中可以包含任意數量的語句,但這個例子只包含一個語句。它是一個列印語句,意味著它會在螢幕上列印一條訊息。有點令人困惑的是,"列印"有時意味著"在螢幕上顯示內容",有時又意味著"將內容傳送到印表機"。在這本書中,我不會過多地討論將內容傳送到印表機,我們將所有列印操作都在螢幕上進行。
在螢幕上列印內容的命令是 `System.out.println`,括號中的內容就是將要列印的內容。語句末尾有一個分號 (;),這是每條語句末尾都必須包含的符號。
關於這個程式的語法,你應該注意以下幾點。首先,Java 使用花括號 { 和 } 來將程式碼分組在一起(花括號也被稱為大括號)。最外層的花括號(第 1 行和第 8 行)包含類定義,內部花括號包含 `main` 的定義。
另外,請注意第 3 行以 `//` 開頭。這表示該行包含註釋,註釋是可以在程式中間新增的英文文字,通常用於解釋程式的功能。當編譯器遇到 `//` 時,它會忽略從該位置到行尾的所有內容。
在第一個實驗中,你將編譯並執行這個程式,還會對其進行各種修改,以便了解語法規則,並觀察編譯器在違反規則時會生成哪些錯誤訊息。
- 問題求解:制定問題、尋找解決方案並表達解決方案的過程。
- 高階語言:像 Java 這樣的程式語言,旨在便於人類閱讀和編寫。
- 低階語言:旨在便於計算機執行的程式語言。也稱為“機器語言”或“組合語言”。
- 形式語言:人們為特定目的設計的所有語言,比如用來表示數學思想或計算機程式。所有程式語言都是形式語言。
- 自然語言:人們自然進化而來的語言。
- 可移植性:程式可以在多種型別的計算機上執行的屬性。
- 解釋:透過逐行翻譯高階語言程式來執行程式。
- 編譯:將高階語言程式翻譯成低階語言,一次性完成,為以後執行做準備。
- 原始碼:高階語言程式,在編譯之前。
- 目的碼:編譯器輸出,在翻譯程式之後。
- 可執行檔案:準備執行的目的碼的另一種名稱。
- 位元組碼:用於 Java 程式的一種特殊目的碼。位元組碼類似於低階語言,但它像高階語言一樣具有可移植性。
- 演算法:解決一類問題的通用流程。
- 錯誤:程式中的錯誤。
- 語法:程式的結構。
- 語義:程式的含義。
- 解析:檢查程式並分析其語法結構。
- 語法錯誤:程式中的錯誤,使其無法解析(因此也無法編譯)。
- 異常:程式中的錯誤,使其在執行時發生故障。也稱為執行時錯誤。
- 邏輯錯誤:程式中的錯誤,使其執行的結果與程式設計師的意圖不符。
- 除錯:查詢並消除任何三種類型錯誤的過程。