學完本章你將能夠:

  • 說出 Java 的設計理念「一次編寫、到處執行」,並解釋它背後的原理。

  • 分辨 JVM、JRE、JDK 三者的角色與關係。

  • 在自己的電腦上安裝 JDK 17,並用指令驗證安裝成功。

  • 親手撰寫、編譯並執行你人生的第一個 Java 程式。

  • 看懂常見的編譯錯誤訊息,並自行修正。

  • 使用三種註解,養成為程式寫說明的好習慣。

歡迎踏上 Java 的學習之旅!學程式和學一門外語很像:一開始要先認識這門語言的 背景與「發音規則」,接著開口說出第一句話。本章就是這趟旅程的起點——我們會先 認識 Java 是什麼、為什麼全世界有數百萬名工程師靠它吃飯,然後把開發環境架設 起來,最後親手寫出第一個能執行的程式。

請不要小看「安裝環境」與「印出一行字」這兩件事。根據多年的教學經驗,初學者 最容易在第一天就被環境問題勸退;反過來說,只要今天能讓電腦聽話地印出一行 訊息,後面的學習就會順利得多。萬事起頭難,我們把「難」的部分拆成一小步一小 步,慢慢來。

Java 是什麼

Java 是一種「物件導向」的程式語言,1995 年由昇陽電腦(Sun Microsystems) 的 James Gosling 團隊發表,2010 年隨著昇陽被甲骨文(Oracle)收購,Java 也 轉由 Oracle 主導發展。它誕生至今將近三十年,始終穩居全世界最多人使用的程式 語言前幾名。你每天使用的許多服務——網路銀行的後端系統、電商平台、Android 手機上的許多應用程式、甚至遊戲《Minecraft》——背後都有 Java 的身影。

一次編寫、到處執行

Java 最著名的口號是「一次編寫、到處執行」(Write Once, Run Anywhere)。 要理解這句話的價值,可以先看看傳統編譯式語言(如 C 語言)的困境:C 的程式 會被編譯成「特定作業系統與特定硬體」才看得懂的機器碼,所以同一份程式想在 Windows、macOS 與 Linux 上執行,就得分別編譯三次,還常常要針對各平台修改 程式碼。

Java 的解法非常聰明:程式不直接編譯成機器碼,而是編譯成一種中間格式,稱為 「位元組碼」(bytecode)。位元組碼不是給任何實體電腦看的,而是給一台「虛擬 的電腦」看的——這台虛擬電腦就是 Java 虛擬機器(JVM,Java Virtual Machine)。只要某個平台上有對應版本的 JVM,同一份位元組碼就能原封不動地 在那裡執行。換句話說:

  • 開發者只需要寫一份程式、編譯一次。

  • 「跨平台」這件苦差事,交給各平台的 JVM 去煩惱。

  • 你在 macOS 上編譯出來的程式,拿到 Windows 的 JVM 上照樣能跑。

Java 的應用領域與生態系

Java 的生態系極為龐大,這也是企業持續選用它的主因——幾乎任何需求都找得到 成熟的函式庫與框架。常見的應用領域包括:

  • 企業後端系統:Spring 框架家族是全球網站後端的主流選擇, 銀行、保險、電信等大型系統大量採用 Java。

  • Android 應用程式:Android 官方開發語言長期以 Java 為 核心(近年加入 Kotlin,而 Kotlin 也執行在 JVM 上)。

  • 大數據處理:Hadoop、Kafka、Elasticsearch 等重量級 大數據工具都以 Java(或 JVM 語言)打造。

  • 金融與交易系統:Java 的穩定性與效能使它成為證券交易、 支付清算系統的常客。

  • 遊戲與工具:《Minecraft》以 Java 開發;IntelliJ、 Eclipse 這些開發工具本身也是 Java 寫的。

下表列出 Java 發展史上幾個關鍵版本。本教材以 Java 17 為基準——它是長期 支援版本(LTS,Long-Term Support),語法現代、業界採用率高,非常適合 作為學習的起點。

年份 版本 重要意義
1995 Java 1.0 正式誕生,主打「一次編寫、到處執行」
2004 Java 5 加入泛型、列舉等重要語法
2014 Java 8 Lambda 與 Stream,劃時代的版本
2021 Java 17 長期支援版本(LTS),本教材的基準
2023 Java 21 目前最新的長期支援版本

自 2017 年起,Java 改為每半年發布一個新版本,其中每兩年挑一版作為 LTS(長期支援版本)提供多年的更新維護。企業與教學通常鎖定 LTS 版本, 例如 11、17、21。你在本書學到的內容,在更新的版本上一樣適用。

JVM、JRE 與 JDK:三兄弟的分工

初學者第一個容易混淆的地方,就是這三個長得很像的縮寫。可以用「餐廳廚房」來 比喻:JVM 是爐子(真正把菜煮熟的核心設備)、JRE 是完整的外場與廚房設備 (讓一道菜能被端上桌的所有東西)、JDK 則是整間附設烹飪教室的餐廳(除了能 出菜,還提供讓你「開發新菜色」的全套工具)。

名稱 全名 內容與用途
JVM Java Virtual Machine 執行位元組碼的虛擬機器,是跨平台的關鍵核心
JRE Java Runtime Environment JVM 加上核心類別庫;只需要「執行」Java 程式的人裝這個就夠
JDK Java Development Kit JRE 加上編譯器與各種開發工具;要「開發」程式的我們必須安裝它

三者是「包含」關係:JDK 包含 JRE,JRE 包含 JVM。身為開發者,你只要記得 一件事:安裝 JDK 就對了,另外兩個會一起附上。

一支程式的旅程:從原始碼到執行

理解了三兄弟的分工後,我們把「寫好的程式如何被執行」的完整流程走一遍。 假設你寫了一個檔案叫 |Hello.java|:

  1. 撰寫原始碼:你用文字編輯器寫下人類看得懂的 Java 程式, 存成副檔名為 |.java| 的檔案。

  2. 編譯:執行 |javac Hello.java|,編譯器檢查語法, 沒問題就產出位元組碼檔 |Hello.class|。

  3. 載入:執行 |java Hello| 時,JVM 啟動並把 |Hello.class| 載入記憶體。

  4. 執行:JVM 逐條解讀位元組碼並執行;經常被執行的「熱點」 程式碼還會被即時編譯成機器碼,越跑越快。

「即時編譯」(JIT,Just-In-Time compilation)是 Java 高效能的祕密武器。 JVM 一邊執行一邊觀察:哪段程式被反覆執行上千次,就把那段位元組碼翻譯成 最佳化過的機器碼。這就是為什麼 Java 程式「暖機」後速度可以逼近 C 語言。 第一次閱讀看不懂沒關係,先有個印象即可。

安裝 JDK 17

接下來把工具裝起來。我們推薦初學者到 Adoptium 官網 (https://adoptium.net)下載免費的 Eclipse Temurin JDK 17 安裝檔, 它是目前業界最常用的 OpenJDK 發行版之一,macOS 與 Windows 都有圖形化 安裝程式,一路按「下一步」即可完成。

macOS 安裝

方式一(推薦):到 Adoptium 官網下載 |.pkg| 安裝檔,雙擊安裝。

方式二:如果你已經會用 Homebrew 套件管理器,也可以用指令安裝:

\(brew install openjdk@17 ==> Fetching openjdk@17 ==> Pouring openjdk@17--17.0.12.arm64_sequoia.bottle.tar.gz ...(安裝訊息略) \end{consoleoutput} 用 Homebrew 安裝後,還需要依照畫面提示建立一個系統連結,macOS 內建的 \jcode|java| 指令才找得到它: \begin{consoleoutput}\) sudo ln -sfn /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk  /Library/Java/JavaVirtualMachines/openjdk-17.jdk

Windows 安裝

到 Adoptium 官網下載 |.msi| 安裝檔,執行後在安裝選項中把 「Add to PATH」(加入環境變數)與「Set JAVA_HOME variable」勾選起來, 其餘保持預設值即可。安裝完成後,開啟「命令提示字元」或 PowerShell 進行下一步的驗證。

驗證安裝

無論用哪種方式安裝,最後都請打開終端機(macOS 的 Terminal 或 Windows 的 命令提示字元),輸入以下兩個指令驗證:

\(java -version openjdk version "17.0.12" 2024-07-16 OpenJDK Runtime Environment Temurin-17.0.12+7 (build 17.0.12+7) OpenJDK 64-Bit Server VM Temurin-17.0.12+7 (build 17.0.12+7, mixed mode)\) javac -version javac 17.0.12

你看到的版本號碼末段(例如 17.0.12 的 12)和本書不同完全沒關係, 只要主要版本是 17 或更新即可。如果出現「找不到指令」之類的錯誤, 最常見的原因是安裝時沒有設定環境變數——重新執行安裝程式並勾選 「Add to PATH」通常就能解決。

第一個程式:HelloWorld

環境就緒,正式開工!請建立一個練習用的資料夾(例如 |java-practice|), 用任何文字編輯器(記事本、TextEdit、VS Code 都行)建立一個名為 |HelloWorld.java| 的檔案,輸入以下內容。請務必親手逐字輸入, 不要複製貼上——打字的過程就是在建立肌肉記憶。

/** * 我的第一個 Java 程式。 * 這個程式只做一件事:在主控台印出一行問候訊息。 */ public class HelloWorld

/** * main 方法:整個程式的進入點。 * 執行程式時,JVM 會從這裡開始,由上往下逐行執行。 */ public static void main(String[] args) // System.out.println 會把括號裡的文字印到主控台, // 印完後自動換行 System.out.println("Hello, World!");

存檔後回到終端機,切換到檔案所在的資料夾,先編譯、再執行:

\(javac HelloWorld.java\) ls HelloWorld.class HelloWorld.java \(java HelloWorld Hello, World! \end{consoleoutput} 注意三個細節:第一,\jcode|javac| 編譯成功時「不會顯示任何訊息」, 沉默就是好消息;第二,編譯後資料夾多了一個 \jcode|HelloWorld.class|, 這就是位元組碼檔案;第三,執行時用的指令是 \jcode|java HelloWorld|, 給的是「類別名稱」,不是檔名,所以\textbf{不要}加上 \jcode|.class| 或 \jcode|.java| 副檔名。 \begin{tipbox} Java 11 之後,簡單的單一檔案程式可以跳過 \jcode|javac|,直接用 \jcode|java HelloWorld.java| 一步執行,很適合快速做小實驗。 不過初學階段請至少完整走過幾次「先編譯、再執行」的流程, 理解兩個階段各自在做什麼。 \end{tipbox} \subsection{逐行解析:每個字都有它的理由} 這短短幾行程式,其實濃縮了 Java 的許多核心概念。我們一個一個拆開看: \begin{itemize} \item \jcode|public class HelloWorld|:定義一個「公開的類別」, 名字叫 HelloWorld。類別是 Java 程式的基本單位,所有程式碼都 必須住在某個類別裡面。\jcode|public| 表示這個類別對外公開, 任何人都能使用它。類別的詳細意義會在第 8 章完整說明, 目前你可以把它想成「程式的容器」。 \item \jcode|public static void main(String[] args)|:這是 \textbf{main 方法},整個程式的進入點——JVM 規定:想被執行的 程式,必須提供一個長得「一模一樣」的 main 方法。拆開來看: \jcode|public| 表示公開(JVM 在外面才呼叫得到); \jcode|static| 表示這是靜態方法(JVM 不必先建立物件就能呼叫, 原因見第 8 章);\jcode|void| 表示這個方法不回傳任何值; \jcode|String[] args| 是一個字串陣列參數,用來接收執行時 附加的指令列引數。 \item \jcode|System.out.println("Hello, World!");|:真正做事的一行。 \jcode|System| 是 Java 內建的類別,\jcode|out| 是它裡面代表 「標準輸出(主控台)」的物件,\jcode|println| 則是「印出並 換行」的方法。括號中用雙引號包住的 \jcode|"Hello, World!"| 是一個字串,也就是要印出的內容。 \item \textbf{大括號}:\jcode|{| 與 \jcode|}| 標出「範圍」——類別的 內容是一對大括號,方法的內容又是一對。它們必須成對出現, 巢狀包裹,就像俄羅斯娃娃。 \item \textbf{分號}:每個「敘述」(statement)結尾都要加分號 \jcode|;|,它是敘述的句點。忘記分號是初學者最常見的編譯錯誤, 沒有之一。 \end{itemize} \begin{warnbox} 兩條鐵律,現在就刻在腦海裡:第一,\textbf{檔名必須與 public 類別同名}, 大小寫也要完全一致——類別叫 \jcode|HelloWorld|,檔案就必須叫 \jcode|HelloWorld.java|,存成 \jcode|helloworld.java| 就會編譯失敗, 錯誤訊息會是「class HelloWorld is public, should be declared in a file named HelloWorld.java」。第二,\textbf{Java 嚴格區分大小寫}: \jcode|System| 與 \jcode|system|、\jcode|main| 與 \jcode|Main| 都是 完全不同的東西。 \end{warnbox} \subsection{再練一次:println 與 print} 輸出是初期最重要的觀察工具,多練一個範例。這次同時認識 \jcode|println| 的兄弟 \jcode|print|——它印完「不會」換行: \begin{javacode}[title={範例:SelfIntroduction.java}] /** * 自我介紹程式:練習 println 與 print 的差別。 */ public class SelfIntroduction { public static void main(String[] args) { // println:印出文字後「會」換行(ln 是 line 的縮寫) System.out.println("大家好,我是小明。"); System.out.println("我今年 18 歲,正在學習 Java。"); // print:印出文字後「不會」換行, // 下一次的輸出會緊接在同一行後面 System.out.print("Java "); System.out.print("好好玩!"); // 單獨呼叫 println() 不給內容,效果是「只換行」 System.out.println(); System.out.println("學習正式開始!"); } } \end{javacode} \begin{consoleoutput} 大家好,我是小明。 我今年 18 歲,正在學習 Java。 Java 好好玩! 學習正式開始! \end{consoleoutput} 對照程式與輸出:兩次 \jcode|print| 的內容「Java 」與「好好玩!」被接在 同一行,這正是它與 \jcode|println| 的差別。 \section{註解:寫給人看的說明} \label{bg-ch01:comments} 程式碼是寫給電腦執行的,但更是寫給「人」讀的——包括三個月後已經忘光細節 的你自己。註解(comment)是完全被編譯器忽略的文字,專門用來向讀者說明 意圖。Java 提供三種註解: \begin{javacode}[title={範例:CommentDemo.java}] /** * 文件註解(Javadoc):以斜線加兩顆星號開頭。 * 慣例上放在類別與方法的正上方,說明「這是什麼、怎麼用」。 * 專用工具可以抽取這種註解,自動產生 API 說明文件。 */ public class CommentDemo { public static void main(String[] args) { // 單行註解:從雙斜線開始到行尾為止,都是給人看的 /* 多行註解:從斜線星號開始,到星號斜線結束, 中間可以橫跨很多行,適合較長的說明文字 */ int price = 100; // 註解也可以跟在程式碼後面 System.out.println("價格:" + price); } } \end{javacode} \begin{consoleoutput} 價格:100 \end{consoleoutput} \begin{tipbox} 好註解的三個原則:(1)解釋「為什麼」而不是「做什麼」—— \jcode|price = price * 2;| 後面寫「乘以二」是廢話,寫「週年慶期間 全面兩倍計價」才有價值;(2)程式改了,註解要跟著改,過期的註解比 沒有註解更害人;(3)不要用註解「封存」大段廢棄程式碼,需要歷史紀錄 請交給版本控制工具(如 Git)。本教材的範例會刻意寫非常多註解, 因為它們同時肩負「教學」的任務。 \end{tipbox} \section{看懂編譯錯誤訊息} \label{bg-ch01:errors} 初學階段有一半的時間在跟編譯錯誤打交道,所以「讀懂錯誤訊息」是最值得 提早投資的技能。編譯器不是在罵你,它是在「精準地告訴你哪裡需要修改」。 我們故意寫兩個錯誤程式,看看編譯器怎麼說。 \subsection{案例一:忘記分號} \begin{javacode}[title={錯誤示範:MissingSemicolon.java}] /** * 錯誤示範:第 7 行的結尾故意漏掉分號。 */ public class MissingSemicolon { public static void main(String[] args) { // 下面這行結尾少了分號,編譯器會停下來抱怨 System.out.println("Hello") } } \end{javacode} \begin{consoleoutput}\) javac MissingSemicolon.java MissingSemicolon.java:7: error: ’;’ expected System.out.println("Hello") ^ 1 error

錯誤訊息的解讀方式:|MissingSemicolon.java:7| 指出「哪個檔案的 第幾行」;|error:| 後面是問題描述——「預期這裡該有個分號」; 下一行把出錯的原始碼印出來,並用插入符號 |^| 指出確切位置。 補上分號重新編譯,問題解決。

案例二:大小寫寫錯

/** * 錯誤示範:第 6 行把 System 誤寫成小寫的 system。 */ public class WrongCase public static void main(String[] args) system.out.println("Hello"); // System 寫成了 system!

這次的訊息比較迂迴:編譯器說「找不到名為 system 的套件」。它猜不到你 想寫的是 |System|,只能按照字面意義去找小寫的 |system|, 找不到就報錯。遇到「does not exist」或「cannot find symbol」這類訊息, 第一件事就是檢查拼字與大小寫。

閱讀編譯錯誤的要領整理如下:

  • 先看「檔名:行號」,直接跳到出錯的位置附近。

  • |error:| 之後的一句話是關鍵描述,搭配 |^| 的位置判讀。

  • 一次可能列出很多錯誤,永遠從第一個開始修——後面的錯誤 常常只是第一個錯誤引發的連鎖反應,修好第一個,其餘可能全消失。

  • 修正後重新編譯,重複這個循環直到編譯器「沉默」為止。

選擇你的開發工具

專業開發者通常使用「整合開發環境」(IDE,Integrated Development Environment)。IDE 集編輯器、編譯器、除錯器於一身,還提供自動完成、 即時錯誤提示等功能。主流選擇如下:

工具 特色 適合對象
IntelliJ IDEA Community 功能最完整、業界市占最高,社群版免費 想長期深耕 Java 的人
VS Code 加 Java 擴充套件 輕量、啟動快、外掛生態豐富 已熟悉 VS Code 的人
Eclipse 老牌免費 IDE,企業舊專案常見 需要接觸既有專案的人

建議的學習路線:前幾章先用「文字編輯器加終端機」的原始方式, 親手執行 |javac| 與 |java|,把編譯流程內化成直覺; 之後再改用 IntelliJ IDEA 或 VS Code 享受效率。太早依賴 IDE 的 「一鍵執行」,容易讓你對底層流程一知半解,之後遇到環境問題會不知 從何查起。

本章小結

  • Java 於 1995 年誕生,是穩居全球前幾名的物件導向程式語言, 廣泛應用於企業後端、Android、大數據與金融系統。

  • 「一次編寫、到處執行」的關鍵:原始碼編譯成位元組碼, 由各平台的 JVM 負責執行。

  • JDK 包含 JRE,JRE 包含 JVM;開發者安裝 JDK 即可。

  • 完整流程:|.java| 原始碼,經 |javac| 編譯成 |.class| 位元組碼,再由 |java| 啟動 JVM 執行。

  • main 方法是程式的進入點,寫法固定: |public static void main(String[] args)|。

  • 檔名必須與 public 類別同名;Java 嚴格區分大小寫; 每個敘述以分號結尾。

  • |println| 印出後換行,|print| 不換行。

  • 三種註解:|//| 單行、|/* */| 多行、 |/** */| 文件註解;註解要解釋「為什麼」。

  • 編譯錯誤訊息會標出檔名、行號與問題描述,永遠從第一個錯誤開始修。

本章練習

  1. 觀念題:請說明 JVM、JRE、JDK 三者的關係,以及為什麼開發者 需要安裝的是 JDK。(提示:包含關係——開發工具、執行環境、 虛擬機器。)

  2. 觀念題:為什麼 Java 可以「一次編寫、到處執行」?C 語言為什麼 不行?(提示:想想位元組碼與 JVM 各自扮演的角色。)

  3. 實作題:在你的電腦上安裝 JDK 17,並在終端機執行 |java -version| 與 |javac -version|,確認兩者的 主要版本一致。(提示:若指令找不到,檢查安裝時是否勾選了 加入 PATH。)

  4. 實作題:寫一個 |AboutMe| 類別,用三行輸出你的名字、 興趣與學習 Java 的目標;其中至少一行用 |print| 與 |println| 組合完成。(提示:注意檔名必須是 AboutMe.java。)

  5. 除錯題:把 HelloWorld 程式中的 |println| 改成 |Println| 再編譯,觀察並記下錯誤訊息;再把 |public class HelloWorld| 改成 |public class helloworld| 試一次。(提示:一個會出現 cannot find symbol,另一個與檔名規則有關。)

  6. 觀念題:|//| 與 |/* */| 兩種註解各適合什麼場合? 文件註解 |/** */| 又特別在哪裡?(提示:行尾短說明、 跨行長說明、可產生 API 文件。)