虽然.NET (C#) 与Java 是两个不同的语言,但这两个都是OOP 物件导向程式架构,而且Java 出现的比较早,我们在.NET 里面也经常看到很多Java 的影子,所以其实有不少相似之处。这篇文章我打算整理一下最近的感受,把一些常见的技术名词与抽象概念做一些对照,帮助想要成为「斜杠青年」的朋友入门。 底下有些抽象概念,必须同时写过.NET 与Java 的人才能理解,但在「斜杠」的过程中知道这些差异是有帮助的。 语言环境 相较于「应用程式」需要一个「作业系统」才能运行。这里的「执行环境」(Runtime) 从概念上来说,就像是「程式语言」的「作业系统」一般,提供程式语言所需的一切基础建设,并且帮我们把「中介码」编译成「机械码」,让应用程式得以运行在不同的CPU 架构上。在Java 与.NET 都有这样的机制。 Java 名词 JVM (Java Virtual Machine) .NET 名词 CLR (Common Language Runtime) 软体开发套件(SDK) 开发应用程式肯定需要许多开发时常见的工具与基础建设,例如 编译器 (Compiler)、 分析工具 (Analyzer)、 函式库 (Libraries)、各种命令列工具等等,这些通称为Software Development Kit (SDK)。 Java 名词 JDK (Java Development Kit) .NET 名词 .NET SDK 程式执行环境(Runtime) 如果你只是单纯的想执行应用程式,只要安装Runtime 即可,不用安装SDK 软体开发套件: Java 名词 JRE (Java Runtime Environment) .NET 名词 .NET Runtime 我们要执行「应用程式」的时候,需要一个 宿主 (host)才能执行,例如跑.NET 的时候,你需要先将 *.cs 编译成 *.dll 档,然后透过 dotnet 命令列工具来执行。而Java 则需要先将 *.java 编译成 *.class 档,然后透过 java 命令列工具才执行。 Java.NET java myapp.classdotnet myapp.dlljava -jar myapp.jardotnet myapp.dll 共用类别库 无论Java 或.NET 都有相当丰富的内建API 可用,这些API 被 分类 成一个一个的 名称 ,在Java 被称为 package (套件),在.NET 则被称为 namespace (命名空间),你在任何一个类别中,必须先 引入 才能使用。 类型Java.NET 类别库名称JCL (Java Class Library)BCL (Base Class Library)宣告套件语法 packagenamespace 引入套件语法 importusing 类别封装 我们可以用 package 或 namespace 对类别进行分类,但是Java 与.NET 的 封装 方法就有不小的差异了。 Java.NET 一个 package 对应到一个 资料夹 ,该资料夹可以包含多个 *.java 原始档一个 namespace 可以放在专案中任意位置、宣告在任意档案,没有严格限制,因此重构时容易乱掉相同 package 下的类别可以存取在不同 *.java 原始档中的任意 private 类别相同 namespace 下的类别 不可以 存取在相同 namespace 或 *.cs 原始档中的任意 private 类别一个 *.java 原始档中,可以有多个类别,但只能有一个 public 公开类别,限制较严格一个 *.java 原始档中,可以有多个类别,也能有多个 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.classdotnet myapp.dlljava -jar myapp.jardotnet myapp.dll 中介语言(intermediary language) 写完程式之后,无论Java 或.NET 都会将原始码编译成 中介码 (Bytecode) 型态,他其实是一种 高阶的组合语言 或称 中介语言 (intermediary language)。 Java.NET BytecodeCIL bytecode或IL code CIL = Common Intermediate Language 建置程式码(Build) 如果要编译单一原始码档案,使用Java 简单很多,使用.NET 就会异常复杂,微软官方也说 不打算让你这样用! Java.NET javac myapp.java 没人这样用 如果要编译多个原始码档案(完整专案或模组),使用JDK 就显得复杂很多,而.NET 就内建MSBuild 让你使用,整件事变的异常轻松! Java.NET javac @filelist -sourcepath src -d bindotnet 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 可言:sweat_smile: 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 套件的时候,也能自动调整 目录结构 与 设定档内容,因此功能相当类似。