Learning Objectives
- 說出 Java 的發展歷程與主要特點:跨平台、強型別、物件導向、垃圾回收
- 說明原始碼(.java)、位元組碼(.class)與 JVM 的關係,分辨 JDK、JRE、JVM
- 用 javac 編譯、用 java 執行程式,也會用 java Main.java 直接執行單一原始檔
- 看懂第一個程式的結構:class、main 方法、System.out.println、分號與大括號
- 分辨 print 與 println,會用跳脫字元 \n、\t、\" 與三種註解
為什麼要學 Java
Java 是使用最廣的程式語言之一:銀行與電商的後端系統、Android 應用程式、Hadoop 這類大數據工具、Minecraft 的 Java 版,都是用 Java 寫的。 它的語法規矩比較多,每個變數都要宣告型別、每句結尾都要分號,剛開始會覺得囉嗦;但這些規矩讓編譯器能在程式執行之前就抓出很多錯誤, 所以很適合多人合作、需要長期維護的大型系統。學會 Java 之後,再看 C#、Kotlin、C++ 這些語法相近的語言也會容易很多。
發展歷程
| 時間 | 事件 |
|---|---|
| 1991 年 6 月 | Sun Microsystems 的 James Gosling、Mike Sheridan、Patrick Naughton 開始這個語言計畫,最初名叫 Oak(取自 Gosling 辦公室外的橡樹),後來改名 Java |
| 1996 年 1 月 23 日 | 第一個正式版本 JDK 1.0 推出 |
| 2006~2007 年 | Sun 把 Java 平台以 GPL 授權開放原始碼,成為今天的 OpenJDK |
| 2010 年 | Oracle 收購 Sun,Java 由 Oracle 主導 |
| 2014 年 3 月 | Java 8 推出,加入 Lambda 與 Stream(第 17、18 課) |
| 2018 年起 | 改成每六個月推出一個新版本,每隔幾版指定一個長期支援(LTS)版本 |
| 2023 年 9 月 | Java 21(LTS)推出,本課程以 Java 21 為準 |
Java 的特點
- 一次編寫,到處執行(write once, run anywhere):同一份編譯好的程式,可以在 Windows、macOS、Linux 上執行,不用重新編譯
- 強型別(strongly typed):每個變數都有固定的型別,型別不合的指派在編譯時就會被擋下(第 02 課)
- 物件導向:程式由類別與物件組成(第 08 課起)
- 垃圾回收(garbage collection):不再使用的記憶體由 Java 自動釋放,不必像 C 那樣自己
free
本課先看懂 Java 怎麼把程式跑起來,再逐行看第一個程式的結構。
JVM、位元組碼與 JDK
Java 程式從寫好到執行分成兩步:
Main.java ──javac 編譯──▶ Main.class ──java 執行──▶ JVM 在這台電腦上執行
(原始碼,人看得懂) (位元組碼 bytecode)
- 編譯:
javac把你寫的原始碼Main.java翻譯成Main.class。.class檔裝的不是某一種 CPU 的機器碼,而是 Java 自己定義的位元組碼(bytecode) - 執行:
java指令啟動 JVM(Java Virtual Machine,Java 虛擬機器),由 JVM 讀取位元組碼,轉成這台電腦的 CPU 能執行的指令
Windows、macOS、Linux 各有自己的 JVM,但它們讀的是同一種位元組碼,這就是「一次編寫,到處執行」的原理:要移植的是 JVM,不是你的程式。
| 名稱 | 全名 | 內容 | 誰需要 |
|---|---|---|---|
| JVM | Java Virtual Machine | 執行位元組碼的虛擬機器 | 執行程式 |
| JRE | Java Runtime Environment | JVM + 標準函式庫(例如 System.out、Math) |
只執行、不寫程式的人 |
| JDK | Java Development Kit | JRE + 開發工具(javac、jar、javadoc…) |
寫程式的人 |
三者是一層包一層的關係:JDK 包含 JRE,JRE 包含 JVM。寫程式要裝的是 JDK,可以從 Oracle 或 Eclipse Temurin(adoptium.net)等來源下載,選 21 這個 LTS 版本即可。
編譯與執行第一個程式
以下是裝好 JDK 之後,在終端機(Windows 的「命令提示字元」或 PowerShell、macOS/Linux 的終端機)裡的操作。ls 在 Windows 命令提示字元要改成 dir,其他指令都一樣。
-
1
確認 JDK 已經安裝
java -version顯示 JVM 的版本,javac -version顯示編譯器的版本。兩個都要有回應,而且版本相同;出現「不是內部或外部命令」或command not found,代表還沒安裝 JDK,或是安裝後沒有加進 PATH 環境變數。版本號碼依你安裝的版本而不同。 -
2
看一下原始碼
用任何文字編輯器(記事本、VS Code 都可以)把程式存成
Main.java。檔名的大小寫要和public class後面的名稱完全一樣。 -
3
用 javac 編譯,再用 java 執行
javac Main.java編譯成功時不會印出任何東西,資料夾裡會多出Main.class。執行時寫的是類別名稱java Main,不加.class;寫成java Main.class反而會找不到類別。 -
4
直接執行單一原始檔(Java 11 起)
從 Java 11 開始,
java後面直接接.java檔,會先在記憶體裡編譯再執行,不會留下.class檔。程式只有一個檔案時很方便,適合練習;正式的專案仍然是先編譯再執行。 -
5
兩個常見的編譯錯誤
把檔案另存成
Hello.java再編譯,javac 會要求public class Main必須放在Main.java。另一個常見錯誤是少了分號:javac 會指出檔名與行號(Main.java:4),並用^標出它認為缺少分號的位置。看到錯誤訊息先找冒號後面的行號,再往那一行(或前一行)找原因。
第一個程式
按「下一步」看 JVM 怎麼執行這支程式:它一定從 main 方法開始,由上往下一行一行執行,每執行一個 println,下方的輸出就多一行。左邊的框架是 main 方法,裡面的 args 是命令列引數(這裡沒有給,所以是空的 String[]),之後的課才會用到。
第一個程式的結構
public class Main { // ① 類別
public static void main(String[] args) { // ② main 方法
System.out.println("Hello, Java!"); // ③ 敘述,以分號結尾
}
}
① 類別(class):Java 的程式碼一定寫在類別裡面。public class Main 宣告一個公開的類別 Main,
一個 .java 檔最多只能有一個 public class,而且檔名必須是類別名稱加上 .java(Main.java)。類別是什麼,第 08 課會詳細說明,現在先把它當成「裝程式的容器」。
② main 方法:程式的進入點,JVM 從這裡開始執行。這一行要一字不差地寫:
- public:任何地方都可以呼叫;static:不必建立物件就能執行(第 08 課)
- void:這個方法不回傳值;String[] args:接收命令列引數的字串陣列(第 06 課)
③ 敘述(statement):System.out.println(...) 把括號裡的東西印到螢幕上。System.out 代表標準輸出(螢幕),println 是 print line 的縮寫。每個敘述都以分號 ; 結尾。
其他規則:
- 大括號
{ }標出類別與方法的範圍,一定成對出現。縮排是給人看的,Java 不靠縮排判斷範圍(這點和 Python 不同),但仍然要縮排整齊 - 大小寫有別:
System不能寫成system,Main和main是不同的名稱。寫成system.out.println時,javac 會回報package system does not exist - 字串用雙引號
"…";單引號'A'只能放一個字元(第 02 課)
print 與 println
print 印完不換行,下一次輸出會接在同一行後面;println 印完會換行。看第 4~6 行三次輸出怎麼接成一行,以及第 9 行不帶任何內容的 println() 印出一個空行。
想一想:程式的結構與執行
一個檔案裡寫了 public class Hello { … },這個檔案應該叫什麼名字?
下面的程式執行後會印出什麼?
public class Main {
public static void main(String[] args) {
System.out.println("A");
system.out.println("B");
}
}
關於 javac Main.java 產生的 Main.class,哪一個說法正確?
跳脫字元
字串用雙引號包起來,那要怎麼在字串裡面放雙引號,或是放進換行?做法是在字元前面加一個反斜線 \,組成跳脫字元(escape sequence):
| 寫法 | 意思 |
|---|---|
\n |
換行 |
\t |
Tab(跳到下一個定位點,常用來對齊欄位) |
\" |
雙引號本身 |
\\ |
反斜線本身 |
\' |
單引號(在字元 '\'' 裡需要) |
System.out.println("她說:\"你好\""); // 她說:"你好"
System.out.println("C:\\temp"); // C:\temp
反斜線後面接的字元如果不在 Java 規定的清單裡(例如 "C:\java" 的 \j),javac 會回報 illegal escape character,所以 Windows 路徑裡的反斜線要寫兩次。
換行、Tab 與引號
每一行都只呼叫一次 println 或 print,注意輸出區實際印出幾行、引號和反斜線變成什麼樣子。
註解
註解(comment)是寫給人看的說明,編譯器會直接略過。Java 有三種註解:
// 單行註解:從 // 到這一行結尾
/* 多行註解:
可以跨好幾行,到 */ 結束 */
/**
* 文件註解:寫在類別或方法前面,
* javadoc 工具可以把它整理成 HTML 說明文件
*/
註解的好用法是說明為什麼這樣寫,而不是重複程式碼本身已經寫清楚的事。
除錯時也常把某一行暫時加上 //(「註解掉」),讓它不執行,又不必刪掉。
註解不會被執行
注意動畫跳過的行:註解所在的行(第 2~5、8~9、11、13 行)完全不會停留,第 13 行的 println 因為整行被註解掉,不會印出任何東西。
想一想:會印出什麼
下面的程式會印出什麼?
public class Main {
public static void main(String[] args) {
System.out.print("A");
System.out.println("B");
System.out.print("C");
System.out.println();
System.out.println("D");
}
}
下面的程式會印出什麼?
public class Main {
public static void main(String[] args) {
System.out.println("1\n2\\n3");
}
}
下面的程式會印出什麼?
public class Main {
public static void main(String[] args) {
System.out.println("W");
// System.out.println("X");
/* System.out.println("Y"); */ System.out.println("Z");
}
}
動手寫:自我介紹卡
依照註解完成三個 TODO,讓輸出和預期完全相同。每個 TODO 都有指定要用哪一種寫法,練習跳脫字元與 print/println 的差別。
重點回顧
- Java 由 Sun 在 1991 年開始開發,1996 年推出 JDK 1.0,2010 年起由 Oracle 主導;目前的長期支援版本包括 Java 21
javac把.java原始碼編譯成.class位元組碼,java啟動 JVM 執行位元組碼;各平台的 JVM 讀同一種位元組碼,所以「一次編寫,到處執行」- JDK ⊃ JRE ⊃ JVM:寫程式要裝 JDK;Java 11 起可以用
java Main.java直接執行單一原始檔 - 程式寫在類別裡,
public class的名稱要和檔名相同;JVM 從public static void main(String[] args)開始執行 - 敘述以分號結尾,大括號標出範圍,大小寫有別;只要有一個編譯錯誤,整支程式就無法執行
print不換行、println換行;跳脫字元\n、\t、\"、\\;註解有//、/* */、/** */三種