本頁內容
Java 教學:日常 Java 問題的實作指南
Java 是一種靜態型別的物件導向語言,執行於 JVM 之上——同一套程式碼既驅動企業後端、Android 應用程式,也驅動銀行系統。大多數人學習 Java 的方式,就是他們使用它的方式:一次解決一個具體問題。本頁是 DelftStack 上 1,000 多篇 Java 操作指南的地圖。下面的指南按你想完成的工作分組——裁剪字串、解讀編譯器錯誤、求陣列之和、讀取檔案——每組開頭都是編輯精選的指南,回答讀者最常問的問題。如果你在寫第一個 Java 程式,從學習路徑開始;如果你是帶著某個具體的錯誤或任務而來,直接跳到與之對應的分組。
Java 學習路徑
你不需要按順序讀完 1,000 篇指南。一條可靠的路線是:先從字串和字元開始,因為每個程式都要處理文字;然後學習陣列和清單如何保存資料;接著加入日常實用工具——日期、亂數、檔案;最後才認真花時間在編譯器錯誤和綁定等物件導向概念上。Java 獎勵這樣的順序:一旦你知道程式碼想表達什麼,編譯器那些嚴格的錯誤訊息就變得可讀了。把下面幾個階段當作一份清單;哪一階段覺得輕鬆了,就進入下一階段。
- 從文字開始。 學習取得字串的最後一個字元和將字串轉換為 char——幾乎每個入門程式都會用到
char的處理。 - 核心資料。 求陣列元素之和並將陣列元素初始化為零;迴圈和索引的概念會在陣列這裡豁然貫通。
- 日常任務。 列印今天的日期、產生亂數、讀取資料夾中的每一個檔案:下面的日期組和檔案組涵蓋了這些日常工作。
- 修復錯誤。 當編譯器拒絕你的程式碼時,identifier expected 指南和其他錯誤指南會教你如何找到原因,而不是靠猜。
- 更進一步。 看看靜態綁定與動態綁定有何不同,或者用 GUI 建置器搭建你的第一個視窗。
走完這條路徑之後,把本頁當作參考手冊使用:下面的每個分組都自成體系,各節下方的完整字母索引列出了全部 1,000 多篇指南,包括精選清單裝不下的長尾內容。
字串與字元
Java 的文字處理始於一個彆扭的事實:String 是物件,char 是基本型別,在兩者之間來回轉換一開始會讓所有人犯迷糊。把前兩篇指南配成一對來讀——它們從相反的方向處理同一個末尾字元。一篇講如何讀取字串的最後一個字元;另一篇——正文中處處互相引用——講如何刪除它,而刪除通常才是你裁剪末尾分隔符或換行符時真正想做的事。之後,char 的幾篇指南會解決宣告方面的疑問:char 預設保存什麼、為什麼它不能是 null,以及當你需要逐字處理時,如何把整個字串變成 char 陣列。
- 在 Java 中取得字串的最後一個字元 — 用三種方式讀取最後一個字元:要
char就用charAt(),要單字元字串就用substring(),或者轉換為字元陣列。 - 在 Java 中刪除字串的最後一個字元 — 上面那篇的刪除對應版:用
substring()或replaceFirst()刪除最後一個字元,或在 Java 8+ 上用 Commons Lang 的removeEnd()和chop()。 - 在 Java 中初始化 char — 用空值宣告一個
char,理解為什麼null不適用於基本型別,並知道欄位的預設值'\u0000'。 - 在 Java 中將字串轉換為 char — 用
charAt()取一個字元,用toCharArray()取全部內容,並了解getBytes()在哪裡適用、在哪裡不適用。
錯誤、異常與疑難排解
Java 編譯器報錯是一份精確的「投訴」,但只有當你知道它在指出哪條假設不成立時,它才真正有用。本組保留下來的這一篇,處理的是你在自己的類別逐漸長大後會遇到的錯誤:把運算子用在了錯誤的型別上。它逐個案例講清 bad operand type 報告背後的成因——不相容的運算元型別,以及運算子優先順序埋下的陷阱。
- 修復 Java 中的 bad operand types 錯誤 — 逐個案例拆解
&、&&、==和<=的bad operand type報告:不相容的運算元型別和運算子優先順序陷阱。
陣列與集合
陣列是 Java 的第一個資料結構,其他所有集合都建立在它之上,所以這一組從你寫得最多的兩個操作開始:把元素全部加起來、把元素全部設為一個已知起點值。求和指南同時也是一次現代 Java 風格的巡覽——用串流重寫同一個迴圈,能看出這門語言已經變了多少。置零指南回答了一個會讓 C 程式設計師吃驚的問題:Java 其實已經替你初始化了陣列元素,明確寫出方法的意義是重置一個已經用過的陣列。排序指南是通向真實工作的橋梁:在真實工作裡,需要按日期這樣的欄位排序的是物件,而不是基本型別。
- 在 Java 中求陣列元素之和 — 用樸素的
for迴圈為int陣列求總和,或者用串流:Arrays.stream(...).sum()、reduce和IntStream.of(...).sum()。 - 在 Java 中將陣列元素全部初始化為零 — 用迴圈、
Arrays.fill()、nCopies()、重新賦值或Arrays.setAll()重置陣列——以及 Java 的預設置零何時已經替你完成了這件事。 - 在 Java 中按日期對 ArrayList 中的物件排序 — 用三種方式按日期欄位為自訂物件排序:
Comparable、Collections.sort(),以及帶比較器的list.sort()。
型別轉換與運算子
Java 的型別系統很嚴格,這使轉換和運算子成為初學者問題的永久來源。被搜尋得最多的那個,其實是一個不存在的功能:從 JavaScript 或 Python 過來的人會尋找 typeof,而 Java 的回答——向物件詢問它自己的執行時期類別——揭示了這門語言看待型別的方式。!= 指南處理的是一個後果嚴重的著名筆誤:字元寫反之後,它就不再是比較,而變成了帶取反的賦值。負數時請閱讀取模指南,因為 Java 的 % 和 Math.floorMod() 在負數上確實會給出不同結果;而當一個 Object 必須變成 int 時,讀轉換指南,正確的方法取決於這個物件實際保存的是什麼。
- 在 Java 中取得物件的執行時期類別(typeof 問題) — Java 沒有
typeof運算子:用getClass()向物件詢問它的執行時期類別,包括那些容易絆倒比較邏輯的包裝類別情形。 - Java 中
!=與=!的區別 —!=比較兩個值是否不相等;=!根本不是運算子——在a = !b中,它賦的是取反後的布林值,一個能通過編譯卻誤導人的筆誤。 - Java 中的取模運算 — 用
%運算子計算餘數;當負運算元也必須返回非負結果時,改用Math.floorMod()。 - 在 Java 中將 Object 轉換為 int — 透過
Integer包裝類別、Number.intValue()或Integer.parseInt()把Object變成int——根據物件實際包含的內容來選。
日期、亂數與實用工具
這個小組在本頁各組中轉換率最高:每篇指南都回答一個簡短而確切的問題。日期的幾篇展現了一個正處於過渡期的函式庫——舊的 java.util.Date 和 Calendar 類別仍充滿現有程式碼,而 java.time(LocalDate、LocalDateTime、ZonedDateTime)才是今天應該寫的,指南會標明哪個是哪個。亂數指南涵蓋了三種產生器以及各自適用的場景。清空主控台和延遲是每個命令列程式最終都會需要的實用工具——並且都附了誠實的提醒,因為 Java 刻意隱藏了終端機,而且阻塞式休眠有時是錯誤的選擇。
- 在 Java 中產生 1 到 10 之間的亂數 — 用
java.util.Random、Math.random()或ThreadLocalRandom抽出一個 1–10(含兩端)的值——並知道並行時該選哪個。 - 在 Java 中取得今天的日期 — 用
java.util.Date取得今天的日期,或者在時區重要時用現代的LocalDate、LocalDateTime和ZonedDateTime。 - 在 Java 中取得目前年份 — 透過
LocalDate、Calendar、Date、YearMonth或 Joda Time 讀取目前年份,從完整日期一直到單個整數。 - 在 Java 中清空主控台 — 在確認終端機和作業系統支援什麼之後,用 ANSI 逸出序列或透過
ProcessBuilder的 OS 命令清空終端機。 - 在 Java 中延遲幾秒 — 用
Thread.sleep()或TimeUnit.sleep()暫停;當其他工作必須繼續時,改用ScheduledExecutorService做排程而不是阻塞。
檔案 IO 與屬性
Java 的檔案程式碼都遵循同一種形態——定位、讀取或設定——本組的兩篇指南涵蓋它的兩種日常形態。讀取資料夾可以從平鋪列舉擴展到完整的遞迴遍歷,指南按目錄樹的深度排列了各種方法。用作收尾的是 properties 指南:.properties 檔案至今仍是 Java 程式接收設定的方式——資料庫設定、功能旗標、環境變數——許多讀者正是為這套工作流程而來。
- 在 Java 中讀取資料夾中的所有檔案 — 用
Files類別、newDirectoryStream()列出並讀取一個目錄,或者用遞迴的walkFileTree()處理巢狀資料夾。 - Java 中的 properties 檔案 — 建立一個
.properties檔案、設定值、讀回單個屬性,並列出該檔案保存的每一個鍵和值。
GUI 與 JavaFX
Java 的桌面故事很老,卻仍被大量搜尋,驅動它的是一個問題——如何在不為每個按鈕手寫版面配置程式碼的情況下建置視窗。誠實的回答是:Swing 仍是大多數現有程式碼所用的框架,JavaFX 是更新的工具包,而在兩種情況下,視覺化建置器都會把工作從「在程式碼中擺放元件」變成「在畫布上排列元件並連上事件」。如果你在維護或剛開始一個 Swing 應用程式,從建置器指南開始;本組的其他指南收錄在下方索引中,是針對具體控制項和事件的後續內容。
- 適用於 Java Swing 應用程式的最佳 GUI 建置器 — 用 Eclipse 的 WindowBuilder 或 NetBeans GUI 建置器視覺化地建置 Swing 介面:安裝外掛、建立類別、在畫布上設計,而不是在程式碼裡寫。
類別與方法
這一組刻意保持精簡,只留下一篇真正能打開一個大主題的指南。綁定回答的是面試和考試都愛問的概念問題,因為它決定了到底哪個方法會被執行:是編譯期根據參照型別做出的選擇,還是執行期根據物件本身做出的選擇。把這篇綁定指南和錯誤組放在一起讀——許多呼叫解析方面的困惑,其實有一半是綁定沒有搞清楚。
- Java 中的靜態綁定與動態綁定 — 多載和靜態方法在編譯期綁定,被覆寫的方法在執行期綁定,並配有例子說明每種呼叫何時被解析。
常見問題
Java 中出現 “identifier expected” 錯誤是什麼原因?
編譯器走到了文法上要求出現名字——變數、方法或參數的名字——的位置,卻在那裡發現了別的東西。常見原因有:敘述直接寫在類別主體中、而不是任何方法之內;參數宣告缺了型別;錯位的括號讓方法提前結束;或者 try-with-resources 中的資源宣告寫錯。讀它指出的那一行,再檢查緊挨著它前面的內容。identifier expected 指南會用一份重新編譯前的檢查清單,逐項講清每種原因。
如何在 Java 中產生 1 到 10 之間的亂數?
建立一個 Random 並呼叫 nextInt(10) + 1:nextInt(10) 返回 0–9,所以加 1 就能讓範圍兩端都包含在內。一次性快速使用的話,(int) (Math.random() * 10) + 1 也能做到同樣的事;在並行程式碼中,ThreadLocalRandom.current().nextInt(1, 11) 可以避免競爭——注意它的上限是排他的。亂數指南比較了三種方法的範圍差異。
Java 中有 typeof 嗎?
沒有——Java 沒有 typeof 運算子。最接近的等價物是 obj.getClass(),它返回物件的執行時期類別;以及 instanceof,它測試一個物件是否屬於某個型別或其子型別。用哪個取決於你的問題:getClass() 回答「這確切是什麼?」,而 instanceof 回答「我能把它當作這一類來處理嗎?」。包裝物件多了一層複雜性,因為 Integer 報告的是 Integer.class,永遠不會是 int。執行時期類別指南兩者都講了,包括包裝類別的情形。
如何在 Java 中讓執行延遲幾秒?
呼叫 Thread.sleep(3000) 讓目前執行緒暫停三秒,並處理它宣告的 InterruptedException——要嘛捕獲它,要嘛讓它傳播。TimeUnit.SECONDS.sleep(3) 用帶名字的單位表達同樣的延遲,在經過審查的程式碼裡讀起來更好。如果執行緒還有其他工作要做,就完全不要阻塞它:改用 ScheduledExecutorService 來排程延遲的步驟。延遲指南既講了阻塞呼叫,也講了排程路線。
為什麼 Java 安裝會失敗並提示 “errors in the following switches”?
這則 Windows 訊息通常意味著一個 .jar 檔案被傳給了 Java 安裝程式或啟動器,並帶著它不理解的開關——最常見的原因是 .jar 檔案關聯指向了錯誤的可執行檔,以至於雙擊一個程式就觸發了安裝風格的錯誤。把關聯指向 javaw.exe,然後從命令列測試同一個 JAR,以區分關聯問題與下載損壞。只有當啟動器本身遺失或損壞時,重新安裝 Java 才會有幫助。安裝開關指南會按順序講清這些檢查。
後續步驟
上面有兩組正在成長為專門的子指南:錯誤、異常與疑難排解——讀者帶著緊急問題來到這裡,identifier expected 問題簇已經像一個獨立主題一樣運作;以及字串與字元,本合集點擊量最高的一組。在它們上線之前,上面的各節就是入口。如果你的工作轉向了動態型別語言或另一個生態,接著看 Python 操作指南 或 C# 操作指南。