從 C++ 轉向 Java,不僅是語法遷移,更是從「手動資源管理、靜態編譯鏈接、多重繼承」轉向「自動記憶體管理、動態類載入、介面導向設計」的思維重塑。本文梳理 13 個最易踩坑、最需重構認知的核心差異,助你快速建立 Java 原生思維。
影響:啟動較慢、預熱後效能強、跨平台無縫遷移、可執行期動態代理/AOP。
main() 為程式入口,非物件方法。public static void main(String[] args),隸屬某個類別。基本型別除外,所有變數皆為物件參考。C++ 可直接 extern "C" 呼叫 C 函式/系統調用。Java 必須透過 JNI (Java Native Interface):
native 方法。javah 生成 C/C++ 標頭檔。System.loadLibrary 載入。警示:JNI 破壞跨平台性、極難除錯、GC 無法管理原生記憶體、呼叫開銷大。能用純 Java 實作(或 REST/gRPC 微服務隔離)絕不碰 JNI。
| C++ | Java |
|---|---|
| .h 宣告 + .cpp 實作,易因不同步導致編譯錯誤 | 單一 .java 檔案同時包含宣告與實作 |
| 常用 .h 當 API 文檔閱讀 | 無 .h,需依賴 Javadoc 生成 HTML 文檔或 IDE 檢視 |
適應:養成寫完整 Javadoc 習慣,善用 IDE 的 Quick Documentation (Ctrl+Q / F2)。
namespace + 命名慣例,編譯期解析,連結期仍可能衝突。package 對應目錄結構,類全名含套件名,編譯期與執行期雙重保障唯一性。但無法解決繼承體系中方法簽名衝突(菱形繼承問題)。throws 或 try-catch 處理,編譯器保證不遺漏。非受檢異常 代表程式邏輯錯誤,通常不捕獲讓其冒泡。最佳實踐:業務邏輯可預期失敗用受檢異常;程式錯誤用非受檢異常;統一在全域異常處理器記錄日誌並回覆友善錯誤碼。
char* / std::string 可變,緩衝區溢位風險。String 為 final 不可變物件,拼接產生新物件。高頻拼接必須用 StringBuilder (非執行緒安全) 或 StringBuffer (執行緒安全)。C++ const | Java final |
|---|---|
const T& 參數保證不修改物件 | 無參數不可變機制,參考傳遞仍可修改物件內部狀態 |
const 成員函式承諾不修改 this | 無方法層級不可變承諾 |
| 編譯期強制檢查 | 僅限基本型別與參考本身不可變 |
對策:設計不可變類別、防御性複製、使用 Lombok @Value 或 Record (Java 16+) 簡化不可變資料類別定義。
delete/free、無懸空指標。new T()、無法判斷 instanceof List、基本型別需裝箱效能損耗。finalize() (已棄用) 或 Cleaner/PhantomReference 處理原生資源釋放。鐵律:實作 AutoCloseable,配合 try-with-resources 確定性釋放檔案、連線、鎖等稀缺資源。永遠不要依賴 GC 釋放非記憶體資源。
default/static 方法,實現「混入」復用,解決傳統介面無法演進的痛點。std::thread、mutex、atomic 等,但記憶體模型複雜。Thread、synchronized、volatile、wait/notify,JMM (Java Memory Model) 明確定義可見性/有序性/原子性。JUC (java.util.concurrent) 提供高階組件,生產力遠超 C++ 標準庫。「工欲善其事,必先利其器。」花一周時間系統學習《Effective Java》第 3 版,比摸索半年少走無數彎路。