雖然 .NET (C#) 與 Java 是兩個不同的語言,但這兩個都是 OOP 物件導向程式架構,而且 Java 出現的比較早,我們在 .NET 裡面也經常看到很多 Java 的影子,所以其實有不少相似之處。這篇文章我打算整理一下最近的感受,把一些常見的技術名詞與抽象概念做一些對照,幫助想要成為「斜槓青年」的朋友入門。
底下有些抽象概念,必須同時寫過 .NET 與 Java 的人才能理解,但在「斜槓」的過程中知道這些差異是有幫助的。
-
語言環境
相較於「應用程式」需要一個「作業系統」才能運行。這裡的「執行環境」(Runtime) 從概念上來說,就像是「程式語言」的「作業系統」一般,提供程式語言所需的一切基礎建設,並且幫我們把「中介碼」編譯成「機械碼」,讓應用程式得以運行在不同的 CPU 架構上。在 Java 與 .NET 都有這樣的機制。
Java 名詞 |
.NET 名詞 |
JVM (Java Virtual Machine) |
CLR (Common Language Runtime) |
-
軟體開發套件 (SDK)
開發應用程式肯定需要許多開發時常見的工具與基礎建設,例如編譯器(Compiler)、分析工具(Analyzer)、函式庫(Libraries)、各種命令列工具等等,這些通稱為 Software Development Kit (SDK)。
-
程式執行環境 (Runtime)
如果你只是單純的想執行應用程式,只要安裝 Runtime 即可,不用安裝 SDK 軟體開發套件:
我們要執行「應用程式」的時候,需要一個宿主(host)才能執行,例如跑 .NET 的時候,你需要先將 *.cs
編譯成 *.dll
檔,然後透過 dotnet
命令列工具來執行。而 Java 則需要先將 *.java
編譯成 *.class
檔,然後透過 java
命令列工具才執行。
Java |
.NET |
java myapp.class |
dotnet myapp.dll |
java -jar myapp.jar |
dotnet myapp.dll |
-
共用類別庫
無論 Java 或 .NET 都有相當豐富的內建 API 可用,這些 API 被分類成一個一個的名稱,在 Java 被稱為 package
(套件),在 .NET 則被稱為 namespace
(命名空間),你在任何一個類別中,必須先引入才能使用。
類型 |
Java |
.NET |
類別庫名稱 |
JCL (Java Class Library) |
BCL (Base Class Library) |
宣告套件語法 |
package |
namespace |
引入套件語法 |
import |
using |
-
類別封裝
我們可以用 package
或 namespace
對類別進行分類,但是 Java 與 .NET 的封裝方法就有不小的差異了。
Java |
.NET |
一個 package 對應到一個資料夾,該資料夾可以包含多個 *.java 原始檔 |
一個 namespace 可以放在專案中任意位置、宣告在任意檔案,沒有嚴格限制,因此重構時容易亂掉 |
相同 package 下的類別可以存取在不同 *.java 原始檔中的任意 private 類別 |
相同 namespace 下的類別不可以存取在相同 namespace 或 *.cs 原始檔中的任意 private 類別 |
一個 *.java 原始檔中,可以有多個類別,但只能有一個 public 公開類別,限制較嚴格 |
一個 *.cs 原始檔中,可以有多個類別,也能有多個 public 公開類別 |
一個 *.java 檔只能編譯成一個 *.class 類別檔 |
無此概念 |
多個 *.class 類別檔通常會被封裝到一個 *.jar 檔中 |
無此概念 |
多個 *.class 類別檔可以被放置在一個目錄下,並可透過 java 執行時使用 -classpath 指定其路徑 |
多個 *.dll 檔案可以跟應用程式放在相同目錄下,如果沒有放在相同目錄就會變的很麻煩 |
多個 *.java
原始檔最終可以合併成一個 *.jar
檔,而多個 *.cs
檔最終會合併到一個 *.dll
檔案,所以若以最終的結果來看,*.dll
比較接近 *.jar
的封裝方式。
由於 .NET 沒有類似 *.jar
檔的機制,通常多個 *.cs
原始檔會使用「專案」進行管理,且最終會編譯到一個 *.dll
檔案中。因此,Java 的 *.jar
最接近 .NET 的概念應該是 *.dll
檔。
Java |
.NET |
說明 |
java myapp.java |
無此用法 |
Java 11 開始允許不用預先編譯原始碼,可直接執行 |
java myapp.class |
dotnet myapp.dll |
|
java -jar myapp.jar |
dotnet myapp.dll |
|
-
中介語言 (intermediary language)
寫完程式之後,無論 Java 或 .NET 都會將原始碼編譯成 中介碼 (Bytecode) 型態,他其實是一種高階的組合語言或稱中介語言(intermediary language)。
CIL = Common Intermediate Language
-
建置程式碼 (Build)
如果要編譯單一原始碼檔案,使用 Java 簡單很多,使用 .NET 就會異常複雜,微軟官方也說不打算讓你這樣用!
Java |
.NET |
javac myapp.java |
沒人這樣用 |
如果要編譯多個原始碼檔案 (完整專案或模組),使用 JDK 就顯得複雜很多,而 .NET 就內建 MSBuild 讓你使用,整件事變的異常輕鬆!
Java |
.NET |
javac @filelist -sourcepath src -d bin |
dotnet build (但你先要有 *.csproj 專案檔才行) |
由於 JDK 並沒有內建好用的專案建置工具(Build Tool),但是要「自製建置工具」其實還蠻簡單的,理論上你只要先把「檔案清單」收集好,任何 Java 專案你都能建置才對。然而大部分的 Java 開發者都會依賴開發工具內建的 Build Tool 來使用,不然就是使用 Maven 或 Gradle 等建置工具。
-
專案範本 (Project Templates)
.NET 在這方面做得相當好,而 Java 陣營的人主要還是靠開發工具提供此功能,或是 Maven 也有提供
Java |
.NET |
沒有這玩意 |
dotnet new -l |
沒有這玩意 |
dotnet new console -n c1 |
如果用 Maven 的專案產生器 (archetype
),可以參考以下範例,指令超長,完全沒有 DX 可言 😅
mvn org.apache.maven.plugins:maven-archetype-plugin:3.1.2:generate -DarchetypeArtifactId="spring-boot-blank-archetype" -DarchetypeGroupId="am.ik.archetype" -DarchetypeVersion="1.0.6" -DgroupId="com.duotify" -DartifactId="demo2"
-
專案與模組 (Projects and Modules)
.NET 使用 專案 (Project) 這個名詞,代表一種比 *.dll
還高一個階層的封裝。而在 Visual Studio 開發工具下,多個專案還可以透過方案(Solution)進行管理。
由於 Java 8 以前並沒有什麼 專案 的概念,但從 Java 9 新推出一個 模組 (Modules) 系統,簡稱 JPMS
(Java 9 Platform Module System),所做到的事情,就跟 .NET 的專案架構相當類似,但我認為 JPMS
更像是 .NET 的 NuGet 套件,不過概念上確實沒辦法 100% 對應。
你可以透過以下指令列出 JDK 內建的模組清單:
java --list-modules
其實「模組」本身就是個相當抽象的概念,在 Java 世界裡,不同的開發工具都有各自的「模組」定義,所以其實初學者很容易搞混這個概念。
我以 IntelliJ IDEA 為例,在新增 Module 的時候,竟然就有 5
種不同概念。但其實你可以把他統合為一個簡單的概念,那就是專案!
我們一個 Repo 裡面,可以有多個 pom.xml
檔,代表多個不同的專案。然後你可以從另一個沒有使用 Build Tool 的資料夾(也可以當成一個專案看待),加入這個 pom.xml
當成參考來源,IntelliJ IDEA 就會自動幫你合併起來,建置的時候自動幫你加入 -classpath
參數,如此而已。
IntelliJ IDEA 的 Add Modules
比較像是 VS2022 裡面的 Add Reference > Projects
功能。
IntelliJ IDEA 的 Libraries
就比較像是 VS2022 裡面的 Add Reference > Assemblies
功能。
IntelliJ IDEA 的 Facets 則是快速加入一個 Framework (框架) 功能,這個功能的背後,其實也只是幫你加入 Modules
或 Libraries
而已,有點類似 Visual Studio 2022 裡面 Manage NuGet Packages
的安裝 NuGet 套件功能,因為 .NET 在新增 NuGet 套件的時候,也能自動調整目錄結構與設定檔內容,因此功能相當類似。
之後我會陸續補充 .NET 與 Java 之間的抽象概念差異到這篇文章。
相關連結